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().
This commit is contained in:
Brett Cannon 2012-05-11 14:48:41 -04:00
parent 0c59b039b8
commit c049952de7
9 changed files with 2485 additions and 2392 deletions

View File

@ -256,9 +256,14 @@ are also provided to help in implementing the core ABCs.
Path to the file of the module. 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) .. method:: get_filename(fullname)
Returns :attr:`path`. Returns :attr:`path` when ``fullname`` equals :attr:`name` or ``None``.
.. method:: get_data(path) .. method:: get_data(path)
@ -638,10 +643,6 @@ find and load modules.
Concrete implementation of :meth:`importlib.abc.SourceLoader.set_data`. 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) .. class:: SourcelessFileLoader(fullname, path)
@ -676,10 +677,6 @@ find and load modules.
Returns ``None`` as bytecode files have no source when this loader is Returns ``None`` as bytecode files have no source when this loader is
used. used.
.. method:: load_module(fullname)
Loads the specified module if it is the same as :attr:`name`.
.. class:: ExtensionFileLoader(fullname, path) .. class:: ExtensionFileLoader(fullname, path)
@ -699,10 +696,10 @@ find and load modules.
Path to the extension module. 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 Loads the extension module if and only if *fullname* is the same as
:attr:`name`. :attr:`name` or is ``None``.
.. method:: is_package(fullname) .. method:: is_package(fullname)

View File

@ -24,8 +24,7 @@ import tokenize
import warnings import warnings
# XXX "deprecate" once find_module(), load_module(), and get_suffixes() are # DEPRECATED
# deprecated.
SEARCH_ERROR = 0 SEARCH_ERROR = 0
PY_SOURCE = 1 PY_SOURCE = 1
PY_COMPILED = 2 PY_COMPILED = 2
@ -112,8 +111,11 @@ class _LoadSourceCompatibility(_HackedGetData, _bootstrap.SourceFileLoader):
"""Compatibility support for implementing load_source().""" """Compatibility support for implementing load_source()."""
# XXX deprecate after better API exposed in importlib
def load_source(name, pathname, file=None): 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) return _LoadSourceCompatibility(name, pathname, file).load_module(name)
@ -123,15 +125,22 @@ class _LoadCompiledCompatibility(_HackedGetData,
"""Compatibility support for implementing load_compiled().""" """Compatibility support for implementing load_compiled()."""
# XXX deprecate
def load_compiled(name, pathname, file=None): 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) return _LoadCompiledCompatibility(name, pathname, file).load_module(name)
# XXX deprecate
def load_package(name, path): 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): if os.path.isdir(path):
extensions = machinery.SOURCE_SUFFIXES[:] + [machinery.BYTECODE_SUFFIXES] extensions = (machinery.SOURCE_SUFFIXES[:] +
machinery.BYTECODE_SUFFIXES[:])
for extension in extensions: for extension in extensions:
path = os.path.join(path, '__init__'+extension) path = os.path.join(path, '__init__'+extension)
if os.path.exists(path): if os.path.exists(path):
@ -149,26 +158,29 @@ def load_module(name, file, filename, details):
""" """
suffix, mode, type_ = details suffix, mode, type_ = details
if mode and (not mode.startswith(('r', 'U')) or '+' in mode): with warnings.catch_warnings():
raise ValueError('invalid file open mode {!r}'.format(mode)) warnings.simplefilter('ignore')
elif file is None and type_ in {PY_SOURCE, PY_COMPILED}: if mode and (not mode.startswith(('r', 'U')) or '+' in mode):
msg = 'file object required for import (type code {})'.format(type_) raise ValueError('invalid file open mode {!r}'.format(mode))
raise ValueError(msg) elif file is None and type_ in {PY_SOURCE, PY_COMPILED}:
elif type_ == PY_SOURCE: msg = 'file object required for import (type code {})'.format(type_)
return load_source(name, filename, file) raise ValueError(msg)
elif type_ == PY_COMPILED: elif type_ == PY_SOURCE:
return load_compiled(name, filename, file) return load_source(name, filename, file)
elif type_ == PKG_DIRECTORY: elif type_ == PY_COMPILED:
return load_package(name, filename) return load_compiled(name, filename, file)
elif type_ == C_BUILTIN: elif type_ == PKG_DIRECTORY:
return init_builtin(name) return load_package(name, filename)
elif type_ == PY_FROZEN: elif type_ == C_BUILTIN:
return init_frozen(name) return init_builtin(name)
else: elif type_ == PY_FROZEN:
msg = "Don't know how to import {} (type code {}".format(name, type_) return init_frozen(name)
raise ImportError(msg, name=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): def find_module(name, path=None):
"""Search for a module. """Search for a module.

View File

@ -282,8 +282,10 @@ def _check_name(method):
compared against. If the comparison fails then ImportError is raised. compared against. If the comparison fails then ImportError is raised.
""" """
def _check_name_wrapper(self, name, *args, **kwargs): def _check_name_wrapper(self, name=None, *args, **kwargs):
if self.name != name: if name is None:
name = self.name
elif self.name != name:
raise ImportError("loader cannot handle %s" % name, name=name) raise ImportError("loader cannot handle %s" % name, name=name)
return method(self, name, *args, **kwargs) return method(self, name, *args, **kwargs)
_wrap(_check_name_wrapper, method) _wrap(_check_name_wrapper, method)
@ -613,6 +615,11 @@ class FileLoader:
self.name = fullname self.name = fullname
self.path = path self.path = path
@_check_name
def load_module(self, fullname):
"""Load a module from a file."""
return super().load_module(fullname)
@_check_name @_check_name
def get_filename(self, fullname): def get_filename(self, fullname):
"""Return the path to the source file as found by the finder.""" """Return the path to the source file as found by the finder."""
@ -713,17 +720,14 @@ class ExtensionFileLoader:
del sys.modules[fullname] del sys.modules[fullname]
raise raise
@_check_name
def is_package(self, fullname): def is_package(self, fullname):
"""Return False as an extension module can never be a package.""" """Return False as an extension module can never be a package."""
return False return False
@_check_name
def get_code(self, fullname): def get_code(self, fullname):
"""Return None as an extension module cannot create a code object.""" """Return None as an extension module cannot create a code object."""
return None return None
@_check_name
def get_source(self, fullname): def get_source(self, fullname):
"""Return None as extension modules have no source code.""" """Return None as extension modules have no source code."""
return None return None

