mirror of https://github.com/python/peps
643 lines
60 KiB
HTML
643 lines
60 KiB
HTML
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="color-scheme" content="light dark">
|
||
<title>PEP 302 – New Import Hooks | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0302/">
|
||
<link rel="stylesheet" href="../_static/style.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/mq.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light">
|
||
<link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark">
|
||
<link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss">
|
||
<meta property="og:title" content='PEP 302 – New Import Hooks | peps.python.org'>
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0302/">
|
||
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
|
||
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
|
||
<meta property="og:image:alt" content="Python PEPs">
|
||
<meta property="og:image:width" content="200">
|
||
<meta property="og:image:height" content="200">
|
||
<meta name="description" content="Python Enhancement Proposals (PEPs)">
|
||
<meta name="theme-color" content="#3776ab">
|
||
</head>
|
||
<body>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Following system colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="9"></circle>
|
||
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected dark colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected light colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="5"></circle>
|
||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||
</svg>
|
||
</symbol>
|
||
</svg>
|
||
<script>
|
||
|
||
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
|
||
</script>
|
||
<section id="pep-page-section">
|
||
<header>
|
||
<h1>Python Enhancement Proposals</h1>
|
||
<ul class="breadcrumbs">
|
||
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </li>
|
||
<li>PEP 302</li>
|
||
</ul>
|
||
<button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())">
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg>
|
||
<span class="visually-hidden">Toggle light / dark / auto colour theme</span>
|
||
</button>
|
||
</header>
|
||
<article>
|
||
<section id="pep-content">
|
||
<h1 class="page-title">PEP 302 – New Import Hooks</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Just van Rossum <just at letterror.com>,
|
||
Paul Moore <p.f.moore at gmail.com></dd>
|
||
<dt class="field-even">Status<span class="colon">:</span></dt>
|
||
<dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd>
|
||
<dt class="field-odd">Type<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
|
||
<dt class="field-even">Created<span class="colon">:</span></dt>
|
||
<dd class="field-even">19-Dec-2002</dd>
|
||
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-odd">2.3</dd>
|
||
<dt class="field-even">Post-History<span class="colon">:</span></dt>
|
||
<dd class="field-even">19-Dec-2002</dd>
|
||
</dl>
|
||
<hr class="docutils" />
|
||
<section id="contents">
|
||
<details><summary>Table of Contents</summary><ul class="simple">
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#motivation">Motivation</a></li>
|
||
<li><a class="reference internal" href="#use-cases">Use cases</a></li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a></li>
|
||
<li><a class="reference internal" href="#specification-part-1-the-importer-protocol">Specification part 1: The Importer Protocol</a></li>
|
||
<li><a class="reference internal" href="#specification-part-2-registering-hooks">Specification part 2: Registering Hooks</a></li>
|
||
<li><a class="reference internal" href="#packages-and-the-role-of-path">Packages and the role of <code class="docutils literal notranslate"><span class="pre">__path__</span></code></a></li>
|
||
<li><a class="reference internal" href="#optional-extensions-to-the-importer-protocol">Optional Extensions to the Importer Protocol</a></li>
|
||
<li><a class="reference internal" href="#integration-with-the-imp-module">Integration with the ‘imp’ module</a></li>
|
||
<li><a class="reference internal" href="#forward-compatibility">Forward Compatibility</a></li>
|
||
<li><a class="reference internal" href="#open-issues">Open Issues</a></li>
|
||
<li><a class="reference internal" href="#implementation">Implementation</a></li>
|
||
<li><a class="reference internal" href="#references-and-footnotes">References and Footnotes</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<div class="admonition warning">
|
||
<p class="admonition-title">Warning</p>
|
||
<p>The language reference for import <a class="footnote-reference brackets" href="#id17" id="id1">[10]</a> and importlib documentation
|
||
<a class="footnote-reference brackets" href="#id18" id="id2">[11]</a> now supersede this PEP. This document is no longer updated
|
||
and provided for historical purposes only.</p>
|
||
</div>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p>This PEP proposes to add a new set of import hooks that offer better
|
||
customization of the Python import mechanism. Contrary to the current
|
||
<code class="docutils literal notranslate"><span class="pre">__import__</span></code> hook, a new-style hook can be injected into the existing
|
||
scheme, allowing for a finer grained control of how modules are found and how
|
||
they are loaded.</p>
|
||
</section>
|
||
<section id="motivation">
|
||
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
|
||
<p>The only way to customize the import mechanism is currently to override the
|
||
built-in <code class="docutils literal notranslate"><span class="pre">__import__</span></code> function. However, overriding <code class="docutils literal notranslate"><span class="pre">__import__</span></code> has many
|
||
problems. To begin with:</p>
|
||
<ul class="simple">
|
||
<li>An <code class="docutils literal notranslate"><span class="pre">__import__</span></code> replacement needs to <em>fully</em> reimplement the entire
|
||
import mechanism, or call the original <code class="docutils literal notranslate"><span class="pre">__import__</span></code> before or after the
|
||
custom code.</li>
|
||
<li>It has very complex semantics and responsibilities.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">__import__</span></code> gets called even for modules that are already in
|
||
<code class="docutils literal notranslate"><span class="pre">sys.modules</span></code>, which is almost never what you want, unless you’re writing
|
||
some sort of monitoring tool.</li>
|
||
</ul>
|
||
<p>The situation gets worse when you need to extend the import mechanism from C:
|
||
it’s currently impossible, apart from hacking Python’s <code class="docutils literal notranslate"><span class="pre">import.c</span></code> or
|
||
reimplementing much of <code class="docutils literal notranslate"><span class="pre">import.c</span></code> from scratch.</p>
|
||
<p>There is a fairly long history of tools written in Python that allow extending
|
||
the import mechanism in various way, based on the <code class="docutils literal notranslate"><span class="pre">__import__</span></code> hook. The
|
||
Standard Library includes two such tools: <code class="docutils literal notranslate"><span class="pre">ihooks.py</span></code> (by GvR) and
|
||
<code class="docutils literal notranslate"><span class="pre">imputil.py</span></code> <a class="footnote-reference brackets" href="#id10" id="id3">[1]</a> (Greg Stein), but perhaps the most famous is <code class="docutils literal notranslate"><span class="pre">iu.py</span></code> by
|
||
Gordon McMillan, available as part of his Installer package. Their usefulness
|
||
is somewhat limited because they are written in Python; bootstrapping issues
|
||
need to worked around as you can’t load the module containing the hook with
|
||
the hook itself. So if you want the entire Standard Library to be loadable
|
||
from an import hook, the hook must be written in C.</p>
|
||
</section>
|
||
<section id="use-cases">
|
||
<h2><a class="toc-backref" href="#use-cases" role="doc-backlink">Use cases</a></h2>
|
||
<p>This section lists several existing applications that depend on import hooks.
|
||
Among these, a lot of duplicate work was done that could have been saved if
|
||
there had been a more flexible import hook at the time. This PEP should make
|
||
life a lot easier for similar projects in the future.</p>
|
||
<p>Extending the import mechanism is needed when you want to load modules that
|
||
are stored in a non-standard way. Examples include modules that are bundled
|
||
together in an archive; byte code that is not stored in a <code class="docutils literal notranslate"><span class="pre">pyc</span></code> formatted
|
||
file; modules that are loaded from a database over a network.</p>
|
||
<p>The work on this PEP was partly triggered by the implementation of <a class="pep reference internal" href="../pep-0273/" title="PEP 273 – Import Modules from Zip Archives">PEP 273</a>,
|
||
which adds imports from Zip archives as a built-in feature to Python. While
|
||
the PEP itself was widely accepted as a must-have feature, the implementation
|
||
left a few things to desire. For one thing it went through great lengths to
|
||
integrate itself with <code class="docutils literal notranslate"><span class="pre">import.c</span></code>, adding lots of code that was either
|
||
specific for Zip file imports or <em>not</em> specific to Zip imports, yet was not
|
||
generally useful (or even desirable) either. Yet the <a class="pep reference internal" href="../pep-0273/" title="PEP 273 – Import Modules from Zip Archives">PEP 273</a> implementation
|
||
can hardly be blamed for this: it is simply extremely hard to do, given the
|
||
current state of <code class="docutils literal notranslate"><span class="pre">import.c</span></code>.</p>
|
||
<p>Packaging applications for end users is a typical use case for import hooks,
|
||
if not <em>the</em> typical use case. Distributing lots of source or <code class="docutils literal notranslate"><span class="pre">pyc</span></code> files
|
||
around is not always appropriate (let alone a separate Python installation),
|
||
so there is a frequent desire to package all needed modules in a single file.
|
||
So frequent in fact that multiple solutions have been implemented over the
|
||
years.</p>
|
||
<p>The oldest one is included with the Python source code: Freeze <a class="footnote-reference brackets" href="#id11" id="id4">[2]</a>. It puts
|
||
marshalled byte code into static objects in C source code. Freeze’s “import
|
||
hook” is hard wired into <code class="docutils literal notranslate"><span class="pre">import.c</span></code>, and has a couple of issues. Later
|
||
solutions include Fredrik Lundh’s Squeeze, Gordon McMillan’s Installer, and
|
||
Thomas Heller’s py2exe <a class="footnote-reference brackets" href="#id12" id="id5">[3]</a>. MacPython ships with a tool called
|
||
<code class="docutils literal notranslate"><span class="pre">BuildApplication</span></code>.</p>
|
||
<p>Squeeze, Installer and py2exe use an <code class="docutils literal notranslate"><span class="pre">__import__</span></code> based scheme (py2exe
|
||
currently uses Installer’s <code class="docutils literal notranslate"><span class="pre">iu.py</span></code>, Squeeze used <code class="docutils literal notranslate"><span class="pre">ihooks.py</span></code>), MacPython
|
||
has two Mac-specific import hooks hard wired into <code class="docutils literal notranslate"><span class="pre">import.c</span></code>, that are
|
||
similar to the Freeze hook. The hooks proposed in this PEP enables us (at
|
||
least in theory; it’s not a short-term goal) to get rid of the hard coded
|
||
hooks in <code class="docutils literal notranslate"><span class="pre">import.c</span></code>, and would allow the <code class="docutils literal notranslate"><span class="pre">__import__</span></code>-based tools to get
|
||
rid of most of their <code class="docutils literal notranslate"><span class="pre">import.c</span></code> emulation code.</p>
|
||
<p>Before work on the design and implementation of this PEP was started, a new
|
||
<code class="docutils literal notranslate"><span class="pre">BuildApplication</span></code>-like tool for Mac OS X prompted one of the authors of
|
||
this PEP (JvR) to expose the table of frozen modules to Python, in the <code class="docutils literal notranslate"><span class="pre">imp</span></code>
|
||
module. The main reason was to be able to use the freeze import hook
|
||
(avoiding fancy <code class="docutils literal notranslate"><span class="pre">__import__</span></code> support), yet to also be able to supply a set
|
||
of modules at runtime. This resulted in issue #642578 <a class="footnote-reference brackets" href="#id13" id="id6">[4]</a>, which was
|
||
mysteriously accepted (mostly because nobody seemed to care either way ;-).
|
||
Yet it is completely superfluous when this PEP gets accepted, as it offers a
|
||
much nicer and general way to do the same thing.</p>
|
||
</section>
|
||
<section id="rationale">
|
||
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
|
||
<p>While experimenting with alternative implementation ideas to get built-in Zip
|
||
import, it was discovered that achieving this is possible with only a fairly
|
||
small amount of changes to <code class="docutils literal notranslate"><span class="pre">import.c</span></code>. This allowed to factor out the
|
||
Zip-specific stuff into a new source file, while at the same time creating a
|
||
<em>general</em> new import hook scheme: the one you’re reading about now.</p>
|
||
<p>An earlier design allowed non-string objects on <code class="docutils literal notranslate"><span class="pre">sys.path</span></code>. Such an object
|
||
would have the necessary methods to handle an import. This has two
|
||
disadvantages: 1) it breaks code that assumes all items on <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> are
|
||
strings; 2) it is not compatible with the <code class="docutils literal notranslate"><span class="pre">PYTHONPATH</span></code> environment variable.
|
||
The latter is directly needed for Zip imports. A compromise came from Jython:
|
||
allow string <em>subclasses</em> on <code class="docutils literal notranslate"><span class="pre">sys.path</span></code>, which would then act as importer
|
||
objects. This avoids some breakage, and seems to work well for Jython (where
|
||
it is used to load modules from <code class="docutils literal notranslate"><span class="pre">.jar</span></code> files), but it was perceived as an
|
||
“ugly hack”.</p>
|
||
<p>This led to a more elaborate scheme, (mostly copied from McMillan’s
|
||
<code class="docutils literal notranslate"><span class="pre">iu.py</span></code>) in which each in a list of candidates is asked whether it can
|
||
handle the <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> item, until one is found that can. This list of
|
||
candidates is a new object in the <code class="docutils literal notranslate"><span class="pre">sys</span></code> module: <code class="docutils literal notranslate"><span class="pre">sys.path_hooks</span></code>.</p>
|
||
<p>Traversing <code class="docutils literal notranslate"><span class="pre">sys.path_hooks</span></code> for each path item for each new import can be
|
||
expensive, so the results are cached in another new object in the <code class="docutils literal notranslate"><span class="pre">sys</span></code>
|
||
module: <code class="docutils literal notranslate"><span class="pre">sys.path_importer_cache</span></code>. It maps <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> entries to importer
|
||
objects.</p>
|
||
<p>To minimize the impact on <code class="docutils literal notranslate"><span class="pre">import.c</span></code> as well as to avoid adding extra
|
||
overhead, it was chosen to not add an explicit hook and importer object for
|
||
the existing file system import logic (as <code class="docutils literal notranslate"><span class="pre">iu.py</span></code> has), but to simply fall
|
||
back to the built-in logic if no hook on <code class="docutils literal notranslate"><span class="pre">sys.path_hooks</span></code> could handle the
|
||
path item. If this is the case, a <code class="docutils literal notranslate"><span class="pre">None</span></code> value is stored in
|
||
<code class="docutils literal notranslate"><span class="pre">sys.path_importer_cache</span></code>, again to avoid repeated lookups. (Later we can
|
||
go further and add a real importer object for the built-in mechanism, for now,
|
||
the <code class="docutils literal notranslate"><span class="pre">None</span></code> fallback scheme should suffice.)</p>
|
||
<p>A question was raised: what about importers that don’t need <em>any</em> entry on
|
||
<code class="docutils literal notranslate"><span class="pre">sys.path</span></code>? (Built-in and frozen modules fall into that category.) Again,
|
||
Gordon McMillan to the rescue: <code class="docutils literal notranslate"><span class="pre">iu.py</span></code> contains a thing he calls the
|
||
<em>metapath</em>. In this PEP’s implementation, it’s a list of importer objects
|
||
that is traversed <em>before</em> <code class="docutils literal notranslate"><span class="pre">sys.path</span></code>. This list is yet another new object
|
||
in the <code class="docutils literal notranslate"><span class="pre">sys</span></code> module: <code class="docutils literal notranslate"><span class="pre">sys.meta_path</span></code>. Currently, this list is empty by
|
||
default, and frozen and built-in module imports are done after traversing
|
||
<code class="docutils literal notranslate"><span class="pre">sys.meta_path</span></code>, but still before <code class="docutils literal notranslate"><span class="pre">sys.path</span></code>.</p>
|
||
</section>
|
||
<section id="specification-part-1-the-importer-protocol">
|
||
<h2><a class="toc-backref" href="#specification-part-1-the-importer-protocol" role="doc-backlink">Specification part 1: The Importer Protocol</a></h2>
|
||
<p>This PEP introduces a new protocol: the “Importer Protocol”. It is important
|
||
to understand the context in which the protocol operates, so here is a brief
|
||
overview of the outer shells of the import mechanism.</p>
|
||
<p>When an import statement is encountered, the interpreter looks up the
|
||
<code class="docutils literal notranslate"><span class="pre">__import__</span></code> function in the built-in name space. <code class="docutils literal notranslate"><span class="pre">__import__</span></code> is then
|
||
called with four arguments, amongst which are the name of the module being
|
||
imported (may be a dotted name) and a reference to the current global
|
||
namespace.</p>
|
||
<p>The built-in <code class="docutils literal notranslate"><span class="pre">__import__</span></code> function (known as <code class="docutils literal notranslate"><span class="pre">PyImport_ImportModuleEx()</span></code>
|
||
in <code class="docutils literal notranslate"><span class="pre">import.c</span></code>) will then check to see whether the module doing the import is
|
||
a package or a submodule of a package. If it is indeed a (submodule of a)
|
||
package, it first tries to do the import relative to the package (the parent
|
||
package for a submodule). For example, if a package named “spam” does “import
|
||
eggs”, it will first look for a module named “spam.eggs”. If that fails, the
|
||
import continues as an absolute import: it will look for a module named
|
||
“eggs”. Dotted name imports work pretty much the same: if package “spam” does
|
||
“import eggs.bacon” (and “spam.eggs” exists and is itself a package),
|
||
“spam.eggs.bacon” is tried. If that fails “eggs.bacon” is tried. (There are
|
||
more subtleties that are not described here, but these are not relevant for
|
||
implementers of the Importer Protocol.)</p>
|
||
<p>Deeper down in the mechanism, a dotted name import is split up by its
|
||
components. For “import spam.ham”, first an “import spam” is done, and only
|
||
when that succeeds is “ham” imported as a submodule of “spam”.</p>
|
||
<p>The Importer Protocol operates at this level of <em>individual</em> imports. By the
|
||
time an importer gets a request for “spam.ham”, module “spam” has already been
|
||
imported.</p>
|
||
<p>The protocol involves two objects: a <em>finder</em> and a <em>loader</em>. A finder object
|
||
has a single method:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">finder</span><span class="o">.</span><span class="n">find_module</span><span class="p">(</span><span class="n">fullname</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This method will be called with the fully qualified name of the module. If
|
||
the finder is installed on <code class="docutils literal notranslate"><span class="pre">sys.meta_path</span></code>, it will receive a second
|
||
argument, which is <code class="docutils literal notranslate"><span class="pre">None</span></code> for a top-level module, or <code class="docutils literal notranslate"><span class="pre">package.__path__</span></code>
|
||
for submodules or subpackages <a class="footnote-reference brackets" href="#id14" id="id7">[5]</a>. It should return a loader object if the
|
||
module was found, or <code class="docutils literal notranslate"><span class="pre">None</span></code> if it wasn’t. If <code class="docutils literal notranslate"><span class="pre">find_module()</span></code> raises an
|
||
exception, it will be propagated to the caller, aborting the import.</p>
|
||
<p>A loader object also has one method:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">loader</span><span class="o">.</span><span class="n">load_module</span><span class="p">(</span><span class="n">fullname</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This method returns the loaded module or raises an exception, preferably
|
||
<code class="docutils literal notranslate"><span class="pre">ImportError</span></code> if an existing exception is not being propagated. If
|
||
<code class="docutils literal notranslate"><span class="pre">load_module()</span></code> is asked to load a module that it cannot, <code class="docutils literal notranslate"><span class="pre">ImportError</span></code> is
|
||
to be raised.</p>
|
||
<p>In many cases the finder and loader can be one and the same object:
|
||
<code class="docutils literal notranslate"><span class="pre">finder.find_module()</span></code> would just return <code class="docutils literal notranslate"><span class="pre">self</span></code>.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">fullname</span></code> argument of both methods is the fully qualified module name,
|
||
for example “spam.eggs.ham”. As explained above, when
|
||
<code class="docutils literal notranslate"><span class="pre">finder.find_module("spam.eggs.ham")</span></code> is called, “spam.eggs” has already
|
||
been imported and added to <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code>. However, the <code class="docutils literal notranslate"><span class="pre">find_module()</span></code>
|
||
method isn’t necessarily always called during an actual import: meta tools
|
||
that analyze import dependencies (such as freeze, Installer or py2exe) don’t
|
||
actually load modules, so a finder shouldn’t <em>depend</em> on the parent package
|
||
being available in <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code>.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">load_module()</span></code> method has a few responsibilities that it must fulfill
|
||
<em>before</em> it runs any code:</p>
|
||
<ul>
|
||
<li>If there is an existing module object named ‘fullname’ in <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code>,
|
||
the loader must use that existing module. (Otherwise, the <code class="docutils literal notranslate"><span class="pre">reload()</span></code>
|
||
builtin will not work correctly.) If a module named ‘fullname’ does not
|
||
exist in <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code>, the loader must create a new module object and
|
||
add it to <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code>.<p>Note that the module object <em>must</em> be in <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code> before the loader
|
||
executes the module code. This is crucial because the module code may
|
||
(directly or indirectly) import itself; adding it to <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code>
|
||
beforehand prevents unbounded recursion in the worst case and multiple
|
||
loading in the best.</p>
|
||
<p>If the load fails, the loader needs to remove any module it may have
|
||
inserted into <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code>. If the module was already in <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code>
|
||
then the loader should leave it alone.</p>
|
||
</li>
|
||
<li>The <code class="docutils literal notranslate"><span class="pre">__file__</span></code> attribute must be set. This must be a string, but it may
|
||
be a dummy value, for example “<frozen>”. The privilege of not having a
|
||
<code class="docutils literal notranslate"><span class="pre">__file__</span></code> attribute at all is reserved for built-in modules.</li>
|
||
<li>The <code class="docutils literal notranslate"><span class="pre">__name__</span></code> attribute must be set. If one uses <code class="docutils literal notranslate"><span class="pre">imp.new_module()</span></code>
|
||
then the attribute is set automatically.</li>
|
||
<li>If it’s a package, the <code class="docutils literal notranslate"><span class="pre">__path__</span></code> variable must be set. This must be a
|
||
list, but may be empty if <code class="docutils literal notranslate"><span class="pre">__path__</span></code> has no further significance to the
|
||
importer (more on this later).</li>
|
||
<li>The <code class="docutils literal notranslate"><span class="pre">__loader__</span></code> attribute must be set to the loader object. This is
|
||
mostly for introspection and reloading, but can be used for
|
||
importer-specific extras, for example getting data associated with an
|
||
importer.</li>
|
||
<li>The <code class="docutils literal notranslate"><span class="pre">__package__</span></code> attribute must be set (<a class="pep reference internal" href="../pep-0366/" title="PEP 366 – Main module explicit relative imports">PEP 366</a>).<p>If the module is a Python module (as opposed to a built-in module or a
|
||
dynamically loaded extension), it should execute the module’s code in the
|
||
module’s global name space (<code class="docutils literal notranslate"><span class="pre">module.__dict__</span></code>).</p>
|
||
<p>Here is a minimal pattern for a <code class="docutils literal notranslate"><span class="pre">load_module()</span></code> method:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Consider using importlib.util.module_for_loader() to handle</span>
|
||
<span class="c1"># most of these details for you.</span>
|
||
<span class="k">def</span> <span class="nf">load_module</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fullname</span><span class="p">):</span>
|
||
<span class="n">code</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_code</span><span class="p">(</span><span class="n">fullname</span><span class="p">)</span>
|
||
<span class="n">ispkg</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">is_package</span><span class="p">(</span><span class="n">fullname</span><span class="p">)</span>
|
||
<span class="n">mod</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">modules</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="n">fullname</span><span class="p">,</span> <span class="n">imp</span><span class="o">.</span><span class="n">new_module</span><span class="p">(</span><span class="n">fullname</span><span class="p">))</span>
|
||
<span class="n">mod</span><span class="o">.</span><span class="vm">__file__</span> <span class="o">=</span> <span class="s2">"<</span><span class="si">%s</span><span class="s2">>"</span> <span class="o">%</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="o">.</span><span class="vm">__name__</span>
|
||
<span class="n">mod</span><span class="o">.</span><span class="n">__loader__</span> <span class="o">=</span> <span class="bp">self</span>
|
||
<span class="k">if</span> <span class="n">ispkg</span><span class="p">:</span>
|
||
<span class="n">mod</span><span class="o">.</span><span class="n">__path__</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="n">mod</span><span class="o">.</span><span class="n">__package__</span> <span class="o">=</span> <span class="n">fullname</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">mod</span><span class="o">.</span><span class="n">__package__</span> <span class="o">=</span> <span class="n">fullname</span><span class="o">.</span><span class="n">rpartition</span><span class="p">(</span><span class="s1">'.'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
|
||
<span class="n">exec</span><span class="p">(</span><span class="n">code</span><span class="p">,</span> <span class="n">mod</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">mod</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
<section id="specification-part-2-registering-hooks">
|
||
<h2><a class="toc-backref" href="#specification-part-2-registering-hooks" role="doc-backlink">Specification part 2: Registering Hooks</a></h2>
|
||
<p>There are two types of import hooks: <em>Meta hooks</em> and <em>Path hooks</em>. Meta
|
||
hooks are called at the start of import processing, before any other import
|
||
processing (so that meta hooks can override <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> processing, frozen
|
||
modules, or even built-in modules). To register a meta hook, simply add the
|
||
finder object to <code class="docutils literal notranslate"><span class="pre">sys.meta_path</span></code> (the list of registered meta hooks).</p>
|
||
<p>Path hooks are called as part of <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> (or <code class="docutils literal notranslate"><span class="pre">package.__path__</span></code>)
|
||
processing, at the point where their associated path item is encountered. A
|
||
path hook is registered by adding an importer factory to <code class="docutils literal notranslate"><span class="pre">sys.path_hooks</span></code>.</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">sys.path_hooks</span></code> is a list of callables, which will be checked in sequence
|
||
to determine if they can handle a given path item. The callable is called
|
||
with one argument, the path item. The callable must raise <code class="docutils literal notranslate"><span class="pre">ImportError</span></code> if
|
||
it is unable to handle the path item, and return an importer object if it can
|
||
handle the path item. Note that if the callable returns an importer object
|
||
for a specific <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> entry, the builtin import machinery will not be
|
||
invoked to handle that entry any longer, even if the importer object later
|
||
fails to find a specific module. The callable is typically the class of the
|
||
import hook, and hence the class <code class="docutils literal notranslate"><span class="pre">__init__()</span></code> method is called. (This is
|
||
also the reason why it should raise <code class="docutils literal notranslate"><span class="pre">ImportError</span></code>: an <code class="docutils literal notranslate"><span class="pre">__init__()</span></code> method
|
||
can’t return anything. This would be possible with a <code class="docutils literal notranslate"><span class="pre">__new__()</span></code> method in
|
||
a new style class, but we don’t want to require anything about how a hook is
|
||
implemented.)</p>
|
||
<p>The results of path hook checks are cached in <code class="docutils literal notranslate"><span class="pre">sys.path_importer_cache</span></code>,
|
||
which is a dictionary mapping path entries to importer objects. The cache is
|
||
checked before <code class="docutils literal notranslate"><span class="pre">sys.path_hooks</span></code> is scanned. If it is necessary to force a
|
||
rescan of <code class="docutils literal notranslate"><span class="pre">sys.path_hooks</span></code>, it is possible to manually clear all or part of
|
||
<code class="docutils literal notranslate"><span class="pre">sys.path_importer_cache</span></code>.</p>
|
||
<p>Just like <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> itself, the new <code class="docutils literal notranslate"><span class="pre">sys</span></code> variables must have specific
|
||
types:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">sys.meta_path</span></code> and <code class="docutils literal notranslate"><span class="pre">sys.path_hooks</span></code> must be Python lists.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">sys.path_importer_cache</span></code> must be a Python dict.</li>
|
||
</ul>
|
||
<p>Modifying these variables in place is allowed, as is replacing them with new
|
||
objects.</p>
|
||
</section>
|
||
<section id="packages-and-the-role-of-path">
|
||
<h2><a class="toc-backref" href="#packages-and-the-role-of-path" role="doc-backlink">Packages and the role of <code class="docutils literal notranslate"><span class="pre">__path__</span></code></a></h2>
|
||
<p>If a module has a <code class="docutils literal notranslate"><span class="pre">__path__</span></code> attribute, the import mechanism will treat it
|
||
as a package. The <code class="docutils literal notranslate"><span class="pre">__path__</span></code> variable is used instead of <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> when
|
||
importing submodules of the package. The rules for <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> therefore
|
||
also apply to <code class="docutils literal notranslate"><span class="pre">pkg.__path__</span></code>. So <code class="docutils literal notranslate"><span class="pre">sys.path_hooks</span></code> is also consulted when
|
||
<code class="docutils literal notranslate"><span class="pre">pkg.__path__</span></code> is traversed. Meta importers don’t necessarily use
|
||
<code class="docutils literal notranslate"><span class="pre">sys.path</span></code> at all to do their work and may therefore ignore the value of
|
||
<code class="docutils literal notranslate"><span class="pre">pkg.__path__</span></code>. In this case it is still advised to set it to list, which
|
||
can be empty.</p>
|
||
</section>
|
||
<section id="optional-extensions-to-the-importer-protocol">
|
||
<h2><a class="toc-backref" href="#optional-extensions-to-the-importer-protocol" role="doc-backlink">Optional Extensions to the Importer Protocol</a></h2>
|
||
<p>The Importer Protocol defines three optional extensions. One is to retrieve
|
||
data files, the second is to support module packaging tools and/or tools that
|
||
analyze module dependencies (for example Freeze), while the last is to support
|
||
execution of modules as scripts. The latter two categories of tools usually
|
||
don’t actually <em>load</em> modules, they only need to know if and where they are
|
||
available. All three extensions are highly recommended for general purpose
|
||
importers, but may safely be left out if those features aren’t needed.</p>
|
||
<p>To retrieve the data for arbitrary “files” from the underlying storage
|
||
backend, loader objects may supply a method named <code class="docutils literal notranslate"><span class="pre">get_data()</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">loader</span><span class="o">.</span><span class="n">get_data</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This method returns the data as a string, or raise <code class="docutils literal notranslate"><span class="pre">IOError</span></code> if the “file”
|
||
wasn’t found. The data is always returned as if “binary” mode was used -
|
||
there is no CRLF translation of text files, for example. It is meant for
|
||
importers that have some file-system-like properties. The ‘path’ argument is
|
||
a path that can be constructed by munging <code class="docutils literal notranslate"><span class="pre">module.__file__</span></code> (or
|
||
<code class="docutils literal notranslate"><span class="pre">pkg.__path__</span></code> items) with the <code class="docutils literal notranslate"><span class="pre">os.path.*</span></code> functions, for example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">d</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">)</span>
|
||
<span class="n">data</span> <span class="o">=</span> <span class="n">__loader__</span><span class="o">.</span><span class="n">get_data</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="s2">"logo.gif"</span><span class="p">))</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>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:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">loader</span><span class="o">.</span><span class="n">is_package</span><span class="p">(</span><span class="n">fullname</span><span class="p">)</span>
|
||
<span class="n">loader</span><span class="o">.</span><span class="n">get_code</span><span class="p">(</span><span class="n">fullname</span><span class="p">)</span>
|
||
<span class="n">loader</span><span class="o">.</span><span class="n">get_source</span><span class="p">(</span><span class="n">fullname</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>All three methods should raise <code class="docutils literal notranslate"><span class="pre">ImportError</span></code> if the module wasn’t found.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">loader.is_package(fullname)</span></code> method should return <code class="docutils literal notranslate"><span class="pre">True</span></code> if the
|
||
module specified by ‘fullname’ is a package and <code class="docutils literal notranslate"><span class="pre">False</span></code> if it isn’t.</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">loader.get_code(fullname)</span></code> method should return the code object
|
||
associated with the module, or <code class="docutils literal notranslate"><span class="pre">None</span></code> if it’s a built-in or extension
|
||
module. If the loader doesn’t have the code object but it <em>does</em> have the
|
||
source code, it should return the compiled source code. (This is so that our
|
||
caller doesn’t also need to check <code class="docutils literal notranslate"><span class="pre">get_source()</span></code> if all it needs is the code
|
||
object.)</p>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">loader.get_source(fullname)</span></code> method should return the source code for
|
||
the module as a string (using newline characters for line endings) or <code class="docutils literal notranslate"><span class="pre">None</span></code>
|
||
if the source is not available (yet it should still raise <code class="docutils literal notranslate"><span class="pre">ImportError</span></code> if
|
||
the module can’t be found by the importer at all).</p>
|
||
<p>To support execution of modules as scripts (<a class="pep reference internal" href="../pep-0338/" title="PEP 338 – Executing modules as scripts">PEP 338</a>),
|
||
the above three methods for
|
||
finding the code associated with a module must be implemented. In addition to
|
||
those methods, the following method may be provided in order to allow the
|
||
<code class="docutils literal notranslate"><span class="pre">runpy</span></code> module to correctly set the <code class="docutils literal notranslate"><span class="pre">__file__</span></code> attribute:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">loader</span><span class="o">.</span><span class="n">get_filename</span><span class="p">(</span><span class="n">fullname</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This method should return the value that <code class="docutils literal notranslate"><span class="pre">__file__</span></code> would be set to if the
|
||
named module was loaded. If the module is not found, then <code class="docutils literal notranslate"><span class="pre">ImportError</span></code>
|
||
should be raised.</p>
|
||
</section>
|
||
<section id="integration-with-the-imp-module">
|
||
<h2><a class="toc-backref" href="#integration-with-the-imp-module" role="doc-backlink">Integration with the ‘imp’ module</a></h2>
|
||
<p>The new import hooks are not easily integrated in the existing
|
||
<code class="docutils literal notranslate"><span class="pre">imp.find_module()</span></code> and <code class="docutils literal notranslate"><span class="pre">imp.load_module()</span></code> calls. It’s questionable
|
||
whether it’s possible at all without breaking code; it is better to simply add
|
||
a new function to the <code class="docutils literal notranslate"><span class="pre">imp</span></code> module. The meaning of the existing
|
||
<code class="docutils literal notranslate"><span class="pre">imp.find_module()</span></code> and <code class="docutils literal notranslate"><span class="pre">imp.load_module()</span></code> calls changes from: “they
|
||
expose the built-in import mechanism” to “they expose the basic <em>unhooked</em>
|
||
built-in import mechanism”. They simply won’t invoke any import hooks. A new
|
||
<code class="docutils literal notranslate"><span class="pre">imp</span></code> module function is proposed (but not yet implemented) under the name
|
||
<code class="docutils literal notranslate"><span class="pre">get_loader()</span></code>, which is used as in the following pattern:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">loader</span> <span class="o">=</span> <span class="n">imp</span><span class="o">.</span><span class="n">get_loader</span><span class="p">(</span><span class="n">fullname</span><span class="p">,</span> <span class="n">path</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">loader</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">loader</span><span class="o">.</span><span class="n">load_module</span><span class="p">(</span><span class="n">fullname</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>In the case of a “basic” import, one the <code class="docutils literal notranslate"><span class="pre">imp.find_module()</span></code> function would
|
||
handle, the loader object would be a wrapper for the current output of
|
||
<code class="docutils literal notranslate"><span class="pre">imp.find_module()</span></code>, and <code class="docutils literal notranslate"><span class="pre">loader.load_module()</span></code> would call
|
||
<code class="docutils literal notranslate"><span class="pre">imp.load_module()</span></code> with that output.</p>
|
||
<p>Note that this wrapper is currently not yet implemented, although a Python
|
||
prototype exists in the <code class="docutils literal notranslate"><span class="pre">test_importhooks.py</span></code> script (the <code class="docutils literal notranslate"><span class="pre">ImpWrapper</span></code>
|
||
class) included with the patch.</p>
|
||
</section>
|
||
<section id="forward-compatibility">
|
||
<h2><a class="toc-backref" href="#forward-compatibility" role="doc-backlink">Forward Compatibility</a></h2>
|
||
<p>Existing <code class="docutils literal notranslate"><span class="pre">__import__</span></code> hooks will not invoke new-style hooks by magic, unless
|
||
they call the original <code class="docutils literal notranslate"><span class="pre">__import__</span></code> function as a fallback. For example,
|
||
<code class="docutils literal notranslate"><span class="pre">ihooks.py</span></code>, <code class="docutils literal notranslate"><span class="pre">iu.py</span></code> and <code class="docutils literal notranslate"><span class="pre">imputil.py</span></code> are in this sense not forward
|
||
compatible with this PEP.</p>
|
||
</section>
|
||
<section id="open-issues">
|
||
<h2><a class="toc-backref" href="#open-issues" role="doc-backlink">Open Issues</a></h2>
|
||
<p>Modules often need supporting data files to do their job, particularly in the
|
||
case of complex packages or full applications. Current practice is generally
|
||
to locate such files via <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> (or a <code class="docutils literal notranslate"><span class="pre">package.__path__</span></code> attribute).
|
||
This approach will not work, in general, for modules loaded via an import
|
||
hook.</p>
|
||
<p>There are a number of possible ways to address this problem:</p>
|
||
<ul class="simple">
|
||
<li>“Don’t do that”. If a package needs to locate data files via its
|
||
<code class="docutils literal notranslate"><span class="pre">__path__</span></code>, it is not suitable for loading via an import hook. The
|
||
package can still be located on a directory in <code class="docutils literal notranslate"><span class="pre">sys.path</span></code>, as at present,
|
||
so this should not be seen as a major issue.</li>
|
||
<li>Locate data files from a standard location, rather than relative to the
|
||
module file. A relatively simple approach (which is supported by
|
||
distutils) would be to locate data files based on <code class="docutils literal notranslate"><span class="pre">sys.prefix</span></code> (or
|
||
<code class="docutils literal notranslate"><span class="pre">sys.exec_prefix</span></code>). For example, looking in
|
||
<code class="docutils literal notranslate"><span class="pre">os.path.join(sys.prefix,</span> <span class="pre">"data",</span> <span class="pre">package_name)</span></code>.</li>
|
||
<li>Import hooks could offer a standard way of getting at data files relative
|
||
to the module file. The standard <code class="docutils literal notranslate"><span class="pre">zipimport</span></code> object provides a method
|
||
<code class="docutils literal notranslate"><span class="pre">get_data(name)</span></code> which returns the content of the “file” called <code class="docutils literal notranslate"><span class="pre">name</span></code>,
|
||
as a string. To allow modules to get at the importer object, <code class="docutils literal notranslate"><span class="pre">zipimport</span></code>
|
||
also adds an attribute <code class="docutils literal notranslate"><span class="pre">__loader__</span></code> to the module, containing the
|
||
<code class="docutils literal notranslate"><span class="pre">zipimport</span></code> object used to load the module. If such an approach is used,
|
||
it is important that client code takes care not to break if the
|
||
<code class="docutils literal notranslate"><span class="pre">get_data()</span></code> method is not available, so it is not clear that this
|
||
approach offers a general answer to the problem.</li>
|
||
</ul>
|
||
<p>It was suggested on python-dev that it would be useful to be able to receive a
|
||
list of available modules from an importer and/or a list of available data
|
||
files for use with the <code class="docutils literal notranslate"><span class="pre">get_data()</span></code> method. The protocol could grow two
|
||
additional extensions, say <code class="docutils literal notranslate"><span class="pre">list_modules()</span></code> and <code class="docutils literal notranslate"><span class="pre">list_files()</span></code>. The
|
||
latter makes sense on loader objects with a <code class="docutils literal notranslate"><span class="pre">get_data()</span></code> method. However,
|
||
it’s a bit unclear which object should implement <code class="docutils literal notranslate"><span class="pre">list_modules()</span></code>: the
|
||
importer or the loader or both?</p>
|
||
<p>This PEP is biased towards loading modules from alternative places: it
|
||
currently doesn’t offer dedicated solutions for loading modules from
|
||
alternative file formats or with alternative compilers. In contrast, the
|
||
<code class="docutils literal notranslate"><span class="pre">ihooks</span></code> module from the standard library does have a fairly straightforward
|
||
way to do this. The Quixote project <a class="footnote-reference brackets" href="#id15" id="id8">[7]</a> uses this technique to import PTL
|
||
files as if they are ordinary Python modules. To do the same with the new
|
||
hooks would either mean to add a new module implementing a subset of
|
||
<code class="docutils literal notranslate"><span class="pre">ihooks</span></code> as a new-style importer, or add a hookable built-in path importer
|
||
object.</p>
|
||
<p>There is no specific support within this PEP for “stacking” hooks. For
|
||
example, it is not obvious how to write a hook to load modules from <code class="docutils literal notranslate"><span class="pre">tar.gz</span></code>
|
||
files by combining separate hooks to load modules from <code class="docutils literal notranslate"><span class="pre">.tar</span></code> and <code class="docutils literal notranslate"><span class="pre">.gz</span></code>
|
||
files. However, there is no support for such stacking in the existing hook
|
||
mechanisms (either the basic “replace <code class="docutils literal notranslate"><span class="pre">__import__</span></code>” method, or any of the
|
||
existing import hook modules) and so this functionality is not an obvious
|
||
requirement of the new mechanism. It may be worth considering as a future
|
||
enhancement, however.</p>
|
||
<p>It is possible (via <code class="docutils literal notranslate"><span class="pre">sys.meta_path</span></code>) to add hooks which run before
|
||
<code class="docutils literal notranslate"><span class="pre">sys.path</span></code> is processed. However, there is no equivalent way of adding
|
||
hooks to run after <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> is processed. For now, if a hook is required
|
||
after <code class="docutils literal notranslate"><span class="pre">sys.path</span></code> has been processed, it can be simulated by adding an
|
||
arbitrary “cookie” string at the end of <code class="docutils literal notranslate"><span class="pre">sys.path</span></code>, and having the required
|
||
hook associated with this cookie, via the normal <code class="docutils literal notranslate"><span class="pre">sys.path_hooks</span></code>
|
||
processing. In the longer term, the path handling code will become a “real”
|
||
hook on <code class="docutils literal notranslate"><span class="pre">sys.meta_path</span></code>, and at that stage it will be possible to insert
|
||
user-defined hooks either before or after it.</p>
|
||
</section>
|
||
<section id="implementation">
|
||
<h2><a class="toc-backref" href="#implementation" role="doc-backlink">Implementation</a></h2>
|
||
<p>The <a class="pep reference internal" href="../pep-0302/" title="PEP 302 – New Import Hooks">PEP 302</a> implementation has been integrated with Python as of 2.3a1. An
|
||
earlier version is available as patch #652586 <a class="footnote-reference brackets" href="#id16" id="id9">[9]</a>, but more interestingly,
|
||
the issue contains a fairly detailed history of the development and design.</p>
|
||
<p><a class="pep reference internal" href="../pep-0273/" title="PEP 273 – Import Modules from Zip Archives">PEP 273</a> has been implemented using <a class="pep reference internal" href="../pep-0302/" title="PEP 302 – New Import Hooks">PEP 302</a>’s import hooks.</p>
|
||
</section>
|
||
<section id="references-and-footnotes">
|
||
<h2><a class="toc-backref" href="#references-and-footnotes" role="doc-backlink">References and Footnotes</a></h2>
|
||
<aside class="footnote-list brackets">
|
||
<aside class="footnote brackets" id="id10" role="doc-footnote">
|
||
<dt class="label" id="id10">[<a href="#id3">1</a>]</dt>
|
||
<dd>imputil module
|
||
<a class="reference external" href="http://docs.python.org/library/imputil.html">http://docs.python.org/library/imputil.html</a></aside>
|
||
<aside class="footnote brackets" id="id11" role="doc-footnote">
|
||
<dt class="label" id="id11">[<a href="#id4">2</a>]</dt>
|
||
<dd>The Freeze tool.
|
||
See also the <code class="docutils literal notranslate"><span class="pre">Tools/freeze/</span></code> directory in a Python source distribution</aside>
|
||
<aside class="footnote brackets" id="id12" role="doc-footnote">
|
||
<dt class="label" id="id12">[<a href="#id5">3</a>]</dt>
|
||
<dd>py2exe by Thomas Heller
|
||
<a class="reference external" href="http://www.py2exe.org/">http://www.py2exe.org/</a></aside>
|
||
<aside class="footnote brackets" id="id13" role="doc-footnote">
|
||
<dt class="label" id="id13">[<a href="#id6">4</a>]</dt>
|
||
<dd>imp.set_frozenmodules() patch
|
||
<a class="reference external" href="http://bugs.python.org/issue642578">http://bugs.python.org/issue642578</a></aside>
|
||
<aside class="footnote brackets" id="id14" role="doc-footnote">
|
||
<dt class="label" id="id14">[<a href="#id7">5</a>]</dt>
|
||
<dd>The path argument to <code class="docutils literal notranslate"><span class="pre">finder.find_module()</span></code> is there because the
|
||
<code class="docutils literal notranslate"><span class="pre">pkg.__path__</span></code> variable may be needed at this point. It may either come
|
||
from the actual parent module or be supplied by <code class="docutils literal notranslate"><span class="pre">imp.find_module()</span></code> or
|
||
the proposed <code class="docutils literal notranslate"><span class="pre">imp.get_loader()</span></code> function.</aside>
|
||
<aside class="footnote brackets" id="id15" role="doc-footnote">
|
||
<dt class="label" id="id15">[<a href="#id8">7</a>]</dt>
|
||
<dd>Quixote, a framework for developing Web applications
|
||
<a class="reference external" href="http://www.mems-exchange.org/software/quixote/">http://www.mems-exchange.org/software/quixote/</a></aside>
|
||
<aside class="footnote brackets" id="id16" role="doc-footnote">
|
||
<dt class="label" id="id16">[<a href="#id9">9</a>]</dt>
|
||
<dd>New import hooks + Import from Zip files
|
||
<a class="reference external" href="http://bugs.python.org/issue652586">http://bugs.python.org/issue652586</a></aside>
|
||
<aside class="footnote brackets" id="id17" role="doc-footnote">
|
||
<dt class="label" id="id17">[<a href="#id1">10</a>]</dt>
|
||
<dd>Language reference for imports
|
||
<a class="reference external" href="http://docs.python.org/3/reference/import.html">http://docs.python.org/3/reference/import.html</a></aside>
|
||
<aside class="footnote brackets" id="id18" role="doc-footnote">
|
||
<dt class="label" id="id18">[<a href="#id2">11</a>]</dt>
|
||
<dd>importlib documentation
|
||
<a class="reference external" href="http://docs.python.org/3/library/importlib.html#module-importlib">http://docs.python.org/3/library/importlib.html#module-importlib</a></aside>
|
||
</aside>
|
||
</section>
|
||
<section id="copyright">
|
||
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
|
||
<p>This document has been placed in the public domain.</p>
|
||
</section>
|
||
</section>
|
||
<hr class="docutils" />
|
||
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0302.rst">https://github.com/python/peps/blob/main/peps/pep-0302.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0302.rst">2023-09-09 17:39:29 GMT</a></p>
|
||
|
||
</article>
|
||
<nav id="pep-sidebar">
|
||
<h2>Contents</h2>
|
||
<ul>
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#motivation">Motivation</a></li>
|
||
<li><a class="reference internal" href="#use-cases">Use cases</a></li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a></li>
|
||
<li><a class="reference internal" href="#specification-part-1-the-importer-protocol">Specification part 1: The Importer Protocol</a></li>
|
||
<li><a class="reference internal" href="#specification-part-2-registering-hooks">Specification part 2: Registering Hooks</a></li>
|
||
<li><a class="reference internal" href="#packages-and-the-role-of-path">Packages and the role of <code class="docutils literal notranslate"><span class="pre">__path__</span></code></a></li>
|
||
<li><a class="reference internal" href="#optional-extensions-to-the-importer-protocol">Optional Extensions to the Importer Protocol</a></li>
|
||
<li><a class="reference internal" href="#integration-with-the-imp-module">Integration with the ‘imp’ module</a></li>
|
||
<li><a class="reference internal" href="#forward-compatibility">Forward Compatibility</a></li>
|
||
<li><a class="reference internal" href="#open-issues">Open Issues</a></li>
|
||
<li><a class="reference internal" href="#implementation">Implementation</a></li>
|
||
<li><a class="reference internal" href="#references-and-footnotes">References and Footnotes</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
|
||
<br>
|
||
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0302.rst">Page Source (GitHub)</a>
|
||
</nav>
|
||
</section>
|
||
<script src="../_static/colour_scheme.js"></script>
|
||
<script src="../_static/wrap_tables.js"></script>
|
||
<script src="../_static/sticky_banner.js"></script>
|
||
</body>
|
||
</html> |