Kaydet (Commit) 2a17bde9 authored tarafından Brett Cannon's avatar Brett Cannon

Issue #20383: Introduce importlib.util.module_from_spec().

Along the way, dismantle importlib._bootstrap._SpecMethods as it was
no longer relevant and constructing the new function required
partially dismantling the class anyway.
üst c8f0d6eb
......@@ -1129,6 +1129,21 @@ an :term:`importer`.
.. versionadded:: 3.4
.. function:: module_from_spec(spec)
Create a new module based on **spec**.
If the module object is from ``spec.loader.create_module()``, then any
pre-existing attributes will not be reset. Also, no :exc:`AttributeError`
will be raised if triggered while accessing **spec** or setting an attribute
on the module.
This function is preferred over using :class:`types.ModuleType` to create a
new module as **spec** is used to set as many import-controlled attributes on
the module as possible.
.. versionadded:: 3.5
.. decorator:: module_for_loader
A :term:`decorator` for :meth:`importlib.abc.Loader.load_module`
......
......@@ -115,6 +115,10 @@ Standard names are defined for the following types:
The type of :term:`modules <module>`. Constructor takes the name of the
module to be created and optionally its :term:`docstring`.
.. note::
Use :func:`importlib.util.module_from_spec` to create a new module if you
wish to set the various import-controlled attributes.
.. attribute:: __doc__
The :term:`docstring` of the module. Defaults to ``None``.
......
......@@ -153,6 +153,10 @@ importlib
With a module object that you want to initialize you can then use
``exec(code, module.__dict__)`` to execute the code in the module.
* :func:`importlib.util.module_from_spec` is now the preferred way to create a
new module. Compared to :class:`types.ModuleType`, this new function will set
the various import-controlled attributes based on the passed-in spec object.
inspect
-------
......
......@@ -16,7 +16,7 @@ except ImportError:
# Platform doesn't support dynamic loading.
load_dynamic = None
from importlib._bootstrap import SourcelessFileLoader, _ERR_MSG, _SpecMethods
from importlib._bootstrap import SourcelessFileLoader, _ERR_MSG, _exec, _load
from importlib import machinery
from importlib import util
......@@ -164,11 +164,10 @@ class _LoadSourceCompatibility(_HackedGetData, machinery.SourceFileLoader):
def load_source(name, pathname, file=None):
loader = _LoadSourceCompatibility(name, pathname, file)
spec = util.spec_from_file_location(name, pathname, loader=loader)
methods = _SpecMethods(spec)
if name in sys.modules:
module = methods.exec(sys.modules[name])
module = _exec(spec, sys.modules[name])
else:
module = methods.load()
module = _load(spec)
# To allow reloading to potentially work, use a non-hacked loader which
# won't rely on a now-closed file object.
module.__loader__ = machinery.SourceFileLoader(name, pathname)
......@@ -185,11 +184,10 @@ def load_compiled(name, pathname, file=None):
"""**DEPRECATED**"""
loader = _LoadCompiledCompatibility(name, pathname, file)
spec = util.spec_from_file_location(name, pathname, loader=loader)
methods = _SpecMethods(spec)
if name in sys.modules:
module = methods.exec(sys.modules[name])
module = _exec(spec, sys.modules[name])
else:
module = methods.load()
module = _load(spec)
# To allow reloading to potentially work, use a non-hacked loader which
# won't rely on a now-closed file object.
module.__loader__ = SourcelessFileLoader(name, pathname)
......@@ -210,11 +208,10 @@ def load_package(name, path):
raise ValueError('{!r} is not a package'.format(path))
spec = util.spec_from_file_location(name, path,
submodule_search_locations=[])
methods = _SpecMethods(spec)
if name in sys.modules:
return methods.exec(sys.modules[name])
return _exec(spec, sys.modules[name])
else:
return methods.load()
return _load(spec)
def load_module(name, file, filename, details):
......
......@@ -145,8 +145,7 @@ def reload(module):
pkgpath = None
target = module
spec = module.__spec__ = _bootstrap._find_spec(name, pkgpath, target)
methods = _bootstrap._SpecMethods(spec)
methods.exec(module)
_bootstrap._exec(spec, module)
# The module may have replaced itself in sys.modules!
return sys.modules[name]
finally:
......
This diff is collapsed.
......@@ -126,7 +126,7 @@ class Loader(metaclass=abc.ABCMeta):
create_module() is optional.
"""
# By default, defer to _SpecMethods.create() for the new module.
# By default, defer to default semantics for the new module.
return None
# We don't define exec_module() here since that would break
......
......@@ -3,6 +3,7 @@ from . import abc
from ._bootstrap import MAGIC_NUMBER
from ._bootstrap import cache_from_source
from ._bootstrap import decode_source
from ._bootstrap import module_from_spec
from ._bootstrap import source_from_cache
from ._bootstrap import spec_from_loader
from ._bootstrap import spec_from_file_location
......
......@@ -616,7 +616,7 @@ def get_data(package, resource):
return None
# XXX needs test
mod = (sys.modules.get(package) or
importlib._bootstrap._SpecMethods(spec).load())
importlib._bootstrap._load(spec))
if mod is None or not hasattr(mod, '__file__'):
return None
......
......@@ -263,9 +263,8 @@ def synopsis(filename, cache={}):
# XXX We probably don't need to pass in the loader here.
spec = importlib.util.spec_from_file_location('__temp__', filename,
loader=loader)
_spec = importlib._bootstrap._SpecMethods(spec)
try:
module = _spec.load()
module = importlib._bootstrap._load(spec)
except:
return None
del sys.modules['__temp__']
......@@ -297,9 +296,8 @@ def importfile(path):
loader = importlib._bootstrap.SourceFileLoader(name, path)
# XXX We probably don't need to pass in the loader here.
spec = importlib.util.spec_from_file_location(name, path, loader=loader)
_spec = importlib._bootstrap._SpecMethods(spec)
try:
return _spec.load()
return importlib._bootstrap._load(spec)
except:
raise ErrorDuringImport(path, sys.exc_info())
......@@ -2057,9 +2055,8 @@ class ModuleScanner:
else:
path = None
else:
_spec = importlib._bootstrap._SpecMethods(spec)
try:
module = _spec.load()
module = importlib._bootstrap._load(spec)
except ImportError:
if onerror:
onerror(modname)
......
......@@ -58,7 +58,7 @@ class _ModifiedArgv0(object):
self.value = self._sentinel
sys.argv[0] = self._saved_value
# TODO: Replace these helpers with importlib._bootstrap._SpecMethods
# TODO: Replace these helpers with importlib._bootstrap functions
def _run_code(code, run_globals, init_globals=None,
mod_name=None, mod_spec=None,
pkg_name=None, script_name=None):
......
......@@ -407,7 +407,7 @@ class PdepsTests(unittest.TestCase):
def setUpClass(self):
path = os.path.join(scriptsdir, 'pdeps.py')
spec = importlib.util.spec_from_file_location('pdeps', path)
self.pdeps = importlib._bootstrap._SpecMethods(spec).load()
self.pdeps = importlib._bootstrap._load(spec)
@classmethod
def tearDownClass(self):
......@@ -432,7 +432,7 @@ class Gprof2htmlTests(unittest.TestCase):
def setUp(self):
path = os.path.join(scriptsdir, 'gprof2html.py')
spec = importlib.util.spec_from_file_location('gprof2html', path)
self.gprof = importlib._bootstrap._SpecMethods(spec).load()
self.gprof = importlib._bootstrap._load(spec)
oldargv = sys.argv
def fixup():
sys.argv = oldargv
......
......@@ -89,6 +89,9 @@ Core and Builtins
Library
-------
- Issue #20383: Introduce importlib.util.module_from_spec() as the preferred way
to create a new module.
- Issue #21552: Fixed possible integer overflow of too long string lengths in
the tkinter module on 64-bit platforms.
......
This diff is collapsed.
......@@ -342,7 +342,7 @@ class PyBuildExt(build_ext):
spec = importlib.util.spec_from_file_location(ext.name, ext_filename,
loader=loader)
try:
importlib._bootstrap._SpecMethods(spec).load()
importlib._bootstrap._load(spec)
except ImportError as why:
self.failed_on_import.append(ext.name)
self.announce('*** WARNING: renaming "%s" since importing it'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment