Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/openpyxl/packaging/relationship.py: 53%
94 statements
« prev ^ index » next coverage.py v7.3.3, created at 2023-12-20 06:34 +0000
« prev ^ index » next coverage.py v7.3.3, created at 2023-12-20 06:34 +0000
1# Copyright (c) 2010-2023 openpyxl
3import posixpath
4from warnings import warn
6from openpyxl.descriptors import (
7 String,
8 Alias,
9 Sequence,
10)
11from openpyxl.descriptors.serialisable import Serialisable
13from openpyxl.xml.constants import REL_NS, PKG_REL_NS
14from openpyxl.xml.functions import (
15 Element,
16 fromstring,
17)
20class Relationship(Serialisable):
21 """Represents many kinds of relationships."""
23 tagname = "Relationship"
25 Type = String()
26 Target = String()
27 target = Alias("Target")
28 TargetMode = String(allow_none=True)
29 Id = String(allow_none=True)
30 id = Alias("Id")
33 def __init__(self,
34 Id=None,
35 Type=None,
36 type=None,
37 Target=None,
38 TargetMode=None
39 ):
40 """
41 `type` can be used as a shorthand with the default relationships namespace
42 otherwise the `Type` must be a fully qualified URL
43 """
44 if type is not None:
45 Type = "{0}/{1}".format(REL_NS, type)
46 self.Type = Type
47 self.Target = Target
48 self.TargetMode = TargetMode
49 self.Id = Id
52class RelationshipList(Serialisable):
54 tagname = "Relationships"
56 Relationship = Sequence(expected_type=Relationship)
59 def __init__(self, Relationship=()):
60 self.Relationship = Relationship
63 def append(self, value):
64 values = self.Relationship[:]
65 values.append(value)
66 if not value.Id:
67 value.Id = "rId{0}".format((len(values)))
68 self.Relationship = values
71 def __len__(self):
72 return len(self.Relationship)
75 def __bool__(self):
76 return bool(self.Relationship)
79 def find(self, content_type):
80 """
81 Find relationships by content-type
82 NB. these content-types namespaced objects and different to the MIME-types
83 in the package manifest :-(
84 """
85 for r in self.Relationship:
86 if r.Type == content_type:
87 yield r
90 def __getitem__(self, key):
91 for r in self.Relationship:
92 if r.Id == key:
93 return r
94 raise KeyError("Unknown relationship: {0}".format(key))
97 def to_tree(self):
98 tree = Element("Relationships", xmlns=PKG_REL_NS)
99 for idx, rel in enumerate(self.Relationship, 1):
100 if not rel.Id:
101 rel.Id = "rId{0}".format(idx)
102 tree.append(rel.to_tree())
104 return tree
107def get_rels_path(path):
108 """
109 Convert relative path to absolutes that can be loaded from a zip
110 archive.
111 The path to be passed in is that of containing object (workbook,
112 worksheet, etc.)
113 """
114 folder, obj = posixpath.split(path)
115 filename = posixpath.join(folder, '_rels', '{0}.rels'.format(obj))
116 return filename
119def get_dependents(archive, filename):
120 """
121 Normalise dependency file paths to absolute ones
123 Relative paths are relative to parent object
124 """
125 src = archive.read(filename)
126 node = fromstring(src)
127 try:
128 rels = RelationshipList.from_tree(node)
129 except TypeError:
130 msg = "{0} contains invalid dependency definitions".format(filename)
131 warn(msg)
132 rels = RelationshipList()
133 folder = posixpath.dirname(filename)
134 parent = posixpath.split(folder)[0]
135 for r in rels.Relationship:
136 if r.TargetMode == "External":
137 continue
138 elif r.target.startswith("/"):
139 r.target = r.target[1:]
140 else:
141 pth = posixpath.join(parent, r.target)
142 r.target = posixpath.normpath(pth)
143 return rels
146def get_rel(archive, deps, id=None, cls=None):
147 """
148 Get related object based on id or rel_type
149 """
150 if not any([id, cls]):
151 raise ValueError("Either the id or the content type are required")
152 if id is not None:
153 rel = deps[id]
154 else:
155 try:
156 rel = next(deps.find(cls.rel_type))
157 except StopIteration: # no known dependency
158 return
160 path = rel.target
161 src = archive.read(path)
162 tree = fromstring(src)
163 obj = cls.from_tree(tree)
165 rels_path = get_rels_path(path)
166 try:
167 obj.deps = get_dependents(archive, rels_path)
168 except KeyError:
169 obj.deps = []
171 return obj