Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Evaluate Expressions Dynamically With Python eval()
Pythonβs eval() allows you to evaluate arbitrary Python expressions from a string-based or compiled-code-based input. This function can be handy when youβre trying to dynamically evaluate Python expressions from any input that comes as a string or a compiled code object.
Although Pythonβs eval() is an incredibly useful tool, the function has some important security implications that you should consider before using it. In this tutorial, youβll learn how eval() works and how to use it safely and effectively in your Python programs.
In this tutorial, youβll learn:
- How Pythonβs
eval()works - How to use
eval()to dynamically evaluate arbitrary string-based or compiled-code-based input - How
eval()can make your code insecure and how to minimize the associated security risks
Additionally, youβll learn how to use Pythonβs eval() to code an application that interactively evaluates math expressions. With this example, youβll apply everything youβve learned about eval() to a real-world problem. If you want to get the code for this application, then you can click on the box below:
Download the sample code: Click here to get the code youβll use to learn about Pythonβs eval() in this tutorial.
Understanding Pythonβs eval()
You can use the built-in Python eval() to dynamically evaluate expressions from a string-based or compiled-code-based input. If you pass in a string to eval(), then the function parses it, compiles it to bytecode, and evaluates it as a Python expression. But if you call eval() with a compiled code object, then the function performs just the evaluation step, which is quite convenient if you call eval() several times with the same input.
The signature of Pythonβs eval() is defined as follows:
eval(expression[, globals[, locals]])
The function takes a first argument, called expression, which holds the expression that you need to evaluate. eval() also takes two optional arguments:
globalslocals
In the next three sections, youβll learn what these arguments are and how eval() uses them to evaluate Python expressions on the fly.
Note: You can also use exec() to dynamically execute Python code. The main difference between eval() and exec() is that eval() can only execute or evaluate expressions, whereas exec() can execute any piece of Python code.
The First Argument: expression
The first argument to eval() is called expression. Itβs a required argument that holds the string-based or compiled-code-based input to the function. When you call eval(), the content of expression is evaluated as a Python expression. Check out the following examples that use string-based input:
>>> eval("2 ** 8")
256
>>> eval("1024 + 1024")
2048
>>> eval("sum([8, 16, 32])")
56
>>> x = 100
>>> eval("x * 2")
200
When you call eval() with a string as an argument, the function returns the value that results from evaluating the input string. By default, eval() has access to global names like x in the above example.
To evaluate a string-based expression, Pythonβs eval() runs the following steps:
- Parse
expression - Compile it to bytecode
- Evaluate it as a Python expression
- Return the result of the evaluation
The name expression for the first argument to eval() highlights that the function works only with expressions and not with compound statements. The Python documentation defines expression as follows:
expression
A piece of syntax which can be evaluated to some value. In other words, an expression is an accumulation of expression elements like literals, names, attribute access, operators or function calls which all return a value. In contrast to many other languages, not all language constructs are expressions. There are also statements which cannot be used as expressions, such as
while. Assignments are also statements, not expressions. (Source)
On the other hand, a Python statement has the following definition:
statement
A statement is part of a suite (a βblockβ of code). A statement is either an expression or one of several constructs with a keyword, such as
if,whileorfor. (Source)
If you try to pass a compound statement to eval(), then youβll get a SyntaxError. Take a look at the following example in which you try to execute an if statement using eval():
>>> x = 100
>>> eval("if x: print(x)")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
if x: print(x)
^
SyntaxError: invalid syntax
If you try to evaluate a compound statement using Pythonβs eval(), then youβll get a SyntaxError like in the above traceback. Thatβs because eval() only accepts expressions. Any other statement, such as if, for, while, import, def, or class, will raise an error.
Note: A for loop is a compound statement, but the for keyword can also be used in comprehensions, which are considered expressions. You can use eval() to evaluate comprehensions even though they use the for keyword.
Assignment operations arenβt allowed with eval() either:
>>> eval("pi = 3.1416")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
pi = 3.1416
^
SyntaxError: invalid syntax
If you try to pass an assignment operation as an argument to Pythonβs eval(), then youβll get a SyntaxError. Assignment operations are statements rather than expressions, and statements arenβt allowed with eval().
Youβll also get a SyntaxError any time the parser doesnβt understand the input expression. Take a look at the following example in which you try to evaluate an expression that violates Python syntax:
>>> # Incomplete expression
>>> eval("5 + 7 *")
Traceback (most recent call last):
File "<input>", line 1, in <module>
eval("5 + 7 *")
File "<string>", line 1
5 + 7 *
^
SyntaxError: unexpected EOF while parsing
You canβt pass an expression to eval() that violates Python syntax. In the above example, you try to evaluate an incomplete expression ("5 + 7 *") and get a SyntaxError because the parser doesnβt understand the syntax of the expression.
You can also pass compiled code objects to Pythonβs eval(). To compile the code that youβre going to pass to eval(), you can use compile(). This is a built-in function that can compile an input string into a code object or an AST object so that you can evaluate it with eval().
The details of how to use compile() are beyond the scope of this tutorial, but hereβs a quick look at its first three required arguments:
sourceholds the source code that you want to compile. This argument accepts normal strings, byte strings, and AST objects.filenamegives the file from which the code was read. If youβre going to use a string-based input, then the value for this argument should be"<string>".modespecifies which kind of compiled code you want to get. If you want to process the compiled code witheval(), then this argument should be set to"eval".
Note: For more information on compile(), check out the official documentation.
You can use compile() to supply code objects to eval() instead of normal strings. Check out the following examples:
>>> # Arithmetic operations
>>> code = compile("5 + 4", "<string>", "eval")
>>> eval(code)
9
>>> code = compile("(5 + 7) * 2", "<string>", "eval")
>>> eval(code)
24
>>> import math
>>> # Volume of a sphere
>>> code = compile("4 / 3 * math.pi * math.pow(25, 3)", "<string>", "eval")
>>> eval(code)
65449.84694978735
If you use compile() to compile the expressions that youβre going to pass to eval(), then eval() goes through the following steps:
- Evaluate the compiled code
- Return the result of the evaluation
If you call Pythonβs eval() using a compiled-code-based input, then the function performs the evaluation step and immediately returns the result. This can be handy when you need to evaluate the same expression multiple times. In this case, itβs best to precompile the expression and reuse the resulting bytecode on subsequent calls to eval().
If you compile the input expression beforehand, then successive calls to eval() will run faster because you wonβt be repeating the parsing and compiling steps. Unneeded repetitions can lead to high CPU times and excessive memory consumption if youβre evaluating complex expressions.
The Second Argument: globals
The second argument to eval() is called globals. Itβs optional and holds a dictionary that provides a global namespace to eval(). With globals, you can tell eval() which global names to use while evaluating expression.
Global names are all those names that are available in your current global scope or namespace. You can access them from anywhere in your code.
All the names passed to globals in a dictionary will be available to eval() at execution time. Check out the following example, which shows how to use a custom dictionary to supply a global namespace to eval():
>>> x = 100 # A global variable
>>> eval("x + 100", {"x": x})
200
>>> y = 200 # Another global variable
>>> eval("x + y", {"x": x})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'y' is not defined
If you supply a custom dictionary to the globals argument of eval(), then eval() will take only those names as globals. Any global names defined outside of this custom dictionary wonβt be accessible from inside eval(). Thatβs why Python raises a NameError when you try to access y in the above code: The dictionary passed to globals doesnβt include y.
You can insert names into globals by listing them in your dictionary, and then those names will be available during the evaluation process. For example, if you insert y into globals, then the evaluation of "x + y" in the above example will work as expected:
>>> eval("x + y", {"x": x, "y": y})
300
Since you add y to your custom globals dictionary, the evaluation of "x + y" is successful and you get the expected return value of 300.
You can also supply names that donβt exist in your current global scope. For this to work, you need to supply a concrete value for each name. eval() will interpret these names as global names when running:
>>> eval("x + y + z", {"x": x, "y": y, "z": 300})
600
>>> z
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined
Even though z isnβt defined in your current global scope, the variable is present in globals with a value of 300. In this case, eval() has access to z as though it were a global variable.
The mechanism behind globals is quite flexible. You can pass any visible variable (global, local, or nonlocal) to globals. You can also pass custom key-value pairs like "z": 300 in the above example. eval() will treat all of them as global variables.
An important point regarding globals is that if you supply a custom dictionary to it that doesnβt contain a value for the key "__builtins__", then a reference to the dictionary of builtins will be automatically inserted under "__builtins__" before expression gets parsed. This ensures that eval() will have full access to all of Pythonβs built-in names when evaluating expression.
The following examples show that even though you supply an empty dictionary to globals, the call to eval() will still have access to Pythonβs built-in names:
>>> eval("sum([2, 2, 2])", {})
6
>>> eval("min([1, 2, 3])", {})
1
>>> eval("pow(10, 2)", {})
100
In the above code, you supply an empty dictionary ({}) to globals. Since that dictionary doesnβt contain a key called "__builtins__", Python automatically inserts one with a reference to the names in builtins. This way, eval() has full access to all of Pythonβs built-in names when it parses expression.
If you call eval() without passing a custom dictionary to globals, then the argument will default to the dictionary returned by globals() in the environment where eval() is called:
>>> x = 100 # A global variable
>>> y = 200 # Another global variable
>>> eval("x + y") # Access both global variables
300
When you call eval() without supplying a globals argument, the function evaluates expression using the dictionary returned by globals() as its global namespace. So, in the above example, you can freely access x and y because theyβre global variables included in your current global scope.
The Third Argument: locals
Pythonβs eval() takes a third argument called locals. This is another optional argument that holds a dictionary. In this case, the dictionary contains the variables that eval() uses as local names when evaluating expression.
Local names are those names (variables, functions, classes, and so on) that you define inside a given function. Local names are visible only from inside the enclosing function. You define these kinds of names when youβre writing a function.
Since eval() is already written, you canβt add local names to its code or local scope. However, you can pass a dictionary to locals, and eval() will treat those names as local names:
>>> eval("x + 100", {}, {"x": 100})
200
>>> eval("x + y", {}, {"x": 100})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'y' is not defined
The second dictionary in the first call to eval() holds the variable x. This variable is interpreted by eval() as a local variable. In other words, itβs seen as a variable defined in the body of eval().
You can use x in expression, and eval() will have access to it. In contrast, if you try to use y, then youβll get a NameError because y isnβt defined in either the globals namespace or the locals namespace.
Like with globals, you can pass any visible variable (global, local, or nonlocal) to locals. You can also pass custom key-value pairs like "x": 100 in the above example. eval() will treat all of them as local variables.
Note that to supply a dictionary to locals, you first need to supply a dictionary to globals. Itβs not possible to use keyword arguments with eval():
>>> eval("x + 100", locals={"x": 100})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: eval() takes no keyword arguments
If you try to use keyword arguments when calling eval(), then youβll get a TypeError explaining that eval() takes no keyword arguments. So, you need to supply a globals dictionary before you can supply a locals dictionary.
If you donβt pass a dictionary to locals, then it defaults to the dictionary passed to globals. Hereβs an example in which you pass an empty dictionary to globals and nothing to locals:
>>> x = 100
>>> eval("x + 100", {})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'x' is not defined
Given that you donβt provide a custom dictionary to locals, the argument defaults to the dictionary passed to globals. In this case, eval() doesnβt have access to x because globals holds an empty dictionary.
The main practical difference between globals and locals is that Python will automatically insert a "__builtins__" key into globals if that key doesnβt already exist. This happens whether or not you supply a custom dictionary to globals. On the other hand, if you supply a custom dictionary to locals, then that dictionary will remain unchanged during the execution of eval().
Evaluating Expressions With Pythonβs eval()
You can use Pythonβs eval() to evaluate any kind of Python expression but not Python statements such as keyword-based compound statements or assignment statements.
eval() can be handy when you need to dynamically evaluate expressions and using other Python techniques or tools would considerably increase your development time and effort. In this section, youβll learn how you can use Pythonβs eval() to evaluate Boolean, math, and general-purpose Python expressions.
Boolean Expressions
Boolean expressions are Python expressions that return a truth value (True or False) when the interpreter evaluates them. Theyβre commonly used in if statements to check if some condition is true or false. Since Boolean expressions arenβt compound statements, you can use eval() to evaluate them:
>>> x = 100
>>> y = 100
>>> eval("x != y")
False
>>> eval("x < 200 and y > 100")
False
>>> eval("x is y")
True
>>> eval("x in {50, 100, 150, 200}")
True
You can use eval() with Boolean expressions that use any of the following Python operators:
- Value comparison operators:
<,>,<=,>=,==,!= - Logical (Boolean) operators:
and,or,not - Membership test operators:
in,not in - Identity operators:
is,is not
In all cases, the function returns the truth value of the expression that youβre evaluating.
Now, you may be thinking, why should I use eval() instead of using the Boolean expression directly? Well, suppose you need to implement a conditional statement, but you want to change the condition on the fly:
>>> def func(a, b, condition):
... if eval(condition):
... return a + b
... return a - b
...
>>> func(2, 4, "a > b")
-2
>>> func(2, 4, "a < b")
6
>>> func(2, 2, "a is b")
4
Inside func(), you use eval() to evaluate the supplied condition and return either a + b or a - b according to the result of the evaluation. You use just a few different conditions in the above example, but you could use any number of others provided that you stick with the names a and b that you defined in func().
Now imagine how you would implement something like this without the use of Pythonβs eval(). Would that take less code and time? No way!
Math Expressions
One common use case of Pythonβs eval() is to evaluate math expressions from a string-based input. For example, if you want to create a Python calculator, then you can use eval() to evaluate the userβs input and return the result of the calculations.
The following examples show how you can use eval() along with math to perform math operations:
>>> # Arithmetic operations
>>> eval("5 + 7")
12
>>> eval("5 * 7")
35
>>> eval("5 ** 7")
78125
>>> eval("(5 + 7) / 2")
6.0
>>> import math
>>> # Area of a circle
>>> eval("math.pi * pow(25, 2)")
1963.4954084936207
>>> # Volume of a sphere
>>> eval("4 / 3 * math.pi * math.pow(25, 3)")
65449.84694978735
>>> # Hypotenuse of a right triangle
>>> eval("math.sqrt(math.pow(10, 2) + math.pow(15, 2))")
18.027756377319946
When you use eval() to evaluate math expressions, you can pass in expressions of any kind or complexity. eval() will parse them, evaluate them and, if everything is okay, give you the expected result.
General-Purpose Expressions
So far, youβve learned how to use eval() with Boolean and math expressions. However, you can use eval() with more complex Python expressions that incorporate function calls, object creation, attribute access, comprehensions, and so on.
For example, you can call a built-in function or one that youβve imported with a standard or third-party module:
>>> # Run the echo command
>>> import subprocess
>>> eval("subprocess.getoutput('echo Hello, World')")
'Hello, World'
>>> # Launch Firefox (if available)
>>> eval("subprocess.getoutput('firefox')")
''
In this example, you use Pythonβs eval() to execute a few system commands. As you can imagine, you can do a ton of useful things with this feature. However, eval() can also expose you to serious security risks, like allowing a malicious user to run system commands or any arbitrary piece of code in your machine.
In the next section, youβll look at ways to address some of the security risks associated with eval().
Minimizing the Security Issues of eval()
Although it has an almost unlimited number of uses, Pythonβs eval() also has important security implications. eval() is considered insecure because it allows you (or your users) to dynamically execute arbitrary Python code.
This is considered bad programming practice because the code that youβre reading (or writing) is not the code that youβll execute. If youβre planning to use eval() to evaluate input from a user or any other external source, then you wonβt know for sure what code is going to be executed. Thatβs a serious security risk if your application runs in the wrong hands.
For this reason, good programming practices generally recommend against using eval(). But if you choose to use the function anyway, then the rule of thumb is to never ever use it with untrusted input. The tricky part of this rule is figuring out which kinds of input you can trust.
As an example of how using eval() irresponsibly can make your code insecure, suppose you want to build an online service for evaluating arbitrary Python expressions. Your user will introduce expressions and then click the Run button. The application will get the userβs input and pass it to eval() for evaluation.
This application will run on your personal server. Yes, the same server where you have all those valuable files. If youβre running a Linux box and the applicationβs process has the right permissions, then a malicious user could introduce a dangerous string like the following:
"__import__('subprocess').getoutput('rm βrf *')"
The above code would delete all the files in the applicationβs current directory. That would be awful, wouldnβt it?
Note: __import__() is a built-in function that takes a module name as a string and returns a reference to the module object. __import__() is a function, which is totally different from an import statement. You canβt evaluate an import statement using eval().
When the input is untrusted, thereβs no completely effective way to avoid the security risks associated with eval(). However, you can minimize your risk by restricting the execution environment of eval(). Youβll learn a few techniques for doing so in the following sections.
Restricting globals and locals
You can restrict the execution environment of eval() by passing custom dictionaries to the globals and locals arguments. For example, you can pass empty dictionaries to both arguments to prevent eval() from accessing names in the callerβs current scope or namespace:
>>> # Avoid access to names in the caller's current scope
>>> x = 100
>>> eval("x * 5", {}, {})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'x' is not defined
If you pass empty dictionaries ({}) to globals and locals, then eval() wonβt find the name x in either its global namespace or its local namespace when evaluating the string "x * 5". As a result, eval() will throw a NameError.
Unfortunately, restricting the globals and locals arguments like this doesnβt eliminate all the security risks associated with the use of Pythonβs eval(), because you can still access all of Pythonβs built-in names.
Restricting the Use of Built-In Names
As you saw earlier, Pythonβs eval() automatically inserts a reference to the dictionary of builtins into globals before parsing expression. A malicious user could exploit this behavior by using the built-in function __import__() to get access to the standard library and any third-party module that youβve installed on your system.
The following examples show that you can use any built-in function and any standard module like math or subprocess even after youβve restricted globals and locals:
>>> eval("sum([5, 5, 5])", {}, {})
15
>>> eval("__import__('math').sqrt(25)", {}, {})
5.0
>>> eval("__import__('subprocess').getoutput('echo Hello, World')", {}, {})
'Hello, World'
Even though you restrict globals and locals using empty dictionaries, you can still use any built-in function like you did with sum() and __import__() in the above code.
You can use __import__() to import any standard or third-party module just like you did above with math and subprocess. With this technique, you can access any function or class defined in math, subprocess, or any other module. Now imagine what a malicious user could do to your system using subprocess or any other powerful module in the standard library.
To minimize this risk, you can restrict access to Pythonβs built-in functions by overriding the "__builtins__" key in globals. Good practice recommends using a custom dictionary containing the key-value pair "__builtins__": {}. Check out the following example:
>>> eval("__import__('math').sqrt(25)", {"__builtins__": {}}, {})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name '__import__' is not defined
If you pass a dictionary containing the key-value pair "__builtins__": {} to globals, then eval() wonβt have direct access to Pythonβs built-in functions like __import__(). However, as youβll see in the next section, this approach still doesnβt make eval() completely secure.
Restricting Names in the Input
Even though you can restrict the execution environment of Pythonβs eval() using custom globals and locals dictionaries, the function will still be vulnerable to a few fancy tricks. For example, you can access the class object using a type literal like "", [], {}, or () along with some special attributes:
>>> "".__class__.__base__
<class 'object'>
>>> [].__class__.__base__
<class 'object'>
>>> {}.__class__.__base__
<class 'object'>
>>> ().__class__.__base__
<class 'object'>
Once you have access to object, you can use a special method, .__subclasses__(), to get access to all of the classes that inherit from object. Hereβs how it works:
>>> for sub_class in ().__class__.__base__.__subclasses__():
... print(sub_class.__name__)
...
type
weakref
weakcallableproxy
weakproxy
int
...
This code will print a large list of classes to your screen. Some of these classes are quite powerful and can be extremely dangerous in the wrong hands. This opens up another important security hole that you canβt close by simply restricting the execution environment of eval():
>>> input_string = """[
... c for c in ().__class__.__base__.__subclasses__()
... if c.__name__ == "range"
... ][0](10)"""
>>> list(eval(input_string, {"__builtins__": {}}, {}))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
The list comprehension in the above code filters the classes that inherit from object to return a list containing the class range. The first index ([0]) returns the class range. Once you have access to range, you call it to generate a range object. Then you call list() on the range object to generate a list of ten integers.
In this example, you use range to illustrate a security hole in eval(). Now imagine what a malicious user could do if your system exposed classes like subprocess.Popen.
Note: For a deeper dive into the vulnerabilities of eval(), check out Ned Batchelderβs
article, Eval really is dangerous.
A possible solution to this vulnerability is to restrict the use of names in the input, either to a bunch of safe names or to no names at all. To implement this technique, you need to go through the following steps:
- Create a dictionary containing the names that you want to use with
eval(). - Compile the input string to bytecode using
compile()in mode"eval". - Check
.co_nameson the bytecode object to make sure it contains only allowed names. - Raise a
NameErrorif the user tries to enter a name thatβs not allowed.
Take a look at the following function in which you implement all these steps:
>>> def eval_expression(input_string):
... # Step 1
... allowed_names = {"sum": sum}
... # Step 2
... code = compile(input_string, "<string>", "eval")
... # Step 3
... for name in code.co_names:
... if name not in allowed_names:
... # Step 4
... raise NameError(f"Use of {name} not allowed")
... return eval(code, {"__builtins__": {}}, allowed_names)
In eval_expression(), you implement all of the steps you saw before. This function restricts the names that you can use with eval() to only those names in the dictionary allowed_names. To do this, the function uses .co_names, which is an attribute of a code object that returns a tuple containing the names in the code object.
The following examples show how eval_expression() works in practice:
>>> eval_expression("3 + 4 * 5 + 25 / 2")
35.5
>>> eval_expression("sum([1, 2, 3])")
6
>>> eval_expression("len([1, 2, 3])")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 10, in eval_expression
NameError: Use of len not allowed
>>> eval_expression("pow(10, 2)")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 10, in eval_expression
NameError: Use of pow not allowed
If you call eval_expression() to evaluate arithmetic operations, or if you use expressions that include allowed names, then youβll get the expected result. Otherwise, youβll get a NameError. In the above examples, the only name youβve allowed is sum(). Other names like len() and pow() are not allowed, so the function raises a NameError when you try to use them.
If you want to completely disallow the use of names, then you can rewrite eval_expression() as follows:
>>> def eval_expression(input_string):
... code = compile(input_string, "<string>", "eval")
... if code.co_names:
... raise NameError(f"Use of names not allowed")
... return eval(code, {"__builtins__": {}}, {})
...
>>> eval_expression("3 + 4 * 5 + 25 / 2")
35.5
>>> eval_expression("sum([1, 2, 3])")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in eval_expression
NameError: Use of names not allowed
Now your function doesnβt allow any names in the input string. To accomplish this, you check for names in .co_names and raise a NameError if one is found. Otherwise, you evaluate input_string and return the result of the evaluation. In this case, you use an empty dictionary to restrict locals as well.
You can use this technique to minimize the security issues of eval() and strengthen your armor against malicious attacks.
Restricting the Input to Only Literals
A common use case for Pythonβs eval() is to evaluate strings that contain standard Python literals and turn them into concrete objects.
The standard library provides a function called literal_eval() that can help achieve this goal. The function doesnβt support operators, but it does support lists, tuples, numbers, strings, and so on:
>>> from ast import literal_eval
>>> # Evaluating literals
>>> literal_eval("15.02")
15.02
>>> literal_eval("[1, 15]")
[1, 15]
>>> literal_eval("(1, 15)")
(1, 15)
>>> literal_eval("{'one': 1, 'two': 2}")
{'one': 1, 'two': 2}
>>> # Trying to evaluate an expression
>>> literal_eval("sum([1, 15]) + 5 + 8 * 2")
Traceback (most recent call last):
...
ValueError: malformed node or string: <_ast.BinOp object at 0x7faedecd7668>
Notice that literal_eval() only works with standard type literals. It doesnβt support the use of operators or names. If you try to feed an expression to literal_eval(), then youβll get a ValueError. This function can also help you minimize the security risks associated with the use of Pythonβs eval().
Using Pythonβs eval() With input()
In Python 3.x, the built-in input() reads the user input at the command line, converts it to a string, strips the trailing newline, and returns the result to the caller. Since the result of input() is a string, you can feed it to eval() and evaluate it as a Python expression:
>>> eval(input("Enter a math expression: "))
Enter a math expression: 15 * 2
30
>>> eval(input("Enter a math expression: "))
Enter a math expression: 5 + 8
13
You can wrap Pythonβs eval() around input() to automatically evaluate the userβs input. This is a common use case for eval() because it emulates the behavior of input() in Python 2.x, in which input() evaluates the userβs input as a Python expression and returns the result.
This behavior of input() in Python 2.x was changed in Python 3.x because of its security implications.
Building a Math Expressions Evaluator
So far, youβve learned how Pythonβs eval() works and how to use it in practice. Youβve also learned that eval() has important security implications and that itβs generally considered good practice to avoid the use of eval() in your code. However, there are some situations in which Pythonβs eval() can save you a lot of time and effort.
In this section, youβre going to code an application to evaluate math expressions on the fly. If you wanted to solve this problem without using eval(), then youβd need to go through the following steps:
- Parse the input expression.
- Change the expressionβs components into Python objects (numbers, operators, functions, and so on).
- Combine everything into an expression.
- Confirm that the expression is valid in Python.
- Evaluate the final expression and return the result.
That would be a lot of work considering the wide variety of possible expressions that Python can process and evaluate. Fortunately, you can use eval() to solve this problem, and youβve already learned several techniques to reduce the associated security risks.
You can get the source code for the application that youβre going to build in this section by clicking on the box below:
Download the sample code: Click here to get the code youβll use to learn about Pythonβs eval() in this tutorial.
First, fire up your favorite code editor. Create a new Python script called mathrepl.py, and then add the following code:
1import math
2
3__version__ = "1.0"
4
5ALLOWED_NAMES = {
6 k: v for k, v in math.__dict__.items() if not k.startswith("__")
7}
8
9PS1 = "mr>>"
10
11WELCOME = f"""
12MathREPL {__version__}, your Python math expressions evaluator!
13Enter a valid math expression after the prompt "{PS1}".
14Type "help" for more information.
15Type "quit" or "exit" to exit.
16"""
17
18USAGE = f"""
19Usage:
20Build math expressions using numeric values and operators.
21Use any of the following functions and constants:
22
23{', '.join(ALLOWED_NAMES.keys())}
24"""
In this piece of code, you first import Pythonβs math module. This module will allow you to perform math operations using predefined functions and constants. The constant ALLOWED_NAMES holds a dictionary containing the non-special names in math. This way, youβll be able to use them with eval().
You also define three more string constants. Youβll use them as the user interface to your script and youβll print them to the screen as needed.
Now youβre ready to code the core functionality of your application. In this case, you want to code a function that receives math expressions as input and returns their result. To do this, you write a function called evaluate():
26def evaluate(expression):
27 """Evaluate a math expression."""
28 # Compile the expression
29 code = compile(expression, "<string>", "eval")
30
31 # Validate allowed names
32 for name in code.co_names:
33 if name not in ALLOWED_NAMES:
34 raise NameError(f"The use of '{name}' is not allowed")
35
36 return eval(code, {"__builtins__": {}}, ALLOWED_NAMES)
Hereβs how the function works:
-
In line
26, you defineevaluate(). This function takes the stringexpressionas an argument and returns a float that represents the result of evaluating the string as a math expression. -
In line
29, you usecompile()to turn the input stringexpressioninto compiled Python code. The compiling operation will raise aSyntaxErrorif the user enters an invalid expression. -
In line
32, you start aforloop to inspect the names contained inexpressionand confirm that they can be used in the final expression. If the user provides a name that is not in the list of allowed names, then you raise aNameError. -
In line
36, you perform the actual evaluation of the math expression. Notice that you pass custom dictionaries toglobalsandlocalsas good practice recommends.ALLOWED_NAMESholds the functions and constants defined inmath.
Note: Since this application uses the functions defined in math, you need to consider that some of these functions will raise a ValueError when you call them with an invalid input value.
For example, math.sqrt(-10) would raise an error because the square root of -10 is undefined. Later on, youβll see how to catch this error in your client code.
The use of custom values for the globals and locals parameters, along with the check of names in line 33, allows you to minimize the security risks associated with the use of eval().
Your math expression evaluator will be finished when you write its client code in main(). In this function, youβll define the programβs main loop and close the cycle of reading and evaluating the expressions that your user enters in the command line.
For this example, the application will:
- Print a welcome message to the user
- Show a prompt ready to read the userβs input
- Provide options to get usage instructions and to terminate the application
- Read the userβs math expression
- Evaluate the userβs math expression
- Print the result of the evaluation to the screen
Check out the following implementation of main():
38def main():
39 """Main loop: Read and evaluate user's input."""
40 print(WELCOME)
41 while True:
42 # Read user's input
43 try:
44 expression = input(f"{PS1} ")
45 except (KeyboardInterrupt, EOFError):
46 raise SystemExit()
47
48 # Handle special commands
49 if expression.lower() == "help":
50 print(USAGE)
51 continue
52 if expression.lower() in {"quit", "exit"}:
53 raise SystemExit()
54
55 # Evaluate the expression and handle errors
56 try:
57 result = evaluate(expression)
58 except SyntaxError:
59 # If the user enters an invalid expression
60 print("Invalid input expression syntax")
61 continue
62 except (NameError, ValueError) as err:
63 # If the user tries to use a name that isn't allowed
64 # or an invalid value for a given math function
65 print(err)
66 continue
67
68 # Print the result if no error occurs
69 print(f"The result is: {result}")
70
71if __name__ == "__main__":
72 main()
Inside main(), you first print the WELCOME message. Then you read the userβs input in a try statement to catch KeyboardInterrupt and EOFError. If either of these exceptions occur, then you terminate the application.
If the user enters the help option, then the application shows your USAGE guide. Likewise, if the user enters quit or exit, then the application terminates.
Finally, you use evaluate() to evaluate the userβs math expression, and then you print the result to the screen. Itβs important to note that a call to evaluate() can raise the following exceptions:
SyntaxError: This happens when the user enters an expression that doesnβt follow Python syntax.NameError: This happens when the user tries to use a name (function, class, or attribute) that isnβt allowed.ValueError: This happens when the user tries to use a value that isnβt allowed as an input to a given function inmath.
Notice that in main(), you catch all of these exceptions and print messages to the user accordingly. This will allow the user to review the expression, fix the problem, and run the program again.
Thatβs it! Youβve built a math expression evaluator in about seventy lines of code using Pythonβs eval(). To run the application, open your systemβs command line and type the following command:
$ python3 mathrepl.py
This command will launch the math expression evaluatorβs command-line interface (CLI). Youβll see something like this on your screen:
MathREPL 1.0, your Python math expressions evaluator!
Enter a valid math expression after the prompt "mr>>".
Type "help" for more information.
Type "quit" or "exit" to exit.
mr>>
Once youβre there, you can enter and evaluate any math expression. For example, type the following expressions:
mr>> 25 * 2
The result is: 50
mr>> sqrt(25)
The result is: 5.0
mr>> pi
The result is: 3.141592653589793
If you enter a valid math expression, then the application evaluates it and prints the result to your screen. If there are any problems with your expressions, then the application will tell you:
mr>> 5 * (25 + 4
Invalid input expression syntax
mr>> sum([1, 2, 3, 4, 5])
The use of 'sum' is not allowed
mr>> sqrt(-15)
math domain error
mr>> factorial(-15)
factorial() not defined for negative values
In the first example, you miss the closing parentheses, so you get a message telling you that the syntax is incorrect. Then you call sum(), which isnβt allowed, and you get an explanatory error message. Finally, you call a math function with an invalid input value, and the application generates a message identifying the problem in your input.
There you have itβyour math expressions evaluator is ready! Feel free to add some extra features. A few ideas to get you started include enlarging the dictionary of allowed names and adding more elaborate warning messages. Give it a shot and let us know in the comments how it goes.
Conclusion
You can use Pythonβs eval() to evaluate Python expressions from a string-based or code-based input. This built-in function can be useful when youβre trying to evaluate Python expressions on the fly and you want to avoid the hassle of creating your own expressions evaluator from scratch.
In this tutorial, youβve learned how eval() works and how to use it safely and effectively to evaluate arbitrary Python expressions.
Youβre now able to:
- Use Pythonβs
eval()to dynamically evaluate basic Python expressions - Run more complex statements like function calls, object creation, and attribute access using
eval() - Minimize the security risks associated with the use of Pythonβs
eval()
Additionally, youβve coded an application that uses eval() to interactively evaluate math expressions using a command-line interface. You can download the applicationβs code by clicking on the link below:
Download the sample code: Click here to get the code youβll use to learn about Pythonβs eval() in this tutorial.
Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Evaluate Expressions Dynamically With Python eval()



