Coverage for gws-app/gws/lib/mime/__init__.py: 96%

70 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-05 13:35 +0200

1"""MIME types. 

2 

3Defines constants for the MIME types used in the application (``PNG``, ``JSON``, ``GML3`` 

4and so on) and functions to normalize MIME types and to map them to and from file extensions. 

5 

6``get`` accepts a MIME type, a content type with parameters, a known alias (e.g. 

7``application/vnd.ogc.gml`` or ``image/jpg``) or a file extension, and returns a normalized 

8MIME type. Lookups that are not covered by the built-in tables fall back to the standard 

9``mimetypes`` module. 

10 

11Example:: 

12 

13 gws.lib.mime.get('image/jpg') # 'image/jpeg' 

14 gws.lib.mime.get('png') # 'image/png' 

15 gws.lib.mime.for_path('/data/report.pdf') # 'application/pdf' 

16 gws.lib.mime.extension_for(gws.lib.mime.JPEG) # 'jpeg' 

17""" 

18 

19from typing import Optional 

20 

21import mimetypes 

22 

23BIN = 'application/octet-stream' 

24CSS = 'text/css' 

25CSV = 'text/csv' 

26DOC = 'application/msword' 

27GEOJSON = 'application/geo+json' 

28GIF = 'image/gif' 

29GML = 'application/gml+xml' 

30GML2 = 'application/gml+xml;version=2' 

31GML3 = 'application/gml+xml;version=3' 

32GZIP = 'application/gzip' 

33HTML = 'text/html' 

34JPEG = 'image/jpeg' 

35JS = 'application/javascript' 

36JSON = 'application/json' 

37KML = 'application/vnd.google-earth.kml+xml' 

38PDF = 'application/pdf' 

39PNG = 'image/png' 

40PPT = 'application/vnd.ms-powerpoint' 

41SVG = 'image/svg+xml' 

42TTF = 'application/x-font-ttf' 

43TXT = 'text/plain' 

44WEBP = 'image/webp' 

45XLS = 'application/vnd.ms-excel' 

46XML = 'text/xml' 

47ZIP = 'application/zip' 

48 

49 

50_common = { 

51 BIN, 

52 CSS, 

53 CSV, 

54 DOC, 

55 GEOJSON, 

56 GIF, 

57 GML, 

58 GML2, 

59 GML3, 

60 GZIP, 

61 HTML, 

62 JPEG, 

63 JS, 

64 JSON, 

65 KML, 

66 PDF, 

67 PNG, 

68 PPT, 

69 SVG, 

70 TTF, 

71 TXT, 

72 XLS, 

73 XML, 

74 ZIP, 

75} 

76 

77_common_extensions = { 

78 'css': CSS, 

79 'csv': CSV, 

80 'doc': DOC, 

81 'gif': GIF, 

82 'gml': GML, 

83 'gml2': GML2, 

84 'html': HTML, 

85 'jpeg': JPEG, 

86 'jpg': JPEG, 

87 'js': JS, 

88 'json': JSON, 

89 'kml': KML, 

90 'pdf': PDF, 

91 'png': PNG, 

92 'ppt': PPT, 

93 'svg': SVG, 

94 'ttf': TTF, 

95 'txt': TXT, 

96 'xls': XLS, 

97 'xml': XML, 

98 'zip': ZIP, 

99} 

100 

101_aliases = { 

102 'application/vnd.ogc.gml': GML2, 

103 'application/vnd.ogc.gml/3.1.1': GML, 

104 'application/gml:3': GML, 

105 'application/xml;subtype=gml/2': GML2, 

106 'application/xml;subtype=gml/3': GML, 

107 'application/html': HTML, 

108 'application/x-gzip': GZIP, 

109 'application/x-pdf': PDF, 

110 'image/jpg': JPEG, 

111 'text/xhtml': HTML, 

112 'application/xml': XML, 

113 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': DOC, 

114 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': XLS, 

115 'application/vnd.openxmlformats-officedocument.presentationml.presentation': PPT, 

116} 

117 

118 

119def get(mt: str) -> Optional[str]: 

120 """Return the normalized MIME type. 

121 

122 Args: 

123 mt: MIME type, content type, alias or file extension. Case and spaces are ignored. 

124 

125 Returns: 

126 The normalized MIME type, or ``None`` if it is unknown. 

127 """ 

128 

129 if not mt: 

130 return None 

131 

132 mt = mt.strip().replace(' ', '').lower() 

133 

134 s = _get_quick(mt) 

135 if s: 

136 return s 

137 

138 for s, m in _aliases.items(): 

139 if mt.startswith(s): 

140 return m 

141 

142 if ';' in mt: 

143 p = mt.partition(';') 

144 s = _get_quick(p[0].strip()) 

145 if s: 

146 return s 

147 

148 if '/' in mt and mimetypes.guess_extension(mt): 

149 return mt 

150 

151 t, _ = mimetypes.guess_type('x.' + mt) 

152 return t 

153 

154 

155def _get_quick(mt): 

156 """Look up a MIME type, an extension or an alias in the built-in tables.""" 

157 if mt in _common: 

158 return mt 

159 if mt in _common_extensions: 

160 return _common_extensions[mt] 

161 if mt in _aliases: 

162 return _aliases[mt] 

163 

164 

165def for_path(path: str) -> str: 

166 """Return the MIME type for a file path, based on its extension. 

167 

168 Args: 

169 path: File path or name. 

170 

171 Returns: 

172 The MIME type, or ``BIN`` if it is unknown. 

173 """ 

174 _, _, e = path.rpartition('.') 

175 if e in _common_extensions: 

176 return _common_extensions[e] 

177 t, _ = mimetypes.guess_type(path) 

178 return t or BIN 

179 

180 

181def extension_for(mt: str, default: str = 'bin') -> str: 

182 """Return the file extension for a MIME type. 

183 

184 Args: 

185 mt: Normalized MIME type. 

186 default: Extension to return if the MIME type is unknown. 

187 

188 Returns: 

189 The extension, without a dot. 

190 """ 

191 

192 for ext, rt in _common_extensions.items(): 

193 if rt == mt: 

194 return ext 

195 s = mimetypes.guess_extension(mt) 

196 if s: 

197 return s[1:] 

198 return default