base.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. # Copyright (c) 2010-2024 openpyxl
  2. """
  3. Based on Python Cookbook 3rd Edition, 8.13
  4. http://chimera.labs.oreilly.com/books/1230000000393/ch08.html#_discussiuncion_130
  5. """
  6. import datetime
  7. import re
  8. from openpyxl import DEBUG
  9. from openpyxl.utils.datetime import from_ISO8601
  10. from .namespace import namespaced
  11. class Descriptor:
  12. def __init__(self, name=None, **kw):
  13. self.name = name
  14. for k, v in kw.items():
  15. setattr(self, k, v)
  16. def __set__(self, instance, value):
  17. instance.__dict__[self.name] = value
  18. class Typed(Descriptor):
  19. """Values must of a particular type"""
  20. expected_type = type(None)
  21. allow_none = False
  22. nested = False
  23. def __init__(self, *args, **kw):
  24. super().__init__(*args, **kw)
  25. self.__doc__ = f"Values must be of type {self.expected_type}"
  26. def __set__(self, instance, value):
  27. if not isinstance(value, self.expected_type):
  28. if (not self.allow_none
  29. or (self.allow_none and value is not None)):
  30. msg = f"{instance.__class__}.{self.name} should be {self.expected_type} but value is {type(value)}"
  31. if DEBUG:
  32. msg = f"{instance.__class__}.{self.name} should be {self.expected_type} but {value} is {type(value)}"
  33. raise TypeError(msg)
  34. super().__set__(instance, value)
  35. def __repr__(self):
  36. return self.__doc__
  37. def _convert(expected_type, value):
  38. """
  39. Check value is of or can be converted to expected type.
  40. """
  41. if not isinstance(value, expected_type):
  42. try:
  43. value = expected_type(value)
  44. except:
  45. raise TypeError('expected ' + str(expected_type))
  46. return value
  47. class Convertible(Typed):
  48. """Values must be convertible to a particular type"""
  49. def __set__(self, instance, value):
  50. if ((self.allow_none and value is not None)
  51. or not self.allow_none):
  52. value = _convert(self.expected_type, value)
  53. super().__set__(instance, value)
  54. class Max(Convertible):
  55. """Values must be less than a `max` value"""
  56. expected_type = float
  57. allow_none = False
  58. def __init__(self, **kw):
  59. if 'max' not in kw and not hasattr(self, 'max'):
  60. raise TypeError('missing max value')
  61. super().__init__(**kw)
  62. def __set__(self, instance, value):
  63. if ((self.allow_none and value is not None)
  64. or not self.allow_none):
  65. value = _convert(self.expected_type, value)
  66. if value > self.max:
  67. raise ValueError('Max value is {0}'.format(self.max))
  68. super().__set__(instance, value)
  69. class Min(Convertible):
  70. """Values must be greater than a `min` value"""
  71. expected_type = float
  72. allow_none = False
  73. def __init__(self, **kw):
  74. if 'min' not in kw and not hasattr(self, 'min'):
  75. raise TypeError('missing min value')
  76. super().__init__(**kw)
  77. def __set__(self, instance, value):
  78. if ((self.allow_none and value is not None)
  79. or not self.allow_none):
  80. value = _convert(self.expected_type, value)
  81. if value < self.min:
  82. raise ValueError('Min value is {0}'.format(self.min))
  83. super().__set__(instance, value)
  84. class MinMax(Min, Max):
  85. """Values must be greater than `min` value and less than a `max` one"""
  86. pass
  87. class Set(Descriptor):
  88. """Value can only be from a set of know values"""
  89. def __init__(self, name=None, **kw):
  90. if not 'values' in kw:
  91. raise TypeError("missing set of values")
  92. kw['values'] = set(kw['values'])
  93. super().__init__(name, **kw)
  94. self.__doc__ = "Value must be one of {0}".format(self.values)
  95. def __set__(self, instance, value):
  96. if value not in self.values:
  97. raise ValueError(self.__doc__)
  98. super().__set__(instance, value)
  99. class NoneSet(Set):
  100. """'none' will be treated as None"""
  101. def __init__(self, name=None, **kw):
  102. super().__init__(name, **kw)
  103. self.values.add(None)
  104. def __set__(self, instance, value):
  105. if value == 'none':
  106. value = None
  107. super().__set__(instance, value)
  108. class Integer(Convertible):
  109. expected_type = int
  110. class Float(Convertible):
  111. expected_type = float
  112. class Bool(Convertible):
  113. expected_type = bool
  114. def __set__(self, instance, value):
  115. if isinstance(value, str):
  116. if value in ('false', 'f', '0'):
  117. value = False
  118. super().__set__(instance, value)
  119. class String(Typed):
  120. expected_type = str
  121. class Text(String, Convertible):
  122. pass
  123. class ASCII(Typed):
  124. expected_type = bytes
  125. class Tuple(Typed):
  126. expected_type = tuple
  127. class Length(Descriptor):
  128. def __init__(self, name=None, **kw):
  129. if "length" not in kw:
  130. raise TypeError("value length must be supplied")
  131. super().__init__(**kw)
  132. def __set__(self, instance, value):
  133. if len(value) != self.length:
  134. raise ValueError("Value must be length {0}".format(self.length))
  135. super().__set__(instance, value)
  136. class Default(Typed):
  137. """
  138. When called returns an instance of the expected type.
  139. Additional default values can be passed in to the descriptor
  140. """
  141. def __init__(self, name=None, **kw):
  142. if "defaults" not in kw:
  143. kw['defaults'] = {}
  144. super().__init__(**kw)
  145. def __call__(self):
  146. return self.expected_type()
  147. class Alias(Descriptor):
  148. """
  149. Aliases can be used when either the desired attribute name is not allowed
  150. or confusing in Python (eg. "type") or a more descriptive name is desired
  151. (eg. "underline" for "u")
  152. """
  153. def __init__(self, alias):
  154. self.alias = alias
  155. def __set__(self, instance, value):
  156. setattr(instance, self.alias, value)
  157. def __get__(self, instance, cls):
  158. return getattr(instance, self.alias)
  159. class MatchPattern(Descriptor):
  160. """Values must match a regex pattern """
  161. allow_none = False
  162. def __init__(self, name=None, **kw):
  163. if 'pattern' not in kw and not hasattr(self, 'pattern'):
  164. raise TypeError('missing pattern value')
  165. super().__init__(name, **kw)
  166. self.test_pattern = re.compile(self.pattern, re.VERBOSE)
  167. def __set__(self, instance, value):
  168. if value is None and not self.allow_none:
  169. raise ValueError("Value must not be none")
  170. if ((self.allow_none and value is not None)
  171. or not self.allow_none):
  172. if not self.test_pattern.match(value):
  173. raise ValueError('Value does not match pattern {0}'.format(self.pattern))
  174. super().__set__(instance, value)
  175. class DateTime(Typed):
  176. expected_type = datetime.datetime
  177. def __set__(self, instance, value):
  178. if value is not None and isinstance(value, str):
  179. try:
  180. value = from_ISO8601(value)
  181. except ValueError:
  182. raise ValueError("Value must be ISO datetime format")
  183. super().__set__(instance, value)