processors.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. # sqlalchemy/processors.py
  2. # Copyright (C) 2010-2022 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. # Copyright (C) 2010 Gaetan de Menten gdementen@gmail.com
  5. #
  6. # This module is part of SQLAlchemy and is released under
  7. # the MIT License: https://www.opensource.org/licenses/mit-license.php
  8. """defines generic type conversion functions, as used in bind and result
  9. processors.
  10. They all share one common characteristic: None is passed through unchanged.
  11. """
  12. import codecs
  13. import datetime
  14. import re
  15. from . import util
  16. def str_to_datetime_processor_factory(regexp, type_):
  17. rmatch = regexp.match
  18. # Even on python2.6 datetime.strptime is both slower than this code
  19. # and it does not support microseconds.
  20. has_named_groups = bool(regexp.groupindex)
  21. def process(value):
  22. if value is None:
  23. return None
  24. else:
  25. try:
  26. m = rmatch(value)
  27. except TypeError as err:
  28. util.raise_(
  29. ValueError(
  30. "Couldn't parse %s string '%r' "
  31. "- value is not a string." % (type_.__name__, value)
  32. ),
  33. from_=err,
  34. )
  35. if m is None:
  36. raise ValueError(
  37. "Couldn't parse %s string: "
  38. "'%s'" % (type_.__name__, value)
  39. )
  40. if has_named_groups:
  41. groups = m.groupdict(0)
  42. return type_(
  43. **dict(
  44. list(
  45. zip(
  46. iter(groups.keys()),
  47. list(map(int, iter(groups.values()))),
  48. )
  49. )
  50. )
  51. )
  52. else:
  53. return type_(*list(map(int, m.groups(0))))
  54. return process
  55. def py_fallback():
  56. def to_unicode_processor_factory(encoding, errors=None):
  57. decoder = codecs.getdecoder(encoding)
  58. def process(value):
  59. if value is None:
  60. return None
  61. else:
  62. # decoder returns a tuple: (value, len). Simply dropping the
  63. # len part is safe: it is done that way in the normal
  64. # 'xx'.decode(encoding) code path.
  65. return decoder(value, errors)[0]
  66. return process
  67. def to_conditional_unicode_processor_factory(encoding, errors=None):
  68. decoder = codecs.getdecoder(encoding)
  69. def process(value):
  70. if value is None:
  71. return None
  72. elif isinstance(value, util.text_type):
  73. return value
  74. else:
  75. # decoder returns a tuple: (value, len). Simply dropping the
  76. # len part is safe: it is done that way in the normal
  77. # 'xx'.decode(encoding) code path.
  78. return decoder(value, errors)[0]
  79. return process
  80. def to_decimal_processor_factory(target_class, scale):
  81. fstring = "%%.%df" % scale
  82. def process(value):
  83. if value is None:
  84. return None
  85. else:
  86. return target_class(fstring % value)
  87. return process
  88. def to_float(value): # noqa
  89. if value is None:
  90. return None
  91. else:
  92. return float(value)
  93. def to_str(value): # noqa
  94. if value is None:
  95. return None
  96. else:
  97. return str(value)
  98. def int_to_boolean(value): # noqa
  99. if value is None:
  100. return None
  101. else:
  102. return bool(value)
  103. DATETIME_RE = re.compile(
  104. r"(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)(?:\.(\d+))?"
  105. )
  106. TIME_RE = re.compile(r"(\d+):(\d+):(\d+)(?:\.(\d+))?")
  107. DATE_RE = re.compile(r"(\d+)-(\d+)-(\d+)")
  108. str_to_datetime = str_to_datetime_processor_factory( # noqa
  109. DATETIME_RE, datetime.datetime
  110. )
  111. str_to_time = str_to_datetime_processor_factory( # noqa
  112. TIME_RE, datetime.time
  113. ) # noqa
  114. str_to_date = str_to_datetime_processor_factory( # noqa
  115. DATE_RE, datetime.date
  116. ) # noqa
  117. return locals()
  118. try:
  119. from sqlalchemy.cprocessors import DecimalResultProcessor # noqa
  120. from sqlalchemy.cprocessors import int_to_boolean # noqa
  121. from sqlalchemy.cprocessors import str_to_date # noqa
  122. from sqlalchemy.cprocessors import str_to_datetime # noqa
  123. from sqlalchemy.cprocessors import str_to_time # noqa
  124. from sqlalchemy.cprocessors import to_float # noqa
  125. from sqlalchemy.cprocessors import to_str # noqa
  126. from sqlalchemy.cprocessors import UnicodeResultProcessor # noqa
  127. def to_unicode_processor_factory(encoding, errors=None):
  128. if errors is not None:
  129. return UnicodeResultProcessor(encoding, errors).process
  130. else:
  131. return UnicodeResultProcessor(encoding).process
  132. def to_conditional_unicode_processor_factory(encoding, errors=None):
  133. if errors is not None:
  134. return UnicodeResultProcessor(encoding, errors).conditional_process
  135. else:
  136. return UnicodeResultProcessor(encoding).conditional_process
  137. def to_decimal_processor_factory(target_class, scale):
  138. # Note that the scale argument is not taken into account for integer
  139. # values in the C implementation while it is in the Python one.
  140. # For example, the Python implementation might return
  141. # Decimal('5.00000') whereas the C implementation will
  142. # return Decimal('5'). These are equivalent of course.
  143. return DecimalResultProcessor(target_class, "%%.%df" % scale).process
  144. except ImportError:
  145. globals().update(py_fallback())