changeset: 76877:626d5c6fbd95 user: Brett Cannon date: Fri May 11 14:48:41 2012 -0400 files: Doc/library/importlib.rst Lib/imp.py Lib/importlib/_bootstrap.py Lib/importlib/test/extension/test_loader.py Lib/importlib/test/source/test_file_loader.py Lib/test/test_imp.py Lib/test/test_tools.py Misc/NEWS Python/importlib.h description: Issue #13959: Have importlib.abc.FileLoader.load_module()/get_filename() and importlib.machinery.ExtensionFileLoader.load_module() have their single argument be optional as the loader's constructor has all the ncessary information. This allows for the deprecation of imp.load_source()/load_compile()/load_package(). diff -r f11015632f6d -r 626d5c6fbd95 Doc/library/importlib.rst --- a/Doc/library/importlib.rst Fri May 11 14:27:29 2012 -0400 +++ b/Doc/library/importlib.rst Fri May 11 14:48:41 2012 -0400 @@ -256,9 +256,14 @@ Path to the file of the module. + .. method:: load_module(fullname=None) + + Calls + ``super().load_module(fullname if fullname is not None else self.name)``. + .. method:: get_filename(fullname) - Returns :attr:`path`. + Returns :attr:`path` when ``fullname`` equals :attr:`name` or ``None``. .. method:: get_data(path) @@ -638,10 +643,6 @@ Concrete implementation of :meth:`importlib.abc.SourceLoader.set_data`. - .. method:: load_module(fullname) - - Load the specified module if it is the same as :attr:`name`. - .. class:: SourcelessFileLoader(fullname, path) @@ -676,10 +677,6 @@ Returns ``None`` as bytecode files have no source when this loader is used. - .. method:: load_module(fullname) - - Loads the specified module if it is the same as :attr:`name`. - .. class:: ExtensionFileLoader(fullname, path) @@ -699,10 +696,10 @@ Path to the extension module. - .. method:: load_module(fullname) + .. method:: load_module(fullname=None) - Loads the extension module if and only if *fullname** is the same as - :attr:`name`. + Loads the extension module if and only if *fullname* is the same as + :attr:`name` or is ``None``. .. method:: is_package(fullname) diff -r f11015632f6d -r 626d5c6fbd95 Lib/imp.py --- a/Lib/imp.py Fri May 11 14:27:29 2012 -0400 +++ b/Lib/imp.py Fri May 11 14:48:41 2012 -0400 @@ -24,8 +24,7 @@ import warnings -# XXX "deprecate" once find_module(), load_module(), and get_suffixes() are -# deprecated. +# DEPRECATED SEARCH_ERROR = 0 PY_SOURCE = 1 PY_COMPILED = 2 @@ -112,8 +111,11 @@ """Compatibility support for implementing load_source().""" -# XXX deprecate after better API exposed in importlib def load_source(name, pathname, file=None): + msg = ('imp.load_source() is deprecated; use ' + 'importlib.machinery.SourceFileLoader(name, pathname).load_module()' + ' instead') + warnings.warn(msg, DeprecationWarning, 2) return _LoadSourceCompatibility(name, pathname, file).load_module(name) @@ -123,15 +125,22 @@ """Compatibility support for implementing load_compiled().""" -# XXX deprecate def load_compiled(name, pathname, file=None): + msg = ('imp.load_compiled() is deprecated; use ' + 'importlib.machinery.SourcelessFileLoader(name, pathname).' + 'load_module() instead ') + warnings.warn(msg, DeprecationWarning, 2) return _LoadCompiledCompatibility(name, pathname, file).load_module(name) -# XXX deprecate def load_package(name, path): + msg = ('imp.load_package() is deprecated; use either ' + 'importlib.machinery.SourceFileLoader() or ' + 'importlib.machinery.SourcelessFileLoader() instead') + warnings.warn(msg, DeprecationWarning, 2) if os.path.isdir(path): - extensions = machinery.SOURCE_SUFFIXES[:] + [machinery.BYTECODE_SUFFIXES] + extensions = (machinery.SOURCE_SUFFIXES[:] + + machinery.BYTECODE_SUFFIXES[:]) for extension in extensions: path = os.path.join(path, '__init__'+extension) if os.path.exists(path): @@ -149,26 +158,29 @@ """ suffix, mode, type_ = details - if mode and (not mode.startswith(('r', 'U')) or '+' in mode): - raise ValueError('invalid file open mode {!r}'.format(mode)) - elif file is None and type_ in {PY_SOURCE, PY_COMPILED}: - msg = 'file object required for import (type code {})'.format(type_) - raise ValueError(msg) - elif type_ == PY_SOURCE: - return load_source(name, filename, file) - elif type_ == PY_COMPILED: - return load_compiled(name, filename, file) - elif type_ == PKG_DIRECTORY: - return load_package(name, filename) - elif type_ == C_BUILTIN: - return init_builtin(name) - elif type_ == PY_FROZEN: - return init_frozen(name) - else: - msg = "Don't know how to import {} (type code {}".format(name, type_) - raise ImportError(msg, name=name) + with warnings.catch_warnings(): + warnings.simplefilter('ignore') + if mode and (not mode.startswith(('r', 'U')) or '+' in mode): + raise ValueError('invalid file open mode {!r}'.format(mode)) + elif file is None and type_ in {PY_SOURCE, PY_COMPILED}: + msg = 'file object required for import (type code {})'.format(type_) + raise ValueError(msg) + elif type_ == PY_SOURCE: + return load_source(name, filename, file) + elif type_ == PY_COMPILED: + return load_compiled(name, filename, file) + elif type_ == PKG_DIRECTORY: + return load_package(name, filename) + elif type_ == C_BUILTIN: + return init_builtin(name) + elif type_ == PY_FROZEN: + return init_frozen(name) + else: + msg = "Don't know how to import {} (type code {}".format(name, type_) + raise ImportError(msg, name=name) +# XXX deprecate def find_module(name, path=None): """Search for a module. diff -r f11015632f6d -r 626d5c6fbd95 Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py Fri May 11 14:27:29 2012 -0400 +++ b/Lib/importlib/_bootstrap.py Fri May 11 14:48:41 2012 -0400 @@ -282,8 +282,10 @@ compared against. If the comparison fails then ImportError is raised. """ - def _check_name_wrapper(self, name, *args, **kwargs): - if self.name != name: + def _check_name_wrapper(self, name=None, *args, **kwargs): + if name is None: + name = self.name + elif self.name != name: raise ImportError("loader cannot handle %s" % name, name=name) return method(self, name, *args, **kwargs) _wrap(_check_name_wrapper, method) @@ -614,6 +616,11 @@ self.path = path @_check_name + def load_module(self, fullname): + """Load a module from a file.""" + return super().load_module(fullname) + + @_check_name def get_filename(self, fullname): """Return the path to the source file as found by the finder.""" return self.path @@ -713,17 +720,14 @@ del sys.modules[fullname] raise - @_check_name def is_package(self, fullname): """Return False as an extension module can never be a package.""" return False - @_check_name def get_code(self, fullname): """Return None as an extension module cannot create a code object.""" return None - @_check_name def get_source(self, fullname): """Return None as extension modules have no source code.""" return None diff -r f11015632f6d -r 626d5c6fbd95 Lib/importlib/test/extension/test_loader.py --- a/Lib/importlib/test/extension/test_loader.py Fri May 11 14:27:29 2012 -0400 +++ b/Lib/importlib/test/extension/test_loader.py Fri May 11 14:48:41 2012 -0400 @@ -1,4 +1,4 @@ -from importlib import _bootstrap +from importlib import machinery from . import util as ext_util from .. import abc from .. import util @@ -11,10 +11,20 @@ """Test load_module() for extension modules.""" + def setUp(self): + self.loader = machinery.ExtensionFileLoader(ext_util.NAME, + ext_util.FILEPATH) + def load_module(self, fullname): - loader = _bootstrap.ExtensionFileLoader(ext_util.NAME, - ext_util.FILEPATH) - return loader.load_module(fullname) + return self.loader.load_module(fullname) + + def test_load_module_API(self): + # Test the default argument for load_module(). + self.loader.load_module() + self.loader.load_module(None) + with self.assertRaises(ImportError): + self.load_module('XXX') + def test_module(self): with util.uncache(ext_util.NAME): @@ -25,7 +35,7 @@ self.assertEqual(getattr(module, attr), value) self.assertTrue(ext_util.NAME in sys.modules) self.assertTrue(isinstance(module.__loader__, - _bootstrap.ExtensionFileLoader)) + machinery.ExtensionFileLoader)) def test_package(self): # Extensions are not found in packages. diff -r f11015632f6d -r 626d5c6fbd95 Lib/importlib/test/source/test_file_loader.py --- a/Lib/importlib/test/source/test_file_loader.py Fri May 11 14:27:29 2012 -0400 +++ b/Lib/importlib/test/source/test_file_loader.py Fri May 11 14:48:41 2012 -0400 @@ -1,5 +1,6 @@ from ... import _bootstrap import importlib +import importlib.abc from .. import abc from .. import util from . import util as source_util @@ -24,6 +25,40 @@ """ + def test_load_module_API(self): + # If fullname is not specified that assume self.name is desired. + class TesterMixin(importlib.abc.Loader): + def load_module(self, fullname): return fullname + + class Tester(importlib.abc.FileLoader, TesterMixin): + def get_code(self, _): pass + def get_source(self, _): pass + def is_package(self, _): pass + + name = 'mod_name' + loader = Tester(name, 'some_path') + self.assertEqual(name, loader.load_module()) + self.assertEqual(name, loader.load_module(None)) + self.assertEqual(name, loader.load_module(name)) + with self.assertRaises(ImportError): + loader.load_module(loader.name + 'XXX') + + def test_get_filename_API(self): + # If fullname is not set then assume self.path is desired. + class Tester(importlib.abc.FileLoader): + def get_code(self, _): pass + def get_source(self, _): pass + def is_package(self, _): pass + + path = 'some_path' + name = 'some_name' + loader = Tester(name, path) + self.assertEqual(path, loader.get_filename(name)) + self.assertEqual(path, loader.get_filename()) + self.assertEqual(path, loader.get_filename(None)) + with self.assertRaises(ImportError): + loader.get_filename(name + 'XXX') + # [basic] def test_module(self): with source_util.create_modules('_temp') as mapping: diff -r f11015632f6d -r 626d5c6fbd95 Lib/test/test_imp.py --- a/Lib/test/test_imp.py Fri May 11 14:27:29 2012 -0400 +++ b/Lib/test/test_imp.py Fri May 11 14:48:41 2012 -0400 @@ -1,11 +1,12 @@ import imp +import importlib import os import os.path import shutil import sys +from test import support import unittest -from test import support -import importlib +import warnings class LockTests(unittest.TestCase): @@ -154,18 +155,24 @@ mod = imp.load_module(temp_mod_name, file, filename, info) self.assertEqual(mod.a, 1) - mod = imp.load_source(temp_mod_name, temp_mod_name + '.py') + with warnings.catch_warnings(): + warnings.simplefilter('ignore') + mod = imp.load_source(temp_mod_name, temp_mod_name + '.py') self.assertEqual(mod.a, 1) - mod = imp.load_compiled( - temp_mod_name, imp.cache_from_source(temp_mod_name + '.py')) + with warnings.catch_warnings(): + warnings.simplefilter('ignore') + mod = imp.load_compiled( + temp_mod_name, imp.cache_from_source(temp_mod_name + '.py')) self.assertEqual(mod.a, 1) if not os.path.exists(test_package_name): os.mkdir(test_package_name) with open(init_file_name, 'w') as file: file.write('b = 2\n') - package = imp.load_package(test_package_name, test_package_name) + with warnings.catch_warnings(): + warnings.simplefilter('ignore') + package = imp.load_package(test_package_name, test_package_name) self.assertEqual(package.b, 2) finally: del sys.path[0] diff -r f11015632f6d -r 626d5c6fbd95 Lib/test/test_tools.py --- a/Lib/test/test_tools.py Fri May 11 14:27:29 2012 -0400 +++ b/Lib/test/test_tools.py Fri May 11 14:48:41 2012 -0400 @@ -6,7 +6,7 @@ import os import sys -import imp +import importlib.machinery import unittest from unittest import mock import sysconfig @@ -80,7 +80,8 @@ @classmethod def setUpClass(self): path = os.path.join(scriptsdir, 'pdeps.py') - self.pdeps = imp.load_source('pdeps', path) + loader = importlib.machinery.SourceFileLoader('pdeps', path) + self.pdeps = loader.load_module() @classmethod def tearDownClass(self): @@ -104,7 +105,8 @@ def setUp(self): path = os.path.join(scriptsdir, 'gprof2html.py') - self.gprof = imp.load_source('gprof2html', path) + loader = importlib.machinery.SourceFileLoader('gprof2html', path) + self.gprof = loader.load_module() oldargv = sys.argv def fixup(): sys.argv = oldargv diff -r f11015632f6d -r 626d5c6fbd95 Misc/NEWS --- a/Misc/NEWS Fri May 11 14:27:29 2012 -0400 +++ b/Misc/NEWS Fri May 11 14:48:41 2012 -0400 @@ -23,6 +23,11 @@ Library ------- +- Issue #13959: Make importlib.abc.FileLoader.load_module()/get_filename() and + importlib.machinery.ExtensionFileLoader.load_module() have their single + argument be optional. Allows for the replacement (and thus deprecation) of + imp.load_source()/load_package()/load_compiled(). + - Issue #13959: imp.get_suffixes() has been deprecated in favour of the new attributes on importlib.machinery: SOURCE_SUFFIXES, DEBUG_BYTECODE_SUFFIXES, OPTIMIZED_BYTECODE_SUFFIXES, BYTECODE_SUFFIXES, and EXTENSION_SUFFIXES. This diff -r f11015632f6d -r 626d5c6fbd95 Python/importlib.h Binary file Python/importlib.h has changed