Don't try to hide exec's quirks

This commit is contained in:
Nick Coghlan 2006-02-11 14:18:13 +00:00
parent 31438973ae
commit 6a93382b16
1 changed files with 44 additions and 72 deletions

View File

@ -8,7 +8,7 @@ Type: Standards Track
Content-Type: text/x-rst Content-Type: text/x-rst
Created: 16-Oct-2004 Created: 16-Oct-2004
Python-Version: 2.5 Python-Version: 2.5
Post-History: 8-Nov-2004, 11-Feb-2006 Post-History: 8-Nov-2004, 11-Feb-2006, 12-Feb-2006
Abstract Abstract
@ -142,36 +142,25 @@ The delegation has the form::
``run_module`` is only one of a number of functions ``runpy`` exposes to ``run_module`` is only one of a number of functions ``runpy`` exposes to
make it easier to run Python code dynamically. The proposed functions make it easier to run Python code dynamically. The proposed functions
are listed below (the descriptions are taken from the proposed are listed below (the descriptions are taken from the proposed
documenation). documentation).
``run_code(code[, globals])`` ``run_code(code[, globals][, locals])``
Execute the supplied Python code object or string of source code and Execute the supplied Python code object or string of source code and
return the resulting module globals dictionary. If supplied, the return the resulting locals dictionary.
optional globals dictionary is used as the module globals.
Otherwise, a new dictionary is used. The optional ``globals`` argument may be used to specify the
dictionary to use as the ``globals()`` dictionary when running the
code. If the argument is omitted, a new dictionary is used.
The ``locals`` argument may be used to specify the
dictionary to use as the ``locals()`` dictionary when running the
code. If it is omitted, the same dictionary is used as is used for
``globals()``.
The special variable ``__builtins__`` in the globals dictionary is The special variable ``__builtins__`` in the globals dictionary is
automatically initialised with a reference to the top level automatically initialised with a reference to the top level
namespace of the ``__builtin__`` module. namespace of the ``__builtin__`` module.
``run_function_code(code[, globals][, locals])``
Execute the supplied function code object and return a tuple
containing the resulting globals and locals dictionaries. If
supplied, the optional globals dictionary is used as the module
globals. Otherwise, a new dictionary is used. Similarly, if
supplied, the optional locals dictionary is used as the function
locals. Otherwise, a new dictionary is used.
As for ``run_code()`` the special variable ``__builtins__`` in the
globals dictionary is automatically initialised with a reference to
the top level namespace of the ``__builtin__`` module.
A function code object is required, as module level Python code
cannot resolve names correctly when the locals and globals
dictionaries are not the same (specifically, new names are bound in
the locals dictionary, but this dictionary is not used when looking
up references to names at module level from inside a function).
``run_module_code(code[, init_globals][, mod_name][, mod_file]\ ``run_module_code(code[, init_globals][, mod_name][, mod_file]\
@ -186,12 +175,12 @@ documenation).
variables below are defined in the supplied dictionary, those variables below are defined in the supplied dictionary, those
definitions are overridden. definitions are overridden.
The special global variables ``__name__``, ``__file__``, The special global variables ``__name__``, ``__file__``, and
``__loader__`` and ``__builtins__`` are set in the globals ``__loader__`` are set in the globals dictionary before the module
dictionary before the module code is executed. ``__name__``, code is executed. ``__name__``, ``__file__``, ``__loader__`` are
``__file__``, ``__loader__`` are set based on the optional arguments set based on the optional arguments ``mod_name``, ``mod_file`` and
``mod_name``, ``mod_file`` and ``mod_loader``. If the arguments are ``mod_loader``. If the arguments are omitted, the corresponding
omitted, the corresponding special variable is set to ``None``. special variable is set to ``None``.
If the argument ``as_script`` is supplied and evaluates to ``True``, If the argument ``as_script`` is supplied and evaluates to ``True``,
then ``sys.argv[0]`` is updated with the value of ``mod_file`` then ``sys.argv[0]`` is updated with the value of ``mod_file``
@ -229,6 +218,9 @@ documenation).
level function is ``run_name`` if this optional argument is supplied level function is ``run_name`` if this optional argument is supplied
and ``None`` otherwise. and ``None`` otherwise.
The ``mod_loader`` argument to the lower level function is set to
``None`` and the ``mod_file`` argument is set to ``filename``.
``run_compiled_file(filename[, init_globals][, run_name]\ ``run_compiled_file(filename[, init_globals][, run_name]\
[, as_script])`` [, as_script])``
@ -241,6 +233,9 @@ documenation).
level function is ``run_name`` if this optional argument is supplied level function is ``run_name`` if this optional argument is supplied
and ``None`` otherwise. and ``None`` otherwise.
The ``mod_loader`` argument to the lower level function is set to
``None`` and the ``mod_file`` argument is set to ``filename``.
``run_file(filename[, init_globals][, run_name][, as_script])`` ``run_file(filename[, init_globals][, run_name][, as_script])``
Execute the specified Python file and return the resulting module Execute the specified Python file and return the resulting module
@ -257,6 +252,9 @@ documenation).
level function is ``run_name`` if this optional argument is supplied level function is ``run_name`` if this optional argument is supplied
and ``None`` otherwise. and ``None`` otherwise.
The ``mod_loader`` argument to the lower level function is set to
``None`` and the ``mod_file`` argument is set to ``filename``.
When invoked as a script, the ``runpy`` module finds and executes the When invoked as a script, the ``runpy`` module finds and executes the
module supplied as the first argument. It adjusts ``sys.argv`` by module supplied as the first argument. It adjusts ``sys.argv`` by
@ -272,46 +270,14 @@ There were some key design decisions that influenced the development of
the ``runpy`` module. These are listed below. the ``runpy`` module. These are listed below.
- the ``-m`` switch really only needs the ``run_module`` function. The - the ``-m`` switch really only needs the ``run_module`` function. The
other six functions are included to give the module API coverage other five functions are included to give the module API coverage
of the other sources of executable Python code (strings, code objects of the other sources of executable Python code (strings, code objects
source files, compiled files). source files, compiled files).
- when using ``exec`` with a separate locals dictionary, name resolution - no attempt is made to conceal the quirks of the exec statement when it
only works right if the code being executed was compiled expecting comes to executing function code objects, or attempting to reference
that the locals dictionary and the globals dictionary were different. module level names from inside functions when a separate locals
Module level code, and anything compiled using ``compile()`` (such as dictionary is supplied.
a source string passed to ``exec``) assumes that the two dictionaries
are the same. If they're different, references to globals from inside
functions aren't resolved using the locals dictionary::
>>> code = """
... x = 1
... print x
... def f():
... print "Try to resolve x from inside a function"
... print x
... f()
... """
>>> exec code
1
Try to resolve x from inside a function
1
>>> exec code in {}
1
Try to resolve x from inside a function
1
>>> exec code in {}, {}
1
Try to resolve x from inside a function
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 7, in <module>
File "<string>", line 6, in f
NameError: global name 'x' is not defined
Accordingly, the basic ``run_code`` function doesn't accept a locals
dictionary, and the ``run_function_code`` function checks that the
supplied code object expects locals and globals to be different.
- The special variables ``__name__``, ``__file__`` and ``__loader__`` - The special variables ``__name__``, ``__file__`` and ``__loader__``
are set in a module's global namespace before the module is executed. are set in a module's global namespace before the module is executed.
@ -325,9 +291,9 @@ the ``runpy`` module. These are listed below.
these variables are simply set to ``None`` when the relevant these variables are simply set to ``None`` when the relevant
information cannot be determined. information cannot be determined.
- Lastly, there is no special protection on the as_script argument when - Lastly, there is no special protection on the as_script argument.
file information is not available. This may result in ``sys.srgv[0]`` This may result in ``sys.argv[0]`` being set to ``None`` if file
being set to ``None`` if file name information is not available. name information is not available.
Alternatives Alternatives
@ -345,6 +311,12 @@ Both approaches were rejected as they do not meet the main goal of the
``-m`` switch -- to allow the full Python namespace to be used to ``-m`` switch -- to allow the full Python namespace to be used to
locate modules for execution from the command line. locate modules for execution from the command line.
An earlier version of this PEP included some mistaken assumption
about the way ``exec`` handled locals dictionaries and code from
function objects. These mistaken assumptions led to some unneeded
design complexity which has now been removed - ``run_code`` shares all
of the quirks of ``exec``.
References References
========== ==========