aboutsummaryrefslogtreecommitdiff
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