about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/openpyxl/descriptors/base.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/openpyxl/descriptors/base.py')
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/descriptors/base.py272
1 files changed, 272 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/descriptors/base.py b/.venv/lib/python3.12/site-packages/openpyxl/descriptors/base.py
new file mode 100644
index 00000000..f1e86ed3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/descriptors/base.py
@@ -0,0 +1,272 @@
+# Copyright (c) 2010-2024 openpyxl
+
+
+"""
+Based on Python Cookbook 3rd Edition, 8.13
+http://chimera.labs.oreilly.com/books/1230000000393/ch08.html#_discussiuncion_130
+"""
+
+import datetime
+import re
+
+from openpyxl import DEBUG
+from openpyxl.utils.datetime import from_ISO8601
+
+from .namespace import namespaced
+
+class Descriptor:
+
+    def __init__(self, name=None, **kw):
+        self.name = name
+        for k, v in kw.items():
+            setattr(self, k, v)
+
+    def __set__(self, instance, value):
+        instance.__dict__[self.name] = value
+
+
+class Typed(Descriptor):
+    """Values must of a particular type"""
+
+    expected_type = type(None)
+    allow_none = False
+    nested = False
+
+    def __init__(self, *args, **kw):
+        super().__init__(*args, **kw)
+        self.__doc__ = f"Values must be of type {self.expected_type}"
+
+    def __set__(self, instance, value):
+        if not isinstance(value, self.expected_type):
+            if (not self.allow_none
+                or (self.allow_none and value is not None)):
+                msg = f"{instance.__class__}.{self.name} should be {self.expected_type} but value is {type(value)}"
+                if DEBUG:
+                    msg = f"{instance.__class__}.{self.name} should be {self.expected_type} but {value} is {type(value)}"
+                raise TypeError(msg)
+        super().__set__(instance, value)
+
+    def __repr__(self):
+        return  self.__doc__
+
+
+def _convert(expected_type, value):
+    """
+    Check value is of or can be converted to expected type.
+    """
+    if not isinstance(value, expected_type):
+        try:
+            value = expected_type(value)
+        except:
+            raise TypeError('expected ' + str(expected_type))
+    return value
+
+
+class Convertible(Typed):
+    """Values must be convertible to a particular type"""
+
+    def __set__(self, instance, value):
+        if ((self.allow_none and value is not None)
+            or not self.allow_none):
+            value = _convert(self.expected_type, value)
+        super().__set__(instance, value)
+
+
+class Max(Convertible):
+    """Values must be less than a `max` value"""
+
+    expected_type = float
+    allow_none = False
+
+    def __init__(self, **kw):
+        if 'max' not in kw and not hasattr(self, 'max'):
+            raise TypeError('missing max value')
+        super().__init__(**kw)
+
+    def __set__(self, instance, value):
+        if ((self.allow_none and value is not None)
+            or not self.allow_none):
+            value = _convert(self.expected_type, value)
+            if value > self.max:
+                raise ValueError('Max value is {0}'.format(self.max))
+        super().__set__(instance, value)
+
+
+class Min(Convertible):
+    """Values must be greater than a `min` value"""
+
+    expected_type = float
+    allow_none = False
+
+    def __init__(self, **kw):
+        if 'min' not in kw and not hasattr(self, 'min'):
+            raise TypeError('missing min value')
+        super().__init__(**kw)
+
+    def __set__(self, instance, value):
+        if ((self.allow_none and value is not None)
+            or not self.allow_none):
+            value = _convert(self.expected_type, value)
+            if value < self.min:
+                raise ValueError('Min value is {0}'.format(self.min))
+        super().__set__(instance, value)
+
+
+class MinMax(Min, Max):
+    """Values must be greater than `min` value and less than a `max` one"""
+    pass
+
+
+class Set(Descriptor):
+    """Value can only be from a set of know values"""
+
+    def __init__(self, name=None, **kw):
+        if not 'values' in kw:
+            raise TypeError("missing set of values")
+        kw['values'] = set(kw['values'])
+        super().__init__(name, **kw)
+        self.__doc__ = "Value must be one of {0}".format(self.values)
+
+    def __set__(self, instance, value):
+        if value not in self.values:
+            raise ValueError(self.__doc__)
+        super().__set__(instance, value)
+
+
+class NoneSet(Set):
+
+    """'none' will be treated as None"""
+
+    def __init__(self, name=None, **kw):
+        super().__init__(name, **kw)
+        self.values.add(None)
+
+    def __set__(self, instance, value):
+        if value == 'none':
+            value = None
+        super().__set__(instance, value)
+
+
+class Integer(Convertible):
+
+    expected_type = int
+
+
+class Float(Convertible):
+
+    expected_type = float
+
+
+class Bool(Convertible):
+
+    expected_type = bool
+
+    def __set__(self, instance, value):
+        if isinstance(value, str):
+            if value in ('false', 'f', '0'):
+                value = False
+        super().__set__(instance, value)
+
+
+class String(Typed):
+
+    expected_type = str
+
+
+class Text(String, Convertible):
+
+    pass
+
+
+class ASCII(Typed):
+
+    expected_type = bytes
+
+
+class Tuple(Typed):
+
+    expected_type = tuple
+
+
+class Length(Descriptor):
+
+    def __init__(self, name=None, **kw):
+        if "length" not in kw:
+            raise TypeError("value length must be supplied")
+        super().__init__(**kw)
+
+
+    def __set__(self, instance, value):
+        if len(value) != self.length:
+            raise ValueError("Value must be length {0}".format(self.length))
+        super().__set__(instance, value)
+
+
+class Default(Typed):
+    """
+    When called returns an instance of the expected type.
+    Additional default values can be passed in to the descriptor
+    """
+
+    def __init__(self, name=None, **kw):
+        if "defaults" not in kw:
+            kw['defaults'] = {}
+        super().__init__(**kw)
+
+    def __call__(self):
+        return self.expected_type()
+
+
+class Alias(Descriptor):
+    """
+    Aliases can be used when either the desired attribute name is not allowed
+    or confusing in Python (eg. "type") or a more descriptive name is desired
+    (eg. "underline" for "u")
+    """
+
+    def __init__(self, alias):
+        self.alias = alias
+
+    def __set__(self, instance, value):
+        setattr(instance, self.alias, value)
+
+    def __get__(self, instance, cls):
+        return getattr(instance, self.alias)
+
+
+class MatchPattern(Descriptor):
+    """Values must match a regex pattern """
+    allow_none = False
+
+    def __init__(self, name=None, **kw):
+        if 'pattern' not in kw and not hasattr(self, 'pattern'):
+            raise TypeError('missing pattern value')
+
+        super().__init__(name, **kw)
+        self.test_pattern = re.compile(self.pattern, re.VERBOSE)
+
+
+    def __set__(self, instance, value):
+
+        if value is None and not self.allow_none:
+            raise ValueError("Value must not be none")
+
+        if ((self.allow_none and value is not None)
+            or not self.allow_none):
+            if not self.test_pattern.match(value):
+                raise ValueError('Value does not match pattern {0}'.format(self.pattern))
+
+        super().__set__(instance, value)
+
+
+class DateTime(Typed):
+
+    expected_type = datetime.datetime
+
+    def __set__(self, instance, value):
+        if value is not None and isinstance(value, str):
+            try:
+                value = from_ISO8601(value)
+            except ValueError:
+                raise ValueError("Value must be ISO datetime format")
+        super().__set__(instance, value)