diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index d7c4e8444f8dec..2a61249a46a366 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -15,6 +15,7 @@ suffix_map -- dictionary mapping suffixes to suffixes encodings_map -- dictionary mapping suffixes to encodings types_map -- dictionary mapping suffixes to types +duplicate_types_map -- dictionary mapping suffixes to types for suffixes that map to multiple types Functions: @@ -42,7 +43,8 @@ "knownfiles", "inited", "MimeTypes", "guess_type", "guess_file_type", "guess_all_extensions", "guess_extension", "add_type", "init", "read_mime_types", - "suffix_map", "encodings_map", "types_map", "common_types" + "suffix_map", "encodings_map", "types_map", "common_types", + "duplicate_ext_types", ] knownfiles = [ @@ -80,6 +82,11 @@ def __init__(self, filenames=(), strict=True): self.add_type(type, ext, True) for (ext, type) in _common_types_default.items(): self.add_type(type, ext, False) + for (ext, type) in _duplicate_ext_types_default.items(): + exts = self.types_map_inv[strict].setdefault(type, []) + if ext not in exts: + exts.append(ext) + for name in filenames: self.read(name, strict) @@ -425,6 +432,7 @@ def _default_mime_types(): global suffix_map, _suffix_map_default global encodings_map, _encodings_map_default global types_map, _types_map_default + global duplicate_ext_types, _duplicate_ext_types_default global common_types, _common_types_default suffix_map = _suffix_map_default = { @@ -608,6 +616,20 @@ def _default_mime_types(): '.movie' : 'video/x-sgi-movie', } + # Some extensions in the default list are linked to multiple mimetypes. + # If you need to add a type that duplicates an extension in the default + # map, add it here. + + # Please sort these too. + # Make sure the entry with the preferred file extension for a particular mime type + # appears before any others of the same mimetype. + duplicate_ext_types = _duplicate_ext_types_default = { + '.3gp' : 'video/3gpp', + '.3gpp' : 'video/3gpp', + '.3g2' : 'video/3gpp2', + '.3gpp2' : 'video/3gpp2', + } + # These are non-standard types, commonly found in the wild. They will # only match if strict=0 flag is given to the API methods. diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index 58f6a4dfae08ba..ca63edc42a710f 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -107,6 +107,13 @@ def test_read_mime_types(self): mock_open.assert_called_with(filename, encoding='utf-8') eq(mime_dict[".Français"], "application/no-mans-land") + def test_duplicate_ext_types(self): + # assert that each extension in the duplicate map is also in the default map + # for a different type + for ext, type in mimetypes.duplicate_ext_types.items(): + self.assertIn(ext, mimetypes._types_map_default) + self.assertNotEqual(type, mimetypes._types_map_default[ext]) + def test_non_standard_types(self): eq = self.assertEqual # First try strict @@ -231,6 +238,8 @@ def check_extensions(): self.assertEqual(mimetypes.guess_extension('application/x-texinfo'), '.texi') self.assertEqual(mimetypes.guess_extension('application/x-troff'), '.roff') self.assertEqual(mimetypes.guess_extension('application/xml'), '.xsl') + self.assertEqual(mimetypes.guess_extension('audio/3gpp'), '.3gp') + self.assertEqual(mimetypes.guess_extension('audio/3gpp2'), '.3g2') self.assertEqual(mimetypes.guess_extension('audio/mpeg'), '.mp3') self.assertEqual(mimetypes.guess_extension('image/avif'), '.avif') self.assertEqual(mimetypes.guess_extension('image/webp'), '.webp') @@ -241,6 +250,8 @@ def check_extensions(): self.assertEqual(mimetypes.guess_extension('text/plain'), '.txt') self.assertEqual(mimetypes.guess_extension('text/rtf'), '.rtf') self.assertEqual(mimetypes.guess_extension('text/x-rst'), '.rst') + self.assertEqual(mimetypes.guess_extension('video/3gpp'), '.3gp') + self.assertEqual(mimetypes.guess_extension('video/3gpp2'), '.3g2') self.assertEqual(mimetypes.guess_extension('video/mpeg'), '.mpeg') self.assertEqual(mimetypes.guess_extension('video/quicktime'), '.mov') @@ -255,6 +266,7 @@ def test_init_stability(self): encodings_map = mimetypes.encodings_map types_map = mimetypes.types_map common_types = mimetypes.common_types + duplicate_ext_types = mimetypes.duplicate_ext_types mimetypes.init() self.assertIsNot(suffix_map, mimetypes.suffix_map) @@ -265,6 +277,7 @@ def test_init_stability(self): self.assertEqual(encodings_map, mimetypes.encodings_map) self.assertEqual(types_map, mimetypes.types_map) self.assertEqual(common_types, mimetypes.common_types) + self.assertEqual(duplicate_ext_types, mimetypes.duplicate_ext_types) def test_path_like_ob(self): filename = "LICENSE.txt" diff --git a/Misc/NEWS.d/next/Library/2024-08-02-20-02-38.gh-issue-122631.p4QdB2.rst b/Misc/NEWS.d/next/Library/2024-08-02-20-02-38.gh-issue-122631.p4QdB2.rst new file mode 100644 index 00000000000000..a2fe1c298187bb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-08-02-20-02-38.gh-issue-122631.p4QdB2.rst @@ -0,0 +1,4 @@ +Add :mod:`mimetypes` support for `video/3gpp`_ and `video/3gpp2`_. + +.. _video/3gpp: https://www.iana.org/assignments/media-types/video/3gpp +.. _video/3gpp2: https://www.iana.org/assignments/media-types/video/3gpp2