Commit 1658797a authored by Mark Dickinson's avatar Mark Dickinson

Issue #16339: Document and test exec(stmt, globals, locals) form in Python 2.7.

parent 508d7d35
...@@ -978,18 +978,18 @@ The :keyword:`exec` statement ...@@ -978,18 +978,18 @@ The :keyword:`exec` statement
exec_stmt: "exec" `or_expr` ["in" `expression` ["," `expression`]] exec_stmt: "exec" `or_expr` ["in" `expression` ["," `expression`]]
This statement supports dynamic execution of Python code. The first expression This statement supports dynamic execution of Python code. The first expression
should evaluate to either a string, an open file object, or a code object. If should evaluate to either a string, an open file object, a code object, or a
it is a string, the string is parsed as a suite of Python statements which is tuple. If it is a string, the string is parsed as a suite of Python statements
then executed (unless a syntax error occurs). [#]_ If it is an open file, the file which is then executed (unless a syntax error occurs). [#]_ If it is an open
is parsed until EOF and executed. If it is a code object, it is simply file, the file is parsed until EOF and executed. If it is a code object, it is
executed. In all cases, the code that's executed is expected to be valid as simply executed. For the interpretation of a tuple, see below. In all cases,
file input (see section :ref:`file-input`). Be aware that the the code that's executed is expected to be valid as file input (see section
:keyword:`return` and :keyword:`yield` statements may not be used outside of :ref:`file-input`). Be aware that the :keyword:`return` and :keyword:`yield`
function definitions even within the context of code passed to the statements may not be used outside of function definitions even within the
:keyword:`exec` statement. context of code passed to the :keyword:`exec` statement.
In all cases, if the optional parts are omitted, the code is executed in the In all cases, if the optional parts are omitted, the code is executed in the
current scope. If only the first expression after :keyword:`in` is specified, current scope. If only the first expression after ``in`` is specified,
it should be a dictionary, which will be used for both the global and the local it should be a dictionary, which will be used for both the global and the local
variables. If two expressions are given, they are used for the global and local variables. If two expressions are given, they are used for the global and local
variables, respectively. If provided, *locals* can be any mapping object. variables, respectively. If provided, *locals* can be any mapping object.
...@@ -997,6 +997,13 @@ Remember that at module level, globals and locals are the same dictionary. If ...@@ -997,6 +997,13 @@ Remember that at module level, globals and locals are the same dictionary. If
two separate objects are given as *globals* and *locals*, the code will be two separate objects are given as *globals* and *locals*, the code will be
executed as if it were embedded in a class definition. executed as if it were embedded in a class definition.
The first expression may also be a tuple of length 2 or 3. In this case, the
optional parts must be omitted. The form ``exec(expr, globals)`` is equivalent
to ``exec expr in globals``, while the form ``exec(expr, globals, locals)`` is
equivalent to ``exec expr in globals, locals``. The tuple form of ``exec``
provides compatibility with Python 3, where ``exec`` is a function rather than
a statement.
.. versionchanged:: 2.4 .. versionchanged:: 2.4
Formerly, *locals* was required to be a dictionary. Formerly, *locals* was required to be a dictionary.
......
...@@ -61,6 +61,34 @@ class TestSpecifics(unittest.TestCase): ...@@ -61,6 +61,34 @@ class TestSpecifics(unittest.TestCase):
except SyntaxError: except SyntaxError:
pass pass
def test_exec_functional_style(self):
# Exec'ing a tuple of length 2 works.
g = {'b': 2}
exec("a = b + 1", g)
self.assertEqual(g['a'], 3)
# As does exec'ing a tuple of length 3.
l = {'b': 3}
g = {'b': 5, 'c': 7}
exec("a = b + c", g, l)
self.assertNotIn('a', g)
self.assertEqual(l['a'], 10)
# Tuples not of length 2 or 3 are invalid.
with self.assertRaises(TypeError):
exec("a = b + 1",)
with self.assertRaises(TypeError):
exec("a = b + 1", {}, {}, {})
# Can't mix and match the two calling forms.
g = {'a': 3, 'b': 4}
l = {}
with self.assertRaises(TypeError):
exec("a = b + 1", g) in g
with self.assertRaises(TypeError):
exec("a = b + 1", g, l) in g, l
def test_exec_with_general_mapping_for_locals(self): def test_exec_with_general_mapping_for_locals(self):
class M: class M:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment