Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/openpyxl/packaging/relationship.py: 26%
94 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1# Copyright (c) 2010-2022 openpyxl
4import posixpath
6from openpyxl.descriptors import (
7 String,
8 Set,
9 NoneSet,
10 Alias,
11 Sequence,
12)
13from openpyxl.descriptors.serialisable import Serialisable
15from openpyxl.xml.constants import REL_NS, PKG_REL_NS
16from openpyxl.xml.functions import (
17 Element,
18 fromstring,
19 tostring
20)
23class Relationship(Serialisable):
24 """Represents many kinds of relationships."""
26 tagname = "Relationship"
28 Type = String()
29 Target = String()
30 target = Alias("Target")
31 TargetMode = String(allow_none=True)
32 Id = String(allow_none=True)
33 id = Alias("Id")
36 def __init__(self,
37 Id=None,
38 Type=None,
39 type=None,
40 Target=None,
41 TargetMode=None
42 ):
43 """
44 `type` can be used as a shorthand with the default relationships namespace
45 otherwise the `Type` must be a fully qualified URL
46 """
47 if type is not None:
48 Type = "{0}/{1}".format(REL_NS, type)
49 self.Type = Type
50 self.Target = Target
51 self.TargetMode = TargetMode
52 self.Id = Id
55class RelationshipList(Serialisable):
57 tagname = "Relationships"
59 Relationship = Sequence(expected_type=Relationship)
62 def __init__(self, Relationship=()):
63 self.Relationship = Relationship
66 def append(self, value):
67 values = self.Relationship[:]
68 values.append(value)
69 if not value.Id:
70 value.Id = "rId{0}".format((len(values)))
71 self.Relationship = values
74 def __len__(self):
75 return len(self.Relationship)
78 def __bool__(self):
79 return bool(self.Relationship)
82 def find(self, content_type):
83 """
84 Find relationships by content-type
85 NB. these content-types namespaced objects and different to the MIME-types
86 in the package manifest :-(
87 """
88 for r in self.Relationship:
89 if r.Type == content_type:
90 yield r
93 def __getitem__(self, key):
94 for r in self.Relationship:
95 if r.Id == key:
96 return r
97 raise KeyError("Unknown relationship: {0}".format(key))
100 def to_tree(self):
101 tree = Element("Relationships", xmlns=PKG_REL_NS)
102 for idx, rel in enumerate(self.Relationship, 1):
103 if not rel.Id:
104 rel.Id = "rId{0}".format(idx)
105 tree.append(rel.to_tree())
107 return tree
110def get_rels_path(path):
111 """
112 Convert relative path to absolutes that can be loaded from a zip
113 archive.
114 The path to be passed in is that of containing object (workbook,
115 worksheet, etc.)
116 """
117 folder, obj = posixpath.split(path)
118 filename = posixpath.join(folder, '_rels', '{0}.rels'.format(obj))
119 return filename
122from warnings import warn
124def get_dependents(archive, filename):
125 """
126 Normalise dependency file paths to absolute ones
128 Relative paths are relative to parent object
129 """
130 src = archive.read(filename)
131 node = fromstring(src)
132 try:
133 rels = RelationshipList.from_tree(node)
134 except TypeError:
135 msg = "{0} contains invalid dependency definitions".format(filename)
136 warn(msg)
137 rels = RelationshipList()
138 folder = posixpath.dirname(filename)
139 parent = posixpath.split(folder)[0]
140 for r in rels.Relationship:
141 if r.TargetMode == "External":
142 continue
143 elif r.target.startswith("/"):
144 r.target = r.target[1:]
145 else:
146 pth = posixpath.join(parent, r.target)
147 r.target = posixpath.normpath(pth)
148 return rels
151def get_rel(archive, deps, id=None, cls=None):
152 """
153 Get related object based on id or rel_type
154 """
155 if not any([id, cls]):
156 raise ValueError("Either the id or the content type are required")
157 if id is not None:
158 rel = deps[id]
159 else:
160 try:
161 rel = next(deps.find(cls.rel_type))
162 except StopIteration: # no known dependency
163 return
165 path = rel.target
166 src = archive.read(path)
167 tree = fromstring(src)
168 obj = cls.from_tree(tree)
170 rels_path = get_rels_path(path)
171 try:
172 obj.deps = get_dependents(archive, rels_path)
173 except KeyError:
174 obj.deps = []
176 return obj