Skip to content

Commit aa4f300

Browse files
committed
Add overrides for mimetype -> extension conversion
Python 3.6 gets some of those "wrong": https://bugs.python.org/issue1043134 This has been fixed in Python 3.7: python/cpython#14375 The override dict has been generated by copying the types_map from the current git master: https://github.com/python/cpython/blob/v3.10.0a3/Lib/mimetypes.py#L414-L547 And then running the following with Python 3.6: import mimetypes reverse = {} for ext, mimetype in types_map.items(): if mimetype not in reverse: reverse[mimetype] = ext assert reverse['text/plain'] == '.txt' for mimetype, ext in reverse.items(): got = mimetypes.guess_extension(mimetype) if got != ext: print(f' "{mimetype}": "{ext}", # not {got}')
1 parent f61ef29 commit aa4f300

3 files changed

Lines changed: 47 additions & 1 deletion

File tree

‎qutebrowser/utils/urlutils.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ def filename_from_url(url: QUrl, fallback: str = None) -> Optional[str]:
412412
if not mimetype:
413413
return fallback
414414

415-
ext = mimetypes.guess_extension(mimetype, strict=False) or ''
415+
ext = utils.mimetype_extension(mimetype) or ''
416416
return 'download' + ext
417417

418418
pathname = posixpath.basename(url.path())

‎qutebrowser/utils/utils.py‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,3 +803,38 @@ def parse_duration(duration: str) -> int:
803803
hours = float(hours_string.rstrip('h'))
804804
milliseconds = int((seconds + minutes * 60 + hours * 3600) * 1000)
805805
return milliseconds
806+
807+
808+
def mimetype_extension(mimetype: str) -> Optional[str]:
809+
"""Get a suitable extension for a given mimetype.
810+
811+
This mostly delegates to Python's mimetypes.guess_extension(), but backports some
812+
changes (via a simple override dict) which are missing from earlier Python versions.
813+
Most likely, this can be dropped once the minimum Python version is raised to 3.7.
814+
"""
815+
overrides = {
816+
# Added around 3.8
817+
"application/manifest+json": ".webmanifest",
818+
"application/x-hdf5": ".h5",
819+
820+
# Added in Python 3.7
821+
"application/wasm": ".wasm",
822+
823+
# Wrong values for Python 3.6
824+
# https://bugs.python.org/issue1043134
825+
# https://github.com/python/cpython/pull/14375
826+
"application/octet-stream": ".bin", # not .a
827+
"application/postscript": ".ps", # not .ai
828+
"application/vnd.ms-excel": ".xls", # not .xlb
829+
"application/vnd.ms-powerpoint": ".ppt", # not .pot
830+
"application/xml": ".xsl", # not .rdf
831+
"audio/mpeg": ".mp3", # not .mp2
832+
"image/jpeg": ".jpg", # not .jpe
833+
"image/tiff": ".tiff", # not .tif
834+
"text/html": ".html", # not .htm
835+
"text/plain": ".txt", # not .bat
836+
"video/mpeg": ".mpeg", # not .m1v
837+
}
838+
if mimetype in overrides:
839+
return overrides[mimetype]
840+
return mimetypes.guess_extension(mimetype, strict=False)

‎tests/unit/utils/test_utils.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,3 +867,14 @@ def test_parse_duration_hypothesis(duration):
867867
utils.parse_duration(duration)
868868
except ValueError:
869869
pass
870+
871+
872+
@pytest.mark.parametrize('mimetype, extension', [
873+
('application/pdf', '.pdf'), # handled by Python
874+
('text/plain', '.txt'), # wrong in Python 3.6, overridden
875+
('application/manifest+json', '.webmanifest'), # newer
876+
('text/xul', '.xul'), # strict=False
877+
('doesnot/exist', None),
878+
])
879+
def test_mimetype_extension(mimetype, extension):
880+
assert utils.mimetype_extension(mimetype) == extension

0 commit comments

Comments
 (0)