Add PEP 3122: Delineation of the main module.
This PEP has already been rejected.
This commit is contained in:
parent
532382f5dc
commit
a15817652d
|
@ -259,6 +259,7 @@ Index by Category
|
||||||
SR 363 Syntax For Dynamic Attribute Access North
|
SR 363 Syntax For Dynamic Attribute Access North
|
||||||
SR 666 Reject Foolish Indentation Creighton
|
SR 666 Reject Foolish Indentation Creighton
|
||||||
SR 3103 A Switch/Case Statement GvR
|
SR 3103 A Switch/Case Statement GvR
|
||||||
|
SR 3122 Delineation of the main module Cannon
|
||||||
|
|
||||||
|
|
||||||
Numerical Index
|
Numerical Index
|
||||||
|
@ -478,6 +479,7 @@ Numerical Index
|
||||||
S 3119 Introducing Abstract Base Classes GvR, Talin
|
S 3119 Introducing Abstract Base Classes GvR, Talin
|
||||||
S 3120 Using UTF-8 as the default source encoding von Löwis
|
S 3120 Using UTF-8 as the default source encoding von Löwis
|
||||||
S 3121 Module Initialization and finalization von Löwis
|
S 3121 Module Initialization and finalization von Löwis
|
||||||
|
SR 3122 Delineation of the main module Cannon
|
||||||
S 3141 A Type Hierarchy for Numbers Yasskin
|
S 3141 A Type Hierarchy for Numbers Yasskin
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,267 @@
|
||||||
|
PEP: 3122
|
||||||
|
Title: Delineation of the main module
|
||||||
|
Version: $Revision$
|
||||||
|
Last-Modified: $Date$
|
||||||
|
Author: Brett Cannon
|
||||||
|
Status: Rejected
|
||||||
|
Type: Standards Track
|
||||||
|
Content-Type: text/x-rst
|
||||||
|
Created: 27-Apr-2007
|
||||||
|
|
||||||
|
.. attention::
|
||||||
|
This PEP has been rejected. Guido views running scripts within a
|
||||||
|
package as an anti-pattern [#guido-rejection]_.
|
||||||
|
|
||||||
|
Abstract
|
||||||
|
========
|
||||||
|
|
||||||
|
Because of how name resolution works for relative imports in a world
|
||||||
|
where PEP 328 is implemented, the ability to execute modules within a
|
||||||
|
package ceases being possible. This failing stems from the fact that
|
||||||
|
the module being executed as the "main" module replaces its
|
||||||
|
``__name__`` attribute with ``"__main__"`` instead of leaving it as
|
||||||
|
the absolute name of the module. This breaks import's ability
|
||||||
|
to resolve relative imports from the main module into absolute names.
|
||||||
|
|
||||||
|
In order to resolve this issue, this PEP proposes to change how the
|
||||||
|
main module is delineated. By leaving the ``__name__`` attribute in
|
||||||
|
a module alone and setting ``sys.main`` to the name of the main
|
||||||
|
module this will allow at least some instances of executing a module
|
||||||
|
within a package that uses relative imports.
|
||||||
|
|
||||||
|
This PEP does not address the idea of introducing a module-level
|
||||||
|
function that is automatically executed like PEP 299 proposes.
|
||||||
|
|
||||||
|
|
||||||
|
The Problem
|
||||||
|
===========
|
||||||
|
|
||||||
|
With the introduction of PEP 328, relative imports became dependent on
|
||||||
|
the ``__name__`` attribute of the module performing the import. This
|
||||||
|
is because the use of dots in a relative import are used to strip away
|
||||||
|
parts of the calling module's name to calculate where in the package
|
||||||
|
hierarchy an import should fall (prior to PEP 328 relative
|
||||||
|
imports could fail and would fall back on absolute imports which had a
|
||||||
|
chance of succeeding).
|
||||||
|
|
||||||
|
For instance, consider the import ``from .. import spam`` made from the
|
||||||
|
``bacon.ham.beans`` module (``bacon.ham.beans`` is not a package
|
||||||
|
itself, i.e., does not define ``__path__``). Name resolution of the
|
||||||
|
relative import takes the caller's name (``bacon.ham.beans``), splits
|
||||||
|
on dots, and then slices off the last n parts based on the level
|
||||||
|
(which is 2). In this example both ``ham`` and ``beans`` are dropped
|
||||||
|
and ``spam`` is joined with what is left (``bacon``). This leads to
|
||||||
|
the proper import of the module ``bacon.spam``.
|
||||||
|
|
||||||
|
This reliance on the ``__name__`` attribute of a module when handling
|
||||||
|
relative imports becomes an issue when executing a script within a
|
||||||
|
package. Because the executing script has its name set to
|
||||||
|
``'__main__'``, import cannot resolve any relative imports, leading to
|
||||||
|
an ``ImportError``.
|
||||||
|
|
||||||
|
For example, assume we have a package named ``bacon`` with an
|
||||||
|
``__init__.py`` file containing::
|
||||||
|
|
||||||
|
from . import spam
|
||||||
|
|
||||||
|
Also create a module named ``spam`` within the ``bacon`` package (it
|
||||||
|
can be an empty file). Now if you try to execute the ``bacon``
|
||||||
|
package (either through ``python bacon/__init__.py`` or
|
||||||
|
``python -m bacon``) you will get an ``ImportError`` about trying to
|
||||||
|
do a relative import from within a non-package. Obviously the import
|
||||||
|
is valid, but because of the setting of ``__name__`` to ``'__main__'``
|
||||||
|
import thinks that ``bacon/__init__.py`` is not in a package since no
|
||||||
|
dots exist in ``__name__``. To see how the algorithm works in more
|
||||||
|
detail, see ``importlib.Import._resolve_name()`` in the sandbox
|
||||||
|
[#importlib]_.
|
||||||
|
|
||||||
|
Currently a work-around is to remove all relative imports in the
|
||||||
|
module being executed and make them absolute. This is unfortunate,
|
||||||
|
though, as one should not be required to use a specific type of
|
||||||
|
resource in order to make a module in a package be able to be
|
||||||
|
executed.
|
||||||
|
|
||||||
|
|
||||||
|
The Solution
|
||||||
|
============
|
||||||
|
|
||||||
|
The solution to the problem is to not change the value of ``__name__``
|
||||||
|
in modules. But there still needs to be a way to let executing code
|
||||||
|
know it is being executed as a script. This is handled with a new
|
||||||
|
attribute in the ``sys`` module named ``main``.
|
||||||
|
|
||||||
|
When a module is being executed as a script, ``sys.main`` will be set
|
||||||
|
to the name of the module. This changes the current idiom of::
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
...
|
||||||
|
|
||||||
|
to::
|
||||||
|
|
||||||
|
import sys
|
||||||
|
if __name__ == sys.main:
|
||||||
|
...
|
||||||
|
|
||||||
|
The newly proposed solution does introduce an added line of
|
||||||
|
boilerplate which is a module import. But as the solution does not
|
||||||
|
introduce a new built-in or module attribute (as discussed in
|
||||||
|
`Rejected Ideas`_) it has been deemed worth the extra line.
|
||||||
|
|
||||||
|
Another issue with the proposed solution (which also applies to all
|
||||||
|
rejected ideas as well) is that it does not directly solve the problem
|
||||||
|
of discovering the name of a file. Consider ``python bacon/spam.py``.
|
||||||
|
By the file name alone it is not obvious whether ``bacon`` is a
|
||||||
|
package. In order to properly find this out both the current
|
||||||
|
direction must exist on ``sys.path`` as well as ``bacon/__init__.py``
|
||||||
|
existing.
|
||||||
|
|
||||||
|
But this is the simple example. Consider ``python ../spam.py``. From
|
||||||
|
the file name alone it is not at all clear if ``spam.py`` is in a
|
||||||
|
package or not. One possible solution is to find out what the
|
||||||
|
absolute name of ``..``, check if a file named ``__init__.py`` exists,
|
||||||
|
and then look if the directory is on ``sys.path``. If it is not, then
|
||||||
|
continue to walk up the directory until no more ``__init__.py`` files
|
||||||
|
are found or the directory is found on ``sys.path``.
|
||||||
|
|
||||||
|
This could potentially be an expensive process. If the package depth
|
||||||
|
happens to be deep then it could require a large amount of disk access
|
||||||
|
to discover where the package is anchored on ``sys.path``, if at all.
|
||||||
|
The stat calls alone can be expensive if the file system the executed
|
||||||
|
script is on is something like NFS.
|
||||||
|
|
||||||
|
Because of these issues, only when the ``-m`` command-line argument
|
||||||
|
(introduced by PEP 338) is used will ``__name__`` be set. Otherwise
|
||||||
|
the fallback semantics of setting ``__name__`` to ``"__main__"`` will
|
||||||
|
occur. ``sys.main`` will still be set to the proper value,
|
||||||
|
regardless of what ``__name__`` is set to.
|
||||||
|
|
||||||
|
|
||||||
|
Implementation
|
||||||
|
==============
|
||||||
|
|
||||||
|
When the ``-m`` option is used, ``sys.main`` will be set to the
|
||||||
|
argument passed in. ``sys.argv`` will be adjusted as it is currently.
|
||||||
|
Then the equivalent of ``__import__(self.main)`` will occur. This
|
||||||
|
differs from current semantics as the ``runpy`` module fetches the
|
||||||
|
code object for the file specified by the module name in order to
|
||||||
|
explicitly set ``__name__`` and other attributes. This is no longer
|
||||||
|
needed as import can perform its normal operation in this situation.
|
||||||
|
|
||||||
|
If a file name is specified, then ``sys.main`` will be set to
|
||||||
|
``"__main__"``. The specified file will then be read and have a code
|
||||||
|
object created and then be executed with ``__name__`` set to
|
||||||
|
``"__main__"``. This mirrors current semantics.
|
||||||
|
|
||||||
|
|
||||||
|
Transition Plan
|
||||||
|
===============
|
||||||
|
|
||||||
|
In order for Python 2.6 to be able to support both the current
|
||||||
|
semantics and the proposed semantics, ``sys.main`` will always be set
|
||||||
|
to ``"__main__"``. Otherwise no change will occur for Python 2.6.
|
||||||
|
This unfortunately means that no benefit from this change will occur
|
||||||
|
in Python 2.6, but it maximizes compatibility for code that is to
|
||||||
|
work as much as possible with 2.6 and 3.0.
|
||||||
|
|
||||||
|
To help transition to the new idiom, 2to3 [#2to3]_ will gain a rule to
|
||||||
|
transform the current ``if __name__ == '__main__': ...`` idiom to the
|
||||||
|
new one. This will not help with code that checks ``__name__``
|
||||||
|
outside of the idiom, though.
|
||||||
|
|
||||||
|
|
||||||
|
Rejected Ideas
|
||||||
|
==============
|
||||||
|
|
||||||
|
``__main__`` built-in
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
A counter-proposal to introduce a built-in named ``__main__``.
|
||||||
|
The value of the built-in would be the name of the module being
|
||||||
|
executed (just like the proposed ``sys.main``). This would lead to a
|
||||||
|
new idiom of::
|
||||||
|
|
||||||
|
if __name__ == __main__:
|
||||||
|
...
|
||||||
|
|
||||||
|
A drawback is that the syntactic difference is subtle; the dropping
|
||||||
|
of quotes around "__main__". Some believe that for existing Python
|
||||||
|
programmers bugs will be introduced where the quotation marks will be
|
||||||
|
put on by accident. But one could argue that the bug would be
|
||||||
|
discovered quickly through testing as it is a very shallow bug.
|
||||||
|
|
||||||
|
While the name of built-in could obviously be different (e.g.,
|
||||||
|
``main``) the other drawback is that it introduces a new built-in.
|
||||||
|
With a simple solution such as ``sys.main`` being possible without
|
||||||
|
adding another built-in to Python, this proposal was rejected.
|
||||||
|
|
||||||
|
``__main__`` module attribute
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
Another proposal was to add a ``__main__`` attribute to every module.
|
||||||
|
For the one that was executing as the main module, the attribute would
|
||||||
|
have a true value while all other modules had a false value. This has
|
||||||
|
a nice consequence of simplify the main module idiom to::
|
||||||
|
|
||||||
|
if __main__:
|
||||||
|
...
|
||||||
|
|
||||||
|
The drawback was the introduction of a new module attribute. It also
|
||||||
|
required more integration with the import machinery than the proposed
|
||||||
|
solution.
|
||||||
|
|
||||||
|
|
||||||
|
Use ``__file__`` instead of ``__name__``
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
Any of the proposals could be changed to use the ``__file__``
|
||||||
|
attribute on modules instead of ``__name__``, including the current
|
||||||
|
semantics. The problem with this is that with the proposed solutions
|
||||||
|
there is the issue of modules having no ``__file__`` attribute defined
|
||||||
|
or having the same value as other modules.
|
||||||
|
|
||||||
|
The problem that comes up with the current semantics is you still have
|
||||||
|
to try to resolve the file path to a module name for the import to
|
||||||
|
work.
|
||||||
|
|
||||||
|
|
||||||
|
Special string subclass for ``__name__`` that overrides ``__eq__``
|
||||||
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
One proposal was to define a subclass of ``str`` that overrode the
|
||||||
|
``__eq__`` method so that it would compare equal to ``"__main__"`` as
|
||||||
|
well as the actual name of the module. In all other respects the
|
||||||
|
subclass would be the same as ``str``.
|
||||||
|
|
||||||
|
This was rejected as it seemed like too much of a hack.
|
||||||
|
|
||||||
|
|
||||||
|
References
|
||||||
|
==========
|
||||||
|
|
||||||
|
.. [#2to3] 2to3 tool
|
||||||
|
(http://svn.python.org/view/sandbox/trunk/2to3/) [ViewVC]
|
||||||
|
|
||||||
|
.. [#importlib] importlib
|
||||||
|
(http://svn.python.org/view/sandbox/trunk/import_in_py/importlib.py?view=markup)
|
||||||
|
[ViewVC]
|
||||||
|
|
||||||
|
.. [#guido-rejection] Python-Dev email: "PEP to change how the main module is delineated"
|
||||||
|
(http://mail.python.org/pipermail/python-3000/2007-April/006793.html)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Copyright
|
||||||
|
=========
|
||||||
|
|
||||||
|
This document has been placed in the public domain.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
..
|
||||||
|
Local Variables:
|
||||||
|
mode: indented-text
|
||||||
|
indent-tabs-mode: nil
|
||||||
|
sentence-end-double-space: t
|
||||||
|
fill-column: 70
|
||||||
|
coding: utf-8
|
||||||
|
End:
|
Loading…
Reference in New Issue