Added dynamic path computation rationale, specification, and discussion.
This commit is contained in:
parent
bb6f79b4ad
commit
eb2097eee6
92
pep-0420.txt
92
pep-0420.txt
|
@ -21,6 +21,7 @@ directories that make up the package. This PEP builds upon previous work,
|
|||
documented in PEP 382 and PEP 402. Those PEPs have since been rejected in
|
||||
favor of this one. An implementation of this PEP is at [1]_.
|
||||
|
||||
|
||||
Terminology
|
||||
===========
|
||||
|
||||
|
@ -43,6 +44,7 @@ Within this PEP:
|
|||
|
||||
This PEP defines a new type of package, the "namespace package".
|
||||
|
||||
|
||||
Namespace packages today
|
||||
========================
|
||||
|
||||
|
@ -78,9 +80,10 @@ setuptools allows declaring namespace packages in a distribution's
|
|||
``setup.py``, so that distribution developers don't need to put the
|
||||
magic ``__path__`` modification into ``__init__.py`` themselves.
|
||||
|
||||
See PEP 402's "The Problem" section [2]_ for more details on the
|
||||
motivation for namespace packages. Note that PEP 402 has been
|
||||
rejected, but the motivating use cases are still valid.
|
||||
See PEP 402's "The Problem" section [2]_ for additional motivations
|
||||
for namespace packages. Note that PEP 402 has been rejected, but the
|
||||
motivating use cases are still valid.
|
||||
|
||||
|
||||
Rationale
|
||||
=========
|
||||
|
@ -107,6 +110,36 @@ completely, and affected portions can be installed into a common
|
|||
directory or split across multiple directories as distributions see
|
||||
fit.
|
||||
|
||||
A namespace package will not be constrained by a fixed ``__path__``,
|
||||
computed from the parent path at namespace package creation time.
|
||||
Consider the standard library ``encodings`` package:
|
||||
|
||||
1. Suppose that ``encodings`` becomes a namespace package.
|
||||
|
||||
2. It sometimes gets imported during interpreter startup to
|
||||
initialize the standard io streams.
|
||||
|
||||
3. An application modifies ``sys.path`` after startup and wants to
|
||||
contribute additional encodings.
|
||||
|
||||
4. An attempt is made to import an ``encodings`` portion that is
|
||||
found on a path added in step 3.
|
||||
|
||||
If the import system was restricted to only finding portions along the
|
||||
value of ``sys.path`` that existed at the time the ``encodings``
|
||||
namespace package was created, the additional paths added in step 3
|
||||
would never be searched for the additional portions imported in step
|
||||
4. In addition, if step 2 were sometimes skipped (due to some runtime
|
||||
flag or other condition), then the path items added in step 3 would
|
||||
indeed be used the first time a portion was imported. Thus this PEP
|
||||
requires that the list of path entries be dynamically computed when
|
||||
each portion is loaded. It is expected that the import machinery will
|
||||
do this efficiently by caching ``__path__`` values and only refreshing
|
||||
them when it detects that the parent path has changed. In the case of
|
||||
a top-level package like ``encodings``, this parent path would be
|
||||
``sys.path``.
|
||||
|
||||
|
||||
Specification
|
||||
=============
|
||||
|
||||
|
@ -161,16 +194,15 @@ between it and a regular package.
|
|||
Dynamic path computation
|
||||
------------------------
|
||||
|
||||
A namespace package's ``__path__`` will be recomputed if the value of
|
||||
the parent path changes. In order for this feature to work, the parent
|
||||
path must be modified in-place, not replaced with a new object. For
|
||||
example, for top-level namespace packages, this will work::
|
||||
The import machinery will behave as if a namespace package's
|
||||
``__path__`` is recomputed before each portion is loaded.
|
||||
|
||||
sys.path.append('new-dir')
|
||||
|
||||
But this will not::
|
||||
|
||||
sys.path = sys.path + ['new-dir']
|
||||
For performance reasons, it is expected that this will be achieved by
|
||||
detecting that the parent path has changed. If no change has taken
|
||||
place, then no ``__path__`` recomputation is required. The
|
||||
implementation must ensure that changes to the contents of the parent
|
||||
path are detected, as well as detecting the replacement of the parent
|
||||
path with a new path entry list object.
|
||||
|
||||
Impact on import finders and loaders
|
||||
------------------------------------
|
||||
|
@ -354,6 +386,33 @@ implement the ``find_loader`` method, described above.
|
|||
The use case for supporting multiple portions per ``find_loader`` call
|
||||
is given in [7]_.
|
||||
|
||||
Dynamic path computation
|
||||
------------------------
|
||||
|
||||
Guido raised a concern that automatic dynamic path computation was an
|
||||
unnecessary feature [8]_. Later in that thread, Philip Eby and Nick
|
||||
Coghlan presented arguments as to why dynamic computation would
|
||||
minimize surprise to Python users. The conclusion of that discussion
|
||||
has been included in this PEP's Rationale section.
|
||||
|
||||
An earlier version of this PEP required that dynamic path computation
|
||||
could only take affect if the parent path object were modified
|
||||
in-place. That is, this would work::
|
||||
|
||||
sys.path.append('new-dir')
|
||||
|
||||
But this would not::
|
||||
|
||||
sys.path = sys.path + ['new-dir']
|
||||
|
||||
In the same thread [8]_, it was pointed out that this restriction is
|
||||
not required. If the parent path is looked up by name instead of by
|
||||
holding a reference to it, then there is no restriction on how the
|
||||
parent path is modified or replaced. For a top-level namespace
|
||||
package, the lookup would be module ``sys`` then attribute ``path``.
|
||||
For a namespace package nested inside a module ``foo``, the lookup
|
||||
would be module ``foo`` then attribute ``__path__``.
|
||||
|
||||
|
||||
Module reprs
|
||||
============
|
||||
|
@ -424,7 +483,7 @@ different sets of the related attributes::
|
|||
>>> m
|
||||
<module 'foo' from 'zippy:/de/do/dah'>
|
||||
>>> class Loader: pass
|
||||
...
|
||||
...
|
||||
>>> m.__loader__ = Loader
|
||||
>>> del m.__file__
|
||||
>>> m
|
||||
|
@ -433,11 +492,11 @@ different sets of the related attributes::
|
|||
... @classmethod
|
||||
... def module_repr(cls, module):
|
||||
... return '<mystery module!>'
|
||||
...
|
||||
...
|
||||
>>> m.__loader__ = NewLoader
|
||||
>>> m
|
||||
<mystery module!>
|
||||
>>>
|
||||
>>>
|
||||
|
||||
|
||||
References
|
||||
|
@ -464,6 +523,9 @@ References
|
|||
.. [7] Use case for multiple portions per ``find_loader`` call
|
||||
(http://mail.python.org/pipermail/import-sig/2012-May/000585.html)
|
||||
|
||||
.. [8] Discussion about dynamic path computation
|
||||
(http://mail.python.org/pipermail/python-dev/2012-May/119560.html)
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
|
|
Loading…
Reference in New Issue