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>
This commit is contained in:
parent
119bcfad9c
commit
328f8b8856
@ -24,7 +24,7 @@ The following modules have a command-line interface.
|
|||||||
* :mod:`!idlelib`
|
* :mod:`!idlelib`
|
||||||
* :ref:`inspect <inspect-module-cli>`
|
* :ref:`inspect <inspect-module-cli>`
|
||||||
* :ref:`json <json-commandline>`
|
* :ref:`json <json-commandline>`
|
||||||
* :mod:`mimetypes`
|
* :ref:`mimetypes <mimetypes-cli>`
|
||||||
* :mod:`pdb`
|
* :mod:`pdb`
|
||||||
* :mod:`pickle`
|
* :mod:`pickle`
|
||||||
* :ref:`pickletools <pickletools-cli>`
|
* :ref:`pickletools <pickletools-cli>`
|
||||||
|
@ -191,7 +191,7 @@ An example usage of the module::
|
|||||||
|
|
||||||
.. _mimetypes-objects:
|
.. _mimetypes-objects:
|
||||||
|
|
||||||
MimeTypes Objects
|
MimeTypes objects
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
The :class:`MimeTypes` class may be useful for applications which may want more
|
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
|
|||||||
|
|
||||||
When *strict* is ``True`` (the default), the mapping will be added to the
|
When *strict* is ``True`` (the default), the mapping will be added to the
|
||||||
official MIME types, otherwise to the non-standard ones.
|
official MIME types, otherwise to the non-standard ones.
|
||||||
|
|
||||||
|
|
||||||
|
.. _mimetypes-cli:
|
||||||
|
|
||||||
|
Command-line usage
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The :mod:`!mimetypes` module can be executed as a script from the command line.
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
python -m mimetypes [-h] [-e] [-l] type [type ...]
|
||||||
|
|
||||||
|
The following options are accepted:
|
||||||
|
|
||||||
|
.. program:: mimetypes
|
||||||
|
|
||||||
|
.. cmdoption:: -h
|
||||||
|
--help
|
||||||
|
|
||||||
|
Show the help message and exit.
|
||||||
|
|
||||||
|
.. cmdoption:: -e
|
||||||
|
--extension
|
||||||
|
|
||||||
|
Guess extension instead of type.
|
||||||
|
|
||||||
|
.. cmdoption:: -l
|
||||||
|
--lenient
|
||||||
|
|
||||||
|
Additionally search for some common, but non-standard types.
|
||||||
|
|
||||||
|
By default the script converts MIME types to file extensions.
|
||||||
|
However, if ``--extension`` is specified,
|
||||||
|
it converts file extensions to MIME types.
|
||||||
|
|
||||||
|
For each ``type`` entry, the script writes a line into the standard output
|
||||||
|
stream. If an unknown type occurs, it writes an error message into the
|
||||||
|
standard error stream and exits with the return code ``1``.
|
||||||
|
|
||||||
|
|
||||||
|
.. mimetypes-cli-example:
|
||||||
|
|
||||||
|
Command-line example
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Here are some examples of typical usage of the :mod:`!mimetypes` command-line
|
||||||
|
interface:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ # get a MIME type by a file name
|
||||||
|
$ python -m mimetypes filename.png
|
||||||
|
type: image/png encoding: None
|
||||||
|
|
||||||
|
$ # get a MIME type by a URL
|
||||||
|
$ python -m mimetypes https://example.com/filename.txt
|
||||||
|
type: text/plain encoding: None
|
||||||
|
|
||||||
|
$ # get a complex MIME type
|
||||||
|
$ python -m mimetypes filename.tar.gz
|
||||||
|
type: application/x-tar encoding: gzip
|
||||||
|
|
||||||
|
$ # get a MIME type for a rare file extension
|
||||||
|
$ python -m mimetypes filename.pict
|
||||||
|
error: unknown extension of filename.pict
|
||||||
|
|
||||||
|
$ # now look in the extended database built into Python
|
||||||
|
$ python -m mimetypes --lenient filename.pict
|
||||||
|
type: image/pict encoding: None
|
||||||
|
|
||||||
|
$ # get a file extension by a MIME type
|
||||||
|
$ python -m mimetypes --extension text/javascript
|
||||||
|
.js
|
||||||
|
|
||||||
|
$ # get a file extension by a rare MIME type
|
||||||
|
$ python -m mimetypes --extension text/xul
|
||||||
|
error: unknown type text/xul
|
||||||
|
|
||||||
|
$ # now look in the extended database again
|
||||||
|
$ python -m mimetypes --extension --lenient text/xul
|
||||||
|
.xul
|
||||||
|
|
||||||
|
$ # try to feed an unknown file extension
|
||||||
|
$ python -m mimetypes filename.sh filename.nc filename.xxx filename.txt
|
||||||
|
type: application/x-sh encoding: None
|
||||||
|
type: application/x-netcdf encoding: None
|
||||||
|
error: unknown extension of filename.xxx
|
||||||
|
|
||||||
|
$ # try to feed an unknown MIME type
|
||||||
|
$ python -m mimetypes --extension audio/aac audio/opus audio/future audio/x-wav
|
||||||
|
.aac
|
||||||
|
.opus
|
||||||
|
error: unknown type audio/future
|
||||||
|
@ -652,6 +652,13 @@ json
|
|||||||
mimetypes
|
mimetypes
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
* Document the command-line for :mod:`mimetypes`.
|
||||||
|
It now exits with ``1`` on failure instead of ``0``
|
||||||
|
and ``2`` on incorrect command-line parameters instead of ``1``.
|
||||||
|
Also, errors are printed to stderr instead of stdout and their text is made
|
||||||
|
tighter.
|
||||||
|
(Contributed by Oleg Iarygin and Hugo van Kemenade in :gh:`93096`.)
|
||||||
|
|
||||||
* Add MS and :rfc:`8081` MIME types for fonts:
|
* Add MS and :rfc:`8081` MIME types for fonts:
|
||||||
|
|
||||||
* Embedded OpenType: ``application/vnd.ms-fontobject``
|
* Embedded OpenType: ``application/vnd.ms-fontobject``
|
||||||
|
@ -670,50 +670,38 @@ _default_mime_types()
|
|||||||
|
|
||||||
|
|
||||||
def _main():
|
def _main():
|
||||||
import getopt
|
"""Run the mimetypes command-line interface."""
|
||||||
import sys
|
import sys
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
USAGE = """\
|
parser = ArgumentParser(description='map filename extensions to MIME types')
|
||||||
Usage: mimetypes.py [options] type
|
parser.add_argument(
|
||||||
|
'-e', '--extension',
|
||||||
|
action='store_true',
|
||||||
|
help='guess extension instead of type'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'-l', '--lenient',
|
||||||
|
action='store_true',
|
||||||
|
help='additionally search for common but non-standard types'
|
||||||
|
)
|
||||||
|
parser.add_argument('type', nargs='+', help='a type to search')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
Options:
|
if args.extension:
|
||||||
--help / -h -- print this message and exit
|
for gtype in args.type:
|
||||||
--lenient / -l -- additionally search of some common, but non-standard
|
guess = guess_extension(gtype, not args.lenient)
|
||||||
types.
|
if guess:
|
||||||
--extension / -e -- guess extension instead of type
|
print(guess)
|
||||||
|
else:
|
||||||
More than one type argument may be given.
|
sys.exit(f"error: unknown type {gtype}")
|
||||||
"""
|
else:
|
||||||
|
for gtype in args.type:
|
||||||
def usage(code, msg=''):
|
guess, encoding = guess_type(gtype, not args.lenient)
|
||||||
print(USAGE)
|
if guess:
|
||||||
if msg: print(msg)
|
print('type:', guess, 'encoding:', encoding)
|
||||||
sys.exit(code)
|
else:
|
||||||
|
sys.exit(f"error: media type unknown for {gtype}")
|
||||||
try:
|
|
||||||
opts, args = getopt.getopt(sys.argv[1:], 'hle',
|
|
||||||
['help', 'lenient', 'extension'])
|
|
||||||
except getopt.error as msg:
|
|
||||||
usage(1, msg)
|
|
||||||
|
|
||||||
strict = 1
|
|
||||||
extension = 0
|
|
||||||
for opt, arg in opts:
|
|
||||||
if opt in ('-h', '--help'):
|
|
||||||
usage(0)
|
|
||||||
elif opt in ('-l', '--lenient'):
|
|
||||||
strict = 0
|
|
||||||
elif opt in ('-e', '--extension'):
|
|
||||||
extension = 1
|
|
||||||
for gtype in args:
|
|
||||||
if extension:
|
|
||||||
guess = guess_extension(gtype, strict)
|
|
||||||
if not guess: print("I don't know anything about type", gtype)
|
|
||||||
else: print(guess)
|
|
||||||
else:
|
|
||||||
guess, encoding = guess_type(gtype, strict)
|
|
||||||
if not guess: print("I don't know anything about type", gtype)
|
|
||||||
else: print('type:', guess, 'encoding:', encoding)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -3,9 +3,11 @@ import mimetypes
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import unittest.mock
|
import unittest.mock
|
||||||
|
from os import linesep
|
||||||
|
|
||||||
from test import support
|
from test import support
|
||||||
from test.support import os_helper
|
from test.support import os_helper
|
||||||
|
from test.support.script_helper import run_python_until_end
|
||||||
from platform import win32_edition
|
from platform import win32_edition
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -390,50 +392,53 @@ class MiscTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
class MimetypesCliTestCase(unittest.TestCase):
|
class MimetypesCliTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def mimetypes_cmd(self, *args, **kwargs):
|
def mimetypes_cmd(cls, *args, **kwargs):
|
||||||
support.patch(self, sys, "argv", [sys.executable, *args])
|
result, _ = run_python_until_end('-m', 'mimetypes', *args)
|
||||||
with support.captured_stdout() as output:
|
return result.rc, result.out.decode(), result.err.decode()
|
||||||
mimetypes._main()
|
|
||||||
return output.getvalue().strip()
|
|
||||||
|
|
||||||
def test_help_option(self):
|
def test_help_option(self):
|
||||||
support.patch(self, sys, "argv", [sys.executable, "-h"])
|
retcode, out, err = self.mimetypes_cmd('-h')
|
||||||
with support.captured_stdout() as output:
|
self.assertEqual(retcode, 0)
|
||||||
with self.assertRaises(SystemExit) as cm:
|
self.assertStartsWith(out, 'usage: ')
|
||||||
mimetypes._main()
|
self.assertEqual(err, '')
|
||||||
|
|
||||||
self.assertIn("Usage: mimetypes.py", output.getvalue())
|
|
||||||
self.assertEqual(cm.exception.code, 0)
|
|
||||||
|
|
||||||
def test_invalid_option(self):
|
def test_invalid_option(self):
|
||||||
support.patch(self, sys, "argv", [sys.executable, "--invalid"])
|
retcode, out, err = self.mimetypes_cmd('--invalid')
|
||||||
with support.captured_stdout() as output:
|
self.assertEqual(retcode, 2)
|
||||||
with self.assertRaises(SystemExit) as cm:
|
self.assertEqual(out, '')
|
||||||
mimetypes._main()
|
self.assertStartsWith(err, 'usage: ')
|
||||||
|
|
||||||
self.assertIn("Usage: mimetypes.py", output.getvalue())
|
|
||||||
self.assertEqual(cm.exception.code, 1)
|
|
||||||
|
|
||||||
def test_guess_extension(self):
|
def test_guess_extension(self):
|
||||||
eq = self.assertEqual
|
retcode, out, err = self.mimetypes_cmd('-l', '-e', 'image/jpg')
|
||||||
|
self.assertEqual(retcode, 0)
|
||||||
|
self.assertEqual(out, f'.jpg{linesep}')
|
||||||
|
self.assertEqual(err, '')
|
||||||
|
|
||||||
extension = self.mimetypes_cmd("-l", "-e", "image/jpg")
|
retcode, out, err = self.mimetypes_cmd('-e', 'image/jpg')
|
||||||
eq(extension, ".jpg")
|
self.assertEqual(retcode, 1)
|
||||||
|
self.assertEqual(out, '')
|
||||||
|
self.assertEqual(err, f'error: unknown type image/jpg{linesep}')
|
||||||
|
|
||||||
extension = self.mimetypes_cmd("-e", "image/jpg")
|
retcode, out, err = self.mimetypes_cmd('-e', 'image/jpeg')
|
||||||
eq(extension, "I don't know anything about type image/jpg")
|
self.assertEqual(retcode, 0)
|
||||||
|
self.assertEqual(out, f'.jpg{linesep}')
|
||||||
extension = self.mimetypes_cmd("-e", "image/jpeg")
|
self.assertEqual(err, '')
|
||||||
eq(extension, ".jpg")
|
|
||||||
|
|
||||||
def test_guess_type(self):
|
def test_guess_type(self):
|
||||||
eq = self.assertEqual
|
retcode, out, err = self.mimetypes_cmd('-l', 'foo.webp')
|
||||||
|
self.assertEqual(retcode, 0)
|
||||||
|
self.assertEqual(out, f'type: image/webp encoding: None{linesep}')
|
||||||
|
self.assertEqual(err, '')
|
||||||
|
|
||||||
type_info = self.mimetypes_cmd("-l", "foo.pic")
|
@unittest.skipIf(
|
||||||
eq(type_info, "type: image/pict encoding: None")
|
sys.platform == 'darwin',
|
||||||
|
'macOS lists common_types in mime.types thus making them always known'
|
||||||
type_info = self.mimetypes_cmd("foo.pic")
|
)
|
||||||
eq(type_info, "I don't know anything about type foo.pic")
|
def test_guess_type_conflicting_with_mimetypes(self):
|
||||||
|
retcode, out, err = self.mimetypes_cmd('foo.pic')
|
||||||
|
self.assertEqual(retcode, 1)
|
||||||
|
self.assertEqual(out, '')
|
||||||
|
self.assertEqual(err, f'error: media type unknown for foo.pic{linesep}')
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -849,6 +849,7 @@ Oleg Höfling
|
|||||||
Robert Hölzl
|
Robert Hölzl
|
||||||
Stefan Hölzl
|
Stefan Hölzl
|
||||||
Catalin Iacob
|
Catalin Iacob
|
||||||
|
Oleg Iarygin
|
||||||
Mihai Ibanescu
|
Mihai Ibanescu
|
||||||
Ali Ikinci
|
Ali Ikinci
|
||||||
Aaron Iles
|
Aaron Iles
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
Document the command-line for :mod:`mimetypes`.
|
||||||
|
It now exits with ``1`` on failure instead of ``0``
|
||||||
|
and ``2`` on incorrect command-line parameters instead of ``1``.
|
||||||
|
Also, errors are printed to stderr instead of stdout and their text is made
|
||||||
|
tighter. Patch by Oleg Iarygin and Hugo van Kemenade.
|
Loading…
x
Reference in New Issue
Block a user