The exec()
function in Python allows us to execute the block of Python code from a string. This built-in function in Python can come in handy when we need to run the dynamically generated Python code but it is recommended not to use it carelessly due to some security risks that come with it.
In this tutorial, we’ll be going to learn
- how to work with Python’s
exec()
function - how to execute Python code using the
exec()
function with code examples - executing Python code from a string and Python source file
- using
globals
andlocals
parameters
Python’s exec() function
Python’s exec()
function allows us to execute any piece of Python code no matter how big or small that code is. This function helps us to execute the dynamically generated code.
Think of a Python interpreter that takes the piece of code, processes it internally, and executes it, the exec()
function does the same. It is like an independent Python interpreter.
The exec()
is capable of executing simple Python programs as well as fully featured Python programs. It can execute function calls and definitions, class definitions and instantiations, imports, and much more.
Syntax
exec(object [ , globals [ , locals]])
object
– It must be a string or a code object. If it is a string, it is parsed as a suite of Python statements which is executed unless a syntax error occurs. If it is a code object then it will simply execute.globals
andlocals
– This allows us to provide dictionaries representing the global and local namespaces.
Return value
The return value of the exec() function is None. It may be because every piece of code doesn’t have the final result.
Initial glance
Here’s an initial look at the working of the exec()
function.
1 2 3 4 5 6 7 |
obj = ["apple", "cherry", "melon", "strawberry"] code = "print([sliced[:4] for sliced in obj if 'a' not in sliced])" exec(code) ......... ['cher', 'melo'] |
Another example of using the exec()
function
1 2 3 4 |
# The code will continuously run and we can run our code as we do in # Python interpreter while True: exec(input(">>> ")) |
1 2 3 4 5 6 7 8 9 10 11 |
>>> print("Welcome to GeekPython") Welcome to GeekPython >>> import numpy as np >>> print(np.random.randint(16, size=(2, 4))) [[11 13 3 13] [ 7 6 15 5]] >>> x = ["apple", "banana", "cherry", "mango"] >>> print([fruit for fruit in x if "a" not in fruit]) ['cherry'] |
It works exactly like a Python interpreter, taking our code, processing it internally, executing it, and returning the correct results.
We’re running an infinite loop, and inside it, we’re taking input from the command line and wrapping it in the exec()
function to execute it.
Executing code from a string input
We can use the exec()
function to execute the code which is in a string format. There are multiple ways that we can use to build the string-based input:
- Using one-liners code
- Using new line characters
- Using triple-quoted strings with proper formatting
Using one-liner string-based input
In Python, single-line code, also known as one-liner code, is code written in a single line that can perform multiple tasks at the same time.
If we wrote a single line of Python code, it would look like this:
1 2 3 |
obj = ["apple", "cherry", "melon", "strawberry"] print([sliced[:4] for sliced in obj if 'a' not in sliced]) |
Output
1 |
['cher', 'melo'] |
But if we run the above code using the exec()
, the code would be
1 2 3 4 5 |
obj = ["apple", "cherry", "melon", "strawberry"] exec("print([sliced[:4] for sliced in obj if 'a' not in sliced])") #-----------------------------OR-------------------------------- exec("code = [sliced[:4] for sliced in obj if 'a' not in sliced]") |
The other code we wrote above will return nothing if we execute it, instead, the output will be stored in the code
variable for later access.
Executing multiple lines of code separated by new line characters
We can combine multiple statements in a single-line string using the new line characters \n
.
1 |
exec("square = int(input('Enter the number: '))\nprint(f'The square of {square}:', square**2)") |
Output
1 2 |
Enter the number: 30 The square of 30: 900 |
A new line character (\n
) is defined to make the exec()
function understand our single-line string-based code as a multiline set of Python statements.
Using triple-quoted string
In Python, we frequently use triple quotes to comment on or document our code. However, in this case, we’ll use it to generate string-based input that looks and behaves exactly like normal Python code.
The code we write within triple quotes must be properly indented and formatted, just like normal Python code. See the example below for a better understanding.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
sample_code = """ integers = [4, 7, 2, 9, 44] def square(num): return num ** 2 def odd_num(num): return num % 2 == 1 square_if_even = [square(number) for number in integers if number % 2 == 0] odd_number = [number for number in integers if odd_num(number)] print("Original values:", integers) print("Square of even number:", square_if_even) print("Odd number:", odd_number) """ exec(sample_code) |
Output
1 2 3 |
Original values: [4, 7, 2, 9, 44] Square of even number: [16, 4, 1936] Odd number: [7, 9] |
The above code is similar to standard Python code, with proper indentation and formatting, but it is wrapped within triple quotes, resulting in string-based input stored in the sample_code
variable, which was then executed using the exec()
function.
Executing code from a Python file
We can use the exec()
function to execute the code from Python(.py
) source file by reading the content of the file using the open() function.
Consider the following example, which includes a sample.py
file containing the following code:
1 2 3 4 5 6 7 8 |
# sample.py def anime(name): print(f"Favourite anime: {name}") anime("One Piece") list_of_anime = input("Enter your favourite anime: ").split(",") print("Your Favourite anime:", list_of_anime) |
The code simply prints the anime’s name here, while the following block of code takes the input of your favorite anime separated by commas and prints the desired output.
Executing the Python source file using the exec function
1 2 3 4 |
with open("sample.py", mode="r") as sample: file = sample.read() exec(file) |
Output
1 2 3 |
Favourite anime: One Piece Enter your favourite anime: One Piece, Naruto, Demon Slayer, Jujutsu kaisen Your Favourite anime: ['One Piece', ' Naruto', ' Demon Slayer', ' Jujutsu kaisen'] |
We used the open()
function using the with
statement to open the .py
file as a regular text file and then used the .read()
on the file object to read the content of the file as a string into the file
variable which is passed in the exec
to execute the code.
Using globals and locals params
These parameters are entirely optional. We can use globals
and locals
parameters to limit the use of functions, methods, and variables that aren’t required.
Because these parameters are optional, omitting them causes the exec()
function to execute the input code in the current scope. Consider the following example to better understand it.
1 2 3 4 5 6 7 8 9 |
code = """ out = str1 + str2 print(out) """ # global variables str1 = "Hel" str2 = "lo" exec(code) |
Output
1 |
Hello |
The code above ran successfully and produced an output that combined both global variables. Because the globals
and locals
parameters were not specified, the exec()
function executed the code input in the current scope. The current scope is global in this case.
Here’s an example of how to get the value of a variable in the current scope of code.
1 2 3 4 5 6 7 8 9 |
code = """ out = str1 + str2 print(out) """ # global variables str1 = "Hel" str2 = "lo" print(out) |
1 2 3 |
Traceback (most recent call last): ..... NameError: name 'out' is not defined. Did you mean: 'oct'? |
In the preceding code, we attempted to access the value of the out
variable before calling exec()
and received an error.
However, we can access the value of the out variable after calling exec()
because the variable defined in the code input will be available in the current scope after calling exec()
.
1 2 |
exec(code) print(out) |
Output
1 2 |
Hello Hello |
Using globals
and locals
parameters
1 2 3 4 5 6 7 8 9 |
code = """ out = str1 + str2 print(out) """ # global variables str1 = "Hel" str2 = "lo" exec(code, {"str1": str1}) |
Output
1 2 3 |
Traceback (most recent call last): .... NameError: name 'str2' is not defined. Did you mean: 'str1'? |
The code returns an error because we didn’t define the key holding the str2
in the dictionary, so the exec()
doesn’t have access to it.
1 2 3 4 5 6 7 8 9 10 11 |
code = """ out = str1 + str2 print(out) """ # global variables str1 = "Hel" str2 = "lo" exec(code, {"str1": str1, "str2": str2}) print(out) |
Output
1 2 3 4 |
Hello Traceback (most recent call last): .... NameError: name 'out' is not defined. Did you mean: 'oct'? |
The exec()
function now has access to both global variables, and the code returns the output without error, but we didn’t have access to the out after the call to exec()
this time because we’re using the custom dictionary to provide an execution scope to the exec()
.
Here’s an example of using locals
and globals
together
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
code = """ out = str1 + str2 + " " + x print(out) """ # global variables str1 = "Hel" str2 = "lo" def local(): # local variable x = "there" exec(code, {"str1": str1, "str2": str2}, {"x": x}) local() |
Output
1 |
Hello there |
In the above code, we called exec()
from within the local function. In the global scope, we have global variables and a local variable in the local scope (function level). The globals parameter specifies the variables str1
and str2
, while the locals parameter specifies the variable x
.
Blocking unnecessary methods and variables
Using the globals
and locals
params, we can control whether to restrict or use any variable or method in our code input. In this section, we’ll limit some of the functions in Python’s datetime
module.
1 2 3 4 5 6 7 |
from datetime import * code = """ curr_time = datetime.now() print(curr_time) """ exec(code, {}) |
Output
1 2 3 |
Traceback (most recent call last): .... NameError: name 'datetime' is not defined |
We restricted the use of the datetime
method from the datetime
module, which resulted in an error.
Using necessary methods and variables
We can only use the methods that are required with the exec()
.
1 2 3 4 5 6 7 8 9 10 |
from datetime import * # Allowing only two methods allowed_param = {'datetime': datetime, 'timedelta': timedelta} exec("print(datetime.now())", allowed_param) exec("print(datetime.now() + timedelta(days=2))", allowed_param) # date() method is not allowed exec("print(date(2022, 9, 4))", allowed_param) |
Output
1 2 3 4 5 6 |
2022-10-15 18:40:44.290550 2022-10-17 18:40:44.290550 Traceback (most recent call last): .... NameError: name 'date' is not defined |
The error occurred because the date
method was not permitted. Except for two methods, datetime
and timedelta
, all methods in the datetime
module were forbidden.
Let’s see what else we can accomplish with globals
and locals
parameters.
1 2 3 4 5 6 7 8 9 10 |
from datetime import * # Setting globals parameter to __builtins__ globals_param = {'__builtins__': __builtins__} # Setting locals parameter to take only print(), slice() and dir() locals_param = {'print': print, 'dir': dir, 'slice': slice} exec('print(slice(2))', globals_param, locals_param) exec('print(dir())', globals_param, locals_param) |
Output
1 2 |
slice(None, 2, None) ['dir', 'print', 'slice'] |
Inside the exec()
function, only the slice()
method and all built-in methods can be executed. Even though the slice()
method is not from the datetime
module, it works perfectly here.
We can also limit the use of __builtins__
by setting it to None
.
1 2 3 4 5 6 7 8 9 10 11 |
from datetime import * # Setting globals parameter to none globals_param = {'__builtins__': None} # Setting locals parameter to take only print(), slice(), sum() and dir() locals_param = {'print': print, 'dir': dir, 'slice': slice, 'sum': sum} # Allowed methods directory exec('print(dir())', globals_param, locals_param) exec('print(f"Sum of numbers: {sum([4, 6, 7])}")', globals_param, locals_param) |
Output
1 2 |
['dir', 'print', 'slice', 'sum'] Sum of numbers: 17 |
We limited the use of __builtins__
, so we can’t use the built-in methods and can only execute the print
, sum
, slice
, and dir
methods inside the exec()
.
Conclusion
We’ve learned how to use the built-in Python exec()
function to execute code from a string-based input. It allows you to run dynamically generated Python code.
The topics we’ve learned
- what is
exec()
function and working with it - executing Python code from a string-based input and Python source files
- understanding the use of the
globals
andlocals
parameters
Additionally, we’ve coded some of the examples which helped us understand the exec()
function better.
πOther articles you might be interested in if you liked this one
β How to use assert statements to debug the code in Python?
β Upload and display images on the frontend using Flask in Python.
β Building a Flask image recognition webapp using a deep learning model.
β What is generator and yield keyword in Python?
β Public, Protected, and Private access modifiers in Python.
β How to build a CLI command in a few steps using argparse in Python?
β How to perform high-level file operations using shutil module in Python?
β How to scrape a webpage’s content using BeautifulSoup in Python?
That’s all for now
Keep Codingββ