Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/openpyxl/packaging/relationship.py: 49%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

86 statements  

1# Copyright (c) 2010-2024 openpyxl 

2 

3import posixpath 

4from warnings import warn 

5 

6from openpyxl.descriptors import ( 

7 String, 

8 Alias, 

9 Sequence, 

10) 

11from openpyxl.descriptors.serialisable import Serialisable 

12from openpyxl.descriptors.container import ElementList 

13 

14from openpyxl.xml.constants import REL_NS, PKG_REL_NS 

15from openpyxl.xml.functions import ( 

16 Element, 

17 fromstring, 

18) 

19 

20 

21class Relationship(Serialisable): 

22 """Represents many kinds of relationships.""" 

23 

24 tagname = "Relationship" 

25 

26 Type = String() 

27 Target = String() 

28 target = Alias("Target") 

29 TargetMode = String(allow_none=True) 

30 Id = String(allow_none=True) 

31 id = Alias("Id") 

32 

33 

34 def __init__(self, 

35 Id=None, 

36 Type=None, 

37 type=None, 

38 Target=None, 

39 TargetMode=None 

40 ): 

41 """ 

42 `type` can be used as a shorthand with the default relationships namespace 

43 otherwise the `Type` must be a fully qualified URL 

44 """ 

45 if type is not None: 

46 Type = "{0}/{1}".format(REL_NS, type) 

47 self.Type = Type 

48 self.Target = Target 

49 self.TargetMode = TargetMode 

50 self.Id = Id 

51 

52 

53class RelationshipList(ElementList): 

54 

55 tagname = "Relationships" 

56 expected_type = Relationship 

57 

58 

59 def append(self, value): 

60 super().append(value) 

61 if not value.Id: 

62 value.Id = f"rId{len(self)}" 

63 

64 

65 def find(self, content_type): 

66 """ 

67 Find relationships by content-type 

68 NB. these content-types namespaced objects and different to the MIME-types 

69 in the package manifest :-( 

70 """ 

71 for r in self: 

72 if r.Type == content_type: 

73 yield r 

74 

75 

76 def get(self, key): 

77 for r in self: 

78 if r.Id == key: 

79 return r 

80 raise KeyError("Unknown relationship: {0}".format(key)) 

81 

82 

83 def to_dict(self): 

84 """Return a dictionary of relations keyed by id""" 

85 return {r.id:r for r in self} 

86 

87 

88 def to_tree(self): 

89 tree = super().to_tree() 

90 tree.set("xmlns", PKG_REL_NS) 

91 return tree 

92 

93 

94def get_rels_path(path): 

95 """ 

96 Convert relative path to absolutes that can be loaded from a zip 

97 archive. 

98 The path to be passed in is that of containing object (workbook, 

99 worksheet, etc.) 

100 """ 

101 folder, obj = posixpath.split(path) 

102 filename = posixpath.join(folder, '_rels', '{0}.rels'.format(obj)) 

103 return filename 

104 

105 

106def get_dependents(archive, filename): 

107 """ 

108 Normalise dependency file paths to absolute ones 

109 

110 Relative paths are relative to parent object 

111 """ 

112 src = archive.read(filename) 

113 node = fromstring(src) 

114 try: 

115 rels = RelationshipList.from_tree(node) 

116 except TypeError: 

117 msg = "{0} contains invalid dependency definitions".format(filename) 

118 warn(msg) 

119 rels = RelationshipList() 

120 folder = posixpath.dirname(filename) 

121 parent = posixpath.split(folder)[0] 

122 for r in rels: 

123 if r.TargetMode == "External": 

124 continue 

125 elif r.target.startswith("/"): 

126 r.target = r.target[1:] 

127 else: 

128 pth = posixpath.join(parent, r.target) 

129 r.target = posixpath.normpath(pth) 

130 return rels 

131 

132 

133def get_rel(archive, deps, id=None, cls=None): 

134 """ 

135 Get related object based on id or rel_type 

136 """ 

137 if not any([id, cls]): 

138 raise ValueError("Either the id or the content type are required") 

139 if id is not None: 

140 rel = deps.get(id) 

141 else: 

142 try: 

143 rel = next(deps.find(cls.rel_type)) 

144 except StopIteration: # no known dependency 

145 return 

146 

147 path = rel.target 

148 src = archive.read(path) 

149 tree = fromstring(src) 

150 obj = cls.from_tree(tree) 

151 

152 rels_path = get_rels_path(path) 

153 try: 

154 obj.deps = get_dependents(archive, rels_path) 

155 except KeyError: 

156 obj.deps = [] 

157 

158 return obj