Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/openpyxl/packaging/manifest.py: 39%

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

4File manifest 

5""" 

6from mimetypes import MimeTypes 

7import os.path 

8 

9from openpyxl.descriptors.serialisable import Serialisable 

10from openpyxl.descriptors import String, Sequence 

11from openpyxl.xml.functions import fromstring 

12from openpyxl.xml.constants import ( 

13 ARC_CORE, 

14 ARC_CONTENT_TYPES, 

15 ARC_WORKBOOK, 

16 ARC_APP, 

17 ARC_THEME, 

18 ARC_STYLE, 

19 ARC_SHARED_STRINGS, 

20 EXTERNAL_LINK, 

21 THEME_TYPE, 

22 STYLES_TYPE, 

23 XLSX, 

24 XLSM, 

25 XLTM, 

26 XLTX, 

27 WORKSHEET_TYPE, 

28 COMMENTS_TYPE, 

29 SHARED_STRINGS, 

30 DRAWING_TYPE, 

31 CHART_TYPE, 

32 CHARTSHAPE_TYPE, 

33 CHARTSHEET_TYPE, 

34 CONTYPES_NS, 

35 ACTIVEX, 

36 CTRL, 

37 VBA, 

38) 

39from openpyxl.xml.functions import tostring 

40 

41# initialise mime-types 

42mimetypes = MimeTypes() 

43mimetypes.add_type('application/xml', ".xml") 

44mimetypes.add_type('application/vnd.openxmlformats-package.relationships+xml', ".rels") 

45mimetypes.add_type("application/vnd.ms-office.vbaProject", ".bin") 

46mimetypes.add_type("application/vnd.openxmlformats-officedocument.vmlDrawing", ".vml") 

47mimetypes.add_type("image/x-emf", ".emf") 

48 

49 

50class FileExtension(Serialisable): 

51 

52 tagname = "Default" 

53 

54 Extension = String() 

55 ContentType = String() 

56 

57 def __init__(self, Extension, ContentType): 

58 self.Extension = Extension 

59 self.ContentType = ContentType 

60 

61 

62class Override(Serialisable): 

63 

64 tagname = "Override" 

65 

66 PartName = String() 

67 ContentType = String() 

68 

69 def __init__(self, PartName, ContentType): 

70 self.PartName = PartName 

71 self.ContentType = ContentType 

72 

73 

74DEFAULT_TYPES = [ 

75 FileExtension("rels", "application/vnd.openxmlformats-package.relationships+xml"), 

76 FileExtension("xml", "application/xml"), 

77] 

78 

79DEFAULT_OVERRIDE = [ 

80 Override("/" + ARC_STYLE, STYLES_TYPE), # Styles 

81 Override("/" + ARC_THEME, THEME_TYPE), # Theme 

82 Override("/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml"), 

83 Override("/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml") 

84] 

85 

86 

87class Manifest(Serialisable): 

88 

89 tagname = "Types" 

90 

91 Default = Sequence(expected_type=FileExtension, unique=True) 

92 Override = Sequence(expected_type=Override, unique=True) 

93 path = "[Content_Types].xml" 

94 

95 __elements__ = ("Default", "Override") 

96 

97 def __init__(self, 

98 Default=(), 

99 Override=(), 

100 ): 

101 if not Default: 

102 Default = DEFAULT_TYPES 

103 self.Default = Default 

104 if not Override: 

105 Override = DEFAULT_OVERRIDE 

106 self.Override = Override 

107 

108 

109 @property 

110 def filenames(self): 

111 return [part.PartName for part in self.Override] 

112 

113 

114 @property 

115 def extensions(self): 

116 """ 

117 Map content types to file extensions 

118 Skip parts without extensions 

119 """ 

120 exts = {os.path.splitext(part.PartName)[-1] for part in self.Override} 

121 return [(ext[1:], mimetypes.types_map[True][ext]) for ext in sorted(exts) if ext] 

122 

123 

124 def to_tree(self): 

125 """ 

126 Custom serialisation method to allow setting a default namespace 

127 """ 

128 defaults = [t.Extension for t in self.Default] 

129 for ext, mime in self.extensions: 

130 if ext not in defaults: 

131 mime = FileExtension(ext, mime) 

132 self.Default.append(mime) 

133 tree = super(Manifest, self).to_tree() 

134 tree.set("xmlns", CONTYPES_NS) 

135 return tree 

136 

137 

138 def __contains__(self, content_type): 

139 """ 

140 Check whether a particular content type is contained 

141 """ 

142 for t in self.Override: 

143 if t.ContentType == content_type: 

144 return True 

145 

146 

147 def find(self, content_type): 

148 """ 

149 Find specific content-type 

150 """ 

151 try: 

152 return next(self.findall(content_type)) 

153 except StopIteration: 

154 return 

155 

156 

157 def findall(self, content_type): 

158 """ 

159 Find all elements of a specific content-type 

160 """ 

161 for t in self.Override: 

162 if t.ContentType == content_type: 

163 yield t 

164 

165 

166 def append(self, obj): 

167 """ 

168 Add content object to the package manifest 

169 # needs a contract... 

170 """ 

171 ct = Override(PartName=obj.path, ContentType=obj.mime_type) 

172 self.Override.append(ct) 

173 

174 

175 def _write(self, archive, workbook): 

176 """ 

177 Write manifest to the archive 

178 """ 

179 self.append(workbook) 

180 self._write_vba(workbook) 

181 self._register_mimetypes(filenames=archive.namelist()) 

182 archive.writestr(self.path, tostring(self.to_tree())) 

183 

184 

185 def _register_mimetypes(self, filenames): 

186 """ 

187 Make sure that the mime type for all file extensions is registered 

188 """ 

189 for fn in filenames: 

190 ext = os.path.splitext(fn)[-1] 

191 if not ext: 

192 continue 

193 mime = mimetypes.types_map[True][ext] 

194 fe = FileExtension(ext[1:], mime) 

195 self.Default.append(fe) 

196 

197 

198 def _write_vba(self, workbook): 

199 """ 

200 Add content types from cached workbook when keeping VBA 

201 """ 

202 if workbook.vba_archive: 

203 node = fromstring(workbook.vba_archive.read(ARC_CONTENT_TYPES)) 

204 mf = Manifest.from_tree(node) 

205 filenames = self.filenames 

206 for override in mf.Override: 

207 if override.PartName not in (ACTIVEX, CTRL, VBA): 

208 continue 

209 if override.PartName not in filenames: 

210 self.Override.append(override)