about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/openpyxl/descriptors/serialisable.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/openpyxl/descriptors/serialisable.py')
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/descriptors/serialisable.py240
1 files changed, 240 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/descriptors/serialisable.py b/.venv/lib/python3.12/site-packages/openpyxl/descriptors/serialisable.py
new file mode 100644
index 00000000..1bc9ef0d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/descriptors/serialisable.py
@@ -0,0 +1,240 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from copy import copy
+from keyword import kwlist
+KEYWORDS = frozenset(kwlist)
+
+from . import Descriptor
+from . import MetaSerialisable
+from .sequence import (
+    Sequence,
+    NestedSequence,
+    MultiSequencePart,
+)
+from .namespace import namespaced
+
+from openpyxl.compat import safe_string
+from openpyxl.xml.functions import (
+    Element,
+    localname,
+)
+
+seq_types = (list, tuple)
+
+class Serialisable(metaclass=MetaSerialisable):
+    """
+    Objects can serialise to XML their attributes and child objects.
+    The following class attributes are created by the metaclass at runtime:
+    __attrs__ = attributes
+    __nested__ = single-valued child treated as an attribute
+    __elements__ = child elements
+    """
+
+    __attrs__ = None
+    __nested__ = None
+    __elements__ = None
+    __namespaced__ = None
+
+    idx_base = 0
+
+    @property
+    def tagname(self):
+        raise(NotImplementedError)
+
+    namespace = None
+
+    @classmethod
+    def from_tree(cls, node):
+        """
+        Create object from XML
+        """
+        # strip known namespaces from attributes
+        attrib = dict(node.attrib)
+        for key, ns in cls.__namespaced__:
+            if ns in attrib:
+                attrib[key] = attrib[ns]
+                del attrib[ns]
+
+        # strip attributes with unknown namespaces
+        for key in list(attrib):
+            if key.startswith('{'):
+                del attrib[key]
+            elif key in KEYWORDS:
+                attrib["_" + key] = attrib[key]
+                del attrib[key]
+            elif "-" in key:
+                n = key.replace("-", "_")
+                attrib[n] = attrib[key]
+                del attrib[key]
+
+        if node.text and "attr_text" in cls.__attrs__:
+            attrib["attr_text"] = node.text
+
+        for el in node:
+            tag = localname(el)
+            if tag in KEYWORDS:
+                tag = "_" + tag
+            desc = getattr(cls, tag, None)
+            if desc is None or isinstance(desc, property):
+                continue
+
+            if hasattr(desc, 'from_tree'):
+                #descriptor manages conversion
+                obj = desc.from_tree(el)
+            else:
+                if hasattr(desc.expected_type, "from_tree"):
+                    #complex type
+                    obj = desc.expected_type.from_tree(el)
+                else:
+                    #primitive
+                    obj = el.text
+
+            if isinstance(desc, NestedSequence):
+                attrib[tag] = obj
+            elif isinstance(desc, Sequence):
+                attrib.setdefault(tag, [])
+                attrib[tag].append(obj)
+            elif isinstance(desc, MultiSequencePart):
+                attrib.setdefault(desc.store, [])
+                attrib[desc.store].append(obj)
+            else:
+                attrib[tag] = obj
+
+        return cls(**attrib)
+
+
+    def to_tree(self, tagname=None, idx=None, namespace=None):
+
+        if tagname is None:
+            tagname = self.tagname
+
+        # keywords have to be masked
+        if tagname.startswith("_"):
+            tagname = tagname[1:]
+
+        tagname = namespaced(self, tagname, namespace)
+        namespace = getattr(self, "namespace", namespace)
+
+        attrs = dict(self)
+        for key, ns in self.__namespaced__:
+            if key in attrs:
+                attrs[ns] = attrs[key]
+                del attrs[key]
+
+        el = Element(tagname, attrs)
+        if "attr_text" in self.__attrs__:
+            el.text = safe_string(getattr(self, "attr_text"))
+
+        for child_tag in self.__elements__:
+            desc = getattr(self.__class__, child_tag, None)
+            obj = getattr(self, child_tag)
+            if hasattr(desc, "namespace") and hasattr(obj, 'namespace'):
+                obj.namespace = desc.namespace
+
+            if isinstance(obj, seq_types):
+                if isinstance(desc, NestedSequence):
+                    # wrap sequence in container
+                    if not obj:
+                        continue
+                    nodes = [desc.to_tree(child_tag, obj, namespace)]
+                elif isinstance(desc, Sequence):
+                    # sequence
+                    desc.idx_base = self.idx_base
+                    nodes = (desc.to_tree(child_tag, obj, namespace))
+                else: # property
+                    nodes = (v.to_tree(child_tag, namespace) for v in obj)
+                for node in nodes:
+                    el.append(node)
+            else:
+                if child_tag in self.__nested__:
+                    node = desc.to_tree(child_tag, obj, namespace)
+                elif obj is None:
+                    continue
+                else:
+                    node = obj.to_tree(child_tag)
+                if node is not None:
+                    el.append(node)
+        return el
+
+
+    def __iter__(self):
+        for attr in self.__attrs__:
+            value = getattr(self, attr)
+            if attr.startswith("_"):
+                attr = attr[1:]
+            elif attr != "attr_text" and "_" in attr:
+                desc = getattr(self.__class__, attr)
+                if getattr(desc, "hyphenated", False):
+                    attr = attr.replace("_", "-")
+            if attr != "attr_text" and value is not None:
+                yield attr, safe_string(value)
+
+
+    def __eq__(self, other):
+        if not self.__class__ == other.__class__:
+            return False
+        elif not dict(self) == dict(other):
+            return False
+        for el in self.__elements__:
+            if getattr(self, el) != getattr(other, el):
+                return False
+        return True
+
+
+    def __ne__(self, other):
+        return not self == other
+
+
+    def __repr__(self):
+        s = u"<{0}.{1} object>\nParameters:".format(
+            self.__module__,
+            self.__class__.__name__
+        )
+        args = []
+        for k in self.__attrs__ + self.__elements__:
+            v = getattr(self, k)
+            if isinstance(v, Descriptor):
+                v = None
+            args.append(u"{0}={1}".format(k, repr(v)))
+        args = u", ".join(args)
+
+        return u"\n".join([s, args])
+
+
+    def __hash__(self):
+        fields = []
+        for attr in self.__attrs__ + self.__elements__:
+            val = getattr(self, attr)
+            if isinstance(val, list):
+                val = tuple(val)
+            fields.append(val)
+
+        return hash(tuple(fields))
+
+
+    def __add__(self, other):
+        if type(self) != type(other):
+            raise TypeError("Cannot combine instances of different types")
+        vals = {}
+        for attr in self.__attrs__:
+            vals[attr] = getattr(self, attr) or getattr(other, attr)
+        for el in self.__elements__:
+            a = getattr(self, el)
+            b = getattr(other, el)
+            if a and b:
+                vals[el] = a + b
+            else:
+                vals[el] = a or b
+        return self.__class__(**vals)
+
+
+    def __copy__(self):
+        # serialise to xml and back to avoid shallow copies
+        xml = self.to_tree(tagname="dummy")
+        cp = self.__class__.from_tree(xml)
+        # copy any non-persisted attributed
+        for k in self.__dict__:
+            if k not in self.__attrs__ + self.__elements__:
+                v = copy(getattr(self, k))
+                setattr(cp, k, v)
+        return cp