From d5b34d4597a7163edf20f831db620b0a4fe57d5d Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 15 Nov 2009 14:10:48 +0000 Subject: [PATCH] Issue #4969: The mimetypes module now reads the MIME database from the registry under Windows. Patch by Gabriel Genellina. --- Doc/library/mimetypes.rst | 22 +++++++++++++-- Lib/mimetypes.py | 56 +++++++++++++++++++++++++++++++++++++- Lib/test/test_mimetypes.py | 26 +++++++++++++++++- Misc/NEWS | 3 ++ 4 files changed, 102 insertions(+), 5 deletions(-) diff --git a/Doc/library/mimetypes.rst b/Doc/library/mimetypes.rst index 818f39f9cbb..956a1f178e8 100644 --- a/Doc/library/mimetypes.rst +++ b/Doc/library/mimetypes.rst @@ -77,9 +77,13 @@ behavior of the module. Initialize the internal data structures. If given, *files* must be a sequence of file names which should be used to augment the default type map. If omitted, - the file names to use are taken from :const:`knownfiles`. Each file named in - *files* or :const:`knownfiles` takes precedence over those named before it. - Calling :func:`init` repeatedly is allowed. + the file names to use are taken from :const:`knownfiles`; on Windows, the + current registry settings are loaded. Each file named in *files* or + :const:`knownfiles` takes precedence over those named before it. Calling + :func:`init` repeatedly is allowed. + + .. versionchanged:: 2.7 + Previously, Windows registry settings were ignored. .. function:: read_mime_types(filename) @@ -213,6 +217,12 @@ MimeTypes Objects of the object. +.. method:: MimeTypes.guess_all_extensions(type[, strict]) + + Similar to the :func:`guess_all_extensions` function, using the tables stored as part + of the object. + + .. method:: MimeTypes.guess_type(url[, strict]) Similar to the :func:`guess_type` function, using the tables stored as part of @@ -230,3 +240,9 @@ MimeTypes Objects Load MIME type information from an open file. The file must have the format of the standard :file:`mime.types` files. + +.. method:: MimeTypes.read_windows_registry() + + Load MIME type information from the Windows registry. Availability: Windows. + + .. versionadded:: 2.7 diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index f0a5ecf7bba..aead6566a58 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -18,13 +18,19 @@ types_map -- dictionary mapping suffixes to types Functions: -init([files]) -- parse a list of files, default knownfiles +init([files]) -- parse a list of files, default knownfiles (on Windows, the + default values are taken from the registry) read_mime_types(file) -- parse one file, return a dictionary or None """ import os +import sys import posixpath import urllib +try: + import _winreg +except ImportError: + _winreg = None __all__ = [ "guess_type","guess_extension","guess_all_extensions", @@ -220,6 +226,52 @@ class MimeTypes: for suff in suffixes: self.add_type(type, '.' + suff, strict) + def read_windows_registry(self, strict=True): + """ + Load the MIME types database from Windows registry. + + If strict is true, information will be added to + list of standard types, else to the list of non-standard + types. + """ + + # Windows only + if not _winreg: + return + + def enum_types(mimedb): + i = 0 + while True: + try: + ctype = _winreg.EnumKey(mimedb, i) + except EnvironmentError: + break + try: + ctype = ctype.encode(default_encoding) # omit in 3.x! + except UnicodeEncodeError: + pass + else: + yield ctype + i += 1 + + default_encoding = sys.getdefaultencoding() + with _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT, + r'MIME\Database\Content Type') as mimedb: + for ctype in enum_types(mimedb): + with _winreg.OpenKey(mimedb, ctype) as key: + try: + suffix, datatype = _winreg.QueryValueEx(key, 'Extension') + except EnvironmentError: + continue + if datatype != _winreg.REG_SZ: + continue + try: + suffix = suffix.encode(default_encoding) # omit in 3.x! + except UnicodeEncodeError: + continue + self.add_type(ctype, suffix, strict) + + def guess_type(url, strict=True): """Guess the type of a file based on its URL. @@ -299,6 +351,8 @@ def init(files=None): inited = True # so that MimeTypes.__init__() doesn't call us again db = MimeTypes() if files is None: + if _winreg: + db.read_windows_registry() files = knownfiles for file in files: if os.path.isfile(file): diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index cc1790e0dff..3508b560568 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -1,6 +1,7 @@ import mimetypes import StringIO import unittest +import sys from test import test_support @@ -62,8 +63,31 @@ class MimeTypesTestCase(unittest.TestCase): eq(all, []) +@unittest.skipUnless(sys.platform.startswith("win"), "Windows only") +class Win32MimeTypesTestCase(unittest.TestCase): + def setUp(self): + # ensure all entries actually come from the Windows registry + self.original_types_map = mimetypes.types_map.copy() + mimetypes.types_map.clear() + mimetypes.init() + self.db = mimetypes.MimeTypes() + + def tearDown(self): + # restore default settings + mimetypes.types_map.clear() + mimetypes.types_map.update(self.original_types_map) + + def test_registry_parsing(self): + # the original, minimum contents of the MIME database in the + # Windows registry is undocumented AFAIK. + # Use file types that should *always* exist: + eq = self.assertEqual + eq(self.db.guess_type("foo.txt"), ("text/plain", None)) + def test_main(): - test_support.run_unittest(MimeTypesTestCase) + test_support.run_unittest(MimeTypesTestCase, + Win32MimeTypesTestCase + ) if __name__ == "__main__": diff --git a/Misc/NEWS b/Misc/NEWS index 3feccda6f66..051bff81a66 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -429,6 +429,9 @@ Core and Builtins Library ------- +- Issue #4969: The mimetypes module now reads the MIME database from + the registry under Windows. Patch by Gabriel Genellina. + - Issue #6816: runpy now provides a run_path function that allows Python code to execute file paths that refer to source or compiled Python files as well as zipfiles, directories and other valid sys.path entries that contain a