View File

@ -1,4 +1,4 @@
from importlib import _bootstrap from importlib import machinery
from . import util as ext_util from . import util as ext_util
from .. import abc from .. import abc
from .. import util from .. import util
@ -11,10 +11,20 @@ class LoaderTests(abc.LoaderTests):
"""Test load_module() for extension modules.""" """Test load_module() for extension modules."""
def setUp(self):
self.loader = machinery.ExtensionFileLoader(ext_util.NAME,
ext_util.FILEPATH)
def load_module(self, fullname): def load_module(self, fullname):
loader = _bootstrap.ExtensionFileLoader(ext_util.NAME, return self.loader.load_module(fullname)
ext_util.FILEPATH)
return 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): def test_module(self):
with util.uncache(ext_util.NAME): with util.uncache(ext_util.NAME):
@ -25,7 +35,7 @@ class LoaderTests(abc.LoaderTests):
self.assertEqual(getattr(module, attr), value) self.assertEqual(getattr(module, attr), value)
self.assertTrue(ext_util.NAME in sys.modules) self.assertTrue(ext_util.NAME in sys.modules)
self.assertTrue(isinstance(module.__loader__, self.assertTrue(isinstance(module.__loader__,
_bootstrap.ExtensionFileLoader)) machinery.ExtensionFileLoader))
def test_package(self): def test_package(self):
# Extensions are not found in packages. # Extensions are not found in packages.

View File

@ -1,5 +1,6 @@
from ... import _bootstrap from ... import _bootstrap
import importlib import importlib
import importlib.abc
from .. import abc from .. import abc
from .. import util from .. import util
from . import util as source_util from . import util as source_util
@ -24,6 +25,40 @@ class SimpleTest(unittest.TestCase):
""" """
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] # [basic]
def test_module(self): def test_module(self):
with source_util.create_modules('_temp') as mapping: with source_util.create_modules('_temp') as mapping:

View File

@ -1,11 +1,12 @@
import imp import imp
import importlib
import os import os
import os.path import os.path
import shutil import shutil
import sys import sys
import unittest
from test import support from test import support
import importlib import unittest
import warnings
class LockTests(unittest.TestCase): class LockTests(unittest.TestCase):
@ -154,18 +155,24 @@ class ImportTests(unittest.TestCase):
mod = imp.load_module(temp_mod_name, file, filename, info) mod = imp.load_module(temp_mod_name, file, filename, info)
self.assertEqual(mod.a, 1) 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) self.assertEqual(mod.a, 1)
mod = imp.load_compiled( with warnings.catch_warnings():
temp_mod_name, imp.cache_from_source(temp_mod_name + '.py')) warnings.simplefilter('ignore')
mod = imp.load_compiled(
temp_mod_name, imp.cache_from_source(temp_mod_name + '.py'))
self.assertEqual(mod.a, 1) self.assertEqual(mod.a, 1)
if not os.path.exists(test_package_name): if not os.path.exists(test_package_name):
os.mkdir(test_package_name) os.mkdir(test_package_name)
with open(init_file_name, 'w') as file: with open(init_file_name, 'w') as file:
file.write('b = 2\n') 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) self.assertEqual(package.b, 2)
finally: finally:
del sys.path[0] del sys.path[0]

View File

@ -6,7 +6,7 @@ Tools directory of a Python checkout or tarball, such as reindent.py.
import os import os
import sys import sys
import imp import importlib.machinery
import unittest import unittest
from unittest import mock from unittest import mock
import sysconfig import sysconfig
@ -80,7 +80,8 @@ class PdepsTests(unittest.TestCase):
@classmethod @classmethod
def setUpClass(self): def setUpClass(self):
path = os.path.join(scriptsdir, 'pdeps.py') 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 @classmethod
def tearDownClass(self): def tearDownClass(self):
@ -104,7 +105,8 @@ class Gprof2htmlTests(unittest.TestCase):
def setUp(self): def setUp(self):
path = os.path.join(scriptsdir, 'gprof2html.py') 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 oldargv = sys.argv
def fixup(): def fixup():
sys.argv = oldargv sys.argv = oldargv

View File

@ -23,6 +23,11 @@ Core and Builtins
Library 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 - Issue #13959: imp.get_suffixes() has been deprecated in favour of the new
attributes on importlib.machinery: SOURCE_SUFFIXES, DEBUG_BYTECODE_SUFFIXES, attributes on importlib.machinery: SOURCE_SUFFIXES, DEBUG_BYTECODE_SUFFIXES,
OPTIMIZED_BYTECODE_SUFFIXES, BYTECODE_SUFFIXES, and EXTENSION_SUFFIXES. This OPTIMIZED_BYTECODE_SUFFIXES, BYTECODE_SUFFIXES, and EXTENSION_SUFFIXES. This

File diff suppressed because it is too large Load Diff