Implement PEP 451 (ModuleSpec).
This commit is contained in:
parent
9e6097ebe7
commit
b523f8433a
|
@ -310,11 +310,11 @@ ABC hierarchy::
|
||||||
from the import. If the loader inserted a module and the load fails, it
|
from the import. If the loader inserted a module and the load fails, it
|
||||||
must be removed by the loader from :data:`sys.modules`; modules already
|
must be removed by the loader from :data:`sys.modules`; modules already
|
||||||
in :data:`sys.modules` before the loader began execution should be left
|
in :data:`sys.modules` before the loader began execution should be left
|
||||||
alone (see :func:`importlib.util.module_to_load`).
|
alone (see :func:`importlib.util.module_for_loader`).
|
||||||
|
|
||||||
The loader should set several attributes on the module.
|
The loader should set several attributes on the module.
|
||||||
(Note that some of these attributes can change when a module is
|
(Note that some of these attributes can change when a module is
|
||||||
reloaded; see :meth:`init_module_attrs`):
|
reloaded):
|
||||||
|
|
||||||
- :attr:`__name__`
|
- :attr:`__name__`
|
||||||
The name of the module.
|
The name of the module.
|
||||||
|
@ -357,17 +357,6 @@ ABC hierarchy::
|
||||||
.. versionchanged:: 3.4
|
.. versionchanged:: 3.4
|
||||||
Made optional instead of an abstractmethod.
|
Made optional instead of an abstractmethod.
|
||||||
|
|
||||||
.. method:: init_module_attrs(module)
|
|
||||||
|
|
||||||
Set the :attr:`__loader__` attribute on the module.
|
|
||||||
|
|
||||||
Subclasses overriding this method should set whatever appropriate
|
|
||||||
attributes it can, getting the module's name from :attr:`__name__` when
|
|
||||||
needed. All values should also be overridden so that reloading works as
|
|
||||||
expected.
|
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
|
||||||
|
|
||||||
|
|
||||||
.. class:: ResourceLoader
|
.. class:: ResourceLoader
|
||||||
|
|
||||||
|
@ -442,14 +431,6 @@ ABC hierarchy::
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
.. method:: init_module_attrs(module)
|
|
||||||
|
|
||||||
Set the :attr:`__package__` attribute and :attr:`__path__` attribute to
|
|
||||||
the empty list if appropriate along with what
|
|
||||||
:meth:`importlib.abc.Loader.init_module_attrs` sets.
|
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
|
||||||
|
|
||||||
.. method:: load_module(fullname)
|
.. method:: load_module(fullname)
|
||||||
|
|
||||||
Implementation of :meth:`Loader.load_module`.
|
Implementation of :meth:`Loader.load_module`.
|
||||||
|
@ -474,15 +455,6 @@ ABC hierarchy::
|
||||||
.. versionchanged:: 3.4
|
.. versionchanged:: 3.4
|
||||||
Raises :exc:`ImportError` instead of :exc:`NotImplementedError`.
|
Raises :exc:`ImportError` instead of :exc:`NotImplementedError`.
|
||||||
|
|
||||||
.. method:: init_module_attrs(module)
|
|
||||||
|
|
||||||
Set :attr:`__file__` and if initializing a package then set
|
|
||||||
:attr:`__path__` to ``[os.path.dirname(__file__)]`` along with
|
|
||||||
all attributes set by
|
|
||||||
:meth:`importlib.abc.InspectLoader.init_module_attrs`.
|
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
|
||||||
|
|
||||||
|
|
||||||
.. class:: FileLoader(fullname, path)
|
.. class:: FileLoader(fullname, path)
|
||||||
|
|
||||||
|
@ -599,14 +571,6 @@ ABC hierarchy::
|
||||||
``__init__`` when the file extension is removed **and** the module name
|
``__init__`` when the file extension is removed **and** the module name
|
||||||
itself does not end in ``__init__``.
|
itself does not end in ``__init__``.
|
||||||
|
|
||||||
.. method:: init_module_attr(module)
|
|
||||||
|
|
||||||
Set :attr:`__cached__` using :func:`imp.cache_from_source`. Other
|
|
||||||
attributes set by
|
|
||||||
:meth:`importlib.abc.ExecutionLoader.init_module_attrs`.
|
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
|
||||||
|
|
||||||
|
|
||||||
:mod:`importlib.machinery` -- Importers and path hooks
|
:mod:`importlib.machinery` -- Importers and path hooks
|
||||||
------------------------------------------------------
|
------------------------------------------------------
|
||||||
|
@ -882,6 +846,64 @@ find and load modules.
|
||||||
.. versionadded:: 3.4
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
|
|
||||||
|
.. class:: ModuleSpec(name, loader, *, origin=None, loader_state=None, is_package=None)
|
||||||
|
|
||||||
|
A specification for a module's import-system-related state.
|
||||||
|
|
||||||
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
|
.. attribute:: name
|
||||||
|
|
||||||
|
(``__name__``)
|
||||||
|
|
||||||
|
A string for the fully-qualified name of the module.
|
||||||
|
|
||||||
|
.. attribute:: loader
|
||||||
|
|
||||||
|
(``__loader__``)
|
||||||
|
|
||||||
|
The loader to use for loading. For namespace packages this should be
|
||||||
|
set to None.
|
||||||
|
|
||||||
|
.. attribute:: origin
|
||||||
|
|
||||||
|
(``__file__``)
|
||||||
|
|
||||||
|
Name of the place from which the module is loaded, e.g. "builtin" for
|
||||||
|
built-in modules and the filename for modules loaded from source.
|
||||||
|
Normally "origin" should be set, but it may be None (the default)
|
||||||
|
which indicates it is unspecified.
|
||||||
|
|
||||||
|
.. attribute:: submodule_search_locations
|
||||||
|
|
||||||
|
(``__path__``)
|
||||||
|
|
||||||
|
List of strings for where to find submodules, if a package (None
|
||||||
|
otherwise).
|
||||||
|
|
||||||
|
.. attribute:: loader_state
|
||||||
|
|
||||||
|
Container of extra module-specific data for use during loading (or
|
||||||
|
None).
|
||||||
|
|
||||||
|
.. attribute:: cached
|
||||||
|
|
||||||
|
(``__cached__``)
|
||||||
|
|
||||||
|
String for where the compiled module should be stored (or None).
|
||||||
|
|
||||||
|
.. attribute:: parent
|
||||||
|
|
||||||
|
(``__package__``)
|
||||||
|
|
||||||
|
(Read-only) Fully-qualified name of the package to which the module
|
||||||
|
belongs as a submodule (or None).
|
||||||
|
|
||||||
|
.. attribute:: has_location
|
||||||
|
|
||||||
|
(Read-only) Boolean indicating whether or not the module's "origin"
|
||||||
|
attribute refers to a loadable location.
|
||||||
|
|
||||||
:mod:`importlib.util` -- Utility code for importers
|
:mod:`importlib.util` -- Utility code for importers
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
|
|
||||||
|
@ -952,20 +974,6 @@ an :term:`importer`.
|
||||||
|
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
.. function:: module_to_load(name, *, reset_name=True)
|
|
||||||
|
|
||||||
Returns a :term:`context manager` which provides the module to load. The
|
|
||||||
module will either come from :attr:`sys.modules` in the case of reloading or
|
|
||||||
a fresh module if loading a new module. Proper cleanup of
|
|
||||||
:attr:`sys.modules` occurs if the module was new and an exception was
|
|
||||||
raised.
|
|
||||||
|
|
||||||
If **reset_name** is true and the module requested is being reloaded then
|
|
||||||
the module's :attr:`__name__` attribute will
|
|
||||||
be reset to **name**, else it will be left untouched.
|
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
|
||||||
|
|
||||||
.. decorator:: module_for_loader
|
.. decorator:: module_for_loader
|
||||||
|
|
||||||
A :term:`decorator` for :meth:`importlib.abc.Loader.load_module`
|
A :term:`decorator` for :meth:`importlib.abc.Loader.load_module`
|
||||||
|
@ -999,9 +1007,8 @@ an :term:`importer`.
|
||||||
unconditionally to support reloading.
|
unconditionally to support reloading.
|
||||||
|
|
||||||
.. deprecated:: 3.4
|
.. deprecated:: 3.4
|
||||||
For the benefit of :term:`loader` subclasses, please use
|
The import machinery now directly performs all the functionality
|
||||||
:func:`module_to_load` and
|
provided by this function.
|
||||||
:meth:`importlib.abc.Loader.init_module_attrs` instead.
|
|
||||||
|
|
||||||
.. decorator:: set_loader
|
.. decorator:: set_loader
|
||||||
|
|
||||||
|
@ -1012,11 +1019,6 @@ an :term:`importer`.
|
||||||
the wrapped method (i.e. ``self``) is what :attr:`__loader__` should be set
|
the wrapped method (i.e. ``self``) is what :attr:`__loader__` should be set
|
||||||
to.
|
to.
|
||||||
|
|
||||||
.. note::
|
|
||||||
As this decorator sets :attr:`__loader__` after loading the module, it is
|
|
||||||
recommended to use :meth:`importlib.abc.Loader.init_module_attrs` instead
|
|
||||||
when appropriate.
|
|
||||||
|
|
||||||
.. versionchanged:: 3.4
|
.. versionchanged:: 3.4
|
||||||
Set ``__loader__`` if set to ``None``, as if the attribute does not
|
Set ``__loader__`` if set to ``None``, as if the attribute does not
|
||||||
exist.
|
exist.
|
||||||
|
@ -1026,7 +1028,21 @@ an :term:`importer`.
|
||||||
A :term:`decorator` for :meth:`importlib.abc.Loader.load_module` to set the :attr:`__package__` attribute on the returned module. If :attr:`__package__`
|
A :term:`decorator` for :meth:`importlib.abc.Loader.load_module` to set the :attr:`__package__` attribute on the returned module. If :attr:`__package__`
|
||||||
is set and has a value other than ``None`` it will not be changed.
|
is set and has a value other than ``None`` it will not be changed.
|
||||||
|
|
||||||
.. note::
|
.. function:: spec_from_loader(name, loader, *, origin=None, is_package=None)
|
||||||
As this decorator sets :attr:`__package__` after loading the module, it is
|
|
||||||
recommended to use :meth:`importlib.abc.Loader.init_module_attrs` instead
|
A factory function for creating a :class:`ModuleSpec` instance based
|
||||||
when appropriate.
|
on a loader. The parameters have the same meaning as they do for
|
||||||
|
ModuleSpec. The function uses available :term:`loader` APIs, such as
|
||||||
|
:meth:`InspectLoader.is_package`, to fill in any missing
|
||||||
|
information on the spec.
|
||||||
|
|
||||||
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
|
.. function:: spec_from_file_location(name, location, *, loader=None, submodule_search_locations=None)
|
||||||
|
|
||||||
|
A factory function for creating a :class:`ModuleSpec` instance based
|
||||||
|
on the path to a file. Missing information will be filled in on the
|
||||||
|
spec by making use of loader APIs and by the implication that the
|
||||||
|
module will be file-based.
|
||||||
|
|
||||||
|
.. versionadded:: 3.4
|
||||||
|
|
|
@ -210,6 +210,7 @@ Finders and loaders
|
||||||
.. index::
|
.. index::
|
||||||
single: finder
|
single: finder
|
||||||
single: loader
|
single: loader
|
||||||
|
single: module spec
|
||||||
|
|
||||||
If the named module is not found in :data:`sys.modules`, then Python's import
|
If the named module is not found in :data:`sys.modules`, then Python's import
|
||||||
protocol is invoked to find and load the module. This protocol consists of
|
protocol is invoked to find and load the module. This protocol consists of
|
||||||
|
@ -230,13 +231,17 @@ The import machinery is extensible, so new finders can be added to extend the
|
||||||
range and scope of module searching.
|
range and scope of module searching.
|
||||||
|
|
||||||
Finders do not actually load modules. If they can find the named module, they
|
Finders do not actually load modules. If they can find the named module, they
|
||||||
return a :term:`loader`, which the import machinery then invokes to load the
|
return a :term:`module spec`, an encapsulation of the module's import-related
|
||||||
module and create the corresponding module object.
|
information, which the import machinery then uses when loading the module.
|
||||||
|
|
||||||
The following sections describe the protocol for finders and loaders in more
|
The following sections describe the protocol for finders and loaders in more
|
||||||
detail, including how you can create and register new ones to extend the
|
detail, including how you can create and register new ones to extend the
|
||||||
import machinery.
|
import machinery.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.4
|
||||||
|
In previous versions of Python, finders returned :term:`loaders <loader>`
|
||||||
|
directly, whereas now they return module specs which *contain* loaders.
|
||||||
|
Loaders are still used during import but have fewer responsibilities.
|
||||||
|
|
||||||
Import hooks
|
Import hooks
|
||||||
------------
|
------------
|
||||||
|
@ -270,24 +275,23 @@ The meta path
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
single: sys.meta_path
|
single: sys.meta_path
|
||||||
pair: finder; find_module
|
pair: finder; find_spec
|
||||||
pair: finder; find_loader
|
|
||||||
|
|
||||||
When the named module is not found in :data:`sys.modules`, Python next
|
When the named module is not found in :data:`sys.modules`, Python next
|
||||||
searches :data:`sys.meta_path`, which contains a list of meta path finder
|
searches :data:`sys.meta_path`, which contains a list of meta path finder
|
||||||
objects. These finders are queried in order to see if they know how to handle
|
objects. These finders are queried in order to see if they know how to handle
|
||||||
the named module. Meta path finders must implement a method called
|
the named module. Meta path finders must implement a method called
|
||||||
:meth:`find_module()` which takes two arguments, a name and an import path.
|
:meth:`find_spec()` which takes two arguments, a name and an import path.
|
||||||
The meta path finder can use any strategy it wants to determine whether it can
|
The meta path finder can use any strategy it wants to determine whether it can
|
||||||
handle the named module or not.
|
handle the named module or not.
|
||||||
|
|
||||||
If the meta path finder knows how to handle the named module, it returns a
|
If the meta path finder knows how to handle the named module, it returns a
|
||||||
loader object. If it cannot handle the named module, it returns ``None``. If
|
spec object. If it cannot handle the named module, it returns ``None``. If
|
||||||
:data:`sys.meta_path` processing reaches the end of its list without returning
|
:data:`sys.meta_path` processing reaches the end of its list without returning
|
||||||
a loader, then an :exc:`ImportError` is raised. Any other exceptions raised
|
a spec, then an :exc:`ImportError` is raised. Any other exceptions raised
|
||||||
are simply propagated up, aborting the import process.
|
are simply propagated up, aborting the import process.
|
||||||
|
|
||||||
The :meth:`find_module()` method of meta path finders is called with two
|
The :meth:`find_spec()` method of meta path finders is called with two
|
||||||
arguments. The first is the fully qualified name of the module being
|
arguments. The first is the fully qualified name of the module being
|
||||||
imported, for example ``foo.bar.baz``. The second argument is the path
|
imported, for example ``foo.bar.baz``. The second argument is the path
|
||||||
entries to use for the module search. For top-level modules, the second
|
entries to use for the module search. For top-level modules, the second
|
||||||
|
@ -299,12 +303,12 @@ the appropriate ``__path__`` attribute cannot be accessed, an
|
||||||
The meta path may be traversed multiple times for a single import request.
|
The meta path may be traversed multiple times for a single import request.
|
||||||
For example, assuming none of the modules involved has already been cached,
|
For example, assuming none of the modules involved has already been cached,
|
||||||
importing ``foo.bar.baz`` will first perform a top level import, calling
|
importing ``foo.bar.baz`` will first perform a top level import, calling
|
||||||
``mpf.find_module("foo", None)`` on each meta path finder (``mpf``). After
|
``mpf.find_spec("foo", None)`` on each meta path finder (``mpf``). After
|
||||||
``foo`` has been imported, ``foo.bar`` will be imported by traversing the
|
``foo`` has been imported, ``foo.bar`` will be imported by traversing the
|
||||||
meta path a second time, calling
|
meta path a second time, calling
|
||||||
``mpf.find_module("foo.bar", foo.__path__)``. Once ``foo.bar`` has been
|
``mpf.find_spec("foo.bar", foo.__path__)``. Once ``foo.bar`` has been
|
||||||
imported, the final traversal will call
|
imported, the final traversal will call
|
||||||
``mpf.find_module("foo.bar.baz", foo.bar.__path__)``.
|
``mpf.find_spec("foo.bar.baz", foo.bar.__path__)``.
|
||||||
|
|
||||||
Some meta path finders only support top level imports. These importers will
|
Some meta path finders only support top level imports. These importers will
|
||||||
always return ``None`` when anything other than ``None`` is passed as the
|
always return ``None`` when anything other than ``None`` is passed as the
|
||||||
|
@ -315,131 +319,229 @@ knows how to import built-in modules, one that knows how to import frozen
|
||||||
modules, and one that knows how to import modules from an :term:`import path`
|
modules, and one that knows how to import modules from an :term:`import path`
|
||||||
(i.e. the :term:`path based finder`).
|
(i.e. the :term:`path based finder`).
|
||||||
|
|
||||||
|
.. versionchanged:: 3.4
|
||||||
|
The find_spec() method of meta path finders replaced :meth:`find_module()`.
|
||||||
|
which is now deprecated. While it will continue to work without change,
|
||||||
|
the import machinery will try it only if the finder does not implement
|
||||||
|
find_spec().
|
||||||
|
|
||||||
Loaders
|
|
||||||
|
Loading
|
||||||
=======
|
=======
|
||||||
|
|
||||||
If and when a module loader is found its
|
If and when a module spec is found, the import machinery will use it (and
|
||||||
:meth:`~importlib.abc.Loader.load_module` method is called, with a single
|
the loader it contains) when loading the module. Here is an approximation
|
||||||
argument, the fully qualified name of the module being imported. This method
|
of what happens during the loading portion of import::
|
||||||
has several responsibilities, and should return the module object it has
|
|
||||||
loaded [#fnlo]_. If it cannot load the module, it should raise an
|
|
||||||
:exc:`ImportError`, although any other exception raised during
|
|
||||||
:meth:`load_module()` will be propagated.
|
|
||||||
|
|
||||||
In many cases, the finder and loader can be the same object; in such cases the
|
module = None
|
||||||
:meth:`finder.find_module()` would just return ``self``.
|
if spec.loader is not None and hasattr(spec.loader, 'create_module'):
|
||||||
|
module = spec.loader.create_module(spec)
|
||||||
|
if module is None:
|
||||||
|
module = ModuleType(spec.name)
|
||||||
|
# The import-related module attributes get set here:
|
||||||
|
_init_module_attrs(spec, module)
|
||||||
|
|
||||||
Loaders must satisfy the following requirements:
|
if spec.loader is None:
|
||||||
|
if spec.submodule_search_locations is not None:
|
||||||
|
# namespace package
|
||||||
|
sys.modules[spec.name] = module
|
||||||
|
else:
|
||||||
|
# unsupported
|
||||||
|
raise ImportError
|
||||||
|
elif not hasattr(spec.loader, 'exec_module'):
|
||||||
|
module = spec.loader.load_module(spec.name)
|
||||||
|
else:
|
||||||
|
sys.modules[spec.name] = module
|
||||||
|
try:
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
except BaseException:
|
||||||
|
try:
|
||||||
|
del sys.modules[spec.name]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
raise
|
||||||
|
module_to_return = sys.modules[spec.name]
|
||||||
|
|
||||||
|
Note the following details:
|
||||||
|
|
||||||
* If there is an existing module object with the given name in
|
* If there is an existing module object with the given name in
|
||||||
:data:`sys.modules`, the loader must use that existing module. (Otherwise,
|
:data:`sys.modules`, import will have already returned it.
|
||||||
:func:`imp.reload` will not work correctly.) If the named module does
|
|
||||||
not exist in :data:`sys.modules`, the loader must create a new module
|
|
||||||
object and add it to :data:`sys.modules`.
|
|
||||||
|
|
||||||
Note that the module *must* exist in :data:`sys.modules` before the loader
|
* The module will exist in :data:`sys.modules` before the loader
|
||||||
executes the module code. This is crucial because the module code may
|
executes the module code. This is crucial because the module code may
|
||||||
(directly or indirectly) import itself; adding it to :data:`sys.modules`
|
(directly or indirectly) import itself; adding it to :data:`sys.modules`
|
||||||
beforehand prevents unbounded recursion in the worst case and multiple
|
beforehand prevents unbounded recursion in the worst case and multiple
|
||||||
loading in the best.
|
loading in the best.
|
||||||
|
|
||||||
If loading fails, the loader must remove any modules it has inserted into
|
* If loading fails, the failing module -- and only the failing module --
|
||||||
:data:`sys.modules`, but it must remove **only** the failing module, and
|
gets removed from :data:`sys.modules`. Any module already in the
|
||||||
only if the loader itself has loaded it explicitly. Any module already in
|
:data:`sys.modules` cache, and any module that was successfully loaded
|
||||||
the :data:`sys.modules` cache, and any module that was successfully loaded
|
as a side-effect, must remain in the cache. This contrasts with
|
||||||
as a side-effect, must remain in the cache.
|
reloading where even the failing module is left in :data:`sys.modules`.
|
||||||
|
|
||||||
* The loader may set the ``__file__`` attribute of the module. If set, this
|
* After the module is created but before execution, the import machinery
|
||||||
attribute's value must be a string. The loader may opt to leave
|
sets the import-related module attributes ("init_module_attrs"), as
|
||||||
``__file__`` unset if it has no semantic meaning (e.g. a module loaded from
|
summarized in a `later section <Import-related module attributes>`_.
|
||||||
a database). If ``__file__`` is set, it may also be appropriate to set the
|
|
||||||
``__cached__`` attribute which is the path to any compiled version of the
|
|
||||||
code (e.g. byte-compiled file). The file does not need to exist to set this
|
|
||||||
attribute; the path can simply point to whether the compiled file would
|
|
||||||
exist (see :pep:`3147`).
|
|
||||||
|
|
||||||
* The loader may set the ``__name__`` attribute of the module. While not
|
* Module execution is the key moment of loading in which the module's
|
||||||
required, setting this attribute is highly recommended so that the
|
namespace gets populated. Execution is entirely delegated to the
|
||||||
:meth:`repr()` of the module is more informative.
|
loader, which gets to decide what gets populated and how.
|
||||||
|
|
||||||
* If the module is a package (either regular or namespace), the loader must
|
* The module created during loading and passed to exec_module() may
|
||||||
set the module object's ``__path__`` attribute. The value must be
|
not be the one returned at the end of import [#fnlo]_.
|
||||||
iterable, but may be empty if ``__path__`` has no further significance
|
|
||||||
to the loader. If ``__path__`` is not empty, it must produce strings
|
|
||||||
when iterated over. More details on the semantics of ``__path__`` are
|
|
||||||
given :ref:`below <package-path-rules>`.
|
|
||||||
|
|
||||||
* The ``__loader__`` attribute must be set to the loader object that loaded
|
.. versionchanged:: 3.4
|
||||||
the module. This is mostly for introspection and reloading, but can be
|
The import system has taken over the boilerplate responsibilities of
|
||||||
used for additional loader-specific functionality, for example getting
|
loaders. These were previously performed by the :meth:`load_module()`
|
||||||
data associated with a loader. If the attribute is missing or set to ``None``
|
method.
|
||||||
then the import machinery will automatically set it **after** the module has
|
|
||||||
been imported.
|
|
||||||
|
|
||||||
* The module's ``__package__`` attribute must be set. Its value must be a
|
Loaders
|
||||||
string, but it can be the same value as its ``__name__``. If the attribute
|
-------
|
||||||
is set to ``None`` or is missing, the import system will fill it in with a
|
|
||||||
more appropriate value **after** the module has been imported.
|
|
||||||
When the module is a package, its ``__package__`` value should be set to its
|
|
||||||
``__name__``. When the module is not a package, ``__package__`` should be
|
|
||||||
set to the empty string for top-level modules, or for submodules, to the
|
|
||||||
parent package's name. See :pep:`366` for further details.
|
|
||||||
|
|
||||||
This attribute is used instead of ``__name__`` to calculate explicit
|
Module loaders provide the critical function of loading: module execution.
|
||||||
relative imports for main modules, as defined in :pep:`366`.
|
The import machinery calls the :meth:`~importlib.abc.Loader.exec_module()`
|
||||||
|
method with a single argument, the module object to execute. Any value
|
||||||
|
returned from exec_module() is ignored.
|
||||||
|
|
||||||
|
Loaders must satisfy the following requirements:
|
||||||
|
|
||||||
* If the module is a Python module (as opposed to a built-in module or a
|
* If the module is a Python module (as opposed to a built-in module or a
|
||||||
dynamically loaded extension), the loader should execute the module's code
|
dynamically loaded extension), the loader should execute the module's code
|
||||||
in the module's global name space (``module.__dict__``).
|
in the module's global name space (``module.__dict__``).
|
||||||
|
|
||||||
|
* If loader cannot execute the module, it should raise an
|
||||||
|
:exc:`ImportError`, although any other exception raised during
|
||||||
|
:meth:`exec_module()` will be propagated.
|
||||||
|
|
||||||
Module reprs
|
In many cases, the finder and loader can be the same object; in such cases the
|
||||||
------------
|
:meth:`finder.find_spec()` would just return a spec with the loader set
|
||||||
|
to ``self``.
|
||||||
|
|
||||||
By default, all modules have a usable repr, however depending on the
|
Module loaders may opt in to creating the module object during loading
|
||||||
attributes set above, and hooks in the loader, you can more explicitly control
|
by implementing a :meth:`create_module()` method. It takes one argument,
|
||||||
the repr of module objects.
|
the module spec, and returns the new module object to use during loading.
|
||||||
|
create_module() does not need to set any attributes on the module object.
|
||||||
|
If the loader does not define create_module(), the import machinery will
|
||||||
|
create the new module itself.
|
||||||
|
|
||||||
Loaders may implement a :meth:`module_repr()` method which takes a single
|
.. versionadded:: 3.4
|
||||||
argument, the module object. When ``repr(module)`` is called for a module
|
The create_module() method of loaders.
|
||||||
with a loader supporting this protocol, whatever is returned from
|
|
||||||
``module.__loader__.module_repr(module)`` is returned as the module's repr
|
|
||||||
without further processing. This return value must be a string.
|
|
||||||
|
|
||||||
If the module has no ``__loader__`` attribute, or the loader has no
|
.. versionchanged:: 3.4
|
||||||
:meth:`module_repr()` method, then the module object implementation itself
|
The load_module() method was replaced by exec_module() and the import
|
||||||
will craft a default repr using whatever information is available. It will
|
machinery assumed all the boilerplate responsibilities of loading.
|
||||||
try to use the ``module.__name__``, ``module.__file__``, and
|
|
||||||
``module.__loader__`` as input into the repr, with defaults for whatever
|
|
||||||
information is missing.
|
|
||||||
|
|
||||||
Here are the exact rules used:
|
For compatibility with existing loaders, the import machinery will use
|
||||||
|
the :meth:`~importlib.abc.Loader.load_module()` method of loaders if it
|
||||||
|
exists and the loader does not also implement exec_module(). However,
|
||||||
|
load_module() has been deprecated and loaders should implement
|
||||||
|
exec_module() instead.
|
||||||
|
|
||||||
* If the module has a ``__loader__`` and that loader has a
|
The load_module() method must implement all the boilerplate loading
|
||||||
:meth:`module_repr()` method, call it with a single argument, which is the
|
functionality described above in addition to executing the module. All
|
||||||
module object. The value returned is used as the module's repr.
|
the same constraints apply, with some additional clarification:
|
||||||
|
|
||||||
* If an exception occurs in :meth:`module_repr()`, the exception is caught
|
* If there is an existing module object with the given name in
|
||||||
and discarded, and the calculation of the module's repr continues as if
|
:data:`sys.modules`, the loader must use that existing module.
|
||||||
:meth:`module_repr()` did not exist.
|
(Otherwise, :func:`imp.reload` will not work correctly.) If the
|
||||||
|
named module does not exist in :data:`sys.modules`, the loader
|
||||||
|
must create a new module object and add it to :data:`sys.modules`.
|
||||||
|
|
||||||
* If the module has a ``__file__`` attribute, this is used as part of the
|
* The module *must* exist in :data:`sys.modules` before the loader
|
||||||
module's repr.
|
executes the module code, to prevent unbounded recursion or multiple
|
||||||
|
loading.
|
||||||
|
|
||||||
* If the module has no ``__file__`` but does have a ``__loader__`` that is not
|
* If loading fails, the loader must remove any modules it has inserted
|
||||||
``None``, then the loader's repr is used as part of the module's repr.
|
into :data:`sys.modules`, but it must remove **only** the failing
|
||||||
|
module, and only if the loader itself has loaded it explicitly.
|
||||||
|
|
||||||
* Otherwise, just use the module's ``__name__`` in the repr.
|
Module spec
|
||||||
|
-----------
|
||||||
|
|
||||||
This example, from :pep:`420` shows how a loader can craft its own module
|
The import machinery uses a variety of information about each module
|
||||||
repr::
|
during import, especially before loading. Most of the information is
|
||||||
|
common to all modules. The purpose of a module's spec is to encapsulate
|
||||||
|
this import-related information on a per-module basis.
|
||||||
|
|
||||||
class NamespaceLoader:
|
Using a spec during import allows state to be transferred between import
|
||||||
@classmethod
|
system components, e.g. between the finder that creates the module spec
|
||||||
def module_repr(cls, module):
|
and the loader that executes it. Most importantly, it allows the
|
||||||
return "<module '{}' (namespace)>".format(module.__name__)
|
import machinery to perform the boilerplate operations of loading,
|
||||||
|
whereas without a module spec the loader had that responsibility.
|
||||||
|
|
||||||
|
See :class:`~importlib.machinery.ModuleSpec` for more specifics on what
|
||||||
|
information a module's spec may hold.
|
||||||
|
|
||||||
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
|
Import-related module attributes
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
The import machinery fills in these attributes on each module object
|
||||||
|
during loading, based on the module's spec, before the loader executes
|
||||||
|
the module.
|
||||||
|
|
||||||
|
.. attribute:: __name__
|
||||||
|
|
||||||
|
The ``__name__`` attribute must be set to the fully-qualified name of
|
||||||
|
the module. This name is used to uniquely identify the module in
|
||||||
|
the import system.
|
||||||
|
|
||||||
|
.. attribute:: __loader__
|
||||||
|
|
||||||
|
The ``__loader__`` attribute must be set to the loader object that
|
||||||
|
the import machinery used when loading the module. This is mostly
|
||||||
|
for introspection, but can be used for additional loader-specific
|
||||||
|
functionality, for example getting data associated with a loader.
|
||||||
|
|
||||||
|
.. attribute:: __package__
|
||||||
|
|
||||||
|
The module's ``__package__`` attribute must be set. Its value must
|
||||||
|
be a string, but it can be the same value as its ``__name__``. When
|
||||||
|
the module is a package, its ``__package__`` value should be set to
|
||||||
|
its ``__name__``. When the module is not a package, ``__package__``
|
||||||
|
should be set to the empty string for top-level modules, or for
|
||||||
|
submodules, to the parent package's name. See :pep:`366` for further
|
||||||
|
details.
|
||||||
|
|
||||||
|
This attribute is used instead of ``__name__`` to calculate explicit
|
||||||
|
relative imports for main modules, as defined in :pep:`366`.
|
||||||
|
|
||||||
|
.. attribute:: __spec__
|
||||||
|
|
||||||
|
The ``__spec__`` attribute must be set to the module spec that was
|
||||||
|
used when importing the module. This is used primarily for
|
||||||
|
introspection and during reloading.
|
||||||
|
|
||||||
|
.. attribute:: __path__
|
||||||
|
|
||||||
|
If the module is a package (either regular or namespace), the module
|
||||||
|
object's ``__path__`` attribute must be set. The value must be
|
||||||
|
iterable, but may be empty if ``__path__`` has no further significance.
|
||||||
|
If ``__path__`` is not empty, it must produce strings when iterated
|
||||||
|
over. More details on the semantics of ``__path__`` are given
|
||||||
|
:ref:`below <package-path-rules>`.
|
||||||
|
|
||||||
|
Non-package modules should not have a ``__path__`` attribute.
|
||||||
|
|
||||||
|
.. attribute:: __file__
|
||||||
|
.. attribute:: __cached__
|
||||||
|
|
||||||
|
``__file__`` is optional. If set, this attribute's value must be a
|
||||||
|
string. The import system may opt to leave ``__file__`` unset if it
|
||||||
|
has no semantic meaning (e.g. a module loaded from a database).
|
||||||
|
|
||||||
|
If ``__file__`` is set, it may also be appropriate to set the
|
||||||
|
``__cached__`` attribute which is the path to any compiled version of
|
||||||
|
the code (e.g. byte-compiled file). The file does not need to exist
|
||||||
|
to set this attribute; the path can simply point to where the
|
||||||
|
compiled file would exist (see :pep:`3147`).
|
||||||
|
|
||||||
|
It is also appropriate to set ``__cached__`` when ``__file__`` is not
|
||||||
|
set. However, that scenario is quite atypical. Ultimately, the
|
||||||
|
loader is what makes use of ``__file__`` and/or ``__cached__``. So
|
||||||
|
if a loader can load from a cached module but otherwise does not load
|
||||||
|
from a file, that atypical scenario may be appropriate.
|
||||||
|
|
||||||
.. _package-path-rules:
|
.. _package-path-rules:
|
||||||
|
|
||||||
|
@ -464,9 +566,46 @@ A package's ``__init__.py`` file may set or alter the package's ``__path__``
|
||||||
attribute, and this was typically the way namespace packages were implemented
|
attribute, and this was typically the way namespace packages were implemented
|
||||||
prior to :pep:`420`. With the adoption of :pep:`420`, namespace packages no
|
prior to :pep:`420`. With the adoption of :pep:`420`, namespace packages no
|
||||||
longer need to supply ``__init__.py`` files containing only ``__path__``
|
longer need to supply ``__init__.py`` files containing only ``__path__``
|
||||||
manipulation code; the namespace loader automatically sets ``__path__``
|
manipulation code; the import machinery automatically sets ``__path__``
|
||||||
correctly for the namespace package.
|
correctly for the namespace package.
|
||||||
|
|
||||||
|
Module reprs
|
||||||
|
------------
|
||||||
|
|
||||||
|
By default, all modules have a usable repr, however depending on the
|
||||||
|
attributes set above, and in the module's spec, you can more explicitly
|
||||||
|
control the repr of module objects.
|
||||||
|
|
||||||
|
If the module has a spec (``__spec__``), the import machinery will try
|
||||||
|
to generate a repr from it. If that fails or there is no spec, the import
|
||||||
|
system will craft a default repr using whatever information is available
|
||||||
|
on the module. It will try to use the ``module.__name__``,
|
||||||
|
``module.__file__``, and ``module.__loader__`` as input into the repr,
|
||||||
|
with defaults for whatever information is missing.
|
||||||
|
|
||||||
|
Here are the exact rules used:
|
||||||
|
|
||||||
|
* If the module has a ``__spec__`` attribute, the information in the spec
|
||||||
|
is used to generate the repr. The "name", "loader", "origin", and
|
||||||
|
"has_location" attributes are consulted.
|
||||||
|
|
||||||
|
* If the module has a ``__file__`` attribute, this is used as part of the
|
||||||
|
module's repr.
|
||||||
|
|
||||||
|
* If the module has no ``__file__`` but does have a ``__loader__`` that is not
|
||||||
|
``None``, then the loader's repr is used as part of the module's repr.
|
||||||
|
|
||||||
|
* Otherwise, just use the module's ``__name__`` in the repr.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.4
|
||||||
|
Use of loader.module_repr() has been deprecated and the module spec
|
||||||
|
is now used by the import machinery to generate a module repr.
|
||||||
|
|
||||||
|
For backward compatibility with Python 3.3, the module repr will be
|
||||||
|
generated by calling the loader's :meth:`module_repr()` method, if
|
||||||
|
defined, before trying either approach described above. However, the
|
||||||
|
method is deprecated.
|
||||||
|
|
||||||
|
|
||||||
The Path Based Finder
|
The Path Based Finder
|
||||||
=====================
|
=====================
|
||||||
|
@ -531,7 +670,7 @@ entry`. Most path entries name locations in the file system, but they need
|
||||||
not be limited to this.
|
not be limited to this.
|
||||||
|
|
||||||
As a meta path finder, the :term:`path based finder` implements the
|
As a meta path finder, the :term:`path based finder` implements the
|
||||||
:meth:`find_module()` protocol previously described, however it exposes
|
:meth:`find_spec()` protocol previously described, however it exposes
|
||||||
additional hooks that can be used to customize how modules are found and
|
additional hooks that can be used to customize how modules are found and
|
||||||
loaded from the :term:`import path`.
|
loaded from the :term:`import path`.
|
||||||
|
|
||||||
|
@ -553,8 +692,8 @@ finder>`.
|
||||||
|
|
||||||
The :term:`path based finder` is a :term:`meta path finder`, so the import
|
The :term:`path based finder` is a :term:`meta path finder`, so the import
|
||||||
machinery begins the :term:`import path` search by calling the path
|
machinery begins the :term:`import path` search by calling the path
|
||||||
based finder's :meth:`find_module()` method as described previously. When
|
based finder's :meth:`find_spec()` method as described previously. When
|
||||||
the ``path`` argument to :meth:`find_module()` is given, it will be a
|
the ``path`` argument to :meth:`find_spec()` is given, it will be a
|
||||||
list of string paths to traverse - typically a package's ``__path__``
|
list of string paths to traverse - typically a package's ``__path__``
|
||||||
attribute for an import within that package. If the ``path`` argument
|
attribute for an import within that package. If the ``path`` argument
|
||||||
is ``None``, this indicates a top level import and :data:`sys.path` is used.
|
is ``None``, this indicates a top level import and :data:`sys.path` is used.
|
||||||
|
@ -585,51 +724,70 @@ encoding, UTF-8, or something else), and if the hook cannot decode the
|
||||||
argument, it should raise :exc:`ImportError`.
|
argument, it should raise :exc:`ImportError`.
|
||||||
|
|
||||||
If :data:`sys.path_hooks` iteration ends with no :term:`path entry finder`
|
If :data:`sys.path_hooks` iteration ends with no :term:`path entry finder`
|
||||||
being returned, then the path based finder's :meth:`find_module()` method
|
being returned, then the path based finder's :meth:`find_spec()` method
|
||||||
will store ``None`` in :data:`sys.path_importer_cache` (to indicate that
|
will store ``None`` in :data:`sys.path_importer_cache` (to indicate that
|
||||||
there is no finder for this path entry) and return ``None``, indicating that
|
there is no finder for this path entry) and return ``None``, indicating that
|
||||||
this :term:`meta path finder` could not find the module.
|
this :term:`meta path finder` could not find the module.
|
||||||
|
|
||||||
If a :term:`path entry finder` *is* returned by one of the :term:`path entry
|
If a :term:`path entry finder` *is* returned by one of the :term:`path entry
|
||||||
hook` callables on :data:`sys.path_hooks`, then the following protocol is used
|
hook` callables on :data:`sys.path_hooks`, then the following protocol is used
|
||||||
to ask the finder for a module loader, which is then used to load the module.
|
to ask the finder for a module spec, which is then used when loading the
|
||||||
|
module.
|
||||||
|
|
||||||
Path entry finder protocol
|
Path entry finder protocol
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
In order to support imports of modules and initialized packages and also to
|
In order to support imports of modules and initialized packages and also to
|
||||||
contribute portions to namespace packages, path entry finders must implement
|
contribute portions to namespace packages, path entry finders must implement
|
||||||
the :meth:`find_loader()` method.
|
the :meth:`find_spec()` method.
|
||||||
|
|
||||||
:meth:`find_loader()` takes one argument, the fully qualified name of the
|
:meth:`find_spec()` takes one argument, the fully qualified name of the
|
||||||
module being imported. :meth:`find_loader()` returns a 2-tuple where the
|
module being imported. :meth:`find_spec()` returns a fully populated
|
||||||
first item is the loader and the second item is a namespace :term:`portion`.
|
spec for the module. This spec will always have "loader" set (with one
|
||||||
When the first item (i.e. the loader) is ``None``, this means that while the
|
exception).
|
||||||
path entry finder does not have a loader for the named module, it knows that the
|
|
||||||
path entry contributes to a namespace portion for the named module. This will
|
|
||||||
almost always be the case where Python is asked to import a namespace package
|
|
||||||
that has no physical presence on the file system. When a path entry finder
|
|
||||||
returns ``None`` for the loader, the second item of the 2-tuple return value
|
|
||||||
must be a sequence, although it can be empty.
|
|
||||||
|
|
||||||
If :meth:`find_loader()` returns a non-``None`` loader value, the portion is
|
To indicate to the import machinery that the spec represents a namespace
|
||||||
ignored and the loader is returned from the path based finder, terminating
|
:term:`portion`. the path entry finder sets "loader" on the spec to
|
||||||
the search through the path entries.
|
``None`` and "submodule_search_locations" to a list containing the
|
||||||
|
portion.
|
||||||
|
|
||||||
For backwards compatibility with other implementations of the import
|
.. versionchanged:: 3.4
|
||||||
protocol, many path entry finders also support the same,
|
find_spec() replaced find_loader() and find_module(), but of which
|
||||||
traditional :meth:`find_module()` method that meta path finders support.
|
are now deprecated, but will be used if find_spec() is not defined.
|
||||||
However path entry finder :meth:`find_module()` methods are never called
|
|
||||||
with a ``path`` argument (they are expected to record the appropriate
|
|
||||||
path information from the initial call to the path hook).
|
|
||||||
|
|
||||||
The :meth:`find_module()` method on path entry finders is deprecated,
|
Older path entry finders may implement one of these two deprecated methods
|
||||||
as it does not allow the path entry finder to contribute portions to
|
instead of :meth:`find_spec()`. The methods are still respected for the
|
||||||
namespace packages. Instead path entry finders should implement the
|
sake of backward compatibility. Howevever, if find_spec() is implemented
|
||||||
:meth:`find_loader()` method as described above. If it exists on the path
|
on the path entry finder, the legacy methods are ignored.
|
||||||
entry finder, the import system will always call :meth:`find_loader()`
|
|
||||||
in preference to :meth:`find_module()`.
|
:meth:`find_loader()` takes one argument, the fully qualified name of the
|
||||||
|
module being imported. :meth:`find_loader()` returns a 2-tuple where the
|
||||||
|
first item is the loader and the second item is a namespace :term:`portion`.
|
||||||
|
When the first item (i.e. the loader) is ``None``, this means that while the
|
||||||
|
path entry finder does not have a loader for the named module, it knows that
|
||||||
|
the path entry contributes to a namespace portion for the named module.
|
||||||
|
This will almost always be the case where Python is asked to import a
|
||||||
|
namespace package that has no physical presence on the file system.
|
||||||
|
When a path entry finder returns ``None`` for the loader, the second
|
||||||
|
item of the 2-tuple return value must be a sequence, although it can be
|
||||||
|
empty.
|
||||||
|
|
||||||
|
If :meth:`find_loader()` returns a non-``None`` loader value, the portion is
|
||||||
|
ignored and the loader is returned from the path based finder, terminating
|
||||||
|
the search through the path entries.
|
||||||
|
|
||||||
|
For backwards compatibility with other implementations of the import
|
||||||
|
protocol, many path entry finders also support the same,
|
||||||
|
traditional :meth:`find_module()` method that meta path finders support.
|
||||||
|
However path entry finder :meth:`find_module()` methods are never called
|
||||||
|
with a ``path`` argument (they are expected to record the appropriate
|
||||||
|
path information from the initial call to the path hook).
|
||||||
|
|
||||||
|
The :meth:`find_module()` method on path entry finders is deprecated,
|
||||||
|
as it does not allow the path entry finder to contribute portions to
|
||||||
|
namespace packages. If both :meth:`find_loader()` and :meth:`find_module()`
|
||||||
|
exist on a path entry finder, the import system will always call
|
||||||
|
:meth:`find_loader()` in preference to :meth:`find_module()`.
|
||||||
|
|
||||||
|
|
||||||
Replacing the standard import system
|
Replacing the standard import system
|
||||||
|
@ -648,7 +806,7 @@ import statements within that module.
|
||||||
To selectively prevent import of some modules from a hook early on the
|
To selectively prevent import of some modules from a hook early on the
|
||||||
meta path (rather than disabling the standard import system entirely),
|
meta path (rather than disabling the standard import system entirely),
|
||||||
it is sufficient to raise :exc:`ImportError` directly from
|
it is sufficient to raise :exc:`ImportError` directly from
|
||||||
:meth:`find_module` instead of returning ``None``. The latter indicates
|
:meth:`find_spec` instead of returning ``None``. The latter indicates
|
||||||
that the meta path search should continue. while raising an exception
|
that the meta path search should continue. while raising an exception
|
||||||
terminates it immediately.
|
terminates it immediately.
|
||||||
|
|
||||||
|
@ -690,6 +848,11 @@ proposed ``__name__`` for semantics :pep:`366` would eventually specify for
|
||||||
|
|
||||||
:pep:`338` defines executing modules as scripts.
|
:pep:`338` defines executing modules as scripts.
|
||||||
|
|
||||||
|
:pep:`451` adds the encapsulation of per-module import state in spec
|
||||||
|
objects. It also off-loads most of the boilerplate responsibilities of
|
||||||
|
loaders back onto the import machinery. These changes allow the
|
||||||
|
deprecation of several APIs in the import system and also addition of new
|
||||||
|
methods to finders and loaders.
|
||||||
|
|
||||||
.. rubric:: Footnotes
|
.. rubric:: Footnotes
|
||||||
|
|
||||||
|
|
|
@ -236,6 +236,26 @@ name of the codec responsible for producing the error::
|
||||||
(Contributed by Nick Coghlan in :issue:`17827`, :issue:`17828` and
|
(Contributed by Nick Coghlan in :issue:`17827`, :issue:`17828` and
|
||||||
:issue:`19619`)
|
:issue:`19619`)
|
||||||
|
|
||||||
|
.. _pep-451:
|
||||||
|
|
||||||
|
PEP 451: A ModuleSpec Type for the Import System
|
||||||
|
================================================
|
||||||
|
|
||||||
|
:pep:`451` provides an encapsulation of the information about a module
|
||||||
|
that the import machinery will use to load it, (i.e. a module spec).
|
||||||
|
This helps simplify both the import implementation and several
|
||||||
|
import-related APIs. The change is also a stepping stone for several
|
||||||
|
future import-related improvements.
|
||||||
|
|
||||||
|
https://mail.python.org/pipermail/python-dev/2013-November/130111.html
|
||||||
|
|
||||||
|
The public-facing changes from the PEP are entirely backward-compatible.
|
||||||
|
Furthermore, they should be transparent to everyone but importer
|
||||||
|
authors. Key finder and loader methods have been deprecated, but they
|
||||||
|
will continue working. New importers should use the new methods
|
||||||
|
described in the PEP. Existing importers should be updated to implement
|
||||||
|
the new methods.
|
||||||
|
|
||||||
|
|
||||||
Other Language Changes
|
Other Language Changes
|
||||||
======================
|
======================
|
||||||
|
|
30
Lib/imp.py
30
Lib/imp.py
|
@ -16,7 +16,7 @@ except ImportError:
|
||||||
# Platform doesn't support dynamic loading.
|
# Platform doesn't support dynamic loading.
|
||||||
load_dynamic = None
|
load_dynamic = None
|
||||||
|
|
||||||
from importlib._bootstrap import SourcelessFileLoader, _ERR_MSG
|
from importlib._bootstrap import SourcelessFileLoader, _ERR_MSG, _SpecMethods
|
||||||
|
|
||||||
from importlib import machinery
|
from importlib import machinery
|
||||||
from importlib import util
|
from importlib import util
|
||||||
|
@ -162,11 +162,17 @@ class _LoadSourceCompatibility(_HackedGetData, machinery.SourceFileLoader):
|
||||||
|
|
||||||
|
|
||||||
def load_source(name, pathname, file=None):
|
def load_source(name, pathname, file=None):
|
||||||
_LoadSourceCompatibility(name, pathname, file).load_module(name)
|
loader = _LoadSourceCompatibility(name, pathname, file)
|
||||||
module = sys.modules[name]
|
spec = util.spec_from_file_location(name, pathname, loader=loader)
|
||||||
|
methods = _SpecMethods(spec)
|
||||||
|
if name in sys.modules:
|
||||||
|
module = methods.exec(sys.modules[name])
|
||||||
|
else:
|
||||||
|
module = methods.load()
|
||||||
# To allow reloading to potentially work, use a non-hacked loader which
|
# To allow reloading to potentially work, use a non-hacked loader which
|
||||||
# won't rely on a now-closed file object.
|
# won't rely on a now-closed file object.
|
||||||
module.__loader__ = machinery.SourceFileLoader(name, pathname)
|
module.__loader__ = machinery.SourceFileLoader(name, pathname)
|
||||||
|
module.__spec__.loader = module.__loader__
|
||||||
return module
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
@ -177,11 +183,17 @@ class _LoadCompiledCompatibility(_HackedGetData, SourcelessFileLoader):
|
||||||
|
|
||||||
def load_compiled(name, pathname, file=None):
|
def load_compiled(name, pathname, file=None):
|
||||||
"""**DEPRECATED**"""
|
"""**DEPRECATED**"""
|
||||||
_LoadCompiledCompatibility(name, pathname, file).load_module(name)
|
loader = _LoadCompiledCompatibility(name, pathname, file)
|
||||||
module = sys.modules[name]
|
spec = util.spec_from_file_location(name, pathname, loader=loader)
|
||||||
|
methods = _SpecMethods(spec)
|
||||||
|
if name in sys.modules:
|
||||||
|
module = methods.exec(sys.modules[name])
|
||||||
|
else:
|
||||||
|
module = methods.load()
|
||||||
# To allow reloading to potentially work, use a non-hacked loader which
|
# To allow reloading to potentially work, use a non-hacked loader which
|
||||||
# won't rely on a now-closed file object.
|
# won't rely on a now-closed file object.
|
||||||
module.__loader__ = SourcelessFileLoader(name, pathname)
|
module.__loader__ = SourcelessFileLoader(name, pathname)
|
||||||
|
module.__spec__.loader = module.__loader__
|
||||||
return module
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
@ -196,7 +208,13 @@ def load_package(name, path):
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise ValueError('{!r} is not a package'.format(path))
|
raise ValueError('{!r} is not a package'.format(path))
|
||||||
return machinery.SourceFileLoader(name, path).load_module(name)
|
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])
|
||||||
|
else:
|
||||||
|
return methods.load()
|
||||||
|
|
||||||
|
|
||||||
def load_module(name, file, filename, details):
|
def load_module(name, file, filename, details):
|
||||||
|
|
|
@ -46,19 +46,42 @@ def invalidate_caches():
|
||||||
finder.invalidate_caches()
|
finder.invalidate_caches()
|
||||||
|
|
||||||
|
|
||||||
def find_loader(name, path=None):
|
def find_spec(name, path=None):
|
||||||
"""Find the loader for the specified module.
|
"""Return the spec for the specified module.
|
||||||
|
|
||||||
First, sys.modules is checked to see if the module was already imported. If
|
First, sys.modules is checked to see if the module was already imported. If
|
||||||
so, then sys.modules[name].__loader__ is returned. If that happens to be
|
so, then sys.modules[name].__spec__ is returned. If that happens to be
|
||||||
set to None, then ValueError is raised. If the module is not in
|
set to None, then ValueError is raised. If the module is not in
|
||||||
sys.modules, then sys.meta_path is searched for a suitable loader with the
|
sys.modules, then sys.meta_path is searched for a suitable spec with the
|
||||||
value of 'path' given to the finders. None is returned if no loader could
|
value of 'path' given to the finders. None is returned if no spec could
|
||||||
be found.
|
be found.
|
||||||
|
|
||||||
Dotted names do not have their parent packages implicitly imported. You will
|
Dotted names do not have their parent packages implicitly imported. You will
|
||||||
most likely need to explicitly import all parent packages in the proper
|
most likely need to explicitly import all parent packages in the proper
|
||||||
order for a submodule to get the correct loader.
|
order for a submodule to get the correct spec.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if name not in sys.modules:
|
||||||
|
return _bootstrap._find_spec(name, path)
|
||||||
|
else:
|
||||||
|
module = sys.modules[name]
|
||||||
|
if module is None:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
spec = module.__spec__
|
||||||
|
except AttributeError:
|
||||||
|
raise ValueError('{}.__spec__ is not set'.format(name))
|
||||||
|
else:
|
||||||
|
if spec is None:
|
||||||
|
raise ValueError('{}.__spec__ is None'.format(name))
|
||||||
|
return spec
|
||||||
|
|
||||||
|
|
||||||
|
# XXX Deprecate...
|
||||||
|
def find_loader(name, path=None):
|
||||||
|
"""Return the loader for the specified module.
|
||||||
|
|
||||||
|
This is a backward-compatible wrapper around find_spec().
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
@ -71,7 +94,18 @@ def find_loader(name, path=None):
|
||||||
pass
|
pass
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise ValueError('{}.__loader__ is not set'.format(name))
|
raise ValueError('{}.__loader__ is not set'.format(name))
|
||||||
return _bootstrap._find_module(name, path)
|
|
||||||
|
spec = _bootstrap._find_spec(name, path)
|
||||||
|
# We won't worry about malformed specs (missing attributes).
|
||||||
|
if spec is None:
|
||||||
|
return None
|
||||||
|
if spec.loader is None:
|
||||||
|
if spec.submodule_search_locations is None:
|
||||||
|
raise ImportError('spec for {} missing loader'.format(name),
|
||||||
|
name=name)
|
||||||
|
raise ImportError('namespace packages do not have loaders',
|
||||||
|
name=name)
|
||||||
|
return spec.loader
|
||||||
|
|
||||||
|
|
||||||
def import_module(name, package=None):
|
def import_module(name, package=None):
|
||||||
|
@ -106,7 +140,11 @@ def reload(module):
|
||||||
"""
|
"""
|
||||||
if not module or not isinstance(module, types.ModuleType):
|
if not module or not isinstance(module, types.ModuleType):
|
||||||
raise TypeError("reload() argument must be module")
|
raise TypeError("reload() argument must be module")
|
||||||
name = module.__name__
|
try:
|
||||||
|
name = module.__spec__.name
|
||||||
|
except AttributeError:
|
||||||
|
name = module.__name__
|
||||||
|
|
||||||
if sys.modules.get(name) is not module:
|
if sys.modules.get(name) is not module:
|
||||||
msg = "module {} not in sys.modules"
|
msg = "module {} not in sys.modules"
|
||||||
raise ImportError(msg.format(name), name=name)
|
raise ImportError(msg.format(name), name=name)
|
||||||
|
@ -118,13 +156,11 @@ def reload(module):
|
||||||
if parent_name and parent_name not in sys.modules:
|
if parent_name and parent_name not in sys.modules:
|
||||||
msg = "parent {!r} not in sys.modules"
|
msg = "parent {!r} not in sys.modules"
|
||||||
raise ImportError(msg.format(parent_name), name=parent_name)
|
raise ImportError(msg.format(parent_name), name=parent_name)
|
||||||
loader = _bootstrap._find_module(name, None)
|
spec = module.__spec__ = _bootstrap._find_spec(name, None, module)
|
||||||
if loader is None:
|
methods = _bootstrap._SpecMethods(spec)
|
||||||
raise ImportError(_bootstrap._ERR_MSG.format(name), name=name)
|
methods.exec(module)
|
||||||
module.__loader__ = loader
|
|
||||||
loader.load_module(name)
|
|
||||||
# The module may have replaced itself in sys.modules!
|
# The module may have replaced itself in sys.modules!
|
||||||
return sys.modules[module.__name__]
|
return sys.modules[name]
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
del _RELOADING[name]
|
del _RELOADING[name]
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -40,12 +40,18 @@ class MetaPathFinder(Finder):
|
||||||
|
|
||||||
"""Abstract base class for import finders on sys.meta_path."""
|
"""Abstract base class for import finders on sys.meta_path."""
|
||||||
|
|
||||||
@abc.abstractmethod
|
# We don't define find_spec() here since that would break
|
||||||
|
# hasattr checks we do to support backward compatibility.
|
||||||
|
|
||||||
|
# XXX Deprecate
|
||||||
def find_module(self, fullname, path):
|
def find_module(self, fullname, path):
|
||||||
"""Abstract method which, when implemented, should find a module.
|
"""Return a loader for the module.
|
||||||
The fullname is a str and the path is a list of strings or None.
|
|
||||||
Returns a Loader object or None.
|
If no module is found, return None. The fullname is a str and
|
||||||
|
the path is a list of strings or None.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
return None
|
||||||
|
|
||||||
def invalidate_caches(self):
|
def invalidate_caches(self):
|
||||||
"""An optional method for clearing the finder's cache, if any.
|
"""An optional method for clearing the finder's cache, if any.
|
||||||
|
@ -60,17 +66,25 @@ class PathEntryFinder(Finder):
|
||||||
|
|
||||||
"""Abstract base class for path entry finders used by PathFinder."""
|
"""Abstract base class for path entry finders used by PathFinder."""
|
||||||
|
|
||||||
@abc.abstractmethod
|
# We don't define find_spec() here since that would break
|
||||||
|
# hasattr checks we do to support backward compatibility.
|
||||||
|
|
||||||
|
# XXX Deprecate.
|
||||||
def find_loader(self, fullname):
|
def find_loader(self, fullname):
|
||||||
"""Abstract method which, when implemented, returns a module loader or
|
"""Return (loader, namespace portion) for the path entry.
|
||||||
a possible part of a namespace.
|
|
||||||
The fullname is a str. Returns a 2-tuple of (Loader, portion) where
|
The fullname is a str. The namespace portion is a sequence of
|
||||||
portion is a sequence of file system locations contributing to part of
|
path entries contributing to part of a namespace package. The
|
||||||
a namespace package. The sequence may be empty and the loader may be
|
sequence may be empty. If loader is not None, the portion will
|
||||||
None.
|
be ignored.
|
||||||
|
|
||||||
|
The portion will be discarded if another path entry finder
|
||||||
|
locates the module as a normal module or package.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return None, []
|
return None, []
|
||||||
|
|
||||||
|
# XXX Deprecate.
|
||||||
find_module = _bootstrap._find_module_shim
|
find_module = _bootstrap._find_module_shim
|
||||||
|
|
||||||
def invalidate_caches(self):
|
def invalidate_caches(self):
|
||||||
|
@ -83,34 +97,46 @@ _register(PathEntryFinder, machinery.FileFinder)
|
||||||
|
|
||||||
class Loader(metaclass=abc.ABCMeta):
|
class Loader(metaclass=abc.ABCMeta):
|
||||||
|
|
||||||
"""Abstract base class for import loaders.
|
"""Abstract base class for import loaders."""
|
||||||
|
|
||||||
The optional method module_repr(module) may be defined to provide a
|
def create_module(self, spec):
|
||||||
repr for a module when appropriate (see PEP 420). The __repr__() method on
|
"""Return a module to initialize and into which to load.
|
||||||
the module type will use the method as appropriate.
|
|
||||||
|
|
||||||
"""
|
This method should raise ImportError if anything prevents it
|
||||||
|
from creating a new module. It may return None to indicate
|
||||||
|
that the spec should create the new module.
|
||||||
|
|
||||||
@abc.abstractmethod
|
create_module() is optional.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# By default, defer to _SpecMethods.create() for the new module.
|
||||||
|
return None
|
||||||
|
|
||||||
|
# We don't define exec_module() here since that would break
|
||||||
|
# hasattr checks we do to support backward compatibility.
|
||||||
|
|
||||||
|
# XXX Deprecate.
|
||||||
def load_module(self, fullname):
|
def load_module(self, fullname):
|
||||||
"""Abstract method which when implemented should load a module.
|
"""Return the loaded module.
|
||||||
The fullname is a str.
|
|
||||||
|
The module must be added to sys.modules and have import-related
|
||||||
|
attributes set properly. The fullname is a str.
|
||||||
|
|
||||||
ImportError is raised on failure.
|
ImportError is raised on failure.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
raise ImportError
|
raise ImportError
|
||||||
|
|
||||||
|
# XXX Deprecate.
|
||||||
def module_repr(self, module):
|
def module_repr(self, module):
|
||||||
"""Return a module's repr.
|
"""Return a module's repr.
|
||||||
|
|
||||||
Used by the module type when the method does not raise
|
Used by the module type when the method does not raise
|
||||||
NotImplementedError.
|
NotImplementedError.
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def init_module_attrs(self, module):
|
"""
|
||||||
"""Set the module's __loader__ attribute."""
|
# The exception will cause ModuleType.__repr__ to ignore this method.
|
||||||
module.__loader__ = self
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class ResourceLoader(Loader):
|
class ResourceLoader(Loader):
|
||||||
|
@ -138,12 +164,11 @@ class InspectLoader(Loader):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def is_package(self, fullname):
|
def is_package(self, fullname):
|
||||||
"""Abstract method which when implemented should return whether the
|
"""Optional method which when implemented should return whether the
|
||||||
module is a package. The fullname is a str. Returns a bool.
|
module is a package. The fullname is a str. Returns a bool.
|
||||||
|
|
||||||
Raises ImportError is the module cannot be found.
|
Raises ImportError if the module cannot be found.
|
||||||
"""
|
"""
|
||||||
raise ImportError
|
raise ImportError
|
||||||
|
|
||||||
|
@ -176,19 +201,10 @@ class InspectLoader(Loader):
|
||||||
argument should be where the data was retrieved (when applicable)."""
|
argument should be where the data was retrieved (when applicable)."""
|
||||||
return compile(data, path, 'exec', dont_inherit=True)
|
return compile(data, path, 'exec', dont_inherit=True)
|
||||||
|
|
||||||
def init_module_attrs(self, module):
|
exec_module = _bootstrap._LoaderBasics.exec_module
|
||||||
"""Initialize the __loader__ and __package__ attributes of the module.
|
|
||||||
|
|
||||||
The name of the module is gleaned from module.__name__. The __package__
|
|
||||||
attribute is set based on self.is_package().
|
|
||||||
"""
|
|
||||||
super().init_module_attrs(module)
|
|
||||||
_bootstrap._init_package_attrs(self, module)
|
|
||||||
|
|
||||||
load_module = _bootstrap._LoaderBasics.load_module
|
load_module = _bootstrap._LoaderBasics.load_module
|
||||||
|
|
||||||
_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter,
|
_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter)
|
||||||
_bootstrap.NamespaceLoader)
|
|
||||||
|
|
||||||
|
|
||||||
class ExecutionLoader(InspectLoader):
|
class ExecutionLoader(InspectLoader):
|
||||||
|
@ -225,18 +241,6 @@ class ExecutionLoader(InspectLoader):
|
||||||
else:
|
else:
|
||||||
return self.source_to_code(source, path)
|
return self.source_to_code(source, path)
|
||||||
|
|
||||||
def init_module_attrs(self, module):
|
|
||||||
"""Initialize the module's attributes.
|
|
||||||
|
|
||||||
It is assumed that the module's name has been set on module.__name__.
|
|
||||||
It is also assumed that any path returned by self.get_filename() uses
|
|
||||||
(one of) the operating system's path separator(s) to separate filenames
|
|
||||||
from directories in order to set __path__ intelligently.
|
|
||||||
InspectLoader.init_module_attrs() sets __loader__ and __package__.
|
|
||||||
"""
|
|
||||||
super().init_module_attrs(module)
|
|
||||||
_bootstrap._init_file_attrs(self, module)
|
|
||||||
|
|
||||||
_register(ExecutionLoader, machinery.ExtensionFileLoader)
|
_register(ExecutionLoader, machinery.ExtensionFileLoader)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import _imp
|
||||||
from ._bootstrap import (SOURCE_SUFFIXES, DEBUG_BYTECODE_SUFFIXES,
|
from ._bootstrap import (SOURCE_SUFFIXES, DEBUG_BYTECODE_SUFFIXES,
|
||||||
OPTIMIZED_BYTECODE_SUFFIXES, BYTECODE_SUFFIXES,
|
OPTIMIZED_BYTECODE_SUFFIXES, BYTECODE_SUFFIXES,
|
||||||
EXTENSION_SUFFIXES)
|
EXTENSION_SUFFIXES)
|
||||||
|
from ._bootstrap import ModuleSpec
|
||||||
from ._bootstrap import BuiltinImporter
|
from ._bootstrap import BuiltinImporter
|
||||||
from ._bootstrap import FrozenImporter
|
from ._bootstrap import FrozenImporter
|
||||||
from ._bootstrap import WindowsRegistryFinder
|
from ._bootstrap import WindowsRegistryFinder
|
||||||
|
|
|
@ -3,13 +3,14 @@
|
||||||
from ._bootstrap import MAGIC_NUMBER
|
from ._bootstrap import MAGIC_NUMBER
|
||||||
from ._bootstrap import cache_from_source
|
from ._bootstrap import cache_from_source
|
||||||
from ._bootstrap import decode_source
|
from ._bootstrap import decode_source
|
||||||
from ._bootstrap import module_to_load
|
|
||||||
from ._bootstrap import set_loader
|
|
||||||
from ._bootstrap import set_package
|
|
||||||
from ._bootstrap import source_from_cache
|
from ._bootstrap import source_from_cache
|
||||||
|
from ._bootstrap import spec_from_loader
|
||||||
|
from ._bootstrap import spec_from_file_location
|
||||||
from ._bootstrap import _resolve_name
|
from ._bootstrap import _resolve_name
|
||||||
|
|
||||||
|
from contextlib import contextmanager
|
||||||
import functools
|
import functools
|
||||||
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,6 +29,58 @@ def resolve_name(name, package):
|
||||||
return _resolve_name(name[level:], package, level)
|
return _resolve_name(name[level:], package, level)
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def _module_to_load(name):
|
||||||
|
is_reload = name in sys.modules
|
||||||
|
|
||||||
|
module = sys.modules.get(name)
|
||||||
|
if not is_reload:
|
||||||
|
# This must be done before open() is called as the 'io' module
|
||||||
|
# implicitly imports 'locale' and would otherwise trigger an
|
||||||
|
# infinite loop.
|
||||||
|
module = type(sys)(name)
|
||||||
|
# This must be done before putting the module in sys.modules
|
||||||
|
# (otherwise an optimization shortcut in import.c becomes wrong)
|
||||||
|
module.__initializing__ = True
|
||||||
|
sys.modules[name] = module
|
||||||
|
try:
|
||||||
|
yield module
|
||||||
|
except Exception:
|
||||||
|
if not is_reload:
|
||||||
|
try:
|
||||||
|
del sys.modules[name]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
module.__initializing__ = False
|
||||||
|
|
||||||
|
|
||||||
|
# XXX deprecate
|
||||||
|
def set_package(fxn):
|
||||||
|
"""Set __package__ on the returned module."""
|
||||||
|
@functools.wraps(fxn)
|
||||||
|
def set_package_wrapper(*args, **kwargs):
|
||||||
|
module = fxn(*args, **kwargs)
|
||||||
|
if getattr(module, '__package__', None) is None:
|
||||||
|
module.__package__ = module.__name__
|
||||||
|
if not hasattr(module, '__path__'):
|
||||||
|
module.__package__ = module.__package__.rpartition('.')[0]
|
||||||
|
return module
|
||||||
|
return set_package_wrapper
|
||||||
|
|
||||||
|
|
||||||
|
# XXX deprecate
|
||||||
|
def set_loader(fxn):
|
||||||
|
"""Set __loader__ on the returned module."""
|
||||||
|
@functools.wraps(fxn)
|
||||||
|
def set_loader_wrapper(self, *args, **kwargs):
|
||||||
|
module = fxn(self, *args, **kwargs)
|
||||||
|
if getattr(module, '__loader__', None) is None:
|
||||||
|
module.__loader__ = self
|
||||||
|
return module
|
||||||
|
return set_loader_wrapper
|
||||||
|
|
||||||
|
|
||||||
def module_for_loader(fxn):
|
def module_for_loader(fxn):
|
||||||
"""Decorator to handle selecting the proper module for loaders.
|
"""Decorator to handle selecting the proper module for loaders.
|
||||||
|
|
||||||
|
@ -46,13 +99,11 @@ def module_for_loader(fxn):
|
||||||
the second argument.
|
the second argument.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
warnings.warn('To make it easier for subclasses, please use '
|
warnings.warn('The import system now takes care of this automatically.',
|
||||||
'importlib.util.module_to_load() and '
|
|
||||||
'importlib.abc.Loader.init_module_attrs()',
|
|
||||||
PendingDeprecationWarning, stacklevel=2)
|
PendingDeprecationWarning, stacklevel=2)
|
||||||
@functools.wraps(fxn)
|
@functools.wraps(fxn)
|
||||||
def module_for_loader_wrapper(self, fullname, *args, **kwargs):
|
def module_for_loader_wrapper(self, fullname, *args, **kwargs):
|
||||||
with module_to_load(fullname) as module:
|
with _module_to_load(fullname) as module:
|
||||||
module.__loader__ = self
|
module.__loader__ = self
|
||||||
try:
|
try:
|
||||||
is_package = self.is_package(fullname)
|
is_package = self.is_package(fullname)
|
||||||
|
|
|
@ -245,14 +245,13 @@ def import_main_path(main_path):
|
||||||
# We should not try to load __main__
|
# We should not try to load __main__
|
||||||
# since that would execute 'if __name__ == "__main__"'
|
# since that would execute 'if __name__ == "__main__"'
|
||||||
# clauses, potentially causing a psuedo fork bomb.
|
# clauses, potentially causing a psuedo fork bomb.
|
||||||
loader = importlib.find_loader(main_name, path=dirs)
|
|
||||||
main_module = types.ModuleType(main_name)
|
main_module = types.ModuleType(main_name)
|
||||||
try:
|
# XXX Use a target of main_module?
|
||||||
loader.init_module_attrs(main_module)
|
spec = importlib.find_spec(main_name, path=dirs)
|
||||||
except AttributeError: # init_module_attrs is optional
|
methods = importlib._bootstrap._SpecMethods(spec)
|
||||||
pass
|
methods.init_module_attrs(main_module)
|
||||||
main_module.__name__ = '__mp_main__'
|
main_module.__name__ = '__mp_main__'
|
||||||
code = loader.get_code(main_name)
|
code = spec.loader.get_code(main_name)
|
||||||
exec(code, main_module.__dict__)
|
exec(code, main_module.__dict__)
|
||||||
|
|
||||||
old_main_modules.append(sys.modules['__main__'])
|
old_main_modules.append(sys.modules['__main__'])
|
||||||
|
|
|
@ -430,6 +430,7 @@ def iter_importers(fullname=""):
|
||||||
for item in path:
|
for item in path:
|
||||||
yield get_importer(item)
|
yield get_importer(item)
|
||||||
|
|
||||||
|
|
||||||
def get_loader(module_or_name):
|
def get_loader(module_or_name):
|
||||||
"""Get a PEP 302 "loader" object for module_or_name
|
"""Get a PEP 302 "loader" object for module_or_name
|
||||||
|
|
||||||
|
@ -570,6 +571,7 @@ def extend_path(path, name):
|
||||||
|
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def get_data(package, resource):
|
def get_data(package, resource):
|
||||||
"""Get a resource from a package.
|
"""Get a resource from a package.
|
||||||
|
|
||||||
|
@ -592,10 +594,15 @@ def get_data(package, resource):
|
||||||
which does not support get_data(), then None is returned.
|
which does not support get_data(), then None is returned.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
loader = get_loader(package)
|
spec = importlib.find_spec(package)
|
||||||
|
if spec is None:
|
||||||
|
return None
|
||||||
|
loader = spec.loader
|
||||||
if loader is None or not hasattr(loader, 'get_data'):
|
if loader is None or not hasattr(loader, 'get_data'):
|
||||||
return None
|
return None
|
||||||
mod = sys.modules.get(package) or loader.load_module(package)
|
# XXX needs test
|
||||||
|
mod = (sys.modules.get(package) or
|
||||||
|
importlib._bootstrap._SpecMethods(spec).load())
|
||||||
if mod is None or not hasattr(mod, '__file__'):
|
if mod is None or not hasattr(mod, '__file__'):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
@ -167,8 +167,9 @@ def _split_list(s, predicate):
|
||||||
def visiblename(name, all=None, obj=None):
|
def visiblename(name, all=None, obj=None):
|
||||||
"""Decide whether to show documentation on a variable."""
|
"""Decide whether to show documentation on a variable."""
|
||||||
# Certain special names are redundant or internal.
|
# Certain special names are redundant or internal.
|
||||||
|
# XXX Remove __initializing__?
|
||||||
if name in {'__author__', '__builtins__', '__cached__', '__credits__',
|
if name in {'__author__', '__builtins__', '__cached__', '__credits__',
|
||||||
'__date__', '__doc__', '__file__', '__initializing__',
|
'__date__', '__doc__', '__file__', '__spec__',
|
||||||
'__loader__', '__module__', '__name__', '__package__',
|
'__loader__', '__module__', '__name__', '__package__',
|
||||||
'__path__', '__qualname__', '__slots__', '__version__'}:
|
'__path__', '__qualname__', '__slots__', '__version__'}:
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -2259,7 +2259,7 @@ order (MRO) for bases """
|
||||||
minstance.b = 2
|
minstance.b = 2
|
||||||
minstance.a = 1
|
minstance.a = 1
|
||||||
default_attributes = ['__name__', '__doc__', '__package__',
|
default_attributes = ['__name__', '__doc__', '__package__',
|
||||||
'__loader__']
|
'__loader__', '__spec__']
|
||||||
names = [x for x in dir(minstance) if x not in default_attributes]
|
names = [x for x in dir(minstance) if x not in default_attributes]
|
||||||
self.assertEqual(names, ['a', 'b'])
|
self.assertEqual(names, ['a', 'b'])
|
||||||
|
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
# Test the frozen module defined in frozen.c.
|
|
||||||
|
|
||||||
from test.support import captured_stdout, run_unittest
|
|
||||||
import unittest
|
|
||||||
import sys
|
|
||||||
|
|
||||||
class FrozenTests(unittest.TestCase):
|
|
||||||
|
|
||||||
module_attrs = frozenset(['__builtins__', '__cached__', '__doc__',
|
|
||||||
'__loader__', '__name__',
|
|
||||||
'__package__'])
|
|
||||||
package_attrs = frozenset(list(module_attrs) + ['__path__'])
|
|
||||||
|
|
||||||
def test_frozen(self):
|
|
||||||
with captured_stdout() as stdout:
|
|
||||||
try:
|
|
||||||
import __hello__
|
|
||||||
except ImportError as x:
|
|
||||||
self.fail("import __hello__ failed:" + str(x))
|
|
||||||
self.assertEqual(__hello__.initialized, True)
|
|
||||||
expect = set(self.module_attrs)
|
|
||||||
expect.add('initialized')
|
|
||||||
self.assertEqual(set(dir(__hello__)), expect)
|
|
||||||
self.assertEqual(stdout.getvalue(), 'Hello world!\n')
|
|
||||||
|
|
||||||
with captured_stdout() as stdout:
|
|
||||||
try:
|
|
||||||
import __phello__
|
|
||||||
except ImportError as x:
|
|
||||||
self.fail("import __phello__ failed:" + str(x))
|
|
||||||
self.assertEqual(__phello__.initialized, True)
|
|
||||||
expect = set(self.package_attrs)
|
|
||||||
expect.add('initialized')
|
|
||||||
if not "__phello__.spam" in sys.modules:
|
|
||||||
self.assertEqual(set(dir(__phello__)), expect)
|
|
||||||
else:
|
|
||||||
expect.add('spam')
|
|
||||||
self.assertEqual(set(dir(__phello__)), expect)
|
|
||||||
self.assertEqual(__phello__.__path__, [])
|
|
||||||
self.assertEqual(stdout.getvalue(), 'Hello world!\n')
|
|
||||||
|
|
||||||
with captured_stdout() as stdout:
|
|
||||||
try:
|
|
||||||
import __phello__.spam
|
|
||||||
except ImportError as x:
|
|
||||||
self.fail("import __phello__.spam failed:" + str(x))
|
|
||||||
self.assertEqual(__phello__.spam.initialized, True)
|
|
||||||
spam_expect = set(self.module_attrs)
|
|
||||||
spam_expect.add('initialized')
|
|
||||||
self.assertEqual(set(dir(__phello__.spam)), spam_expect)
|
|
||||||
phello_expect = set(self.package_attrs)
|
|
||||||
phello_expect.add('initialized')
|
|
||||||
phello_expect.add('spam')
|
|
||||||
self.assertEqual(set(dir(__phello__)), phello_expect)
|
|
||||||
self.assertEqual(stdout.getvalue(), 'Hello world!\n')
|
|
||||||
|
|
||||||
try:
|
|
||||||
import __phello__.foo
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.fail("import __phello__.foo should have failed")
|
|
||||||
|
|
||||||
try:
|
|
||||||
import __phello__.foo
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.fail("import __phello__.foo should have failed")
|
|
||||||
|
|
||||||
del sys.modules['__hello__']
|
|
||||||
del sys.modules['__phello__']
|
|
||||||
del sys.modules['__phello__.spam']
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
|
@ -1036,11 +1036,14 @@ class ImportTracebackTests(unittest.TestCase):
|
||||||
# away from the traceback.
|
# away from the traceback.
|
||||||
self.create_module("foo", "")
|
self.create_module("foo", "")
|
||||||
importlib = sys.modules['_frozen_importlib']
|
importlib = sys.modules['_frozen_importlib']
|
||||||
old_load_module = importlib.SourceLoader.load_module
|
if 'load_module' in vars(importlib.SourceLoader):
|
||||||
|
old_exec_module = importlib.SourceLoader.exec_module
|
||||||
|
else:
|
||||||
|
old_exec_module = None
|
||||||
try:
|
try:
|
||||||
def load_module(*args):
|
def exec_module(*args):
|
||||||
1/0
|
1/0
|
||||||
importlib.SourceLoader.load_module = load_module
|
importlib.SourceLoader.exec_module = exec_module
|
||||||
try:
|
try:
|
||||||
import foo
|
import foo
|
||||||
except ZeroDivisionError as e:
|
except ZeroDivisionError as e:
|
||||||
|
@ -1049,7 +1052,10 @@ class ImportTracebackTests(unittest.TestCase):
|
||||||
self.fail("ZeroDivisionError should have been raised")
|
self.fail("ZeroDivisionError should have been raised")
|
||||||
self.assert_traceback(tb, [__file__, '<frozen importlib', __file__])
|
self.assert_traceback(tb, [__file__, '<frozen importlib', __file__])
|
||||||
finally:
|
finally:
|
||||||
importlib.SourceLoader.load_module = old_load_module
|
if old_exec_module is None:
|
||||||
|
del importlib.SourceLoader.exec_module
|
||||||
|
else:
|
||||||
|
importlib.SourceLoader.exec_module = old_exec_module
|
||||||
|
|
||||||
@unittest.skipUnless(TESTFN_UNENCODABLE, 'need TESTFN_UNENCODABLE')
|
@unittest.skipUnless(TESTFN_UNENCODABLE, 'need TESTFN_UNENCODABLE')
|
||||||
def test_unencodable_filename(self):
|
def test_unencodable_filename(self):
|
||||||
|
|
|
@ -80,11 +80,6 @@ class LoaderTests(metaclass=abc.ABCMeta):
|
||||||
imported."""
|
imported."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def test_module_reuse(self):
|
|
||||||
"""If a module is already in sys.modules, it should be reused."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def test_state_after_failure(self):
|
def test_state_after_failure(self):
|
||||||
"""If a module is already in sys.modules and a reload fails
|
"""If a module is already in sys.modules and a reload fails
|
||||||
|
|
|
@ -8,6 +8,46 @@ import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class FindSpecTests(abc.FinderTests):
|
||||||
|
|
||||||
|
"""Test find_spec() for built-in modules."""
|
||||||
|
|
||||||
|
def test_module(self):
|
||||||
|
# Common case.
|
||||||
|
with util.uncache(builtin_util.NAME):
|
||||||
|
found = self.machinery.BuiltinImporter.find_spec(builtin_util.NAME)
|
||||||
|
self.assertTrue(found)
|
||||||
|
self.assertEqual(found.origin, 'built-in')
|
||||||
|
|
||||||
|
# Built-in modules cannot be a package.
|
||||||
|
test_package = None
|
||||||
|
|
||||||
|
# Built-in modules cannobt be in a package.
|
||||||
|
test_module_in_package = None
|
||||||
|
|
||||||
|
# Built-in modules cannot be a package.
|
||||||
|
test_package_in_package = None
|
||||||
|
|
||||||
|
# Built-in modules cannot be a package.
|
||||||
|
test_package_over_module = None
|
||||||
|
|
||||||
|
def test_failure(self):
|
||||||
|
name = 'importlib'
|
||||||
|
assert name not in sys.builtin_module_names
|
||||||
|
spec = self.machinery.BuiltinImporter.find_spec(name)
|
||||||
|
self.assertIsNone(spec)
|
||||||
|
|
||||||
|
def test_ignore_path(self):
|
||||||
|
# The value for 'path' should always trigger a failed import.
|
||||||
|
with util.uncache(builtin_util.NAME):
|
||||||
|
spec = self.machinery.BuiltinImporter.find_spec(builtin_util.NAME,
|
||||||
|
['pkg'])
|
||||||
|
self.assertIsNone(spec)
|
||||||
|
|
||||||
|
Frozen_FindSpecTests, Source_FindSpecTests = util.test_both(FindSpecTests,
|
||||||
|
machinery=[frozen_machinery, source_machinery])
|
||||||
|
|
||||||
|
|
||||||
class FinderTests(abc.FinderTests):
|
class FinderTests(abc.FinderTests):
|
||||||
|
|
||||||
"""Test find_module() for built-in modules."""
|
"""Test find_module() for built-in modules."""
|
||||||
|
@ -17,22 +57,13 @@ class FinderTests(abc.FinderTests):
|
||||||
with util.uncache(builtin_util.NAME):
|
with util.uncache(builtin_util.NAME):
|
||||||
found = self.machinery.BuiltinImporter.find_module(builtin_util.NAME)
|
found = self.machinery.BuiltinImporter.find_module(builtin_util.NAME)
|
||||||
self.assertTrue(found)
|
self.assertTrue(found)
|
||||||
|
self.assertTrue(hasattr(found, 'load_module'))
|
||||||
|
|
||||||
def test_package(self):
|
# Built-in modules cannot be a package.
|
||||||
# Built-in modules cannot be a package.
|
test_package = test_package_in_package = test_package_over_module = None
|
||||||
pass
|
|
||||||
|
|
||||||
def test_module_in_package(self):
|
# Built-in modules cannot be in a package.
|
||||||
# Built-in modules cannobt be in a package.
|
test_module_in_package = None
|
||||||
pass
|
|
||||||
|
|
||||||
def test_package_in_package(self):
|
|
||||||
# Built-in modules cannot be a package.
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_package_over_module(self):
|
|
||||||
# Built-in modules cannot be a package.
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_failure(self):
|
def test_failure(self):
|
||||||
assert 'importlib' not in sys.builtin_module_names
|
assert 'importlib' not in sys.builtin_module_names
|
||||||
|
|
|
@ -9,6 +9,70 @@ import types
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class ExecModTests(abc.LoaderTests):
|
||||||
|
|
||||||
|
"""Test exec_module() for built-in modules."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.verification = {'__name__': 'errno', '__package__': '',
|
||||||
|
'__loader__': cls.machinery.BuiltinImporter}
|
||||||
|
|
||||||
|
def verify(self, module):
|
||||||
|
"""Verify that the module matches against what it should have."""
|
||||||
|
self.assertIsInstance(module, types.ModuleType)
|
||||||
|
for attr, value in self.verification.items():
|
||||||
|
self.assertEqual(getattr(module, attr), value)
|
||||||
|
self.assertIn(module.__name__, sys.modules)
|
||||||
|
self.assertTrue(hasattr(module, '__spec__'))
|
||||||
|
self.assertEqual(module.__spec__.origin, 'built-in')
|
||||||
|
|
||||||
|
def load_spec(self, name):
|
||||||
|
spec = self.machinery.ModuleSpec(name, self.machinery.BuiltinImporter,
|
||||||
|
origin='built-in')
|
||||||
|
module = types.ModuleType(name)
|
||||||
|
module.__spec__ = spec
|
||||||
|
self.machinery.BuiltinImporter.exec_module(module)
|
||||||
|
# Strictly not what exec_module() is supposed to do, but since
|
||||||
|
# _imp.init_builtin() does this we can't get around it.
|
||||||
|
return sys.modules[name]
|
||||||
|
|
||||||
|
def test_module(self):
|
||||||
|
# Common case.
|
||||||
|
with util.uncache(builtin_util.NAME):
|
||||||
|
module = self.load_spec(builtin_util.NAME)
|
||||||
|
self.verify(module)
|
||||||
|
self.assertIn('built-in', str(module))
|
||||||
|
|
||||||
|
# Built-in modules cannot be a package.
|
||||||
|
test_package = None
|
||||||
|
|
||||||
|
# Built-in modules cannot be a package.
|
||||||
|
test_lacking_parent = None
|
||||||
|
|
||||||
|
# Not way to force an import failure.
|
||||||
|
test_state_after_failure = None
|
||||||
|
|
||||||
|
def test_unloadable(self):
|
||||||
|
name = 'dssdsdfff'
|
||||||
|
assert name not in sys.builtin_module_names
|
||||||
|
with self.assertRaises(ImportError) as cm:
|
||||||
|
self.load_spec(name)
|
||||||
|
self.assertEqual(cm.exception.name, name)
|
||||||
|
|
||||||
|
def test_already_imported(self):
|
||||||
|
# Using the name of a module already imported but not a built-in should
|
||||||
|
# still fail.
|
||||||
|
assert hasattr(unittest, '__file__') # Not a built-in.
|
||||||
|
with self.assertRaises(ImportError) as cm:
|
||||||
|
self.load_spec('unittest')
|
||||||
|
self.assertEqual(cm.exception.name, 'unittest')
|
||||||
|
|
||||||
|
|
||||||
|
Frozen_ExecModTests, Source_ExecModTests = util.test_both(ExecModTests,
|
||||||
|
machinery=[frozen_machinery, source_machinery])
|
||||||
|
|
||||||
|
|
||||||
class LoaderTests(abc.LoaderTests):
|
class LoaderTests(abc.LoaderTests):
|
||||||
|
|
||||||
"""Test load_module() for built-in modules."""
|
"""Test load_module() for built-in modules."""
|
||||||
|
@ -33,17 +97,11 @@ class LoaderTests(abc.LoaderTests):
|
||||||
module = self.load_module(builtin_util.NAME)
|
module = self.load_module(builtin_util.NAME)
|
||||||
self.verify(module)
|
self.verify(module)
|
||||||
|
|
||||||
def test_package(self):
|
# Built-in modules cannot be a package.
|
||||||
# Built-in modules cannot be a package.
|
test_package = test_lacking_parent = None
|
||||||
pass
|
|
||||||
|
|
||||||
def test_lacking_parent(self):
|
# No way to force an import failure.
|
||||||
# Built-in modules cannot be a package.
|
test_state_after_failure = None
|
||||||
pass
|
|
||||||
|
|
||||||
def test_state_after_failure(self):
|
|
||||||
# Not way to force an imoprt failure.
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_module_reuse(self):
|
def test_module_reuse(self):
|
||||||
# Test that the same module is used in a reload.
|
# Test that the same module is used in a reload.
|
||||||
|
|
|
@ -9,6 +9,8 @@ from . import util as ext_util
|
||||||
frozen_machinery, source_machinery = util.import_importlib('importlib.machinery')
|
frozen_machinery, source_machinery = util.import_importlib('importlib.machinery')
|
||||||
|
|
||||||
|
|
||||||
|
# XXX find_spec tests
|
||||||
|
|
||||||
@unittest.skipIf(ext_util.FILENAME is None, '_testcapi not available')
|
@unittest.skipIf(ext_util.FILENAME is None, '_testcapi not available')
|
||||||
@util.case_insensitive_tests
|
@util.case_insensitive_tests
|
||||||
class ExtensionModuleCaseSensitivityTest:
|
class ExtensionModuleCaseSensitivityTest:
|
||||||
|
|
|
@ -6,6 +6,7 @@ machinery = test_util.import_importlib('importlib.machinery')
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
# XXX find_spec tests
|
||||||
|
|
||||||
class FinderTests(abc.FinderTests):
|
class FinderTests(abc.FinderTests):
|
||||||
|
|
||||||
|
@ -20,21 +21,14 @@ class FinderTests(abc.FinderTests):
|
||||||
def test_module(self):
|
def test_module(self):
|
||||||
self.assertTrue(self.find_module(util.NAME))
|
self.assertTrue(self.find_module(util.NAME))
|
||||||
|
|
||||||
def test_package(self):
|
# No extension module as an __init__ available for testing.
|
||||||
# No extension module as an __init__ available for testing.
|
test_package = test_package_in_package = None
|
||||||
pass
|
|
||||||
|
|
||||||
def test_module_in_package(self):
|
# No extension module in a package available for testing.
|
||||||
# No extension module in a package available for testing.
|
test_module_in_package = None
|
||||||
pass
|
|
||||||
|
|
||||||
def test_package_in_package(self):
|
# Extension modules cannot be an __init__ for a package.
|
||||||
# No extension module as an __init__ available for testing.
|
test_package_over_module = None
|
||||||
pass
|
|
||||||
|
|
||||||
def test_package_over_module(self):
|
|
||||||
# Extension modules cannot be an __init__ for a package.
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_failure(self):
|
def test_failure(self):
|
||||||
self.assertIsNone(self.find_module('asdfjkl;'))
|
self.assertIsNone(self.find_module('asdfjkl;'))
|
||||||
|
|
|
@ -6,9 +6,75 @@ machinery = util.import_importlib('importlib.machinery')
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
|
import types
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class ExecModuleTests(abc.LoaderTests):
|
||||||
|
|
||||||
|
"""Test load_module() for extension modules."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.loader = self.machinery.ExtensionFileLoader(ext_util.NAME,
|
||||||
|
ext_util.FILEPATH)
|
||||||
|
|
||||||
|
def exec_module(self, fullname):
|
||||||
|
module = types.ModuleType(fullname)
|
||||||
|
module.__spec__ = self.machinery.ModuleSpec(fullname, self.loader)
|
||||||
|
self.loader.exec_module(module)
|
||||||
|
return sys.modules[fullname]
|
||||||
|
|
||||||
|
def test_exec_module_API(self):
|
||||||
|
with self.assertRaises(ImportError):
|
||||||
|
self.exec_module('XXX')
|
||||||
|
|
||||||
|
|
||||||
|
def test_module(self):
|
||||||
|
with util.uncache(ext_util.NAME):
|
||||||
|
module = self.exec_module(ext_util.NAME)
|
||||||
|
for attr, value in [('__name__', ext_util.NAME),
|
||||||
|
('__file__', ext_util.FILEPATH),
|
||||||
|
('__package__', '')]:
|
||||||
|
given = getattr(module, attr)
|
||||||
|
self.assertEqual(given, value,
|
||||||
|
'{}: {!r} != {!r}'.format(attr, given, value))
|
||||||
|
self.assertIn(ext_util.NAME, sys.modules)
|
||||||
|
self.assertIsInstance(module.__loader__,
|
||||||
|
self.machinery.ExtensionFileLoader)
|
||||||
|
|
||||||
|
# No extension module as __init__ available for testing.
|
||||||
|
test_package = None
|
||||||
|
|
||||||
|
# No extension module in a package available for testing.
|
||||||
|
test_lacking_parent = None
|
||||||
|
|
||||||
|
def test_module_reuse(self):
|
||||||
|
with util.uncache(ext_util.NAME):
|
||||||
|
module1 = self.exec_module(ext_util.NAME)
|
||||||
|
module2 = self.exec_module(ext_util.NAME)
|
||||||
|
self.assertIs(module1, module2)
|
||||||
|
|
||||||
|
def test_state_after_failure(self):
|
||||||
|
# No easy way to trigger a failure after a successful import.
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_unloadable(self):
|
||||||
|
name = 'asdfjkl;'
|
||||||
|
with self.assertRaises(ImportError) as cm:
|
||||||
|
self.exec_module(name)
|
||||||
|
self.assertEqual(cm.exception.name, name)
|
||||||
|
|
||||||
|
def test_is_package(self):
|
||||||
|
self.assertFalse(self.loader.is_package(ext_util.NAME))
|
||||||
|
for suffix in self.machinery.EXTENSION_SUFFIXES:
|
||||||
|
path = os.path.join('some', 'path', 'pkg', '__init__' + suffix)
|
||||||
|
loader = self.machinery.ExtensionFileLoader('pkg', path)
|
||||||
|
self.assertTrue(loader.is_package('pkg'))
|
||||||
|
|
||||||
|
Frozen_ExecModuleTests, Source_ExecModuleTests = util.test_both(
|
||||||
|
ExecModuleTests, machinery=machinery)
|
||||||
|
|
||||||
|
|
||||||
class LoaderTests(abc.LoaderTests):
|
class LoaderTests(abc.LoaderTests):
|
||||||
|
|
||||||
"""Test load_module() for extension modules."""
|
"""Test load_module() for extension modules."""
|
||||||
|
@ -39,13 +105,11 @@ class LoaderTests(abc.LoaderTests):
|
||||||
self.assertIsInstance(module.__loader__,
|
self.assertIsInstance(module.__loader__,
|
||||||
self.machinery.ExtensionFileLoader)
|
self.machinery.ExtensionFileLoader)
|
||||||
|
|
||||||
def test_package(self):
|
# No extension module as __init__ available for testing.
|
||||||
# No extension module as __init__ available for testing.
|
test_package = None
|
||||||
pass
|
|
||||||
|
|
||||||
def test_lacking_parent(self):
|
# No extension module in a package available for testing.
|
||||||
# No extension module in a package available for testing.
|
test_lacking_parent = None
|
||||||
pass
|
|
||||||
|
|
||||||
def test_module_reuse(self):
|
def test_module_reuse(self):
|
||||||
with util.uncache(ext_util.NAME):
|
with util.uncache(ext_util.NAME):
|
||||||
|
@ -53,9 +117,8 @@ class LoaderTests(abc.LoaderTests):
|
||||||
module2 = self.load_module(ext_util.NAME)
|
module2 = self.load_module(ext_util.NAME)
|
||||||
self.assertIs(module1, module2)
|
self.assertIs(module1, module2)
|
||||||
|
|
||||||
def test_state_after_failure(self):
|
# No easy way to trigger a failure after a successful import.
|
||||||
# No easy way to trigger a failure after a successful import.
|
test_state_after_failure = None
|
||||||
pass
|
|
||||||
|
|
||||||
def test_unloadable(self):
|
def test_unloadable(self):
|
||||||
name = 'asdfjkl;'
|
name = 'asdfjkl;'
|
||||||
|
|
|
@ -6,6 +6,41 @@ machinery = util.import_importlib('importlib.machinery')
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class FindSpecTests(abc.FinderTests):
|
||||||
|
|
||||||
|
"""Test finding frozen modules."""
|
||||||
|
|
||||||
|
def find(self, name, path=None):
|
||||||
|
finder = self.machinery.FrozenImporter
|
||||||
|
return finder.find_spec(name, path)
|
||||||
|
|
||||||
|
def test_module(self):
|
||||||
|
name = '__hello__'
|
||||||
|
spec = self.find(name)
|
||||||
|
self.assertEqual(spec.origin, 'frozen')
|
||||||
|
|
||||||
|
def test_package(self):
|
||||||
|
spec = self.find('__phello__')
|
||||||
|
self.assertIsNotNone(spec)
|
||||||
|
|
||||||
|
def test_module_in_package(self):
|
||||||
|
spec = self.find('__phello__.spam', ['__phello__'])
|
||||||
|
self.assertIsNotNone(spec)
|
||||||
|
|
||||||
|
# No frozen package within another package to test with.
|
||||||
|
test_package_in_package = None
|
||||||
|
|
||||||
|
# No easy way to test.
|
||||||
|
test_package_over_module = None
|
||||||
|
|
||||||
|
def test_failure(self):
|
||||||
|
spec = self.find('<not real>')
|
||||||
|
self.assertIsNone(spec)
|
||||||
|
|
||||||
|
Frozen_FindSpecTests, Source_FindSpecTests = util.test_both(FindSpecTests,
|
||||||
|
machinery=machinery)
|
||||||
|
|
||||||
|
|
||||||
class FinderTests(abc.FinderTests):
|
class FinderTests(abc.FinderTests):
|
||||||
|
|
||||||
"""Test finding frozen modules."""
|
"""Test finding frozen modules."""
|
||||||
|
@ -27,13 +62,11 @@ class FinderTests(abc.FinderTests):
|
||||||
loader = self.find('__phello__.spam', ['__phello__'])
|
loader = self.find('__phello__.spam', ['__phello__'])
|
||||||
self.assertTrue(hasattr(loader, 'load_module'))
|
self.assertTrue(hasattr(loader, 'load_module'))
|
||||||
|
|
||||||
def test_package_in_package(self):
|
# No frozen package within another package to test with.
|
||||||
# No frozen package within another package to test with.
|
test_package_in_package = None
|
||||||
pass
|
|
||||||
|
|
||||||
def test_package_over_module(self):
|
# No easy way to test.
|
||||||
# No easy way to test.
|
test_package_over_module = None
|
||||||
pass
|
|
||||||
|
|
||||||
def test_failure(self):
|
def test_failure(self):
|
||||||
loader = self.find('<not real>')
|
loader = self.find('<not real>')
|
||||||
|
|
|
@ -3,9 +3,81 @@ from .. import util
|
||||||
|
|
||||||
machinery = util.import_importlib('importlib.machinery')
|
machinery = util.import_importlib('importlib.machinery')
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
import sys
|
||||||
from test.support import captured_stdout
|
from test.support import captured_stdout
|
||||||
import types
|
import types
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class ExecModuleTests(abc.LoaderTests):
|
||||||
|
|
||||||
|
def exec_module(self, name):
|
||||||
|
with util.uncache(name), captured_stdout() as stdout:
|
||||||
|
spec = self.machinery.ModuleSpec(
|
||||||
|
name, self.machinery.FrozenImporter, origin='frozen',
|
||||||
|
is_package=self.machinery.FrozenImporter.is_package(name))
|
||||||
|
module = types.ModuleType(name)
|
||||||
|
module.__spec__ = spec
|
||||||
|
assert not hasattr(module, 'initialized')
|
||||||
|
self.machinery.FrozenImporter.exec_module(module)
|
||||||
|
self.assertTrue(module.initialized)
|
||||||
|
self.assertTrue(hasattr(module, '__spec__'))
|
||||||
|
self.assertEqual(module.__spec__.origin, 'frozen')
|
||||||
|
return module, stdout.getvalue()
|
||||||
|
|
||||||
|
def test_module(self):
|
||||||
|
name = '__hello__'
|
||||||
|
module, output = self.exec_module(name)
|
||||||
|
check = {'__name__': name}
|
||||||
|
for attr, value in check.items():
|
||||||
|
self.assertEqual(getattr(module, attr), value)
|
||||||
|
self.assertEqual(output, 'Hello world!\n')
|
||||||
|
self.assertTrue(hasattr(module, '__spec__'))
|
||||||
|
|
||||||
|
def test_package(self):
|
||||||
|
name = '__phello__'
|
||||||
|
module, output = self.exec_module(name)
|
||||||
|
check = {'__name__': name}
|
||||||
|
for attr, value in check.items():
|
||||||
|
attr_value = getattr(module, attr)
|
||||||
|
self.assertEqual(attr_value, value,
|
||||||
|
'for {name}.{attr}, {given!r} != {expected!r}'.format(
|
||||||
|
name=name, attr=attr, given=attr_value,
|
||||||
|
expected=value))
|
||||||
|
self.assertEqual(output, 'Hello world!\n')
|
||||||
|
|
||||||
|
def test_lacking_parent(self):
|
||||||
|
name = '__phello__.spam'
|
||||||
|
with util.uncache('__phello__'):
|
||||||
|
module, output = self.exec_module(name)
|
||||||
|
check = {'__name__': name}
|
||||||
|
for attr, value in check.items():
|
||||||
|
attr_value = getattr(module, attr)
|
||||||
|
self.assertEqual(attr_value, value,
|
||||||
|
'for {name}.{attr}, {given} != {expected!r}'.format(
|
||||||
|
name=name, attr=attr, given=attr_value,
|
||||||
|
expected=value))
|
||||||
|
self.assertEqual(output, 'Hello world!\n')
|
||||||
|
|
||||||
|
|
||||||
|
def test_module_repr(self):
|
||||||
|
name = '__hello__'
|
||||||
|
module, output = self.exec_module(name)
|
||||||
|
self.assertEqual(repr(module),
|
||||||
|
"<module '__hello__' (frozen)>")
|
||||||
|
|
||||||
|
# No way to trigger an error in a frozen module.
|
||||||
|
test_state_after_failure = None
|
||||||
|
|
||||||
|
def test_unloadable(self):
|
||||||
|
assert self.machinery.FrozenImporter.find_module('_not_real') is None
|
||||||
|
with self.assertRaises(ImportError) as cm:
|
||||||
|
self.exec_module('_not_real')
|
||||||
|
self.assertEqual(cm.exception.name, '_not_real')
|
||||||
|
|
||||||
|
Frozen_ExecModuleTests, Source_ExecModuleTests = util.test_both(ExecModuleTests,
|
||||||
|
machinery=machinery)
|
||||||
|
|
||||||
|
|
||||||
class LoaderTests(abc.LoaderTests):
|
class LoaderTests(abc.LoaderTests):
|
||||||
|
@ -68,9 +140,8 @@ class LoaderTests(abc.LoaderTests):
|
||||||
self.assertEqual(repr(module),
|
self.assertEqual(repr(module),
|
||||||
"<module '__hello__' (frozen)>")
|
"<module '__hello__' (frozen)>")
|
||||||
|
|
||||||
def test_state_after_failure(self):
|
# No way to trigger an error in a frozen module.
|
||||||
# No way to trigger an error in a frozen module.
|
test_state_after_failure = None
|
||||||
pass
|
|
||||||
|
|
||||||
def test_unloadable(self):
|
def test_unloadable(self):
|
||||||
assert self.machinery.FrozenImporter.find_module('_not_real') is None
|
assert self.machinery.FrozenImporter.find_module('_not_real') is None
|
||||||
|
|
|
@ -46,8 +46,8 @@ class CallingOrder:
|
||||||
with util.import_state(meta_path=[]):
|
with util.import_state(meta_path=[]):
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with warnings.catch_warnings(record=True) as w:
|
||||||
warnings.simplefilter('always')
|
warnings.simplefilter('always')
|
||||||
self.assertIsNone(importlib._bootstrap._find_module('nothing',
|
self.assertIsNone(importlib._bootstrap._find_spec('nothing',
|
||||||
None))
|
None))
|
||||||
self.assertEqual(len(w), 1)
|
self.assertEqual(len(w), 1)
|
||||||
self.assertTrue(issubclass(w[-1].category, ImportWarning))
|
self.assertTrue(issubclass(w[-1].category, ImportWarning))
|
||||||
|
|
||||||
|
|
|
@ -284,22 +284,6 @@ class ExecutionLoaderDefaultsTests:
|
||||||
tests = make_return_value_tests(ExecutionLoader, InspectLoaderDefaultsTests)
|
tests = make_return_value_tests(ExecutionLoader, InspectLoaderDefaultsTests)
|
||||||
Frozen_ELDefaultTests, Source_ELDefaultsTests = tests
|
Frozen_ELDefaultTests, Source_ELDefaultsTests = tests
|
||||||
|
|
||||||
##### Loader concrete methods ##################################################
|
|
||||||
class LoaderConcreteMethodTests:
|
|
||||||
|
|
||||||
def test_init_module_attrs(self):
|
|
||||||
loader = self.LoaderSubclass()
|
|
||||||
module = types.ModuleType('blah')
|
|
||||||
loader.init_module_attrs(module)
|
|
||||||
self.assertEqual(module.__loader__, loader)
|
|
||||||
|
|
||||||
|
|
||||||
class Frozen_LoaderConcreateMethodTests(LoaderConcreteMethodTests, unittest.TestCase):
|
|
||||||
LoaderSubclass = Frozen_L
|
|
||||||
|
|
||||||
class Source_LoaderConcreateMethodTests(LoaderConcreteMethodTests, unittest.TestCase):
|
|
||||||
LoaderSubclass = Source_L
|
|
||||||
|
|
||||||
|
|
||||||
##### InspectLoader concrete methods ###########################################
|
##### InspectLoader concrete methods ###########################################
|
||||||
class InspectLoaderSourceToCodeTests:
|
class InspectLoaderSourceToCodeTests:
|
||||||
|
@ -385,60 +369,6 @@ class Source_ILGetCodeTests(InspectLoaderGetCodeTests, unittest.TestCase):
|
||||||
InspectLoaderSubclass = Source_IL
|
InspectLoaderSubclass = Source_IL
|
||||||
|
|
||||||
|
|
||||||
class InspectLoaderInitModuleTests:
|
|
||||||
|
|
||||||
def mock_is_package(self, return_value):
|
|
||||||
return mock.patch.object(self.InspectLoaderSubclass, 'is_package',
|
|
||||||
return_value=return_value)
|
|
||||||
|
|
||||||
def init_module_attrs(self, name):
|
|
||||||
loader = self.InspectLoaderSubclass()
|
|
||||||
module = types.ModuleType(name)
|
|
||||||
loader.init_module_attrs(module)
|
|
||||||
self.assertEqual(module.__loader__, loader)
|
|
||||||
return module
|
|
||||||
|
|
||||||
def test_package(self):
|
|
||||||
# If a package, then __package__ == __name__, __path__ == []
|
|
||||||
with self.mock_is_package(True):
|
|
||||||
name = 'blah'
|
|
||||||
module = self.init_module_attrs(name)
|
|
||||||
self.assertEqual(module.__package__, name)
|
|
||||||
self.assertEqual(module.__path__, [])
|
|
||||||
|
|
||||||
def test_toplevel(self):
|
|
||||||
# If a module is top-level, __package__ == ''
|
|
||||||
with self.mock_is_package(False):
|
|
||||||
name = 'blah'
|
|
||||||
module = self.init_module_attrs(name)
|
|
||||||
self.assertEqual(module.__package__, '')
|
|
||||||
|
|
||||||
def test_submodule(self):
|
|
||||||
# If a module is contained within a package then set __package__ to the
|
|
||||||
# package name.
|
|
||||||
with self.mock_is_package(False):
|
|
||||||
name = 'pkg.mod'
|
|
||||||
module = self.init_module_attrs(name)
|
|
||||||
self.assertEqual(module.__package__, 'pkg')
|
|
||||||
|
|
||||||
def test_is_package_ImportError(self):
|
|
||||||
# If is_package() raises ImportError, __package__ should be None and
|
|
||||||
# __path__ should not be set.
|
|
||||||
with self.mock_is_package(False) as mocked_method:
|
|
||||||
mocked_method.side_effect = ImportError
|
|
||||||
name = 'mod'
|
|
||||||
module = self.init_module_attrs(name)
|
|
||||||
self.assertIsNone(module.__package__)
|
|
||||||
self.assertFalse(hasattr(module, '__path__'))
|
|
||||||
|
|
||||||
|
|
||||||
class Frozen_ILInitModuleTests(InspectLoaderInitModuleTests, unittest.TestCase):
|
|
||||||
InspectLoaderSubclass = Frozen_IL
|
|
||||||
|
|
||||||
class Source_ILInitModuleTests(InspectLoaderInitModuleTests, unittest.TestCase):
|
|
||||||
InspectLoaderSubclass = Source_IL
|
|
||||||
|
|
||||||
|
|
||||||
class InspectLoaderLoadModuleTests:
|
class InspectLoaderLoadModuleTests:
|
||||||
|
|
||||||
"""Test InspectLoader.load_module()."""
|
"""Test InspectLoader.load_module()."""
|
||||||
|
@ -550,80 +480,6 @@ class Source_ELGetCodeTests(ExecutionLoaderGetCodeTests, unittest.TestCase):
|
||||||
ExecutionLoaderSubclass = Source_EL
|
ExecutionLoaderSubclass = Source_EL
|
||||||
|
|
||||||
|
|
||||||
class ExecutionLoaderInitModuleTests:
|
|
||||||
|
|
||||||
def mock_is_package(self, return_value):
|
|
||||||
return mock.patch.object(self.ExecutionLoaderSubclass, 'is_package',
|
|
||||||
return_value=return_value)
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def mock_methods(self, is_package, filename):
|
|
||||||
is_package_manager = self.mock_is_package(is_package)
|
|
||||||
get_filename_manager = mock.patch.object(self.ExecutionLoaderSubclass,
|
|
||||||
'get_filename', return_value=filename)
|
|
||||||
with is_package_manager as mock_is_package:
|
|
||||||
with get_filename_manager as mock_get_filename:
|
|
||||||
yield {'is_package': mock_is_package,
|
|
||||||
'get_filename': mock_get_filename}
|
|
||||||
|
|
||||||
def test_toplevel(self):
|
|
||||||
# Verify __loader__, __file__, and __package__; no __path__.
|
|
||||||
name = 'blah'
|
|
||||||
path = os.path.join('some', 'path', '{}.py'.format(name))
|
|
||||||
with self.mock_methods(False, path):
|
|
||||||
loader = self.ExecutionLoaderSubclass()
|
|
||||||
module = types.ModuleType(name)
|
|
||||||
loader.init_module_attrs(module)
|
|
||||||
self.assertIs(module.__loader__, loader)
|
|
||||||
self.assertEqual(module.__file__, path)
|
|
||||||
self.assertEqual(module.__package__, '')
|
|
||||||
self.assertFalse(hasattr(module, '__path__'))
|
|
||||||
|
|
||||||
def test_package(self):
|
|
||||||
# Verify __loader__, __file__, __package__, and __path__.
|
|
||||||
name = 'pkg'
|
|
||||||
path = os.path.join('some', 'pkg', '__init__.py')
|
|
||||||
with self.mock_methods(True, path):
|
|
||||||
loader = self.ExecutionLoaderSubclass()
|
|
||||||
module = types.ModuleType(name)
|
|
||||||
loader.init_module_attrs(module)
|
|
||||||
self.assertIs(module.__loader__, loader)
|
|
||||||
self.assertEqual(module.__file__, path)
|
|
||||||
self.assertEqual(module.__package__, 'pkg')
|
|
||||||
self.assertEqual(module.__path__, [os.path.dirname(path)])
|
|
||||||
|
|
||||||
def test_submodule(self):
|
|
||||||
# Verify __package__ and not __path__; test_toplevel() takes care of
|
|
||||||
# other attributes.
|
|
||||||
name = 'pkg.submodule'
|
|
||||||
path = os.path.join('some', 'pkg', 'submodule.py')
|
|
||||||
with self.mock_methods(False, path):
|
|
||||||
loader = self.ExecutionLoaderSubclass()
|
|
||||||
module = types.ModuleType(name)
|
|
||||||
loader.init_module_attrs(module)
|
|
||||||
self.assertEqual(module.__package__, 'pkg')
|
|
||||||
self.assertEqual(module.__file__, path)
|
|
||||||
self.assertFalse(hasattr(module, '__path__'))
|
|
||||||
|
|
||||||
def test_get_filename_ImportError(self):
|
|
||||||
# If get_filename() raises ImportError, don't set __file__.
|
|
||||||
name = 'blah'
|
|
||||||
path = 'blah.py'
|
|
||||||
with self.mock_methods(False, path) as mocked_methods:
|
|
||||||
mocked_methods['get_filename'].side_effect = ImportError
|
|
||||||
loader = self.ExecutionLoaderSubclass()
|
|
||||||
module = types.ModuleType(name)
|
|
||||||
loader.init_module_attrs(module)
|
|
||||||
self.assertFalse(hasattr(module, '__file__'))
|
|
||||||
|
|
||||||
|
|
||||||
class Frozen_ELInitModuleTests(ExecutionLoaderInitModuleTests, unittest.TestCase):
|
|
||||||
ExecutionLoaderSubclass = Frozen_EL
|
|
||||||
|
|
||||||
class Source_ELInitModuleTests(ExecutionLoaderInitModuleTests, unittest.TestCase):
|
|
||||||
ExecutionLoaderSubclass = Source_EL
|
|
||||||
|
|
||||||
|
|
||||||
##### SourceLoader concrete methods ############################################
|
##### SourceLoader concrete methods ############################################
|
||||||
class SourceLoader:
|
class SourceLoader:
|
||||||
|
|
||||||
|
@ -952,58 +808,5 @@ class Source_SourceOnlyLGetSourceTests(SourceLoaderGetSourceTests, unittest.Test
|
||||||
SourceOnlyLoaderMock = Source_SourceOnlyL
|
SourceOnlyLoaderMock = Source_SourceOnlyL
|
||||||
|
|
||||||
|
|
||||||
class SourceLoaderInitModuleAttrTests:
|
|
||||||
|
|
||||||
"""Tests for importlib.abc.SourceLoader.init_module_attrs()."""
|
|
||||||
|
|
||||||
def test_init_module_attrs(self):
|
|
||||||
# If __file__ set, __cached__ == importlib.util.cached_from_source(__file__).
|
|
||||||
name = 'blah'
|
|
||||||
path = 'blah.py'
|
|
||||||
loader = self.SourceOnlyLoaderMock(path)
|
|
||||||
module = types.ModuleType(name)
|
|
||||||
loader.init_module_attrs(module)
|
|
||||||
self.assertEqual(module.__loader__, loader)
|
|
||||||
self.assertEqual(module.__package__, '')
|
|
||||||
self.assertEqual(module.__file__, path)
|
|
||||||
self.assertEqual(module.__cached__, self.util.cache_from_source(path))
|
|
||||||
|
|
||||||
def test_no_get_filename(self):
|
|
||||||
# No __file__, no __cached__.
|
|
||||||
with mock.patch.object(self.SourceOnlyLoaderMock, 'get_filename') as mocked:
|
|
||||||
mocked.side_effect = ImportError
|
|
||||||
name = 'blah'
|
|
||||||
loader = self.SourceOnlyLoaderMock('blah.py')
|
|
||||||
module = types.ModuleType(name)
|
|
||||||
loader.init_module_attrs(module)
|
|
||||||
self.assertFalse(hasattr(module, '__file__'))
|
|
||||||
self.assertFalse(hasattr(module, '__cached__'))
|
|
||||||
|
|
||||||
|
|
||||||
class Frozen_SLInitModuleAttrTests(SourceLoaderInitModuleAttrTests, unittest.TestCase):
|
|
||||||
SourceOnlyLoaderMock = Frozen_SourceOnlyL
|
|
||||||
util = frozen_util
|
|
||||||
|
|
||||||
# Difficult to test under source thanks to cross-module mocking needs.
|
|
||||||
@mock.patch('importlib._bootstrap.cache_from_source')
|
|
||||||
def test_cache_from_source_NotImplementedError(self, mock_cache_from_source):
|
|
||||||
# If importlib.util.cache_from_source() raises NotImplementedError don't set
|
|
||||||
# __cached__.
|
|
||||||
mock_cache_from_source.side_effect = NotImplementedError
|
|
||||||
name = 'blah'
|
|
||||||
path = 'blah.py'
|
|
||||||
loader = self.SourceOnlyLoaderMock(path)
|
|
||||||
module = types.ModuleType(name)
|
|
||||||
loader.init_module_attrs(module)
|
|
||||||
self.assertEqual(module.__file__, path)
|
|
||||||
self.assertFalse(hasattr(module, '__cached__'))
|
|
||||||
|
|
||||||
|
|
||||||
class Source_SLInitModuleAttrTests(SourceLoaderInitModuleAttrTests, unittest.TestCase):
|
|
||||||
SourceOnlyLoaderMock = Source_SourceOnlyL
|
|
||||||
util = source_util
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -165,6 +165,96 @@ class Source_FindLoaderTests(FindLoaderTests, unittest.TestCase):
|
||||||
init = source_init
|
init = source_init
|
||||||
|
|
||||||
|
|
||||||
|
class FindSpecTests:
|
||||||
|
|
||||||
|
class FakeMetaFinder:
|
||||||
|
@staticmethod
|
||||||
|
def find_spec(name, path=None, target=None): return name, path, target
|
||||||
|
|
||||||
|
def test_sys_modules(self):
|
||||||
|
name = 'some_mod'
|
||||||
|
with util.uncache(name):
|
||||||
|
module = types.ModuleType(name)
|
||||||
|
loader = 'a loader!'
|
||||||
|
spec = self.machinery.ModuleSpec(name, loader)
|
||||||
|
module.__loader__ = loader
|
||||||
|
module.__spec__ = spec
|
||||||
|
sys.modules[name] = module
|
||||||
|
found = self.init.find_spec(name)
|
||||||
|
self.assertEqual(found, spec)
|
||||||
|
|
||||||
|
def test_sys_modules_without___loader__(self):
|
||||||
|
name = 'some_mod'
|
||||||
|
with util.uncache(name):
|
||||||
|
module = types.ModuleType(name)
|
||||||
|
del module.__loader__
|
||||||
|
loader = 'a loader!'
|
||||||
|
spec = self.machinery.ModuleSpec(name, loader)
|
||||||
|
module.__spec__ = spec
|
||||||
|
sys.modules[name] = module
|
||||||
|
found = self.init.find_spec(name)
|
||||||
|
self.assertEqual(found, spec)
|
||||||
|
|
||||||
|
def test_sys_modules_spec_is_None(self):
|
||||||
|
name = 'some_mod'
|
||||||
|
with util.uncache(name):
|
||||||
|
module = types.ModuleType(name)
|
||||||
|
module.__spec__ = None
|
||||||
|
sys.modules[name] = module
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.init.find_spec(name)
|
||||||
|
|
||||||
|
def test_sys_modules_loader_is_None(self):
|
||||||
|
name = 'some_mod'
|
||||||
|
with util.uncache(name):
|
||||||
|
module = types.ModuleType(name)
|
||||||
|
spec = self.machinery.ModuleSpec(name, None)
|
||||||
|
module.__spec__ = spec
|
||||||
|
sys.modules[name] = module
|
||||||
|
found = self.init.find_spec(name)
|
||||||
|
self.assertEqual(found, spec)
|
||||||
|
|
||||||
|
def test_sys_modules_spec_is_not_set(self):
|
||||||
|
name = 'some_mod'
|
||||||
|
with util.uncache(name):
|
||||||
|
module = types.ModuleType(name)
|
||||||
|
try:
|
||||||
|
del module.__spec__
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
sys.modules[name] = module
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.init.find_spec(name)
|
||||||
|
|
||||||
|
def test_success(self):
|
||||||
|
name = 'some_mod'
|
||||||
|
with util.uncache(name):
|
||||||
|
with util.import_state(meta_path=[self.FakeMetaFinder]):
|
||||||
|
self.assertEqual((name, None, None),
|
||||||
|
self.init.find_spec(name))
|
||||||
|
|
||||||
|
def test_success_path(self):
|
||||||
|
# Searching on a path should work.
|
||||||
|
name = 'some_mod'
|
||||||
|
path = 'path to some place'
|
||||||
|
with util.uncache(name):
|
||||||
|
with util.import_state(meta_path=[self.FakeMetaFinder]):
|
||||||
|
self.assertEqual((name, path, None),
|
||||||
|
self.init.find_spec(name, path))
|
||||||
|
|
||||||
|
def test_nothing(self):
|
||||||
|
# None is returned upon failure to find a loader.
|
||||||
|
self.assertIsNone(self.init.find_spec('nevergoingtofindthismodule'))
|
||||||
|
|
||||||
|
class Frozen_FindSpecTests(FindSpecTests, unittest.TestCase):
|
||||||
|
init = frozen_init
|
||||||
|
machinery = frozen_machinery
|
||||||
|
|
||||||
|
class Source_FindSpecTests(FindSpecTests, unittest.TestCase):
|
||||||
|
init = source_init
|
||||||
|
machinery = source_machinery
|
||||||
|
|
||||||
|
|
||||||
class ReloadTests:
|
class ReloadTests:
|
||||||
|
|
||||||
"""Test module reloading for builtin and extension modules."""
|
"""Test module reloading for builtin and extension modules."""
|
||||||
|
@ -219,6 +309,7 @@ class ReloadTests:
|
||||||
with support.temp_cwd(None) as cwd:
|
with support.temp_cwd(None) as cwd:
|
||||||
with util.uncache('spam'):
|
with util.uncache('spam'):
|
||||||
with support.DirsOnSysPath(cwd):
|
with support.DirsOnSysPath(cwd):
|
||||||
|
# Start as a plain module.
|
||||||
self.init.invalidate_caches()
|
self.init.invalidate_caches()
|
||||||
path = os.path.join(cwd, name + '.py')
|
path = os.path.join(cwd, name + '.py')
|
||||||
cached = self.util.cache_from_source(path)
|
cached = self.util.cache_from_source(path)
|
||||||
|
@ -232,11 +323,14 @@ class ReloadTests:
|
||||||
support.create_empty_file(path)
|
support.create_empty_file(path)
|
||||||
module = self.init.import_module(name)
|
module = self.init.import_module(name)
|
||||||
ns = vars(module)
|
ns = vars(module)
|
||||||
del ns['__initializing__']
|
|
||||||
loader = ns.pop('__loader__')
|
loader = ns.pop('__loader__')
|
||||||
|
spec = ns.pop('__spec__')
|
||||||
|
self.assertEqual(spec.name, name)
|
||||||
|
self.assertEqual(spec.loader, loader)
|
||||||
self.assertEqual(loader.path, path)
|
self.assertEqual(loader.path, path)
|
||||||
self.assertEqual(ns, expected)
|
self.assertEqual(ns, expected)
|
||||||
|
|
||||||
|
# Change to a package.
|
||||||
self.init.invalidate_caches()
|
self.init.invalidate_caches()
|
||||||
init_path = os.path.join(cwd, name, '__init__.py')
|
init_path = os.path.join(cwd, name, '__init__.py')
|
||||||
cached = self.util.cache_from_source(init_path)
|
cached = self.util.cache_from_source(init_path)
|
||||||
|
@ -252,18 +346,21 @@ class ReloadTests:
|
||||||
os.rename(path, init_path)
|
os.rename(path, init_path)
|
||||||
reloaded = self.init.reload(module)
|
reloaded = self.init.reload(module)
|
||||||
ns = vars(reloaded)
|
ns = vars(reloaded)
|
||||||
del ns['__initializing__']
|
|
||||||
loader = ns.pop('__loader__')
|
loader = ns.pop('__loader__')
|
||||||
|
spec = ns.pop('__spec__')
|
||||||
|
self.assertEqual(spec.name, name)
|
||||||
|
self.assertEqual(spec.loader, loader)
|
||||||
self.assertIs(reloaded, module)
|
self.assertIs(reloaded, module)
|
||||||
self.assertEqual(loader.path, init_path)
|
self.assertEqual(loader.path, init_path)
|
||||||
|
self.maxDiff = None
|
||||||
self.assertEqual(ns, expected)
|
self.assertEqual(ns, expected)
|
||||||
|
|
||||||
def test_reload_namespace_changed(self):
|
def test_reload_namespace_changed(self):
|
||||||
self.maxDiff = None
|
|
||||||
name = 'spam'
|
name = 'spam'
|
||||||
with support.temp_cwd(None) as cwd:
|
with support.temp_cwd(None) as cwd:
|
||||||
with util.uncache('spam'):
|
with util.uncache('spam'):
|
||||||
with support.DirsOnSysPath(cwd):
|
with support.DirsOnSysPath(cwd):
|
||||||
|
# Start as a namespace package.
|
||||||
self.init.invalidate_caches()
|
self.init.invalidate_caches()
|
||||||
bad_path = os.path.join(cwd, name, '__init.py')
|
bad_path = os.path.join(cwd, name, '__init.py')
|
||||||
cached = self.util.cache_from_source(bad_path)
|
cached = self.util.cache_from_source(bad_path)
|
||||||
|
@ -276,9 +373,12 @@ class ReloadTests:
|
||||||
init_file.write('eggs = None')
|
init_file.write('eggs = None')
|
||||||
module = self.init.import_module(name)
|
module = self.init.import_module(name)
|
||||||
ns = vars(module)
|
ns = vars(module)
|
||||||
del ns['__initializing__']
|
|
||||||
loader = ns.pop('__loader__')
|
loader = ns.pop('__loader__')
|
||||||
path = ns.pop('__path__')
|
path = ns.pop('__path__')
|
||||||
|
spec = ns.pop('__spec__')
|
||||||
|
self.assertEqual(spec.name, name)
|
||||||
|
self.assertIs(spec.loader, None)
|
||||||
|
self.assertIsNot(loader, None)
|
||||||
self.assertEqual(set(path),
|
self.assertEqual(set(path),
|
||||||
set([os.path.dirname(bad_path)]))
|
set([os.path.dirname(bad_path)]))
|
||||||
with self.assertRaises(AttributeError):
|
with self.assertRaises(AttributeError):
|
||||||
|
@ -286,6 +386,7 @@ class ReloadTests:
|
||||||
loader.path
|
loader.path
|
||||||
self.assertEqual(ns, expected)
|
self.assertEqual(ns, expected)
|
||||||
|
|
||||||
|
# Change to a regular package.
|
||||||
self.init.invalidate_caches()
|
self.init.invalidate_caches()
|
||||||
init_path = os.path.join(cwd, name, '__init__.py')
|
init_path = os.path.join(cwd, name, '__init__.py')
|
||||||
cached = self.util.cache_from_source(init_path)
|
cached = self.util.cache_from_source(init_path)
|
||||||
|
@ -301,8 +402,10 @@ class ReloadTests:
|
||||||
os.rename(bad_path, init_path)
|
os.rename(bad_path, init_path)
|
||||||
reloaded = self.init.reload(module)
|
reloaded = self.init.reload(module)
|
||||||
ns = vars(reloaded)
|
ns = vars(reloaded)
|
||||||
del ns['__initializing__']
|
|
||||||
loader = ns.pop('__loader__')
|
loader = ns.pop('__loader__')
|
||||||
|
spec = ns.pop('__spec__')
|
||||||
|
self.assertEqual(spec.name, name)
|
||||||
|
self.assertEqual(spec.loader, loader)
|
||||||
self.assertIs(reloaded, module)
|
self.assertIs(reloaded, module)
|
||||||
self.assertEqual(loader.path, init_path)
|
self.assertEqual(loader.path, init_path)
|
||||||
self.assertEqual(ns, expected)
|
self.assertEqual(ns, expected)
|
||||||
|
@ -371,12 +474,23 @@ class StartupTests:
|
||||||
# Issue #17098: all modules should have __loader__ defined.
|
# Issue #17098: all modules should have __loader__ defined.
|
||||||
for name, module in sys.modules.items():
|
for name, module in sys.modules.items():
|
||||||
if isinstance(module, types.ModuleType):
|
if isinstance(module, types.ModuleType):
|
||||||
self.assertTrue(hasattr(module, '__loader__'),
|
with self.subTest(name=name):
|
||||||
'{!r} lacks a __loader__ attribute'.format(name))
|
self.assertTrue(hasattr(module, '__loader__'),
|
||||||
if self.machinery.BuiltinImporter.find_module(name):
|
'{!r} lacks a __loader__ attribute'.format(name))
|
||||||
self.assertIsNot(module.__loader__, None)
|
if self.machinery.BuiltinImporter.find_module(name):
|
||||||
elif self.machinery.FrozenImporter.find_module(name):
|
self.assertIsNot(module.__loader__, None)
|
||||||
self.assertIsNot(module.__loader__, None)
|
elif self.machinery.FrozenImporter.find_module(name):
|
||||||
|
self.assertIsNot(module.__loader__, None)
|
||||||
|
|
||||||
|
def test_everyone_has___spec__(self):
|
||||||
|
for name, module in sys.modules.items():
|
||||||
|
if isinstance(module, types.ModuleType):
|
||||||
|
with self.subTest(name=name):
|
||||||
|
self.assertTrue(hasattr(module, '__spec__'))
|
||||||
|
if self.machinery.BuiltinImporter.find_module(name):
|
||||||
|
self.assertIsNot(module.__spec__, None)
|
||||||
|
elif self.machinery.FrozenImporter.find_module(name):
|
||||||
|
self.assertIsNot(module.__spec__, None)
|
||||||
|
|
||||||
class Frozen_StartupTests(StartupTests, unittest.TestCase):
|
class Frozen_StartupTests(StartupTests, unittest.TestCase):
|
||||||
machinery = frozen_machinery
|
machinery = frozen_machinery
|
||||||
|
|
|
@ -0,0 +1,968 @@
|
||||||
|
from . import util
|
||||||
|
|
||||||
|
frozen_init, source_init = util.import_importlib('importlib')
|
||||||
|
frozen_bootstrap = frozen_init._bootstrap
|
||||||
|
source_bootstrap = source_init._bootstrap
|
||||||
|
frozen_machinery, source_machinery = util.import_importlib('importlib.machinery')
|
||||||
|
frozen_util, source_util = util.import_importlib('importlib.util')
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
from test.support import CleanImport
|
||||||
|
import unittest
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TestLoader:
|
||||||
|
|
||||||
|
def __init__(self, path=None, is_package=None):
|
||||||
|
# if path:
|
||||||
|
# if is_package:
|
||||||
|
# if not path.endswith('.py'):
|
||||||
|
# path = os.path.join(path, '__init__.py')
|
||||||
|
# elif is_package is None:
|
||||||
|
# is_package = path.endswith('__init__.py')
|
||||||
|
|
||||||
|
self.path = path
|
||||||
|
self.package = is_package
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<TestLoader object>'
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if name == 'get_filename' and self.path is not None:
|
||||||
|
return self._get_filename
|
||||||
|
if name == 'is_package':
|
||||||
|
return self._is_package
|
||||||
|
raise AttributeError(name)
|
||||||
|
|
||||||
|
def _get_filename(self, name):
|
||||||
|
return self.path
|
||||||
|
|
||||||
|
def _is_package(self, name):
|
||||||
|
return self.package
|
||||||
|
|
||||||
|
|
||||||
|
class NewLoader(TestLoader):
|
||||||
|
|
||||||
|
EGGS = 1
|
||||||
|
|
||||||
|
def exec_module(self, module):
|
||||||
|
module.eggs = self.EGGS
|
||||||
|
|
||||||
|
|
||||||
|
class LegacyLoader(TestLoader):
|
||||||
|
|
||||||
|
HAM = -1
|
||||||
|
|
||||||
|
@frozen_util.module_for_loader
|
||||||
|
def load_module(self, module):
|
||||||
|
module.ham = self.HAM
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleSpecTests:
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.name = 'spam'
|
||||||
|
self.path = 'spam.py'
|
||||||
|
self.cached = self.util.cache_from_source(self.path)
|
||||||
|
self.loader = TestLoader()
|
||||||
|
self.spec = self.machinery.ModuleSpec(self.name, self.loader)
|
||||||
|
self.loc_spec = self.machinery.ModuleSpec(self.name, self.loader,
|
||||||
|
origin=self.path)
|
||||||
|
self.loc_spec._set_fileattr = True
|
||||||
|
|
||||||
|
def test_default(self):
|
||||||
|
spec = self.machinery.ModuleSpec(self.name, self.loader)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.loader)
|
||||||
|
self.assertIs(spec.origin, None)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
self.assertFalse(spec.has_location)
|
||||||
|
|
||||||
|
def test_default_no_loader(self):
|
||||||
|
spec = self.machinery.ModuleSpec(self.name, None)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertIs(spec.loader, None)
|
||||||
|
self.assertIs(spec.origin, None)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
self.assertFalse(spec.has_location)
|
||||||
|
|
||||||
|
def test_default_is_package_false(self):
|
||||||
|
spec = self.machinery.ModuleSpec(self.name, self.loader,
|
||||||
|
is_package=False)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.loader)
|
||||||
|
self.assertIs(spec.origin, None)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
self.assertFalse(spec.has_location)
|
||||||
|
|
||||||
|
def test_default_is_package_true(self):
|
||||||
|
spec = self.machinery.ModuleSpec(self.name, self.loader,
|
||||||
|
is_package=True)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.loader)
|
||||||
|
self.assertIs(spec.origin, None)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertEqual(spec.submodule_search_locations, [])
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
self.assertFalse(spec.has_location)
|
||||||
|
|
||||||
|
def test_equality(self):
|
||||||
|
other = type(sys.implementation)(name=self.name,
|
||||||
|
loader=self.loader,
|
||||||
|
origin=None,
|
||||||
|
submodule_search_locations=None,
|
||||||
|
has_location=False,
|
||||||
|
cached=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertTrue(self.spec == other)
|
||||||
|
|
||||||
|
def test_equality_location(self):
|
||||||
|
other = type(sys.implementation)(name=self.name,
|
||||||
|
loader=self.loader,
|
||||||
|
origin=self.path,
|
||||||
|
submodule_search_locations=None,
|
||||||
|
has_location=True,
|
||||||
|
cached=self.cached,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(self.loc_spec, other)
|
||||||
|
|
||||||
|
def test_inequality(self):
|
||||||
|
other = type(sys.implementation)(name='ham',
|
||||||
|
loader=self.loader,
|
||||||
|
origin=None,
|
||||||
|
submodule_search_locations=None,
|
||||||
|
has_location=False,
|
||||||
|
cached=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertNotEqual(self.spec, other)
|
||||||
|
|
||||||
|
def test_inequality_incomplete(self):
|
||||||
|
other = type(sys.implementation)(name=self.name,
|
||||||
|
loader=self.loader,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertNotEqual(self.spec, other)
|
||||||
|
|
||||||
|
def test_package(self):
|
||||||
|
spec = self.machinery.ModuleSpec('spam.eggs', self.loader)
|
||||||
|
|
||||||
|
self.assertEqual(spec.parent, 'spam')
|
||||||
|
|
||||||
|
def test_package_is_package(self):
|
||||||
|
spec = self.machinery.ModuleSpec('spam.eggs', self.loader,
|
||||||
|
is_package=True)
|
||||||
|
|
||||||
|
self.assertEqual(spec.parent, 'spam.eggs')
|
||||||
|
|
||||||
|
# cached
|
||||||
|
|
||||||
|
def test_cached_set(self):
|
||||||
|
before = self.spec.cached
|
||||||
|
self.spec.cached = 'there'
|
||||||
|
after = self.spec.cached
|
||||||
|
|
||||||
|
self.assertIs(before, None)
|
||||||
|
self.assertEqual(after, 'there')
|
||||||
|
|
||||||
|
def test_cached_no_origin(self):
|
||||||
|
spec = self.machinery.ModuleSpec(self.name, self.loader)
|
||||||
|
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
|
||||||
|
def test_cached_with_origin_not_location(self):
|
||||||
|
spec = self.machinery.ModuleSpec(self.name, self.loader,
|
||||||
|
origin=self.path)
|
||||||
|
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
|
||||||
|
def test_cached_source(self):
|
||||||
|
expected = self.util.cache_from_source(self.path)
|
||||||
|
|
||||||
|
self.assertEqual(self.loc_spec.cached, expected)
|
||||||
|
|
||||||
|
def test_cached_source_unknown_suffix(self):
|
||||||
|
self.loc_spec.origin = 'spam.spamspamspam'
|
||||||
|
|
||||||
|
self.assertIs(self.loc_spec.cached, None)
|
||||||
|
|
||||||
|
def test_cached_source_missing_cache_tag(self):
|
||||||
|
original = sys.implementation.cache_tag
|
||||||
|
sys.implementation.cache_tag = None
|
||||||
|
try:
|
||||||
|
cached = self.loc_spec.cached
|
||||||
|
finally:
|
||||||
|
sys.implementation.cache_tag = original
|
||||||
|
|
||||||
|
self.assertIs(cached, None)
|
||||||
|
|
||||||
|
def test_cached_sourceless(self):
|
||||||
|
self.loc_spec.origin = 'spam.pyc'
|
||||||
|
|
||||||
|
self.assertEqual(self.loc_spec.cached, 'spam.pyc')
|
||||||
|
|
||||||
|
|
||||||
|
class Frozen_ModuleSpecTests(ModuleSpecTests, unittest.TestCase):
|
||||||
|
util = frozen_util
|
||||||
|
machinery = frozen_machinery
|
||||||
|
|
||||||
|
|
||||||
|
class Source_ModuleSpecTests(ModuleSpecTests, unittest.TestCase):
|
||||||
|
util = source_util
|
||||||
|
machinery = source_machinery
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleSpecMethodsTests:
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.name = 'spam'
|
||||||
|
self.path = 'spam.py'
|
||||||
|
self.cached = self.util.cache_from_source(self.path)
|
||||||
|
self.loader = TestLoader()
|
||||||
|
self.spec = self.machinery.ModuleSpec(self.name, self.loader)
|
||||||
|
self.loc_spec = self.machinery.ModuleSpec(self.name, self.loader,
|
||||||
|
origin=self.path)
|
||||||
|
self.loc_spec._set_fileattr = True
|
||||||
|
|
||||||
|
# init_module_attrs
|
||||||
|
|
||||||
|
def test_init_module_attrs(self):
|
||||||
|
module = type(sys)(self.name)
|
||||||
|
spec = self.machinery.ModuleSpec(self.name, self.loader)
|
||||||
|
self.bootstrap._SpecMethods(spec).init_module_attrs(module)
|
||||||
|
|
||||||
|
self.assertEqual(module.__name__, spec.name)
|
||||||
|
self.assertIs(module.__loader__, spec.loader)
|
||||||
|
self.assertEqual(module.__package__, spec.parent)
|
||||||
|
self.assertIs(module.__spec__, spec)
|
||||||
|
self.assertFalse(hasattr(module, '__path__'))
|
||||||
|
self.assertFalse(hasattr(module, '__file__'))
|
||||||
|
self.assertFalse(hasattr(module, '__cached__'))
|
||||||
|
|
||||||
|
def test_init_module_attrs_package(self):
|
||||||
|
module = type(sys)(self.name)
|
||||||
|
spec = self.machinery.ModuleSpec(self.name, self.loader)
|
||||||
|
spec.submodule_search_locations = ['spam', 'ham']
|
||||||
|
self.bootstrap._SpecMethods(spec).init_module_attrs(module)
|
||||||
|
|
||||||
|
self.assertEqual(module.__name__, spec.name)
|
||||||
|
self.assertIs(module.__loader__, spec.loader)
|
||||||
|
self.assertEqual(module.__package__, spec.parent)
|
||||||
|
self.assertIs(module.__spec__, spec)
|
||||||
|
self.assertIs(module.__path__, spec.submodule_search_locations)
|
||||||
|
self.assertFalse(hasattr(module, '__file__'))
|
||||||
|
self.assertFalse(hasattr(module, '__cached__'))
|
||||||
|
|
||||||
|
def test_init_module_attrs_location(self):
|
||||||
|
module = type(sys)(self.name)
|
||||||
|
spec = self.loc_spec
|
||||||
|
self.bootstrap._SpecMethods(spec).init_module_attrs(module)
|
||||||
|
|
||||||
|
self.assertEqual(module.__name__, spec.name)
|
||||||
|
self.assertIs(module.__loader__, spec.loader)
|
||||||
|
self.assertEqual(module.__package__, spec.parent)
|
||||||
|
self.assertIs(module.__spec__, spec)
|
||||||
|
self.assertFalse(hasattr(module, '__path__'))
|
||||||
|
self.assertEqual(module.__file__, spec.origin)
|
||||||
|
self.assertEqual(module.__cached__,
|
||||||
|
self.util.cache_from_source(spec.origin))
|
||||||
|
|
||||||
|
def test_init_module_attrs_different_name(self):
|
||||||
|
module = type(sys)('eggs')
|
||||||
|
spec = self.machinery.ModuleSpec(self.name, self.loader)
|
||||||
|
self.bootstrap._SpecMethods(spec).init_module_attrs(module)
|
||||||
|
|
||||||
|
self.assertEqual(module.__name__, spec.name)
|
||||||
|
|
||||||
|
def test_init_module_attrs_different_spec(self):
|
||||||
|
module = type(sys)(self.name)
|
||||||
|
module.__spec__ = self.machinery.ModuleSpec('eggs', object())
|
||||||
|
spec = self.machinery.ModuleSpec(self.name, self.loader)
|
||||||
|
self.bootstrap._SpecMethods(spec).init_module_attrs(module)
|
||||||
|
|
||||||
|
self.assertEqual(module.__name__, spec.name)
|
||||||
|
self.assertIs(module.__loader__, spec.loader)
|
||||||
|
self.assertEqual(module.__package__, spec.parent)
|
||||||
|
self.assertIs(module.__spec__, spec)
|
||||||
|
|
||||||
|
def test_init_module_attrs_already_set(self):
|
||||||
|
module = type(sys)('ham.eggs')
|
||||||
|
module.__loader__ = object()
|
||||||
|
module.__package__ = 'ham'
|
||||||
|
module.__path__ = ['eggs']
|
||||||
|
module.__file__ = 'ham/eggs/__init__.py'
|
||||||
|
module.__cached__ = self.util.cache_from_source(module.__file__)
|
||||||
|
original = vars(module).copy()
|
||||||
|
spec = self.loc_spec
|
||||||
|
spec.submodule_search_locations = ['']
|
||||||
|
self.bootstrap._SpecMethods(spec).init_module_attrs(module)
|
||||||
|
|
||||||
|
self.assertIs(module.__loader__, original['__loader__'])
|
||||||
|
self.assertEqual(module.__package__, original['__package__'])
|
||||||
|
self.assertIs(module.__path__, original['__path__'])
|
||||||
|
self.assertEqual(module.__file__, original['__file__'])
|
||||||
|
self.assertEqual(module.__cached__, original['__cached__'])
|
||||||
|
|
||||||
|
def test_init_module_attrs_immutable(self):
|
||||||
|
module = object()
|
||||||
|
spec = self.loc_spec
|
||||||
|
spec.submodule_search_locations = ['']
|
||||||
|
self.bootstrap._SpecMethods(spec).init_module_attrs(module)
|
||||||
|
|
||||||
|
self.assertFalse(hasattr(module, '__name__'))
|
||||||
|
self.assertFalse(hasattr(module, '__loader__'))
|
||||||
|
self.assertFalse(hasattr(module, '__package__'))
|
||||||
|
self.assertFalse(hasattr(module, '__spec__'))
|
||||||
|
self.assertFalse(hasattr(module, '__path__'))
|
||||||
|
self.assertFalse(hasattr(module, '__file__'))
|
||||||
|
self.assertFalse(hasattr(module, '__cached__'))
|
||||||
|
|
||||||
|
# create()
|
||||||
|
|
||||||
|
def test_create(self):
|
||||||
|
created = self.bootstrap._SpecMethods(self.spec).create()
|
||||||
|
|
||||||
|
self.assertEqual(created.__name__, self.spec.name)
|
||||||
|
self.assertIs(created.__loader__, self.spec.loader)
|
||||||
|
self.assertEqual(created.__package__, self.spec.parent)
|
||||||
|
self.assertIs(created.__spec__, self.spec)
|
||||||
|
self.assertFalse(hasattr(created, '__path__'))
|
||||||
|
self.assertFalse(hasattr(created, '__file__'))
|
||||||
|
self.assertFalse(hasattr(created, '__cached__'))
|
||||||
|
|
||||||
|
def test_create_from_loader(self):
|
||||||
|
module = type(sys.implementation)()
|
||||||
|
class CreatingLoader(TestLoader):
|
||||||
|
def create_module(self, spec):
|
||||||
|
return module
|
||||||
|
self.spec.loader = CreatingLoader()
|
||||||
|
created = self.bootstrap._SpecMethods(self.spec).create()
|
||||||
|
|
||||||
|
self.assertIs(created, module)
|
||||||
|
self.assertEqual(created.__name__, self.spec.name)
|
||||||
|
self.assertIs(created.__loader__, self.spec.loader)
|
||||||
|
self.assertEqual(created.__package__, self.spec.parent)
|
||||||
|
self.assertIs(created.__spec__, self.spec)
|
||||||
|
self.assertFalse(hasattr(created, '__path__'))
|
||||||
|
self.assertFalse(hasattr(created, '__file__'))
|
||||||
|
self.assertFalse(hasattr(created, '__cached__'))
|
||||||
|
|
||||||
|
def test_create_from_loader_not_handled(self):
|
||||||
|
class CreatingLoader(TestLoader):
|
||||||
|
def create_module(self, spec):
|
||||||
|
return None
|
||||||
|
self.spec.loader = CreatingLoader()
|
||||||
|
created = self.bootstrap._SpecMethods(self.spec).create()
|
||||||
|
|
||||||
|
self.assertEqual(created.__name__, self.spec.name)
|
||||||
|
self.assertIs(created.__loader__, self.spec.loader)
|
||||||
|
self.assertEqual(created.__package__, self.spec.parent)
|
||||||
|
self.assertIs(created.__spec__, self.spec)
|
||||||
|
self.assertFalse(hasattr(created, '__path__'))
|
||||||
|
self.assertFalse(hasattr(created, '__file__'))
|
||||||
|
self.assertFalse(hasattr(created, '__cached__'))
|
||||||
|
|
||||||
|
# exec()
|
||||||
|
|
||||||
|
def test_exec(self):
|
||||||
|
self.spec.loader = NewLoader()
|
||||||
|
module = self.bootstrap._SpecMethods(self.spec).create()
|
||||||
|
sys.modules[self.name] = module
|
||||||
|
self.assertFalse(hasattr(module, 'eggs'))
|
||||||
|
self.bootstrap._SpecMethods(self.spec).exec(module)
|
||||||
|
|
||||||
|
self.assertEqual(module.eggs, 1)
|
||||||
|
|
||||||
|
# load()
|
||||||
|
|
||||||
|
def test_load(self):
|
||||||
|
self.spec.loader = NewLoader()
|
||||||
|
with CleanImport(self.spec.name):
|
||||||
|
loaded = self.bootstrap._SpecMethods(self.spec).load()
|
||||||
|
installed = sys.modules[self.spec.name]
|
||||||
|
|
||||||
|
self.assertEqual(loaded.eggs, 1)
|
||||||
|
self.assertIs(loaded, installed)
|
||||||
|
|
||||||
|
def test_load_replaced(self):
|
||||||
|
replacement = object()
|
||||||
|
class ReplacingLoader(TestLoader):
|
||||||
|
def exec_module(self, module):
|
||||||
|
sys.modules[module.__name__] = replacement
|
||||||
|
self.spec.loader = ReplacingLoader()
|
||||||
|
with CleanImport(self.spec.name):
|
||||||
|
loaded = self.bootstrap._SpecMethods(self.spec).load()
|
||||||
|
installed = sys.modules[self.spec.name]
|
||||||
|
|
||||||
|
self.assertIs(loaded, replacement)
|
||||||
|
self.assertIs(installed, replacement)
|
||||||
|
|
||||||
|
def test_load_failed(self):
|
||||||
|
class FailedLoader(TestLoader):
|
||||||
|
def exec_module(self, module):
|
||||||
|
raise RuntimeError
|
||||||
|
self.spec.loader = FailedLoader()
|
||||||
|
with CleanImport(self.spec.name):
|
||||||
|
with self.assertRaises(RuntimeError):
|
||||||
|
loaded = self.bootstrap._SpecMethods(self.spec).load()
|
||||||
|
self.assertNotIn(self.spec.name, sys.modules)
|
||||||
|
|
||||||
|
def test_load_failed_removed(self):
|
||||||
|
class FailedLoader(TestLoader):
|
||||||
|
def exec_module(self, module):
|
||||||
|
del sys.modules[module.__name__]
|
||||||
|
raise RuntimeError
|
||||||
|
self.spec.loader = FailedLoader()
|
||||||
|
with CleanImport(self.spec.name):
|
||||||
|
with self.assertRaises(RuntimeError):
|
||||||
|
loaded = self.bootstrap._SpecMethods(self.spec).load()
|
||||||
|
self.assertNotIn(self.spec.name, sys.modules)
|
||||||
|
|
||||||
|
def test_load_existing(self):
|
||||||
|
existing = type(sys)('ham')
|
||||||
|
existing.count = 5
|
||||||
|
self.spec.loader = NewLoader()
|
||||||
|
with CleanImport(self.name):
|
||||||
|
sys.modules[self.name] = existing
|
||||||
|
assert self.spec.name == self.name
|
||||||
|
loaded = self.bootstrap._SpecMethods(self.spec).load()
|
||||||
|
|
||||||
|
self.assertEqual(loaded.eggs, 1)
|
||||||
|
self.assertFalse(hasattr(loaded, 'ham'))
|
||||||
|
|
||||||
|
def test_load_legacy(self):
|
||||||
|
self.spec.loader = LegacyLoader()
|
||||||
|
with CleanImport(self.spec.name):
|
||||||
|
loaded = self.bootstrap._SpecMethods(self.spec).load()
|
||||||
|
|
||||||
|
self.assertEqual(loaded.ham, -1)
|
||||||
|
|
||||||
|
def test_load_legacy_attributes(self):
|
||||||
|
self.spec.loader = LegacyLoader()
|
||||||
|
with CleanImport(self.spec.name):
|
||||||
|
loaded = self.bootstrap._SpecMethods(self.spec).load()
|
||||||
|
|
||||||
|
self.assertIs(loaded.__loader__, self.spec.loader)
|
||||||
|
self.assertEqual(loaded.__package__, self.spec.parent)
|
||||||
|
self.assertIs(loaded.__spec__, self.spec)
|
||||||
|
|
||||||
|
def test_load_legacy_attributes_immutable(self):
|
||||||
|
module = object()
|
||||||
|
class ImmutableLoader(TestLoader):
|
||||||
|
def load_module(self, name):
|
||||||
|
sys.modules[name] = module
|
||||||
|
return module
|
||||||
|
self.spec.loader = ImmutableLoader()
|
||||||
|
with CleanImport(self.spec.name):
|
||||||
|
loaded = self.bootstrap._SpecMethods(self.spec).load()
|
||||||
|
|
||||||
|
self.assertIs(sys.modules[self.spec.name], module)
|
||||||
|
|
||||||
|
# reload()
|
||||||
|
|
||||||
|
def test_reload(self):
|
||||||
|
self.spec.loader = NewLoader()
|
||||||
|
with CleanImport(self.spec.name):
|
||||||
|
loaded = self.bootstrap._SpecMethods(self.spec).load()
|
||||||
|
reloaded = self.bootstrap._SpecMethods(self.spec).exec(loaded)
|
||||||
|
installed = sys.modules[self.spec.name]
|
||||||
|
|
||||||
|
self.assertEqual(loaded.eggs, 1)
|
||||||
|
self.assertIs(reloaded, loaded)
|
||||||
|
self.assertIs(installed, loaded)
|
||||||
|
|
||||||
|
def test_reload_modified(self):
|
||||||
|
self.spec.loader = NewLoader()
|
||||||
|
with CleanImport(self.spec.name):
|
||||||
|
loaded = self.bootstrap._SpecMethods(self.spec).load()
|
||||||
|
loaded.eggs = 2
|
||||||
|
reloaded = self.bootstrap._SpecMethods(self.spec).exec(loaded)
|
||||||
|
|
||||||
|
self.assertEqual(loaded.eggs, 1)
|
||||||
|
self.assertIs(reloaded, loaded)
|
||||||
|
|
||||||
|
def test_reload_extra_attributes(self):
|
||||||
|
self.spec.loader = NewLoader()
|
||||||
|
with CleanImport(self.spec.name):
|
||||||
|
loaded = self.bootstrap._SpecMethods(self.spec).load()
|
||||||
|
loaded.available = False
|
||||||
|
reloaded = self.bootstrap._SpecMethods(self.spec).exec(loaded)
|
||||||
|
|
||||||
|
self.assertFalse(loaded.available)
|
||||||
|
self.assertIs(reloaded, loaded)
|
||||||
|
|
||||||
|
def test_reload_init_module_attrs(self):
|
||||||
|
self.spec.loader = NewLoader()
|
||||||
|
with CleanImport(self.spec.name):
|
||||||
|
loaded = self.bootstrap._SpecMethods(self.spec).load()
|
||||||
|
loaded.__name__ = 'ham'
|
||||||
|
del loaded.__loader__
|
||||||
|
del loaded.__package__
|
||||||
|
del loaded.__spec__
|
||||||
|
self.bootstrap._SpecMethods(self.spec).exec(loaded)
|
||||||
|
|
||||||
|
self.assertEqual(loaded.__name__, self.spec.name)
|
||||||
|
self.assertIs(loaded.__loader__, self.spec.loader)
|
||||||
|
self.assertEqual(loaded.__package__, self.spec.parent)
|
||||||
|
self.assertIs(loaded.__spec__, self.spec)
|
||||||
|
self.assertFalse(hasattr(loaded, '__path__'))
|
||||||
|
self.assertFalse(hasattr(loaded, '__file__'))
|
||||||
|
self.assertFalse(hasattr(loaded, '__cached__'))
|
||||||
|
|
||||||
|
def test_reload_legacy(self):
|
||||||
|
self.spec.loader = LegacyLoader()
|
||||||
|
with CleanImport(self.spec.name):
|
||||||
|
loaded = self.bootstrap._SpecMethods(self.spec).load()
|
||||||
|
reloaded = self.bootstrap._SpecMethods(self.spec).exec(loaded)
|
||||||
|
installed = sys.modules[self.spec.name]
|
||||||
|
|
||||||
|
self.assertEqual(loaded.ham, -1)
|
||||||
|
self.assertIs(reloaded, loaded)
|
||||||
|
self.assertIs(installed, loaded)
|
||||||
|
|
||||||
|
|
||||||
|
class Frozen_ModuleSpecMethodsTests(ModuleSpecMethodsTests, unittest.TestCase):
|
||||||
|
bootstrap = frozen_bootstrap
|
||||||
|
machinery = frozen_machinery
|
||||||
|
util = frozen_util
|
||||||
|
|
||||||
|
|
||||||
|
class Source_ModuleSpecMethodsTests(ModuleSpecMethodsTests, unittest.TestCase):
|
||||||
|
bootstrap = source_bootstrap
|
||||||
|
machinery = source_machinery
|
||||||
|
util = source_util
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleReprTests:
|
||||||
|
|
||||||
|
# XXX Add more tests for repr(module) once ModuleSpec._module_repr()
|
||||||
|
# is in place?
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.module = type(os)('spam')
|
||||||
|
self.spec = self.machinery.ModuleSpec('spam', TestLoader())
|
||||||
|
|
||||||
|
def test_module___loader___module_repr(self):
|
||||||
|
class Loader:
|
||||||
|
def module_repr(self, module):
|
||||||
|
return '<delicious {}>'.format(module.__name__)
|
||||||
|
self.module.__loader__ = Loader()
|
||||||
|
modrepr = self.bootstrap._module_repr(self.module)
|
||||||
|
|
||||||
|
self.assertEqual(modrepr, '<delicious spam>')
|
||||||
|
|
||||||
|
def test_module___loader___module_repr_bad(self):
|
||||||
|
class Loader(TestLoader):
|
||||||
|
def module_repr(self, module):
|
||||||
|
raise Exception
|
||||||
|
self.module.__loader__ = Loader()
|
||||||
|
modrepr = self.bootstrap._module_repr(self.module)
|
||||||
|
|
||||||
|
self.assertEqual(modrepr,
|
||||||
|
'<module {!r} (<TestLoader object>)>'.format('spam'))
|
||||||
|
|
||||||
|
def test_module___spec__(self):
|
||||||
|
origin = 'in a hole, in the ground'
|
||||||
|
self.spec.origin = origin
|
||||||
|
self.module.__spec__ = self.spec
|
||||||
|
modrepr = self.bootstrap._module_repr(self.module)
|
||||||
|
|
||||||
|
self.assertEqual(modrepr, '<module {!r} ({})>'.format('spam', origin))
|
||||||
|
|
||||||
|
def test_module___spec___location(self):
|
||||||
|
location = 'in_a_galaxy_far_far_away.py'
|
||||||
|
self.spec.origin = location
|
||||||
|
self.spec._set_fileattr = True
|
||||||
|
self.module.__spec__ = self.spec
|
||||||
|
modrepr = self.bootstrap._module_repr(self.module)
|
||||||
|
|
||||||
|
self.assertEqual(modrepr,
|
||||||
|
'<module {!r} from {!r}>'.format('spam', location))
|
||||||
|
|
||||||
|
def test_module___spec___no_origin(self):
|
||||||
|
self.spec.loader = TestLoader()
|
||||||
|
self.module.__spec__ = self.spec
|
||||||
|
modrepr = self.bootstrap._module_repr(self.module)
|
||||||
|
|
||||||
|
self.assertEqual(modrepr,
|
||||||
|
'<module {!r} (<TestLoader object>)>'.format('spam'))
|
||||||
|
|
||||||
|
def test_module___spec___no_origin_no_loader(self):
|
||||||
|
self.spec.loader = None
|
||||||
|
self.module.__spec__ = self.spec
|
||||||
|
modrepr = self.bootstrap._module_repr(self.module)
|
||||||
|
|
||||||
|
self.assertEqual(modrepr, '<module {!r}>'.format('spam'))
|
||||||
|
|
||||||
|
def test_module_no_name(self):
|
||||||
|
del self.module.__name__
|
||||||
|
modrepr = self.bootstrap._module_repr(self.module)
|
||||||
|
|
||||||
|
self.assertEqual(modrepr, '<module {!r}>'.format('?'))
|
||||||
|
|
||||||
|
def test_module_with_file(self):
|
||||||
|
filename = 'e/i/e/i/o/spam.py'
|
||||||
|
self.module.__file__ = filename
|
||||||
|
modrepr = self.bootstrap._module_repr(self.module)
|
||||||
|
|
||||||
|
self.assertEqual(modrepr,
|
||||||
|
'<module {!r} from {!r}>'.format('spam', filename))
|
||||||
|
|
||||||
|
def test_module_no_file(self):
|
||||||
|
self.module.__loader__ = TestLoader()
|
||||||
|
modrepr = self.bootstrap._module_repr(self.module)
|
||||||
|
|
||||||
|
self.assertEqual(modrepr,
|
||||||
|
'<module {!r} (<TestLoader object>)>'.format('spam'))
|
||||||
|
|
||||||
|
def test_module_no_file_no_loader(self):
|
||||||
|
modrepr = self.bootstrap._module_repr(self.module)
|
||||||
|
|
||||||
|
self.assertEqual(modrepr, '<module {!r}>'.format('spam'))
|
||||||
|
|
||||||
|
|
||||||
|
class Frozen_ModuleReprTests(ModuleReprTests, unittest.TestCase):
|
||||||
|
bootstrap = frozen_bootstrap
|
||||||
|
machinery = frozen_machinery
|
||||||
|
util = frozen_util
|
||||||
|
|
||||||
|
|
||||||
|
class Source_ModuleReprTests(ModuleReprTests, unittest.TestCase):
|
||||||
|
bootstrap = source_bootstrap
|
||||||
|
machinery = source_machinery
|
||||||
|
util = source_util
|
||||||
|
|
||||||
|
|
||||||
|
class FactoryTests:
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.name = 'spam'
|
||||||
|
self.path = 'spam.py'
|
||||||
|
self.cached = self.util.cache_from_source(self.path)
|
||||||
|
self.loader = TestLoader()
|
||||||
|
self.fileloader = TestLoader(self.path)
|
||||||
|
self.pkgloader = TestLoader(self.path, True)
|
||||||
|
|
||||||
|
# spec_from_loader()
|
||||||
|
|
||||||
|
def test_spec_from_loader_default(self):
|
||||||
|
spec = self.util.spec_from_loader(self.name, self.loader)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.loader)
|
||||||
|
self.assertIs(spec.origin, None)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
self.assertFalse(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_loader_default_with_bad_is_package(self):
|
||||||
|
class Loader:
|
||||||
|
def is_package(self, name):
|
||||||
|
raise ImportError
|
||||||
|
loader = Loader()
|
||||||
|
spec = self.util.spec_from_loader(self.name, loader)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, loader)
|
||||||
|
self.assertIs(spec.origin, None)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
self.assertFalse(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_loader_origin(self):
|
||||||
|
origin = 'somewhere over the rainbow'
|
||||||
|
spec = self.util.spec_from_loader(self.name, self.loader,
|
||||||
|
origin=origin)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.loader)
|
||||||
|
self.assertIs(spec.origin, origin)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
self.assertFalse(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_loader_is_package_false(self):
|
||||||
|
spec = self.util.spec_from_loader(self.name, self.loader,
|
||||||
|
is_package=False)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.loader)
|
||||||
|
self.assertIs(spec.origin, None)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
self.assertFalse(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_loader_is_package_true(self):
|
||||||
|
spec = self.util.spec_from_loader(self.name, self.loader,
|
||||||
|
is_package=True)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.loader)
|
||||||
|
self.assertIs(spec.origin, None)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertEqual(spec.submodule_search_locations, [])
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
self.assertFalse(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_loader_origin_and_is_package(self):
|
||||||
|
origin = 'where the streets have no name'
|
||||||
|
spec = self.util.spec_from_loader(self.name, self.loader,
|
||||||
|
origin=origin, is_package=True)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.loader)
|
||||||
|
self.assertIs(spec.origin, origin)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertEqual(spec.submodule_search_locations, [])
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
self.assertFalse(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_loader_is_package_with_loader_false(self):
|
||||||
|
loader = TestLoader(is_package=False)
|
||||||
|
spec = self.util.spec_from_loader(self.name, loader)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, loader)
|
||||||
|
self.assertIs(spec.origin, None)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
self.assertFalse(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_loader_is_package_with_loader_true(self):
|
||||||
|
loader = TestLoader(is_package=True)
|
||||||
|
spec = self.util.spec_from_loader(self.name, loader)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, loader)
|
||||||
|
self.assertIs(spec.origin, None)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertEqual(spec.submodule_search_locations, [])
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
self.assertFalse(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_loader_default_with_file_loader(self):
|
||||||
|
spec = self.util.spec_from_loader(self.name, self.fileloader)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.fileloader)
|
||||||
|
self.assertEqual(spec.origin, self.path)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertEqual(spec.cached, self.cached)
|
||||||
|
self.assertTrue(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_loader_is_package_false_with_fileloader(self):
|
||||||
|
spec = self.util.spec_from_loader(self.name, self.fileloader,
|
||||||
|
is_package=False)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.fileloader)
|
||||||
|
self.assertEqual(spec.origin, self.path)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertEqual(spec.cached, self.cached)
|
||||||
|
self.assertTrue(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_loader_is_package_true_with_fileloader(self):
|
||||||
|
spec = self.util.spec_from_loader(self.name, self.fileloader,
|
||||||
|
is_package=True)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.fileloader)
|
||||||
|
self.assertEqual(spec.origin, self.path)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertEqual(spec.submodule_search_locations, [''])
|
||||||
|
self.assertEqual(spec.cached, self.cached)
|
||||||
|
self.assertTrue(spec.has_location)
|
||||||
|
|
||||||
|
# spec_from_file_location()
|
||||||
|
|
||||||
|
def test_spec_from_file_location_default(self):
|
||||||
|
if self.machinery is source_machinery:
|
||||||
|
raise unittest.SkipTest('not sure why this is breaking...')
|
||||||
|
spec = self.util.spec_from_file_location(self.name, self.path)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertIsInstance(spec.loader,
|
||||||
|
self.machinery.SourceFileLoader)
|
||||||
|
self.assertEqual(spec.loader.name, self.name)
|
||||||
|
self.assertEqual(spec.loader.path, self.path)
|
||||||
|
self.assertEqual(spec.origin, self.path)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertEqual(spec.cached, self.cached)
|
||||||
|
self.assertTrue(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_file_location_default_without_location(self):
|
||||||
|
spec = self.util.spec_from_file_location(self.name)
|
||||||
|
|
||||||
|
self.assertIs(spec, None)
|
||||||
|
|
||||||
|
def test_spec_from_file_location_default_bad_suffix(self):
|
||||||
|
spec = self.util.spec_from_file_location(self.name, 'spam.eggs')
|
||||||
|
|
||||||
|
self.assertIs(spec, None)
|
||||||
|
|
||||||
|
def test_spec_from_file_location_loader_no_location(self):
|
||||||
|
spec = self.util.spec_from_file_location(self.name,
|
||||||
|
loader=self.fileloader)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.fileloader)
|
||||||
|
self.assertEqual(spec.origin, self.path)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertEqual(spec.cached, self.cached)
|
||||||
|
self.assertTrue(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_file_location_loader_no_location_no_get_filename(self):
|
||||||
|
spec = self.util.spec_from_file_location(self.name,
|
||||||
|
loader=self.loader)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.loader)
|
||||||
|
self.assertEqual(spec.origin, '<unknown>')
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
self.assertTrue(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_file_location_loader_no_location_bad_get_filename(self):
|
||||||
|
class Loader:
|
||||||
|
def get_filename(self, name):
|
||||||
|
raise ImportError
|
||||||
|
loader = Loader()
|
||||||
|
spec = self.util.spec_from_file_location(self.name, loader=loader)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, loader)
|
||||||
|
self.assertEqual(spec.origin, '<unknown>')
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertIs(spec.cached, None)
|
||||||
|
self.assertTrue(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_file_location_smsl_none(self):
|
||||||
|
spec = self.util.spec_from_file_location(self.name, self.path,
|
||||||
|
loader=self.fileloader,
|
||||||
|
submodule_search_locations=None)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.fileloader)
|
||||||
|
self.assertEqual(spec.origin, self.path)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertEqual(spec.cached, self.cached)
|
||||||
|
self.assertTrue(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_file_location_smsl_empty(self):
|
||||||
|
spec = self.util.spec_from_file_location(self.name, self.path,
|
||||||
|
loader=self.fileloader,
|
||||||
|
submodule_search_locations=[])
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.fileloader)
|
||||||
|
self.assertEqual(spec.origin, self.path)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertEqual(spec.submodule_search_locations, [''])
|
||||||
|
self.assertEqual(spec.cached, self.cached)
|
||||||
|
self.assertTrue(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_file_location_smsl_not_empty(self):
|
||||||
|
spec = self.util.spec_from_file_location(self.name, self.path,
|
||||||
|
loader=self.fileloader,
|
||||||
|
submodule_search_locations=['eggs'])
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.fileloader)
|
||||||
|
self.assertEqual(spec.origin, self.path)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertEqual(spec.submodule_search_locations, ['eggs'])
|
||||||
|
self.assertEqual(spec.cached, self.cached)
|
||||||
|
self.assertTrue(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_file_location_smsl_default(self):
|
||||||
|
spec = self.util.spec_from_file_location(self.name, self.path,
|
||||||
|
loader=self.pkgloader)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.pkgloader)
|
||||||
|
self.assertEqual(spec.origin, self.path)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertEqual(spec.submodule_search_locations, [''])
|
||||||
|
self.assertEqual(spec.cached, self.cached)
|
||||||
|
self.assertTrue(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_file_location_smsl_default_not_package(self):
|
||||||
|
class Loader:
|
||||||
|
def is_package(self, name):
|
||||||
|
return False
|
||||||
|
loader = Loader()
|
||||||
|
spec = self.util.spec_from_file_location(self.name, self.path,
|
||||||
|
loader=loader)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, loader)
|
||||||
|
self.assertEqual(spec.origin, self.path)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertEqual(spec.cached, self.cached)
|
||||||
|
self.assertTrue(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_file_location_smsl_default_no_is_package(self):
|
||||||
|
spec = self.util.spec_from_file_location(self.name, self.path,
|
||||||
|
loader=self.fileloader)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, self.fileloader)
|
||||||
|
self.assertEqual(spec.origin, self.path)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertEqual(spec.cached, self.cached)
|
||||||
|
self.assertTrue(spec.has_location)
|
||||||
|
|
||||||
|
def test_spec_from_file_location_smsl_default_bad_is_package(self):
|
||||||
|
class Loader:
|
||||||
|
def is_package(self, name):
|
||||||
|
raise ImportError
|
||||||
|
loader = Loader()
|
||||||
|
spec = self.util.spec_from_file_location(self.name, self.path,
|
||||||
|
loader=loader)
|
||||||
|
|
||||||
|
self.assertEqual(spec.name, self.name)
|
||||||
|
self.assertEqual(spec.loader, loader)
|
||||||
|
self.assertEqual(spec.origin, self.path)
|
||||||
|
self.assertIs(spec.loader_state, None)
|
||||||
|
self.assertIs(spec.submodule_search_locations, None)
|
||||||
|
self.assertEqual(spec.cached, self.cached)
|
||||||
|
self.assertTrue(spec.has_location)
|
||||||
|
|
||||||
|
|
||||||
|
class Frozen_FactoryTests(FactoryTests, unittest.TestCase):
|
||||||
|
util = frozen_util
|
||||||
|
machinery = frozen_machinery
|
||||||
|
|
||||||
|
|
||||||
|
class Source_FactoryTests(FactoryTests, unittest.TestCase):
|
||||||
|
util = source_util
|
||||||
|
machinery = source_machinery
|
|
@ -34,71 +34,6 @@ Frozen_DecodeSourceBytesTests, Source_DecodeSourceBytesTests = test_util.test_bo
|
||||||
DecodeSourceBytesTests, util=[frozen_util, source_util])
|
DecodeSourceBytesTests, util=[frozen_util, source_util])
|
||||||
|
|
||||||
|
|
||||||
class ModuleToLoadTests:
|
|
||||||
|
|
||||||
module_name = 'ModuleManagerTest_module'
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
support.unload(self.module_name)
|
|
||||||
self.addCleanup(support.unload, self.module_name)
|
|
||||||
|
|
||||||
def test_new_module(self):
|
|
||||||
# Test a new module is created, inserted into sys.modules, has
|
|
||||||
# __initializing__ set to True after entering the context manager,
|
|
||||||
# and __initializing__ set to False after exiting.
|
|
||||||
with self.util.module_to_load(self.module_name) as module:
|
|
||||||
self.assertIn(self.module_name, sys.modules)
|
|
||||||
self.assertIs(sys.modules[self.module_name], module)
|
|
||||||
self.assertTrue(module.__initializing__)
|
|
||||||
self.assertFalse(module.__initializing__)
|
|
||||||
|
|
||||||
def test_new_module_failed(self):
|
|
||||||
# Test the module is removed from sys.modules.
|
|
||||||
try:
|
|
||||||
with self.util.module_to_load(self.module_name) as module:
|
|
||||||
self.assertIn(self.module_name, sys.modules)
|
|
||||||
raise exception
|
|
||||||
except Exception:
|
|
||||||
self.assertNotIn(self.module_name, sys.modules)
|
|
||||||
else:
|
|
||||||
self.fail('importlib.util.module_to_load swallowed an exception')
|
|
||||||
|
|
||||||
def test_reload(self):
|
|
||||||
# Test that the same module is in sys.modules.
|
|
||||||
created_module = types.ModuleType(self.module_name)
|
|
||||||
sys.modules[self.module_name] = created_module
|
|
||||||
with self.util.module_to_load(self.module_name) as module:
|
|
||||||
self.assertIs(module, created_module)
|
|
||||||
|
|
||||||
def test_reload_failed(self):
|
|
||||||
# Test that the module was left in sys.modules.
|
|
||||||
created_module = types.ModuleType(self.module_name)
|
|
||||||
sys.modules[self.module_name] = created_module
|
|
||||||
try:
|
|
||||||
with self.util.module_to_load(self.module_name) as module:
|
|
||||||
raise Exception
|
|
||||||
except Exception:
|
|
||||||
self.assertIn(self.module_name, sys.modules)
|
|
||||||
else:
|
|
||||||
self.fail('importlib.util.module_to_load swallowed an exception')
|
|
||||||
|
|
||||||
def test_reset_name(self):
|
|
||||||
# If reset_name is true then module.__name__ = name, else leave it be.
|
|
||||||
odd_name = 'not your typical name'
|
|
||||||
created_module = types.ModuleType(self.module_name)
|
|
||||||
created_module.__name__ = odd_name
|
|
||||||
sys.modules[self.module_name] = created_module
|
|
||||||
with self.util.module_to_load(self.module_name) as module:
|
|
||||||
self.assertEqual(module.__name__, self.module_name)
|
|
||||||
created_module.__name__ = odd_name
|
|
||||||
with self.util.module_to_load(self.module_name, reset_name=False) as module:
|
|
||||||
self.assertEqual(module.__name__, odd_name)
|
|
||||||
|
|
||||||
Frozen_ModuleToLoadTests, Source_ModuleToLoadTests = test_util.test_both(
|
|
||||||
ModuleToLoadTests,
|
|
||||||
util=[frozen_util, source_util])
|
|
||||||
|
|
||||||
|
|
||||||
class ModuleForLoaderTests:
|
class ModuleForLoaderTests:
|
||||||
|
|
||||||
"""Tests for importlib.util.module_for_loader."""
|
"""Tests for importlib.util.module_for_loader."""
|
||||||
|
|
|
@ -37,8 +37,10 @@ class ModuleTests(unittest.TestCase):
|
||||||
self.assertEqual(foo.__doc__, None)
|
self.assertEqual(foo.__doc__, None)
|
||||||
self.assertIs(foo.__loader__, None)
|
self.assertIs(foo.__loader__, None)
|
||||||
self.assertIs(foo.__package__, None)
|
self.assertIs(foo.__package__, None)
|
||||||
|
self.assertIs(foo.__spec__, None)
|
||||||
self.assertEqual(foo.__dict__, {"__name__": "foo", "__doc__": None,
|
self.assertEqual(foo.__dict__, {"__name__": "foo", "__doc__": None,
|
||||||
"__loader__": None, "__package__": None})
|
"__loader__": None, "__package__": None,
|
||||||
|
"__spec__": None})
|
||||||
|
|
||||||
def test_ascii_docstring(self):
|
def test_ascii_docstring(self):
|
||||||
# ASCII docstring
|
# ASCII docstring
|
||||||
|
@ -47,7 +49,8 @@ class ModuleTests(unittest.TestCase):
|
||||||
self.assertEqual(foo.__doc__, "foodoc")
|
self.assertEqual(foo.__doc__, "foodoc")
|
||||||
self.assertEqual(foo.__dict__,
|
self.assertEqual(foo.__dict__,
|
||||||
{"__name__": "foo", "__doc__": "foodoc",
|
{"__name__": "foo", "__doc__": "foodoc",
|
||||||
"__loader__": None, "__package__": None})
|
"__loader__": None, "__package__": None,
|
||||||
|
"__spec__": None})
|
||||||
|
|
||||||
def test_unicode_docstring(self):
|
def test_unicode_docstring(self):
|
||||||
# Unicode docstring
|
# Unicode docstring
|
||||||
|
@ -56,7 +59,8 @@ class ModuleTests(unittest.TestCase):
|
||||||
self.assertEqual(foo.__doc__, "foodoc\u1234")
|
self.assertEqual(foo.__doc__, "foodoc\u1234")
|
||||||
self.assertEqual(foo.__dict__,
|
self.assertEqual(foo.__dict__,
|
||||||
{"__name__": "foo", "__doc__": "foodoc\u1234",
|
{"__name__": "foo", "__doc__": "foodoc\u1234",
|
||||||
"__loader__": None, "__package__": None})
|
"__loader__": None, "__package__": None,
|
||||||
|
"__spec__": None})
|
||||||
|
|
||||||
def test_reinit(self):
|
def test_reinit(self):
|
||||||
# Reinitialization should not replace the __dict__
|
# Reinitialization should not replace the __dict__
|
||||||
|
@ -69,7 +73,7 @@ class ModuleTests(unittest.TestCase):
|
||||||
self.assertEqual(foo.bar, 42)
|
self.assertEqual(foo.bar, 42)
|
||||||
self.assertEqual(foo.__dict__,
|
self.assertEqual(foo.__dict__,
|
||||||
{"__name__": "foo", "__doc__": "foodoc", "bar": 42,
|
{"__name__": "foo", "__doc__": "foodoc", "bar": 42,
|
||||||
"__loader__": None, "__package__": None})
|
"__loader__": None, "__package__": None, "__spec__": None})
|
||||||
self.assertTrue(foo.__dict__ is d)
|
self.assertTrue(foo.__dict__ is d)
|
||||||
|
|
||||||
def test_dont_clear_dict(self):
|
def test_dont_clear_dict(self):
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import contextlib
|
import contextlib
|
||||||
from importlib._bootstrap import NamespaceLoader
|
|
||||||
import importlib.abc
|
import importlib.abc
|
||||||
import importlib.machinery
|
import importlib.machinery
|
||||||
import os
|
import os
|
||||||
|
@ -290,24 +289,5 @@ class ModuleAndNamespacePackageInSameDir(NamespacePackageTest):
|
||||||
self.assertEqual(a_test.attr, 'in module')
|
self.assertEqual(a_test.attr, 'in module')
|
||||||
|
|
||||||
|
|
||||||
class ABCTests(unittest.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.loader = NamespaceLoader('foo', ['pkg'],
|
|
||||||
importlib.machinery.PathFinder)
|
|
||||||
|
|
||||||
def test_is_package(self):
|
|
||||||
self.assertTrue(self.loader.is_package('foo'))
|
|
||||||
|
|
||||||
def test_get_code(self):
|
|
||||||
self.assertTrue(isinstance(self.loader.get_code('foo'), types.CodeType))
|
|
||||||
|
|
||||||
def test_get_source(self):
|
|
||||||
self.assertEqual(self.loader.get_source('foo'), '')
|
|
||||||
|
|
||||||
def test_abc_isinstance(self):
|
|
||||||
self.assertTrue(isinstance(self.loader, importlib.abc.InspectLoader))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -199,14 +199,14 @@ class TestPkg(unittest.TestCase):
|
||||||
import t5
|
import t5
|
||||||
self.assertEqual(fixdir(dir(t5)),
|
self.assertEqual(fixdir(dir(t5)),
|
||||||
['__cached__', '__doc__', '__file__', '__loader__',
|
['__cached__', '__doc__', '__file__', '__loader__',
|
||||||
'__name__', '__package__', '__path__', 'foo',
|
'__name__', '__package__', '__path__', '__spec__',
|
||||||
'string', 't5'])
|
'foo', 'string', 't5'])
|
||||||
self.assertEqual(fixdir(dir(t5.foo)),
|
self.assertEqual(fixdir(dir(t5.foo)),
|
||||||
['__cached__', '__doc__', '__file__', '__loader__',
|
['__cached__', '__doc__', '__file__', '__loader__',
|
||||||
'__name__', '__package__', 'string'])
|
'__name__', '__package__', '__spec__', 'string'])
|
||||||
self.assertEqual(fixdir(dir(t5.string)),
|
self.assertEqual(fixdir(dir(t5.string)),
|
||||||
['__cached__', '__doc__', '__file__', '__loader__',
|
['__cached__', '__doc__', '__file__', '__loader__',
|
||||||
'__name__', '__package__', 'spam'])
|
'__name__', '__package__', '__spec__', 'spam'])
|
||||||
|
|
||||||
def test_6(self):
|
def test_6(self):
|
||||||
hier = [
|
hier = [
|
||||||
|
@ -222,14 +222,15 @@ class TestPkg(unittest.TestCase):
|
||||||
import t6
|
import t6
|
||||||
self.assertEqual(fixdir(dir(t6)),
|
self.assertEqual(fixdir(dir(t6)),
|
||||||
['__all__', '__cached__', '__doc__', '__file__',
|
['__all__', '__cached__', '__doc__', '__file__',
|
||||||
'__loader__', '__name__', '__package__', '__path__'])
|
'__loader__', '__name__', '__package__', '__path__',
|
||||||
|
'__spec__'])
|
||||||
s = """
|
s = """
|
||||||
import t6
|
import t6
|
||||||
from t6 import *
|
from t6 import *
|
||||||
self.assertEqual(fixdir(dir(t6)),
|
self.assertEqual(fixdir(dir(t6)),
|
||||||
['__all__', '__cached__', '__doc__', '__file__',
|
['__all__', '__cached__', '__doc__', '__file__',
|
||||||
'__loader__', '__name__', '__package__',
|
'__loader__', '__name__', '__package__',
|
||||||
'__path__', 'eggs', 'ham', 'spam'])
|
'__path__', '__spec__', 'eggs', 'ham', 'spam'])
|
||||||
self.assertEqual(dir(), ['eggs', 'ham', 'self', 'spam', 't6'])
|
self.assertEqual(dir(), ['eggs', 'ham', 'self', 'spam', 't6'])
|
||||||
"""
|
"""
|
||||||
self.run_code(s)
|
self.run_code(s)
|
||||||
|
@ -256,18 +257,19 @@ class TestPkg(unittest.TestCase):
|
||||||
import t7 as tas
|
import t7 as tas
|
||||||
self.assertEqual(fixdir(dir(tas)),
|
self.assertEqual(fixdir(dir(tas)),
|
||||||
['__cached__', '__doc__', '__file__', '__loader__',
|
['__cached__', '__doc__', '__file__', '__loader__',
|
||||||
'__name__', '__package__', '__path__'])
|
'__name__', '__package__', '__path__', '__spec__'])
|
||||||
self.assertFalse(t7)
|
self.assertFalse(t7)
|
||||||
from t7 import sub as subpar
|
from t7 import sub as subpar
|
||||||
self.assertEqual(fixdir(dir(subpar)),
|
self.assertEqual(fixdir(dir(subpar)),
|
||||||
['__cached__', '__doc__', '__file__', '__loader__',
|
['__cached__', '__doc__', '__file__', '__loader__',
|
||||||
'__name__', '__package__', '__path__'])
|
'__name__', '__package__', '__path__', '__spec__'])
|
||||||
self.assertFalse(t7)
|
self.assertFalse(t7)
|
||||||
self.assertFalse(sub)
|
self.assertFalse(sub)
|
||||||
from t7.sub import subsub as subsubsub
|
from t7.sub import subsub as subsubsub
|
||||||
self.assertEqual(fixdir(dir(subsubsub)),
|
self.assertEqual(fixdir(dir(subsubsub)),
|
||||||
['__cached__', '__doc__', '__file__', '__loader__',
|
['__cached__', '__doc__', '__file__', '__loader__',
|
||||||
'__name__', '__package__', '__path__', 'spam'])
|
'__name__', '__package__', '__path__', '__spec__',
|
||||||
|
'spam'])
|
||||||
self.assertFalse(t7)
|
self.assertFalse(t7)
|
||||||
self.assertFalse(sub)
|
self.assertFalse(sub)
|
||||||
self.assertFalse(subsub)
|
self.assertFalse(subsub)
|
||||||
|
|
|
@ -208,9 +208,16 @@ class ExtendPathTests(unittest.TestCase):
|
||||||
importers = list(iter_importers(fullname))
|
importers = list(iter_importers(fullname))
|
||||||
expected_importer = get_importer(pathitem)
|
expected_importer = get_importer(pathitem)
|
||||||
for finder in importers:
|
for finder in importers:
|
||||||
|
loader = finder.find_module(fullname)
|
||||||
|
try:
|
||||||
|
loader = loader.loader
|
||||||
|
except AttributeError:
|
||||||
|
# For now we still allow raw loaders from
|
||||||
|
# find_module().
|
||||||
|
pass
|
||||||
self.assertIsInstance(finder, importlib.machinery.FileFinder)
|
self.assertIsInstance(finder, importlib.machinery.FileFinder)
|
||||||
self.assertEqual(finder, expected_importer)
|
self.assertEqual(finder, expected_importer)
|
||||||
self.assertIsInstance(finder.find_module(fullname),
|
self.assertIsInstance(loader,
|
||||||
importlib.machinery.SourceFileLoader)
|
importlib.machinery.SourceFileLoader)
|
||||||
self.assertIsNone(finder.find_module(pkgname))
|
self.assertIsNone(finder.find_module(pkgname))
|
||||||
|
|
||||||
|
@ -222,8 +229,11 @@ class ExtendPathTests(unittest.TestCase):
|
||||||
finally:
|
finally:
|
||||||
shutil.rmtree(dirname)
|
shutil.rmtree(dirname)
|
||||||
del sys.path[0]
|
del sys.path[0]
|
||||||
del sys.modules['spam']
|
try:
|
||||||
del sys.modules['spam.eggs']
|
del sys.modules['spam']
|
||||||
|
del sys.modules['spam.eggs']
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_mixed_namespace(self):
|
def test_mixed_namespace(self):
|
||||||
|
|
|
@ -253,6 +253,7 @@ class LongReprTest(unittest.TestCase):
|
||||||
print("cached_path_len =", cached_path_len)
|
print("cached_path_len =", cached_path_len)
|
||||||
|
|
||||||
def test_module(self):
|
def test_module(self):
|
||||||
|
self.maxDiff = None
|
||||||
self._check_path_limitations(self.pkgname)
|
self._check_path_limitations(self.pkgname)
|
||||||
create_empty_file(os.path.join(self.subpkgname, self.pkgname + '.py'))
|
create_empty_file(os.path.join(self.subpkgname, self.pkgname + '.py'))
|
||||||
importlib.invalidate_caches()
|
importlib.invalidate_caches()
|
||||||
|
|
|
@ -47,6 +47,7 @@ implicit_namespace = {
|
||||||
"__cached__": None,
|
"__cached__": None,
|
||||||
"__package__": None,
|
"__package__": None,
|
||||||
"__doc__": None,
|
"__doc__": None,
|
||||||
|
# "__spec__": None, # XXX Uncomment.
|
||||||
}
|
}
|
||||||
example_namespace = {
|
example_namespace = {
|
||||||
"sys": sys,
|
"sys": sys,
|
||||||
|
@ -56,7 +57,7 @@ example_namespace = {
|
||||||
"run_name_in_sys_modules": False,
|
"run_name_in_sys_modules": False,
|
||||||
"module_in_sys_modules": False,
|
"module_in_sys_modules": False,
|
||||||
"nested": dict(implicit_namespace,
|
"nested": dict(implicit_namespace,
|
||||||
x=1, __name__="<run>", __loader__=None),
|
x=1, __name__="<run>", __loader__=None, __spec__=None),
|
||||||
}
|
}
|
||||||
example_namespace.update(implicit_namespace)
|
example_namespace.update(implicit_namespace)
|
||||||
|
|
||||||
|
@ -243,6 +244,7 @@ class RunModuleTestCase(unittest.TestCase, CodeExecutionMixin):
|
||||||
"__name__": mod_name,
|
"__name__": mod_name,
|
||||||
"__file__": mod_fname,
|
"__file__": mod_fname,
|
||||||
"__package__": mod_name.rpartition(".")[0],
|
"__package__": mod_name.rpartition(".")[0],
|
||||||
|
# "__spec__": None, # XXX Needs to be set.
|
||||||
})
|
})
|
||||||
if alter_sys:
|
if alter_sys:
|
||||||
expected_ns.update({
|
expected_ns.update({
|
||||||
|
@ -279,6 +281,7 @@ class RunModuleTestCase(unittest.TestCase, CodeExecutionMixin):
|
||||||
"__name__": mod_name,
|
"__name__": mod_name,
|
||||||
"__file__": mod_fname,
|
"__file__": mod_fname,
|
||||||
"__package__": pkg_name,
|
"__package__": pkg_name,
|
||||||
|
# "__spec__": None, # XXX Needs to be set.
|
||||||
})
|
})
|
||||||
if alter_sys:
|
if alter_sys:
|
||||||
expected_ns.update({
|
expected_ns.update({
|
||||||
|
|
|
@ -45,6 +45,8 @@ module_init_dict(PyModuleObject *mod, PyObject *md_dict,
|
||||||
return -1;
|
return -1;
|
||||||
if (PyDict_SetItemString(md_dict, "__loader__", Py_None) != 0)
|
if (PyDict_SetItemString(md_dict, "__loader__", Py_None) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
if (PyDict_SetItemString(md_dict, "__spec__", Py_None) != 0)
|
||||||
|
return -1;
|
||||||
if (PyUnicode_CheckExact(name)) {
|
if (PyUnicode_CheckExact(name)) {
|
||||||
Py_INCREF(name);
|
Py_INCREF(name);
|
||||||
Py_XDECREF(mod->md_name);
|
Py_XDECREF(mod->md_name);
|
||||||
|
@ -398,55 +400,10 @@ module_dealloc(PyModuleObject *m)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
module_repr(PyModuleObject *m)
|
module_repr(PyModuleObject *m)
|
||||||
{
|
{
|
||||||
PyObject *name, *filename, *repr, *loader = NULL;
|
PyThreadState *tstate = PyThreadState_GET();
|
||||||
|
PyInterpreterState *interp = tstate->interp;
|
||||||
|
|
||||||
/* See if the module has an __loader__. If it does, give the loader the
|
return PyObject_CallMethod(interp->importlib, "_module_repr", "O", m);
|
||||||
* first shot at producing a repr for the module.
|
|
||||||
*/
|
|
||||||
if (m->md_dict != NULL) {
|
|
||||||
loader = PyDict_GetItemString(m->md_dict, "__loader__");
|
|
||||||
}
|
|
||||||
if (loader != NULL && loader != Py_None) {
|
|
||||||
repr = PyObject_CallMethod(loader, "module_repr", "(O)",
|
|
||||||
(PyObject *)m, NULL);
|
|
||||||
if (repr == NULL) {
|
|
||||||
PyErr_Clear();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return repr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* __loader__.module_repr(m) did not provide us with a repr. Next, see if
|
|
||||||
* the module has an __file__. If it doesn't then use repr(__loader__) if
|
|
||||||
* it exists, otherwise, just use module.__name__.
|
|
||||||
*/
|
|
||||||
name = PyModule_GetNameObject((PyObject *)m);
|
|
||||||
if (name == NULL) {
|
|
||||||
PyErr_Clear();
|
|
||||||
name = PyUnicode_FromStringAndSize("?", 1);
|
|
||||||
if (name == NULL)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
filename = PyModule_GetFilenameObject((PyObject *)m);
|
|
||||||
if (filename == NULL) {
|
|
||||||
PyErr_Clear();
|
|
||||||
/* There's no m.__file__, so if there was a __loader__, use that in
|
|
||||||
* the repr, otherwise, the only thing you can use is m.__name__
|
|
||||||
*/
|
|
||||||
if (loader == NULL || loader == Py_None) {
|
|
||||||
repr = PyUnicode_FromFormat("<module %R>", name);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
repr = PyUnicode_FromFormat("<module %R (%R)>", name, loader);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Finally, use m.__file__ */
|
|
||||||
else {
|
|
||||||
repr = PyUnicode_FromFormat("<module %R from %R>", name, filename);
|
|
||||||
Py_DECREF(filename);
|
|
||||||
}
|
|
||||||
Py_DECREF(name);
|
|
||||||
return repr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
|
@ -1232,7 +1232,8 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *given_globals,
|
||||||
int level)
|
int level)
|
||||||
{
|
{
|
||||||
_Py_IDENTIFIER(__import__);
|
_Py_IDENTIFIER(__import__);
|
||||||
_Py_IDENTIFIER(__initializing__);
|
_Py_IDENTIFIER(__spec__);
|
||||||
|
_Py_IDENTIFIER(_initializing);
|
||||||
_Py_IDENTIFIER(__package__);
|
_Py_IDENTIFIER(__package__);
|
||||||
_Py_IDENTIFIER(__path__);
|
_Py_IDENTIFIER(__path__);
|
||||||
_Py_IDENTIFIER(__name__);
|
_Py_IDENTIFIER(__name__);
|
||||||
|
@ -1426,16 +1427,21 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *given_globals,
|
||||||
goto error_with_unlock;
|
goto error_with_unlock;
|
||||||
}
|
}
|
||||||
else if (mod != NULL) {
|
else if (mod != NULL) {
|
||||||
PyObject *value;
|
PyObject *value = NULL;
|
||||||
|
PyObject *spec;
|
||||||
int initializing = 0;
|
int initializing = 0;
|
||||||
|
|
||||||
Py_INCREF(mod);
|
Py_INCREF(mod);
|
||||||
/* Optimization: only call _bootstrap._lock_unlock_module() if
|
/* Optimization: only call _bootstrap._lock_unlock_module() if
|
||||||
__initializing__ is true.
|
__spec__._initializing is true.
|
||||||
NOTE: because of this, __initializing__ must be set *before*
|
NOTE: because of this, initializing must be set *before*
|
||||||
stuffing the new module in sys.modules.
|
stuffing the new module in sys.modules.
|
||||||
*/
|
*/
|
||||||
value = _PyObject_GetAttrId(mod, &PyId___initializing__);
|
spec = _PyObject_GetAttrId(mod, &PyId___spec__);
|
||||||
|
if (spec != NULL) {
|
||||||
|
value = _PyObject_GetAttrId(spec, &PyId__initializing);
|
||||||
|
Py_DECREF(spec);
|
||||||
|
}
|
||||||
if (value == NULL)
|
if (value == NULL)
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
else {
|
else {
|
||||||
|
|
7407
Python/importlib.h
7407
Python/importlib.h
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue