Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 328f8b8

Browse filesBrowse files
arhadthedevAA-Turnerhugovk
authored
gh-93096: Make mimetypes CLI tool public (#93097)
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
1 parent 119bcfa commit 328f8b8
Copy full SHA for 328f8b8

File tree

7 files changed

+179
-79
lines changed
Filter options

7 files changed

+179
-79
lines changed

‎Doc/library/cmdline.rst

Copy file name to clipboardExpand all lines: Doc/library/cmdline.rst
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ The following modules have a command-line interface.
2424
* :mod:`!idlelib`
2525
* :ref:`inspect <inspect-module-cli>`
2626
* :ref:`json <json-commandline>`
27-
* :mod:`mimetypes`
27+
* :ref:`mimetypes <mimetypes-cli>`
2828
* :mod:`pdb`
2929
* :mod:`pickle`
3030
* :ref:`pickletools <pickletools-cli>`

‎Doc/library/mimetypes.rst

Copy file name to clipboardExpand all lines: Doc/library/mimetypes.rst
+95-1Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ An example usage of the module::
191191

192192
.. _mimetypes-objects:
193193

194-
MimeTypes Objects
194+
MimeTypes objects
195195
-----------------
196196

197197
The :class:`MimeTypes` class may be useful for applications which may want more
@@ -307,3 +307,97 @@ than one MIME-type database; it provides an interface similar to the one of the
307307

308308
When *strict* is ``True`` (the default), the mapping will be added to the
309309
official MIME types, otherwise to the non-standard ones.
310+
311+
312+
.. _mimetypes-cli:
313+
314+
Command-line usage
315+
------------------
316+
317+
The :mod:`!mimetypes` module can be executed as a script from the command line.
318+
319+
.. code-block:: sh
320+
321+
python -m mimetypes [-h] [-e] [-l] type [type ...]
322+
323+
The following options are accepted:
324+
325+
.. program:: mimetypes
326+
327+
.. cmdoption:: -h
328+
--help
329+
330+
Show the help message and exit.
331+
332+
.. cmdoption:: -e
333+
--extension
334+
335+
Guess extension instead of type.
336+
337+
.. cmdoption:: -l
338+
--lenient
339+
340+
Additionally search for some common, but non-standard types.
341+
342+
By default the script converts MIME types to file extensions.
343+
However, if ``--extension`` is specified,
344+
it converts file extensions to MIME types.
345+
346+
For each ``type`` entry, the script writes a line into the standard output
347+
stream. If an unknown type occurs, it writes an error message into the
348+
standard error stream and exits with the return code ``1``.
349+
350+
351+
.. mimetypes-cli-example:
352+
353+
Command-line example
354+
--------------------
355+
356+
Here are some examples of typical usage of the :mod:`!mimetypes` command-line
357+
interface:
358+
359+
.. code-block:: console
360+
361+
$ # get a MIME type by a file name
362+
$ python -m mimetypes filename.png
363+
type: image/png encoding: None
364+
365+
$ # get a MIME type by a URL
366+
$ python -m mimetypes https://example.com/filename.txt
367+
type: text/plain encoding: None
368+
369+
$ # get a complex MIME type
370+
$ python -m mimetypes filename.tar.gz
371+
type: application/x-tar encoding: gzip
372+
373+
$ # get a MIME type for a rare file extension
374+
$ python -m mimetypes filename.pict
375+
error: unknown extension of filename.pict
376+
377+
$ # now look in the extended database built into Python
378+
$ python -m mimetypes --lenient filename.pict
379+
type: image/pict encoding: None
380+
381+
$ # get a file extension by a MIME type
382+
$ python -m mimetypes --extension text/javascript
383+
.js
384+
385+
$ # get a file extension by a rare MIME type
386+
$ python -m mimetypes --extension text/xul
387+
error: unknown type text/xul
388+
389+
$ # now look in the extended database again
390+
$ python -m mimetypes --extension --lenient text/xul
391+
.xul
392+
393+
$ # try to feed an unknown file extension
394+
$ python -m mimetypes filename.sh filename.nc filename.xxx filename.txt
395+
type: application/x-sh encoding: None
396+
type: application/x-netcdf encoding: None
397+
error: unknown extension of filename.xxx
398+
399+
$ # try to feed an unknown MIME type
400+
$ python -m mimetypes --extension audio/aac audio/opus audio/future audio/x-wav
401+
.aac
402+
.opus
403+
error: unknown type audio/future

‎Doc/whatsnew/3.14.rst

Copy file name to clipboardExpand all lines: Doc/whatsnew/3.14.rst
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,13 @@ json
652652
mimetypes
653653
---------
654654

655+
* Document the command-line for :mod:`mimetypes`.
656+
It now exits with ``1`` on failure instead of ``0``
657+
and ``2`` on incorrect command-line parameters instead of ``1``.
658+
Also, errors are printed to stderr instead of stdout and their text is made
659+
tighter.
660+
(Contributed by Oleg Iarygin and Hugo van Kemenade in :gh:`93096`.)
661+
655662
* Add MS and :rfc:`8081` MIME types for fonts:
656663

657664
* Embedded OpenType: ``application/vnd.ms-fontobject``

‎Lib/mimetypes.py

Copy file name to clipboardExpand all lines: Lib/mimetypes.py
+31-43Lines changed: 31 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -670,50 +670,38 @@ def _default_mime_types():
670670

671671

672672
def _main():
673-
import getopt
673+
"""Run the mimetypes command-line interface."""
674674
import sys
675-
676-
USAGE = """\
677-
Usage: mimetypes.py [options] type
678-
679-
Options:
680-
--help / -h -- print this message and exit
681-
--lenient / -l -- additionally search of some common, but non-standard
682-
types.
683-
--extension / -e -- guess extension instead of type
684-
685-
More than one type argument may be given.
686-
"""
687-
688-
def usage(code, msg=''):
689-
print(USAGE)
690-
if msg: print(msg)
691-
sys.exit(code)
692-
693-
try:
694-
opts, args = getopt.getopt(sys.argv[1:], 'hle',
695-
['help', 'lenient', 'extension'])
696-
except getopt.error as msg:
697-
usage(1, msg)
698-
699-
strict = 1
700-
extension = 0
701-
for opt, arg in opts:
702-
if opt in ('-h', '--help'):
703-
usage(0)
704-
elif opt in ('-l', '--lenient'):
705-
strict = 0
706-
elif opt in ('-e', '--extension'):
707-
extension = 1
708-
for gtype in args:
709-
if extension:
710-
guess = guess_extension(gtype, strict)
711-
if not guess: print("I don't know anything about type", gtype)
712-
else: print(guess)
713-
else:
714-
guess, encoding = guess_type(gtype, strict)
715-
if not guess: print("I don't know anything about type", gtype)
716-
else: print('type:', guess, 'encoding:', encoding)
675+
from argparse import ArgumentParser
676+
677+
parser = ArgumentParser(description='map filename extensions to MIME types')
678+
parser.add_argument(
679+
'-e', '--extension',
680+
action='store_true',
681+
help='guess extension instead of type'
682+
)
683+
parser.add_argument(
684+
'-l', '--lenient',
685+
action='store_true',
686+
help='additionally search for common but non-standard types'
687+
)
688+
parser.add_argument('type', nargs='+', help='a type to search')
689+
args = parser.parse_args()
690+
691+
if args.extension:
692+
for gtype in args.type:
693+
guess = guess_extension(gtype, not args.lenient)
694+
if guess:
695+
print(guess)
696+
else:
697+
sys.exit(f"error: unknown type {gtype}")
698+
else:
699+
for gtype in args.type:
700+
guess, encoding = guess_type(gtype, not args.lenient)
701+
if guess:
702+
print('type:', guess, 'encoding:', encoding)
703+
else:
704+
sys.exit(f"error: media type unknown for {gtype}")
717705

718706

719707
if __name__ == '__main__':

‎Lib/test/test_mimetypes.py

Copy file name to clipboardExpand all lines: Lib/test/test_mimetypes.py
+39-34Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import os
44
import sys
55
import unittest.mock
6+
from os import linesep
67

78
from test import support
89
from test.support import os_helper
10+
from test.support.script_helper import run_python_until_end
911
from platform import win32_edition
1012

1113
try:
@@ -390,50 +392,53 @@ def test__all__(self):
390392

391393
class MimetypesCliTestCase(unittest.TestCase):
392394

393-
def mimetypes_cmd(self, *args, **kwargs):
394-
support.patch(self, sys, "argv", [sys.executable, *args])
395-
with support.captured_stdout() as output:
396-
mimetypes._main()
397-
return output.getvalue().strip()
395+
def mimetypes_cmd(cls, *args, **kwargs):
396+
result, _ = run_python_until_end('-m', 'mimetypes', *args)
397+
return result.rc, result.out.decode(), result.err.decode()
398398

399399
def test_help_option(self):
400-
support.patch(self, sys, "argv", [sys.executable, "-h"])
401-
with support.captured_stdout() as output:
402-
with self.assertRaises(SystemExit) as cm:
403-
mimetypes._main()
404-
405-
self.assertIn("Usage: mimetypes.py", output.getvalue())
406-
self.assertEqual(cm.exception.code, 0)
400+
retcode, out, err = self.mimetypes_cmd('-h')
401+
self.assertEqual(retcode, 0)
402+
self.assertStartsWith(out, 'usage: ')
403+
self.assertEqual(err, '')
407404

408405
def test_invalid_option(self):
409-
support.patch(self, sys, "argv", [sys.executable, "--invalid"])
410-
with support.captured_stdout() as output:
411-
with self.assertRaises(SystemExit) as cm:
412-
mimetypes._main()
413-
414-
self.assertIn("Usage: mimetypes.py", output.getvalue())
415-
self.assertEqual(cm.exception.code, 1)
406+
retcode, out, err = self.mimetypes_cmd('--invalid')
407+
self.assertEqual(retcode, 2)
408+
self.assertEqual(out, '')
409+
self.assertStartsWith(err, 'usage: ')
416410

417411
def test_guess_extension(self):
418-
eq = self.assertEqual
419-
420-
extension = self.mimetypes_cmd("-l", "-e", "image/jpg")
421-
eq(extension, ".jpg")
412+
retcode, out, err = self.mimetypes_cmd('-l', '-e', 'image/jpg')
413+
self.assertEqual(retcode, 0)
414+
self.assertEqual(out, f'.jpg{linesep}')
415+
self.assertEqual(err, '')
422416

423-
extension = self.mimetypes_cmd("-e", "image/jpg")
424-
eq(extension, "I don't know anything about type image/jpg")
417+
retcode, out, err = self.mimetypes_cmd('-e', 'image/jpg')
418+
self.assertEqual(retcode, 1)
419+
self.assertEqual(out, '')
420+
self.assertEqual(err, f'error: unknown type image/jpg{linesep}')
425421

426-
extension = self.mimetypes_cmd("-e", "image/jpeg")
427-
eq(extension, ".jpg")
422+
retcode, out, err = self.mimetypes_cmd('-e', 'image/jpeg')
423+
self.assertEqual(retcode, 0)
424+
self.assertEqual(out, f'.jpg{linesep}')
425+
self.assertEqual(err, '')
428426

429427
def test_guess_type(self):
430-
eq = self.assertEqual
431-
432-
type_info = self.mimetypes_cmd("-l", "foo.pic")
433-
eq(type_info, "type: image/pict encoding: None")
434-
435-
type_info = self.mimetypes_cmd("foo.pic")
436-
eq(type_info, "I don't know anything about type foo.pic")
428+
retcode, out, err = self.mimetypes_cmd('-l', 'foo.webp')
429+
self.assertEqual(retcode, 0)
430+
self.assertEqual(out, f'type: image/webp encoding: None{linesep}')
431+
self.assertEqual(err, '')
432+
433+
@unittest.skipIf(
434+
sys.platform == 'darwin',
435+
'macOS lists common_types in mime.types thus making them always known'
436+
)
437+
def test_guess_type_conflicting_with_mimetypes(self):
438+
retcode, out, err = self.mimetypes_cmd('foo.pic')
439+
self.assertEqual(retcode, 1)
440+
self.assertEqual(out, '')
441+
self.assertEqual(err, f'error: media type unknown for foo.pic{linesep}')
437442

438443
if __name__ == "__main__":
439444
unittest.main()

‎Misc/ACKS

Copy file name to clipboardExpand all lines: Misc/ACKS
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,7 @@ Oleg Höfling
849849
Robert Hölzl
850850
Stefan Hölzl
851851
Catalin Iacob
852+
Oleg Iarygin
852853
Mihai Ibanescu
853854
Ali Ikinci
854855
Aaron Iles
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Document the command-line for :mod:`mimetypes`.
2+
It now exits with ``1`` on failure instead of ``0``
3+
and ``2`` on incorrect command-line parameters instead of ``1``.
4+
Also, errors are printed to stderr instead of stdout and their text is made
5+
tighter. Patch by Oleg Iarygin and Hugo van Kemenade.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.