Table of Contents | |
Understanding Python exec()
Function
Python exec() is a built-in function that executes dynamically created Python code. Unlike eval(), used for evaluating expressions, exec() is designed to execute statements or even entire blocks of code represented as strings or code objects. This function provides a powerful way to run code you might not know when writing the program, such as code generated at runtime or fetched from external sources. Python exec() is more powerful than the eval() function.
Syntax of Python exec()
result = exec(object, globals, locals)
Explanation
result
: Variable will always beNone
asexec()
function does not return any value.exec()
: Built-in function that executes the code.object
: Either a string containing Python code or a code object. It is the main parameter forexec()
function.globals
(optional): Dictionary specifying the global namespace for the code execution.locals
(optional): Dictionary specifying the local namespace for the code execution.
Example of Python exec()
code_string = "print('Hello, world!')"
exec(code_string)
Explanation
code_string = "print('Hello, world!')"
: Creates a stringcode_string
that contains a simple Python print statement.exec(code_string)
: Executes the code incode_string
usingexec()
.
Output
Hello, world!
exec()
Parameters
Python exec() function takes up to three parameters: object, globals, and locals. The object parameter is the code you want to execute, which can be a string or a compiled code object. The globals parameter is an optional dictionary that provides a global namespace for the code execution; if omitted, the current global namespace is used. The locals parameter is another optional dictionary for the local namespace; if omitted, it defaults to the value of globals. You can pass a single dictionary for globals and skip locals parameter; in this case, globals will be used for both global and local namespace.
Syntax
exec(object, globals, locals)
Explanation
exec()
: Function that executes the dynamic code.object
: Either a string containing Python code or a code object to be executed. It is the main parameter forexec()
function.globals
(optional): Dictionary representing the global namespace. It is the second parameter forexec()
.locals
(optional): Dictionary representing the local namespace. It is the third parameter forexec()
..
Example
code = "x = 5\ny = 10\nprint(x + y)"
global_vars = {}
local_vars = {}
exec(code, global_vars, local_vars)
Explanation
code = "x = 5\ny = 10\nprint(x + y)"
: Creates a multi-line stringcode
containing Python code.global_vars = {}
: Initializes an empty dictionaryglobal_vars
to be used as the global namespace.local_vars = {}
: Initializes an empty dictionarylocal_vars
to be used as the local namespace.exec(code, global_vars, local_vars)
: Executes the code incode
using the specifiedglobal_vars
andlocal_vars
for the global and local namespaces.
Output
15
exec()
Return Value
Python exec() function always returns None. Unlike eval(), which returns the result of an expression, exec() is designed for executing statements and code blocks that don’t necessarily produce a value. The primary purpose of exec() is to perform actions, modify variables, define functions, or execute any other Python code dynamically, not to compute and return a specific value.
Syntax
return_value = exec(code)
Explanation
return_value
: Variable will always beNone
becauseexec()
does not return anything else.exec()
: Function executes the code incode
but does not return any value.code
: String or code object to be executed. It is used as input forexec()
function.
Example
result = exec("a = 5")
print(result)
Explanation
result = exec("a = 5")
: Executes the code string “a = 5” usingexec()
, which assigns 5 toa
in the current namespace but returnsNone
.print(result)
: Prints the value ofresult
, which isNone
.
Output
None
exec()
With a Single Line Program Input
You can use Python exec() to execute a single line of Python code represented as a string. This can be handy for quickly running a simple statement generated or modified at runtime. The single line of code can be an assignment, a function call, or any other valid Python statement. You must pass a single-line statement as a string to the object parameter.
Syntax
exec(single_line_code_string)
Explanation
exec()
: Function executes the single line of code.single_line_code_string
: String containing a single line of Python code. It is passed as input toexec()
function.
Example
exec("b = 10")
print(b)
Explanation
exec("b = 10")
: Executes the code string “b = 10”, which assigns the value 10 to the variableb
.print(b)
: Prints the value ofb
, which is now 10.
Output
10
exec()
with a Multi-line Input Program
Python exec() function can handle multi-line strings as input, allowing you to dynamically execute entire blocks of code. This is useful for running scripts or code snippets generated at runtime or loaded from external sources. To use multi-line input, create a string that contains multiple lines of Python code, separated by newline characters (\n), and pass it to exec().
Syntax
exec(multi_line_code_string)
Explanation
exec()
: Function executes the multi-line code string.multi_line_code_string
: String containing multiple lines of Python code, separated by newline characters. It is used as input forexec()
function.
Example
code = """
c = 7
d = 3
result = c * d
print(result)
"""
exec(code)
Explanation
code = """..."""
: Creates a multi-line stringcode
containing Python code that defines variables and performs a calculation.exec(code)
: Executes the multi-line code string usingexec()
.
Output
21
Checking Usable Code with exec()
Before executing dynamically generated or loaded code with Python exec(), it’s often a good idea to check if the code is valid and safe to run. One basic check is to use compile() to try to compile the code string into a code object. If the code is syntactically incorrect, compile() will raise an exception, allowing you to catch the error before executing the code with exec().
Syntax
try:
code_object = compile(code_string, '<string>', 'exec')
exec(code_object)
except SyntaxError:
# Handle syntax error
Explanation
try:
: Block attempts to compile and execute the code.code_object = compile(code_string, '<string>', 'exec')
: Tries to compilecode_string
into a code object.exec(code_object)
: Executes the compiled code object if compilation is successful.except SyntaxError:
: Block catches aSyntaxError
if one occurs during compilation.# Handle syntax error
: Where you would write code to handle syntax errors.
Example
code_string = "print('Hello, world')" # Missing colon to cause a SyntaxError
try:
code_object = compile(code_string, "<string>", "exec")
exec(code_object)
except SyntaxError as e:
print(f"Syntax error: {e}")
Explanation
code_string = "print('Hello, world')"
: Creates a stringcode_string
with a syntax error (missing colon).try:
: Block attempts to compile and execute the code.code_object = compile(code_string, "<string>", "exec")
: Attempts to compilecode_string
, which will raise aSyntaxError
.exec(code_object)
: Execute the code if it were valid.except SyntaxError as e:
: Block catches theSyntaxError
.print(f"Syntax error: {e}")
: Prints an error message.
Output
Syntax error: invalid syntax (<string>, line 1)
Blocking Unnecessary Methods and Variables in exec()
When using Python exec(), you can enhance security and control by blocking unnecessary methods and variables from being accessed within the executed code. You do this by providing custom globals
and locals
dictionaries that don’t include the names you want to block. This prevents the dynamically executed code from interacting with parts of your program that it shouldn’t have access to.
Syntax
globals_dict = {‘__builtins__’: None} # Block built-in functions and variables
locals_dict = {}
exec(code_string, globals_dict, locals_dict)
Explanation
globals_dict
: Dictionary defines the global namespace for the executed code.{'__builtins__': None}
: Disables access to built-in functions and variables by setting__builtins__
toNone
.locals_dict
: Dictionary defines the local namespace for the executed code.exec()
: Function executescode_string
with the specifiedglobals_dict
andlocals_dict
.code_string
: Code to be executed. It is used as input forexec()
.
Example
code = "print(len([1, 2, 3]))" # Attempt to use built-in len()
safe_globals = {'__builtins__': None}
safe_locals = {}
try:
exec(code, safe_globals, safe_locals)
except NameError as e:
print(f"Error: {e}")
Explanation
code = "print(len([1, 2, 3]))"
: Code string attempts to use the built-in functionsprint
andlen
.safe_globals = {'__builtins__': None}
: Creates aglobals
dictionary that blocks access to built-ins.safe_locals = {}
: Creates an emptylocals
dictionary.try:
: Block attempts to execute the code with restricted access.exec(code, safe_globals, safe_locals)
: Executescode
with the restricted namespaces, which will raise aNameError
.except NameError as e:
: Block catches theNameError
.print(f"Error: {e}")
: Prints the error message.
Output
Error: name ‘print’ is not defined
Using Necessary Methods and Variables in exec()
While it’s often important to restrict access when using Python exec(), you can also selectively allow certain methods and variables to be used within the dynamically executed code. You do this by including them in the globals and/or locals dictionaries that you pass to exec(). This way, you can provide the executed code with the specific tools it needs while maintaining control over the execution environment.
Syntax
globals_dict = {‘allowed_variable’: some_value, ‘allowed_function’: some_function}
locals_dict = {}
exec(code_string, globals_dict, locals_dict)
Explanation
globals_dict
: Dictionary defines the global namespace, including any allowed variables or functions.{'allowed_variable': some_value, 'allowed_function': some_function}
: Adds specific variables and functions to the global namespace.locals_dict
: Dictionary defines the local namespace (can be empty if you only want to allow globals).exec()
: Function executescode_string
with the specifiedglobals_dict
andlocals_dict
.code_string
: Code to be executed, which can now use the allowed names. It is used as input forexec()
.
Example
def safe_power(a, b):
return a ** b
code = "result = safe_power(2, 3)\nprint(result)"
globals_dict = {'safe_power': safe_power}
locals_dict = {}
exec(code, globals_dict, locals_dict)
Explanation
def safe_power(a, b):
: Defines a functionsafe_power
that’s allowed to be used in theexec()
call.return a ** b
: Returnsa
raised to the power ofb
.code = "result = safe_power(2, 3)\nprint(result)"
: Code string uses the allowedsafe_power
function.globals_dict = {'safe_power': safe_power}
: Creates aglobals
dictionary that includes onlysafe_power
.locals_dict = {}
: Creates an emptylocals
dictionary.exec(code, globals_dict, locals_dict)
: Executescode
with access tosafe_power
but no other globals or locals.
Output
8
Dynamic Code Generation
One of the most powerful Python exec() applications is dynamic code generation. This involves creating strings of Python code at runtime, often based on user input, data from files, or other programmatic logic. Once the code string is generated, you can use exec() to execute it. This technique allows you to create highly flexible programs that adapt their behavior based on changing conditions or requirements.
Syntax
dynamically_generated_code = generate_code(some_input)
exec(dynamically_generated_code)
Explanation
dynamically_generated_code
: Variable will hold the string of Python code generated at runtime.generate_code(some_input)
: Represents a function that creates a code string based onsome_input
.exec()
: Function executes the dynamically generated code.dynamically_generated_code
: Code string to be executed. It is passed as input toexec()
.
Example
def generate_code(number):
return f"print({number} * 2)"
code_to_execute = generate_code(5)
exec(code_to_execute)
Explanation
def generate_code(number):
: Defines a functiongenerate_code
that creates a code string.return f"print({number} * 2)"
: Returns a string that, when executed, will print twice the inputnumber
.code_to_execute = generate_code(5)
: Callsgenerate_code
to create a code string for the number 5.exec(code_to_execute)
: Executes the generated code string usingexec()
.
Output
10
Warning or Limitations
While Python exec() is powerful, it has significant limitations and security implications. The primary concern is that executing arbitrary code strings, especially from untrusted sources, can introduce severe security vulnerabilities. Malicious code could be injected and executed, potentially harming your system or data. Additionally, exec() can make debugging harder, as the executed code is not directly part of your source file. It’s generally recommended to use safer alternatives to exec() when possible and to carefully validate and sanitize any external input before executing it.
Syntax
exec(untrusted_code_string) # Potentially unsafe
Explanation
exec()
: Function executes the code inuntrusted_code_string
.untrusted_code_string
: Represents a string that may come from an untrusted source, making its execution risky. It is passed as input toexec()
function.
Example
untrusted_code = "import os; os.system('echo Malicious code executed!')" # Example of potentially harmful code
try:
exec(untrusted_code)
except Exception as e:
print(f"An error occurred:{e}")
Explanation
untrusted_code = "import os; os.system('echo Malicious code executed!')"
: This simulates a potentially malicious code string that imports theos
module and attempts to run a system command.try:
: This block attempts to execute the untrusted code.exec(untrusted_code)
: This line executes theuntrusted_code
, which could be harmful.except Exception as e:
: This catches any exception that might occur.print(f"An error occurred: {e}")
: This prints an error message.
Output
Malicious code executed!
Python3
In Python 3, the Python exec() function remains a built-in function with the same core functionality as in earlier versions but with some improvements in terms of safety and flexibility. Notably, exec() in Python 3 is a function (in Python 2, it was a statement), which means you always use parentheses to call it. Python 3 lets you specify separate global and local namespaces more cleanly using the globals and locals parameters. No new features have been introduced for exec() in Python3.
Syntax
exec(code_string, globals_dict, locals_dict)
Explanation
exec()
: Function executes the code incode_string
within the given namespaces.code_string
: Python code to execute, as a string. It is passed as input toexec()
.globals_dict
: (Optional) Dictionary for the global namespace. It is passed as a parameter toexec()
.locals_dict
: (Optional) Dictionary for the local namespace. It is passed as a parameter toexec()
.
Example
code_string = "result = a + b"
globals_dict = {"a": 5}
locals_dict = {"b": 8}
exec(code_string, globals_dict, locals_dict)
print(locals_dict['result'])
Explanation
code_string = "result = a + b"
: Defines a code string that adds two variables and stores the sum inresult
.globals_dict = {"a": 5}
: Creates aglobals
dictionary with variablea
.locals_dict = {"b": 8}
: Creates alocals
dictionary with variableb
.exec(code_string, globals_dict, locals_dict)
: Executescode_string
usingglobals_dict
andlocals_dict
for global and local variables.print(locals_dict['result'])
: Prints the value ofresult
from thelocals_dict
.
Output
13
Global and Local Parameters
When using Python exec() in Python 3, you can specify global and local namespaces using dictionaries. The globals parameter defines the global variables and functions that the executed code can access, while the locals parameter defines the local namespace. If you omit locals, it defaults to the same as globals. By carefully controlling these dictionaries, you can limit the environment in which the dynamic code runs, improving security and preventing unintended side effects.
Syntax
exec(code_string, globals_dict, locals_dict)
Explanation
exec()
: Function executes the code stringcode_string
.code_string
: Python code to execute, as a string. It is passed as input toexec()
.globals_dict
: Dictionary representing the global namespace for the execution. It is passed as a parameter toexec()
.locals_dict
: Dictionary representing the local namespace for the execution. It is passed as a parameter toexec()
.
Example
global_x = 10
local_y = 5
exec("z = global_x + local_y", {"global_x": global_x}, {"local_y": local_y})
print(globals()['z'])
Explanation
global_x = 10
: Defines a global variableglobal_x
.local_y = 5
: Defines a local variablelocal_y
.exec("z = global_x + local_y", {"global_x": global_x}, {"local_y": local_y})
: Executes the code string, using separate dictionaries for global and local variables.z
will be available in global namespace.print(globals()['z'])
: Prints the value of the global variablez
, which was set by theexec()
call.
Output
15
Conclusion
Python exec() is a powerful function for executing dynamically generated or loaded Python code represented as strings or code objects. It offers flexibility in running code whose content isn’t known until runtime. However, this power comes with significant security risks, especially when using exec() with untrusted input. It’s crucial to understand how to use globals and locals parameters to control the execution environment and limit potential vulnerabilities. While Python exec can be useful in specific cases, such as creating highly dynamic programs or implementing certain kinds of interpreters, it’s generally recommended to explore safer alternatives when possible and use exec() judiciously and carefully. Avoid using exec() if you are unsure about the input string.