aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/openpyxl/packaging/relationship.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/openpyxl/packaging/relationship.py')
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/packaging/relationship.py158
1 files changed, 158 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/packaging/relationship.py b/.venv/lib/python3.12/site-packages/openpyxl/packaging/relationship.py
new file mode 100644
index 00000000..4318282d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/packaging/relationship.py
@@ -0,0 +1,158 @@
+# Copyright (c) 2010-2024 openpyxl
+
+import posixpath
+from warnings import warn
+
+from openpyxl.descriptors import (
+ String,
+ Alias,
+ Sequence,
+)
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors.container import ElementList
+
+from openpyxl.xml.constants import REL_NS, PKG_REL_NS
+from openpyxl.xml.functions import (
+ Element,
+ fromstring,
+)
+
+
+class Relationship(Serialisable):
+ """Represents many kinds of relationships."""
+
+ tagname = "Relationship"
+
+ Type = String()
+ Target = String()
+ target = Alias("Target")
+ TargetMode = String(allow_none=True)
+ Id = String(allow_none=True)
+ id = Alias("Id")
+
+
+ def __init__(self,
+ Id=None,
+ Type=None,
+ type=None,
+ Target=None,
+ TargetMode=None
+ ):
+ """
+ `type` can be used as a shorthand with the default relationships namespace
+ otherwise the `Type` must be a fully qualified URL
+ """
+ if type is not None:
+ Type = "{0}/{1}".format(REL_NS, type)
+ self.Type = Type
+ self.Target = Target
+ self.TargetMode = TargetMode
+ self.Id = Id
+
+
+class RelationshipList(ElementList):
+
+ tagname = "Relationships"
+ expected_type = Relationship
+
+
+ def append(self, value):
+ super().append(value)
+ if not value.Id:
+ value.Id = f"rId{len(self)}"
+
+
+ def find(self, content_type):
+ """
+ Find relationships by content-type
+ NB. these content-types namespaced objects and different to the MIME-types
+ in the package manifest :-(
+ """
+ for r in self:
+ if r.Type == content_type:
+ yield r
+
+
+ def get(self, key):
+ for r in self:
+ if r.Id == key:
+ return r
+ raise KeyError("Unknown relationship: {0}".format(key))
+
+
+ def to_dict(self):
+ """Return a dictionary of relations keyed by id"""
+ return {r.id:r for r in self}
+
+
+ def to_tree(self):
+ tree = super().to_tree()
+ tree.set("xmlns", PKG_REL_NS)
+ return tree
+
+
+def get_rels_path(path):
+ """
+ Convert relative path to absolutes that can be loaded from a zip
+ archive.
+ The path to be passed in is that of containing object (workbook,
+ worksheet, etc.)
+ """
+ folder, obj = posixpath.split(path)
+ filename = posixpath.join(folder, '_rels', '{0}.rels'.format(obj))
+ return filename
+
+
+def get_dependents(archive, filename):
+ """
+ Normalise dependency file paths to absolute ones
+
+ Relative paths are relative to parent object
+ """
+ src = archive.read(filename)
+ node = fromstring(src)
+ try:
+ rels = RelationshipList.from_tree(node)
+ except TypeError:
+ msg = "{0} contains invalid dependency definitions".format(filename)
+ warn(msg)
+ rels = RelationshipList()
+ folder = posixpath.dirname(filename)
+ parent = posixpath.split(folder)[0]
+ for r in rels:
+ if r.TargetMode == "External":
+ continue
+ elif r.target.startswith("/"):
+ r.target = r.target[1:]
+ else:
+ pth = posixpath.join(parent, r.target)
+ r.target = posixpath.normpath(pth)
+ return rels
+
+
+def get_rel(archive, deps, id=None, cls=None):
+ """
+ Get related object based on id or rel_type
+ """
+ if not any([id, cls]):
+ raise ValueError("Either the id or the content type are required")
+ if id is not None:
+ rel = deps.get(id)
+ else:
+ try:
+ rel = next(deps.find(cls.rel_type))
+ except StopIteration: # no known dependency
+ return
+
+ path = rel.target
+ src = archive.read(path)
+ tree = fromstring(src)
+ obj = cls.from_tree(tree)
+
+ rels_path = get_rels_path(path)
+ try:
+ obj.deps = get_dependents(archive, rels_path)
+ except KeyError:
+ obj.deps = []
+
+ return obj