- some reflow caused by David Goodger's spell checking
- added "Optional Extensions to the Importer Protocol" section - fixed flaw in the importer protocol: i.find_module() will now receive the package.__path__ (or None for a plain module) as an additional argument, if it's installed on sys.meta_path. This is needed to be able to add a hook that implements the full sys.path/pkg.__path__ semantics. See also footnote [7]. The patch on sf will be updated shortly to match the new version of the PEP.
This commit is contained in:
parent
fb0080cbcb
commit
74737f0d45
121
pep-0302.txt
121
pep-0302.txt
|
@ -190,17 +190,17 @@ Specification part 1: The Importer Protocol
|
||||||
module being imported (may be a dotted name) and a reference to the
|
module being imported (may be a dotted name) and a reference to the
|
||||||
current global namespace.
|
current global namespace.
|
||||||
|
|
||||||
The built-in __import__ function (known as PyImport_ImportModuleEx in
|
The built-in __import__ function (known as PyImport_ImportModuleEx
|
||||||
import.c) will then check to see whether the module doing the import
|
in import.c) will then check to see whether the module doing the
|
||||||
is a package by looking for a __path__ variable in the current
|
import is a package by looking for a __path__ variable in the
|
||||||
global namespace. If it is indeed a package, it first tries to do
|
current global namespace. If it is indeed a package, it first tries
|
||||||
the import relative to the package. For example if a package named
|
to do the import relative to the package. For example if a package
|
||||||
"spam" does "import eggs", it will first look for a module named
|
named "spam" does "import eggs", it will first look for a module
|
||||||
"spam.eggs". If that fails, the import continues as an absolute
|
named "spam.eggs". If that fails, the import continues as an
|
||||||
import: it will look for a module named "eggs". Dotted name imports
|
absolute import: it will look for a module named "eggs". Dotted
|
||||||
work pretty much the same: if package "spam" does "import
|
name imports work pretty much the same: if package "spam" does
|
||||||
eggs.bacon", first "spam.eggs.bacon" is tried, and only if that
|
"import eggs.bacon", first "spam.eggs.bacon" is tried, and only if
|
||||||
fails "eggs.bacon" is tried.
|
that fails "eggs.bacon" is tried.
|
||||||
|
|
||||||
Deeper down in the mechanism, a dotted name import is split up by
|
Deeper down in the mechanism, a dotted name import is split up by
|
||||||
its components. For "import spam.ham", first an "import spam" is
|
its components. For "import spam.ham", first an "import spam" is
|
||||||
|
@ -214,11 +214,15 @@ Specification part 1: The Importer Protocol
|
||||||
The protocol involves two objects: an importer and a loader. An
|
The protocol involves two objects: an importer and a loader. An
|
||||||
importer object has a single method:
|
importer object has a single method:
|
||||||
|
|
||||||
importer.find_module(fullname)
|
importer.find_module(fullname, path=None)
|
||||||
|
|
||||||
This method returns a loader object if the module was found, or None
|
This method will be called with the fully qualified name of the
|
||||||
if it wasn't. If find_module() raises an exception, it will be
|
module. If the importer is installed on sys.meta_path, it will
|
||||||
propagated to the caller, aborting the import.
|
receive a second argument, which is None for a top-level module, or
|
||||||
|
package.__path__ for submodules or subpackages[7]. It should return
|
||||||
|
a loader object if the module was found, or None if it wasn't. If
|
||||||
|
find_module() raises an exception, it will be propagated to the
|
||||||
|
caller, aborting the import.
|
||||||
|
|
||||||
A loader object also has one method:
|
A loader object also has one method:
|
||||||
|
|
||||||
|
@ -348,6 +352,72 @@ Packages and the role of __path__
|
||||||
which can be empty.
|
which can be empty.
|
||||||
|
|
||||||
|
|
||||||
|
Optional Extensions to the Importer Protocol
|
||||||
|
|
||||||
|
The Importer Protocol defines two optional extensions. One is to
|
||||||
|
retrieve data files, the other is to support module packaging tools
|
||||||
|
and/or tools that analyze module dependencies (for example Freeze
|
||||||
|
[3]). The latter category of tools usually don't actually *load*
|
||||||
|
modules, they only need to know if and where they are available.
|
||||||
|
Both extensions are highly recommended for general purpose
|
||||||
|
importers, but may safely be left out if those features aren't
|
||||||
|
needed.
|
||||||
|
|
||||||
|
To retrieve the data for arbitrary "files" from the underlying
|
||||||
|
storage backend, loader objects may supply a method named get_data:
|
||||||
|
|
||||||
|
loader.get_data(name)
|
||||||
|
|
||||||
|
This method returns the data as a string, or raise IOError if the
|
||||||
|
"file" wasn't found. The 'name' argument should be seen as a
|
||||||
|
'cookie', meaning the protocol doesn't prescribe any semantics for
|
||||||
|
it. However, for importer objects that have some file system-like
|
||||||
|
properties (for example zipimporter) it is recommended to use os.sep
|
||||||
|
as a separator character to specify a (possibly virtual) directory
|
||||||
|
hierarchy. For example if the importer allows access to a module's
|
||||||
|
source code via i.get_data(name), the 'name' argument should be
|
||||||
|
constructed like this:
|
||||||
|
|
||||||
|
name = mod.__name__.replace(".", os.sep) + ".py"
|
||||||
|
|
||||||
|
Note that this is not the recommended way to retrieve source code,
|
||||||
|
the (also optional) method loader.get_source(fullname) is more
|
||||||
|
general, as it doesn't imply *any* file-system-like characteristics.
|
||||||
|
This leads us to the next extension.
|
||||||
|
|
||||||
|
The following set of methods may be implemented if support for (for
|
||||||
|
example) Freeze-like tools is desirable. It consists of three
|
||||||
|
additional methods which, to make it easier for the caller, each of
|
||||||
|
which should be implemented, or none at all.
|
||||||
|
|
||||||
|
loader.get_package_path(fullname)
|
||||||
|
loader.get_code(fullname)
|
||||||
|
loader.get_source(fullname)
|
||||||
|
|
||||||
|
All three methods should raise ImportError if the module wasn't
|
||||||
|
found.
|
||||||
|
|
||||||
|
The loader.get_package_path(fullname) method should return None if
|
||||||
|
the module specified by 'fullname' is not a package, or a list to
|
||||||
|
serve as pkg.__path__ if it is. It can be used to check
|
||||||
|
package-ness for a module ("loader.get_package_path(fullname) is not
|
||||||
|
None") but its main purpose is to tell our caller what pkg.__path__
|
||||||
|
would be if the module would actually be loaded.
|
||||||
|
|
||||||
|
The loader.get_code(fullname) method should return the code object
|
||||||
|
associated with the module, or None if it's a built-in or extension
|
||||||
|
module. If the loader doesn't have the code object but it _does_
|
||||||
|
have the source code, it should return the compiled the source code.
|
||||||
|
(This is so that our caller doesn't also need to check get_source()
|
||||||
|
if all it needs is the code object.)
|
||||||
|
|
||||||
|
The loader.get_source(fullname) method should return the source code
|
||||||
|
for the module as a string (using newline characters for line
|
||||||
|
endings) or None if the source is not available (yet it should still
|
||||||
|
raise ImportError if the module can't be found by the importer at
|
||||||
|
all).
|
||||||
|
|
||||||
|
|
||||||
Integration with the 'imp' module
|
Integration with the 'imp' module
|
||||||
|
|
||||||
The new import hooks are not easily integrated in the existing
|
The new import hooks are not easily integrated in the existing
|
||||||
|
@ -355,10 +425,11 @@ Integration with the 'imp' module
|
||||||
whether it's possible at all without breaking code; it is better to
|
whether it's possible at all without breaking code; it is better to
|
||||||
simply add a new function to the imp module. The meaning of the
|
simply add a new function to the imp module. The meaning of the
|
||||||
existing imp.find_module() and imp.load_module() calls changes from:
|
existing imp.find_module() and imp.load_module() calls changes from:
|
||||||
"they expose the built-in import mechanism" to "they expose the basic
|
"they expose the built-in import mechanism" to "they expose the
|
||||||
*unhooked* built-in import mechanism". They simply won't invoke any
|
basic *unhooked* built-in import mechanism". They simply won't
|
||||||
import hooks. A new imp module function is proposed under the name
|
invoke any import hooks. A new imp module function is proposed
|
||||||
"find_module2", with is used like the following pattern:
|
under the name "find_module2", with is used like the following
|
||||||
|
pattern:
|
||||||
|
|
||||||
loader = imp.find_module2(fullname, path)
|
loader = imp.find_module2(fullname, path)
|
||||||
if loader is not None:
|
if loader is not None:
|
||||||
|
@ -438,21 +509,31 @@ Implementation
|
||||||
http://www.python.org/sf/652586
|
http://www.python.org/sf/652586
|
||||||
|
|
||||||
|
|
||||||
References
|
References and Footnotes
|
||||||
|
|
||||||
[1] Installer by Gordon McMillan
|
[1] Installer by Gordon McMillan
|
||||||
http://www.mcmillan-inc.com/install1.html
|
http://www.mcmillan-inc.com/install1.html
|
||||||
|
|
||||||
[2] PEP 273, Import Modules from Zip Archives, Ahlstrom
|
[2] PEP 273, Import Modules from Zip Archives, Ahlstrom
|
||||||
http://www.python.org/peps/pep-0273.html
|
http://www.python.org/peps/pep-0273.html
|
||||||
|
|
||||||
[3] The Freeze tool
|
[3] The Freeze tool
|
||||||
Tools/freeze/ in a Python source distribution
|
Tools/freeze/ in a Python source distribution
|
||||||
|
|
||||||
[4] Squeeze
|
[4] Squeeze
|
||||||
http://starship.python.net/crew/fredrik/ipa/squeeze.htm
|
http://starship.python.net/crew/fredrik/ipa/squeeze.htm
|
||||||
|
|
||||||
[5] py2exe by Thomas Heller
|
[5] py2exe by Thomas Heller
|
||||||
http://py2exe.sourceforge.net/
|
http://py2exe.sourceforge.net/
|
||||||
|
|
||||||
[6] imp.set_frozenmodules() patch
|
[6] imp.set_frozenmodules() patch
|
||||||
http://www.python.org/sf/642578
|
http://www.python.org/sf/642578
|
||||||
|
|
||||||
|
[7] The path argument to importer.find_module() is there because the
|
||||||
|
pkg.__path__ variable may be needed at this point. It may either
|
||||||
|
come from the actual parent module or be supplied by
|
||||||
|
imp.find_module() or the proposed imp.find_module2() function.
|
||||||
|
|
||||||
|
|
||||||
Copyright
|
Copyright
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue