Why doesn't exec("break") work inside a while loop
This is because exec()
is ignorant to your surrounding while loop. So the only statement that exec()
sees in your example is break
. Instead of using exec("break")
, simply use break
as is.
The only access the exec()
function has to its surrounding scope, is the globals()
and locals()
dictionaries. The documentation for exec()
provides some insight into how exec()
works:
This function supports dynamic execution of Python code. object must be either a string or a code object. If it is a string, the string is parsed as a suite of Python statements which is then executed (unless a syntax error occurs). [1] If it is a code object, it is simply executed. In all cases, the code that’s executed is expected to be valid as file input (see the section “File input” in the Reference Manual). Be aware that the return and yield statements may not be used outside of function definitions even within the context of code passed to the exec() function. The return value is None.
In all cases, if the optional parts are omitted, the code is executed in the current scope. If only globals is provided, it must be a dictionary, which will be used for both the global and the local variables. If globals and locals are given, they are used for the global and local variables, respectively. If provided, locals can be any mapping object. Remember that at module level, globals and locals are the same dictionary. If exec gets two separate objects as globals and locals, the code will be executed as if it were embedded in a class definition.
If the globals dictionary does not contain a value for the key builtins, a reference to the dictionary of the built-in module builtins is inserted under that key. That way you can control what builtins are available to the executed code by inserting your own builtins dictionary into globals before passing it to exec().
The exec
statement runs a bit of code independently from the rest of your code.
Hence, the line:
exec("break")
is tantamount to calling break
out of nowhere, in a script where nothing else happens, and where no loop exists.
The right way to call the break
statement is:
while True:
break
EDIT
The comment from Leaf made me think about it.
Actually, the exec
statement does not run the code out of nowhere.
>>> i = 12
>>> exec("print(i)")
12
A better answer, as far as I understand, is that exec
runs a piece of code in the same environment as the original code, but independently from it.
This basically means that all the variables that exist at the moment exec
is called can be used in the code called by exec
. But the context is all new, so return
, break
, continue
and other statements that need a context, will not work, unless the right context is created.
By the way, I kept the word "statement" when talking about exec
, but it has become a function in Python3, the same way print
did.
exec()
is a function. Assuming for simplicity that a function call constitutes a statement of its own (just like in your example), it may end in one of the following ways:
the function returns normally - in this case the next statement according to the control flow is executed;
an exception is raised/thrown from the function - in this case the matching
except
clause on the call stack (if any) is executedthe entire program is terminated due to an explicit call to exit() or equivalent - there is nothing to execute.
Calling a break
(as well as return
or yield
) from inside exec()
would modify the program execution flow in a way that is incompatible with the described aspect of the function call semantics.
Note that the documentation on exec()
contains a special note on the use of return
and yield
inside exec()
:
Be aware that the
return
andyield
statements may not be used outside of function definitions even within the context of code passed to theexec()
function.
A similar restriction applies to the break
statement (with the difference that it may not be used outside loops), and I wonder why it was not included in the documentation.