1import io 2import locale 3import mimetypes 4import pathlib 5import sys 6import unittest.mock 7 8from test import support 9from test.support import os_helper 10from platform import win32_edition 11 12try: 13 import _winapi 14except ImportError: 15 _winapi = None 16 17 18def setUpModule(): 19 global knownfiles 20 knownfiles = mimetypes.knownfiles 21 22 # Tell it we don't know about external files: 23 mimetypes.knownfiles = [] 24 mimetypes.inited = False 25 mimetypes._default_mime_types() 26 27 28def tearDownModule(): 29 # Restore knownfiles to its initial state 30 mimetypes.knownfiles = knownfiles 31 32 33class MimeTypesTestCase(unittest.TestCase): 34 def setUp(self): 35 self.db = mimetypes.MimeTypes() 36 37 def test_default_data(self): 38 eq = self.assertEqual 39 eq(self.db.guess_type("foo.html"), ("text/html", None)) 40 eq(self.db.guess_type("foo.HTML"), ("text/html", None)) 41 eq(self.db.guess_type("foo.tgz"), ("application/x-tar", "gzip")) 42 eq(self.db.guess_type("foo.tar.gz"), ("application/x-tar", "gzip")) 43 eq(self.db.guess_type("foo.tar.Z"), ("application/x-tar", "compress")) 44 eq(self.db.guess_type("foo.tar.bz2"), ("application/x-tar", "bzip2")) 45 eq(self.db.guess_type("foo.tar.xz"), ("application/x-tar", "xz")) 46 47 def test_data_urls(self): 48 eq = self.assertEqual 49 guess_type = self.db.guess_type 50 eq(guess_type("data:invalidDataWithoutComma"), (None, None)) 51 eq(guess_type("data:,thisIsTextPlain"), ("text/plain", None)) 52 eq(guess_type("data:;base64,thisIsTextPlain"), ("text/plain", None)) 53 eq(guess_type("data:text/x-foo,thisIsTextXFoo"), ("text/x-foo", None)) 54 55 def test_file_parsing(self): 56 eq = self.assertEqual 57 sio = io.StringIO("x-application/x-unittest pyunit\n") 58 self.db.readfp(sio) 59 eq(self.db.guess_type("foo.pyunit"), 60 ("x-application/x-unittest", None)) 61 eq(self.db.guess_extension("x-application/x-unittest"), ".pyunit") 62 63 def test_read_mime_types(self): 64 eq = self.assertEqual 65 66 # Unreadable file returns None 67 self.assertIsNone(mimetypes.read_mime_types("non-existent")) 68 69 with os_helper.temp_dir() as directory: 70 data = "x-application/x-unittest pyunit\n" 71 file = pathlib.Path(directory, "sample.mimetype") 72 file.write_text(data, encoding="utf-8") 73 mime_dict = mimetypes.read_mime_types(file) 74 eq(mime_dict[".pyunit"], "x-application/x-unittest") 75 76 # bpo-41048: read_mime_types should read the rule file with 'utf-8' encoding. 77 # Not with locale encoding. _bootlocale has been imported because io.open(...) 78 # uses it. 79 data = "application/no-mans-land Fran\u00E7ais" 80 filename = "filename" 81 fp = io.StringIO(data) 82 with unittest.mock.patch.object(mimetypes, 'open', 83 return_value=fp) as mock_open: 84 mime_dict = mimetypes.read_mime_types(filename) 85 mock_open.assert_called_with(filename, encoding='utf-8') 86 eq(mime_dict[".Français"], "application/no-mans-land") 87 88 def test_non_standard_types(self): 89 eq = self.assertEqual 90 # First try strict 91 eq(self.db.guess_type('foo.xul', strict=True), (None, None)) 92 eq(self.db.guess_extension('image/jpg', strict=True), None) 93 # And then non-strict 94 eq(self.db.guess_type('foo.xul', strict=False), ('text/xul', None)) 95 eq(self.db.guess_type('foo.XUL', strict=False), ('text/xul', None)) 96 eq(self.db.guess_type('foo.invalid', strict=False), (None, None)) 97 eq(self.db.guess_extension('image/jpg', strict=False), '.jpg') 98 eq(self.db.guess_extension('image/JPG', strict=False), '.jpg') 99 100 def test_filename_with_url_delimiters(self): 101 # bpo-38449: URL delimiters cases should be handled also. 102 # They would have different mime types if interpreted as URL as 103 # compared to when interpreted as filename because of the semicolon. 104 eq = self.assertEqual 105 gzip_expected = ('application/x-tar', 'gzip') 106 eq(self.db.guess_type(";1.tar.gz"), gzip_expected) 107 eq(self.db.guess_type("?1.tar.gz"), gzip_expected) 108 eq(self.db.guess_type("#1.tar.gz"), gzip_expected) 109 eq(self.db.guess_type("#1#.tar.gz"), gzip_expected) 110 eq(self.db.guess_type(";1#.tar.gz"), gzip_expected) 111 eq(self.db.guess_type(";&1=123;?.tar.gz"), gzip_expected) 112 eq(self.db.guess_type("?k1=v1&k2=v2.tar.gz"), gzip_expected) 113 eq(self.db.guess_type(r" \"\`;b&b&c |.tar.gz"), gzip_expected) 114 115 def test_guess_all_types(self): 116 # First try strict. Use a set here for testing the results because if 117 # test_urllib2 is run before test_mimetypes, global state is modified 118 # such that the 'all' set will have more items in it. 119 all = self.db.guess_all_extensions('text/plain', strict=True) 120 self.assertTrue(set(all) >= {'.bat', '.c', '.h', '.ksh', '.pl', '.txt'}) 121 self.assertEqual(len(set(all)), len(all)) # no duplicates 122 # And now non-strict 123 all = self.db.guess_all_extensions('image/jpg', strict=False) 124 self.assertEqual(all, ['.jpg']) 125 # And now for no hits 126 all = self.db.guess_all_extensions('image/jpg', strict=True) 127 self.assertEqual(all, []) 128 # And now for type existing in both strict and non-strict mappings. 129 self.db.add_type('test-type', '.strict-ext') 130 self.db.add_type('test-type', '.non-strict-ext', strict=False) 131 all = self.db.guess_all_extensions('test-type', strict=False) 132 self.assertEqual(all, ['.strict-ext', '.non-strict-ext']) 133 all = self.db.guess_all_extensions('test-type') 134 self.assertEqual(all, ['.strict-ext']) 135 # Test that changing the result list does not affect the global state 136 all.append('.no-such-ext') 137 all = self.db.guess_all_extensions('test-type') 138 self.assertNotIn('.no-such-ext', all) 139 140 def test_encoding(self): 141 getpreferredencoding = locale.getpreferredencoding 142 self.addCleanup(setattr, locale, 'getpreferredencoding', 143 getpreferredencoding) 144 locale.getpreferredencoding = lambda: 'ascii' 145 146 filename = support.findfile("mime.types") 147 mimes = mimetypes.MimeTypes([filename]) 148 exts = mimes.guess_all_extensions('application/vnd.geocube+xml', 149 strict=True) 150 self.assertEqual(exts, ['.g3', '.g\xb3']) 151 152 def test_init_reinitializes(self): 153 # Issue 4936: make sure an init starts clean 154 # First, put some poison into the types table 155 mimetypes.add_type('foo/bar', '.foobar') 156 self.assertEqual(mimetypes.guess_extension('foo/bar'), '.foobar') 157 # Reinitialize 158 mimetypes.init() 159 # Poison should be gone. 160 self.assertEqual(mimetypes.guess_extension('foo/bar'), None) 161 162 def test_preferred_extension(self): 163 def check_extensions(): 164 self.assertEqual(mimetypes.guess_extension('application/octet-stream'), '.bin') 165 self.assertEqual(mimetypes.guess_extension('application/postscript'), '.ps') 166 self.assertEqual(mimetypes.guess_extension('application/vnd.apple.mpegurl'), '.m3u') 167 self.assertEqual(mimetypes.guess_extension('application/vnd.ms-excel'), '.xls') 168 self.assertEqual(mimetypes.guess_extension('application/vnd.ms-powerpoint'), '.ppt') 169 self.assertEqual(mimetypes.guess_extension('application/x-texinfo'), '.texi') 170 self.assertEqual(mimetypes.guess_extension('application/x-troff'), '.roff') 171 self.assertEqual(mimetypes.guess_extension('application/xml'), '.xsl') 172 self.assertEqual(mimetypes.guess_extension('audio/mpeg'), '.mp3') 173 self.assertEqual(mimetypes.guess_extension('image/jpeg'), '.jpg') 174 self.assertEqual(mimetypes.guess_extension('image/tiff'), '.tiff') 175 self.assertEqual(mimetypes.guess_extension('message/rfc822'), '.eml') 176 self.assertEqual(mimetypes.guess_extension('text/html'), '.html') 177 self.assertEqual(mimetypes.guess_extension('text/plain'), '.txt') 178 self.assertEqual(mimetypes.guess_extension('video/mpeg'), '.mpeg') 179 self.assertEqual(mimetypes.guess_extension('video/quicktime'), '.mov') 180 181 check_extensions() 182 mimetypes.init() 183 check_extensions() 184 185 def test_init_stability(self): 186 mimetypes.init() 187 188 suffix_map = mimetypes.suffix_map 189 encodings_map = mimetypes.encodings_map 190 types_map = mimetypes.types_map 191 common_types = mimetypes.common_types 192 193 mimetypes.init() 194 self.assertIsNot(suffix_map, mimetypes.suffix_map) 195 self.assertIsNot(encodings_map, mimetypes.encodings_map) 196 self.assertIsNot(types_map, mimetypes.types_map) 197 self.assertIsNot(common_types, mimetypes.common_types) 198 self.assertEqual(suffix_map, mimetypes.suffix_map) 199 self.assertEqual(encodings_map, mimetypes.encodings_map) 200 self.assertEqual(types_map, mimetypes.types_map) 201 self.assertEqual(common_types, mimetypes.common_types) 202 203 def test_path_like_ob(self): 204 filename = "LICENSE.txt" 205 filepath = pathlib.Path(filename) 206 filepath_with_abs_dir = pathlib.Path('/dir/'+filename) 207 filepath_relative = pathlib.Path('../dir/'+filename) 208 path_dir = pathlib.Path('./') 209 210 expected = self.db.guess_type(filename) 211 212 self.assertEqual(self.db.guess_type(filepath), expected) 213 self.assertEqual(self.db.guess_type( 214 filepath_with_abs_dir), expected) 215 self.assertEqual(self.db.guess_type(filepath_relative), expected) 216 self.assertEqual(self.db.guess_type(path_dir), (None, None)) 217 218 def test_keywords_args_api(self): 219 self.assertEqual(self.db.guess_type( 220 url="foo.html", strict=True), ("text/html", None)) 221 self.assertEqual(self.db.guess_all_extensions( 222 type='image/jpg', strict=True), []) 223 self.assertEqual(self.db.guess_extension( 224 type='image/jpg', strict=False), '.jpg') 225 226 227@unittest.skipUnless(sys.platform.startswith("win"), "Windows only") 228class Win32MimeTypesTestCase(unittest.TestCase): 229 def setUp(self): 230 # ensure all entries actually come from the Windows registry 231 self.original_types_map = mimetypes.types_map.copy() 232 mimetypes.types_map.clear() 233 mimetypes.init() 234 self.db = mimetypes.MimeTypes() 235 236 def tearDown(self): 237 # restore default settings 238 mimetypes.types_map.clear() 239 mimetypes.types_map.update(self.original_types_map) 240 241 @unittest.skipIf(win32_edition() in ('NanoServer', 'WindowsCoreHeadless', 'IoTEdgeOS'), 242 "MIME types registry keys unavailable") 243 def test_registry_parsing(self): 244 # the original, minimum contents of the MIME database in the 245 # Windows registry is undocumented AFAIK. 246 # Use file types that should *always* exist: 247 eq = self.assertEqual 248 eq(self.db.guess_type("foo.txt"), ("text/plain", None)) 249 eq(self.db.guess_type("image.jpg"), ("image/jpeg", None)) 250 eq(self.db.guess_type("image.png"), ("image/png", None)) 251 252 @unittest.skipIf(not hasattr(_winapi, "_mimetypes_read_windows_registry"), 253 "read_windows_registry accelerator unavailable") 254 def test_registry_accelerator(self): 255 from_accel = {} 256 from_reg = {} 257 _winapi._mimetypes_read_windows_registry( 258 lambda v, k: from_accel.setdefault(k, set()).add(v) 259 ) 260 mimetypes.MimeTypes._read_windows_registry( 261 lambda v, k: from_reg.setdefault(k, set()).add(v) 262 ) 263 self.assertEqual(list(from_reg), list(from_accel)) 264 for k in from_reg: 265 self.assertEqual(from_reg[k], from_accel[k]) 266 267 268class MiscTestCase(unittest.TestCase): 269 def test__all__(self): 270 support.check__all__(self, mimetypes) 271 272 273class MimetypesCliTestCase(unittest.TestCase): 274 275 def mimetypes_cmd(self, *args, **kwargs): 276 support.patch(self, sys, "argv", [sys.executable, *args]) 277 with support.captured_stdout() as output: 278 mimetypes._main() 279 return output.getvalue().strip() 280 281 def test_help_option(self): 282 support.patch(self, sys, "argv", [sys.executable, "-h"]) 283 with support.captured_stdout() as output: 284 with self.assertRaises(SystemExit) as cm: 285 mimetypes._main() 286 287 self.assertIn("Usage: mimetypes.py", output.getvalue()) 288 self.assertEqual(cm.exception.code, 0) 289 290 def test_invalid_option(self): 291 support.patch(self, sys, "argv", [sys.executable, "--invalid"]) 292 with support.captured_stdout() as output: 293 with self.assertRaises(SystemExit) as cm: 294 mimetypes._main() 295 296 self.assertIn("Usage: mimetypes.py", output.getvalue()) 297 self.assertEqual(cm.exception.code, 1) 298 299 def test_guess_extension(self): 300 eq = self.assertEqual 301 302 extension = self.mimetypes_cmd("-l", "-e", "image/jpg") 303 eq(extension, ".jpg") 304 305 extension = self.mimetypes_cmd("-e", "image/jpg") 306 eq(extension, "I don't know anything about type image/jpg") 307 308 extension = self.mimetypes_cmd("-e", "image/jpeg") 309 eq(extension, ".jpg") 310 311 def test_guess_type(self): 312 eq = self.assertEqual 313 314 type_info = self.mimetypes_cmd("-l", "foo.pic") 315 eq(type_info, "type: image/pict encoding: None") 316 317 type_info = self.mimetypes_cmd("foo.pic") 318 eq(type_info, "I don't know anything about type foo.pic") 319 320if __name__ == "__main__": 321 unittest.main() 322