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

1# Copyright (c) 2010-2022 openpyxl 

2 

3 

4import posixpath 

5 

6from openpyxl.descriptors import ( 

7 String, 

8 Set, 

9 NoneSet, 

10 Alias, 

11 Sequence, 

12) 

13from openpyxl.descriptors.serialisable import Serialisable 

14 

15from openpyxl.xml.constants import REL_NS, PKG_REL_NS 

16from openpyxl.xml.functions import ( 

17 Element, 

18 fromstring, 

19 tostring 

20) 

21 

22 

23class Relationship(Serialisable): 

24 """Represents many kinds of relationships.""" 

25 

26 tagname = "Relationship" 

27 

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") 

34 

35 

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 

53 

54 

55class RelationshipList(Serialisable): 

56 

57 tagname = "Relationships" 

58 

59 Relationship = Sequence(expected_type=Relationship) 

60 

61 

62 def __init__(self, Relationship=()): 

63 self.Relationship = Relationship 

64 

65 

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 

72 

73 

74 def __len__(self): 

75 return len(self.Relationship) 

76 

77 

78 def __bool__(self): 

79 return bool(self.Relationship) 

80 

81 

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 

91 

92 

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)) 

98 

99 

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()) 

106 

107 return tree 

108 

109 

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 

120 

121 

122from warnings import warn 

123 

124def get_dependents(archive, filename): 

125 """ 

126 Normalise dependency file paths to absolute ones 

127 

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 

149 

150 

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 

164 

165 path = rel.target 

166 src = archive.read(path) 

167 tree = fromstring(src) 

168 obj = cls.from_tree(tree) 

169 

170 rels_path = get_rels_path(path) 

171 try: 

172 obj.deps = get_dependents(archive, rels_path) 

173 except KeyError: 

174 obj.deps = [] 

175 

176 return obj