341

In the book that I am reading on Python, it keeps using the code eval(input('blah'))

I read the documentation, and I understand it, but I still do not see how it changes the input() function.

What does it do? Can someone explain?

BYS2
  • 5,199
  • 5
  • 24
  • 32
Billjk
  • 10,387
  • 23
  • 54
  • 73
  • 4
    Eval function try to execute and interpret the string(argument) passed to it as python code. x=1 print (eval('x+1')) Output of the above code will be 2. The disadvantage of such approach is that ,user get independence of writing code which can result in havoc conditions.Though you can restrict users from accessing many variables and methods by passing global and local parameter in eval function. – ATIF IBAD KHAN Apr 22 '17 at 23:15
  • 1
    The code in this book exists because the author mechanically translated **dangerous** (but easy to write) Python 2.x code into **dangerous** (exactly as dangerous, in exactly the same way) Python 3.x code. The 2.x code was used because it's a convenient way to, e.g. input an integer without having to parse a string. This was, however, a **serious design flaw** in the language. Properly written code will parse the string, because user input **can never be trusted**. I hope we can find out what book this was, so that **everyone can be warned not to use it**. – Karl Knechtel May 30 '22 at 00:19

12 Answers12

308

The eval function lets a Python program run Python code within itself.

eval example (interactive shell):

>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1
Xantium
  • 11,201
  • 10
  • 62
  • 89
BYS2
  • 5,199
  • 5
  • 24
  • 32
  • 28
    haha, that was a trivial example, but you could let the user type in an arbitrary command and have python execute it. So you could have the user type in a command string and then have python run it as code. So for example: eval("__import__('os').remove('file')"). – BYS2 Feb 21 '12 at 19:24
  • 66
    It will seem useless until you find a need for it. It is used on sites like codepad.org to allow you to execute scripts in a test environment. `eval()` can also be used to execute highly-dynamic code, but you should make yourself fully aware of the security and performance risks before using it. – George Cummins Feb 21 '12 at 19:25
  • 8
    @GeorgeCummins, codepad.org does not use `eval`, nor could it do what it does with `eval`. – Mike Graham Feb 21 '12 at 20:01
  • @Mike Graham: Will you elaborate? – George Cummins Feb 21 '12 at 20:10
  • 17
    @GeorgeCummins: codepag.org runs everything in a sandbox: a chroot jail with ptrace checks in a virtual machine to prevent malicious code from doing anything bad. Far more complicated than a simple eval. Also, eval is Python-specific. codepad supports a bunch of languages. – FogleBird Feb 21 '12 at 20:16
  • 4
    @GeorgeCummins, codepad runs a very complex system to safely run arbitrary programs. `eval`, other than being insecure, cannot run whole programs like codepad does because it can only evaluate a single expression. – Mike Graham Feb 21 '12 at 20:23
  • This is very interesting. It's like "preprocessing" code, so you can be even more dynamic, like @GeorgeCummins notes. It's astonishing how flexible Python is. Nonetheless, I wouldn't use this to "preprocess" my Python code except in very explicit situations, since that'd mean otherwise that I don't know how to properly design a program structure in Python. –  Apr 03 '15 at 13:48
  • @GeorgeCummins What are the security risks associated with using eval? I am just using it to convert a tuple in string format to just a regular tuple. (Eg: eval('(1,2)') gives me (1,2). – vineeshvs Dec 14 '18 at 07:23
  • 1
    @vineeshvs I think it may be insecure when you use it for running user input, which could be anything – Stanowczo Mar 12 '19 at 08:08
  • This is inaccurate. `eval` only accepts a Python **expression**, not any code. For example, it cannot handle `eval("if True: print('test')")`; but in 3.x it **can** (since `print` is a function, and a call to a function is an expression) handle `eval("print('test') if True else None")`. To accept statements, `exec` is needed. – Karl Knechtel Jul 02 '22 at 03:34
  • @FogleBird Interesting. Is it really affordable for free websites like codepad to run VM's for each execution? – stickynotememo Jun 26 '23 at 08:25
179

eval() interprets a string as code. The reason why so many people have warned you about using this is because a user can use this as an option to run code on the computer. If you have eval(input()) and os imported, a person could type into input() os.system('rm -R *') which would delete all your files in your home directory. (Assuming you have a unix system). Using eval() is a security hole. If you need to convert strings to other formats, try to use things that do that, like int().

CoffeeRain
  • 4,460
  • 4
  • 31
  • 50
  • 19
    You mean using `eval` with `input()` is a security hole. Don't put `input()` inside an eval statement and you'll be fine. – tim-phillips Apr 02 '15 at 09:45
  • 28
    @Rohmer, unsafe data can come from everywhere: web requests, form input fields, file reads, ... not just from the console input. Even if you write the files yourself, it can still contain input that originally came from an untrusted source. So `eval` is a security issue in many cases. – sanderd17 Jul 26 '17 at 14:07
  • 4
    since `input` usually takes its data from the console the user could just exit the program and type `rm -R *` anyway... – c z Nov 09 '18 at 16:17
  • 1
    @cz consider the scenario that eval is used from a request's data. this would allow the user to execute arbitrary code on the server. this is why eval is dangerous. – kahveciderin Mar 19 '21 at 13:22
  • Got it on the negative side of using eval(). Is there some totally safe use in code? I would like to see the positive side as well. I use eval() in one instance in my code and I am very glad this function exists. – Robin Feb 17 '22 at 03:39
72

As described in the documentation, eval() also has globals and locals keyword arguments which can be used to limit the functions that are available through the eval function. For example, if you load up a fresh Python interpreter the locals() and globals() will be the same and look something like this:

>>> globals()
{'__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__doc__': None,
 '__spec__': None, '__builtins__': <module 'builtins' (built-in)>,
 '__package__': None, '__name__': '__main__'}

There are certainly functions within the builtins module that can do significant damage to a system. But it is possible to block anything and everything we don't want available. Say we want to construct a list to represent a domain of the available cores on a system. For me I have 8 cores, so I would want a list [1, 8].

>>> from os import cpu_count
>>> eval('[1, cpu_count()]')
[1, 8]

Likewise all of __builtins__ is available.

>>> eval('abs(-1)')
1

Let's try blocking access to any globals:

>>> eval('[1, cpu_count()]', {'__builtins__':None}, {})
TypeError: 'NoneType' object is not subscriptable

We have effectively blocked all of the __builtins__ functions and as such brought a level of protection into our system. At this point we can start to add back in functions that we do want exposed.

>>>from os import cpu_count
>>>exposed_methods = {'cpu_count': cpu_count}
>>>eval('cpu_count()', {'__builtins__':None}, exposed_methods)
8
>>>eval('abs(cpu_count())', {'__builtins__':None}, exposed_methods)
TypeError: 'NoneType' object is not subscriptable

Now we have the cpu_count function available while still blocking everything we do not want. In my opinion, this is super powerful and clearly from the scope of the other answers, not a common implementation. There are numerous uses for something like this and as long as it is handled correctly, I personally feel eval can be safely used to great value.

N.B.

Something else that is cool about these kwargs is that you can start to use shorthand for your code. Let's say you use eval as part of a pipeline to execute some imported text. The text doesn't need to have exact code, it can follow some template file format, and still execute anything you'd like. For example:

>>> from os import cpu_count
>>> eval('[1,cores]', {'__builtins__': None}, {'cores': cpu_count()})
[1, 8]
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Grr
  • 15,553
  • 7
  • 65
  • 85
  • 2
    This does not work as a jail. You can get builtins and still run dangerous code with something like this (runs `rm -rf *`): `eval("[a for a in ().__class__.__bases__[0].__subclasses__() if 'catch_warnings' in a.__name__][0]()._module.__builtins__['__import__']('os').system('rm -rf *')", {"__builtins__": None})`. – muddyfish Mar 30 '22 at 22:21
  • 1
    It is absolutely not safe to try to "sandbox" or "jail" `eval`'d code this way. – Karl Knechtel Jul 02 '22 at 03:40
29

In Python 2.x input(...) is equivalent to eval(raw_input(...)), in Python 3.x raw_input was renamed input, which I suspect lead to your confusion (you were probably looking at the documentation for input in Python 2.x). Additionally, eval(input(...)) would work fine in Python 3.x, but would raise a TypeError in Python 2.

In this case eval is used to coerce the string returned from input into an expression and interpreted. Generally this is considered bad practice.

Mike Graham
  • 73,987
  • 14
  • 101
  • 130
Zach Kelling
  • 52,505
  • 13
  • 109
  • 108
7

eval(), as the name suggests, evaluates the passed argument.

raw_input() is now input() in Python 3.x versions. So the most commonly found example for the use of eval() is its use to provide the functionality that input() provided in 2.x version of Python.

raw_input returned the user-entered data as a string, while input evaluated the value of data entered and returned it.

eval(input("bla bla")) thus replicates the functionality of input() in 2.x, i.e., of evaluating the user-entered data.

In short: eval() evaluates the arguments passed to it and hence eval('1 + 1') returned 2.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Rubal
  • 986
  • 11
  • 12
6

One of the useful applications of eval() is to evaluate Python expressions from a string. For example, load the string representation of a dictionary from a file:

running_params = {"Greeting": "Hello, "}
fout = open("params.dat", 'w')
fout.write(repr(running_params))
fout.close()

Read it out as a variable and edit it:

fin = open("params.dat", 'r')
diction = eval(fin.read())
diction["Greeting"] += "World!"
fin.close()
print diction

Output:

{'Greeting': 'Hello, World!'}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nikolay Frick
  • 2,145
  • 22
  • 17
6

Another option if you want to limit the evaluation string to simple literals is to use ast.literal_eval(). Some examples:

import ast

# print(ast.literal_eval(''))          # SyntaxError: unexpected EOF while parsing
# print(ast.literal_eval('a'))         # ValueError: malformed node or string
# print(ast.literal_eval('import os')) # SyntaxError: invalid syntax
# print(ast.literal_eval('1+1'))       # 2: but only works due to a quirk in parser
# print(ast.literal_eval('1*1'))       # ValueError: malformed node or string
print(ast.literal_eval("{'a':1}"))     # {'a':1}

From the docs:

Safely evaluate an expression node or a string containing a Python literal or container display. The string or node provided may only consist of the following Python literal structures: strings, bytes, numbers, tuples, lists, dicts, sets, booleans, and None.

This can be used for safely evaluating strings containing Python values from untrusted sources without the need to parse the values oneself. It is not capable of evaluating arbitrarily complex expressions, for example involving operators or indexing.

As for why it's so limited, from the mailing list:

Allowing operator expressions with literals is possible, but much more complex than the current implementation. A simple implementation is not safe: you can induce basically unbounded CPU and memory usage with no effort (try "9**9**9" or "[None] * 9**9").

As for the usefulness, this function is useful to "read back" literal values and containers as stringified by repr(). This can for example be used for serialization in a format that is similar to but more powerful than JSON.

Community
  • 1
  • 1
Brian Burns
  • 20,575
  • 8
  • 83
  • 77
  • 1
    `ast.literal_eval` does not support operators, contrary to your `'1+1'` example. Nonetheless it does support lists, numbers, strings etc, and so is a good alternative for common `eval` use-cases. – benjimin Apr 11 '19 at 23:06
  • @benjimin oh you're right - it's just a quirk that it accepts 1+1! https://stackoverflow.com/questions/40584417/why-does-ast-literal-eval5-7-fail – Brian Burns Apr 12 '19 at 05:10
  • 1
    The short version: it supports `+` and `-` because they're needed to support the syntax for complex numbers, and it's too hard (or at least, considerable extra work) to special-case the logic for that. – Karl Knechtel Jul 02 '22 at 03:44
6

Maybe a misleading example of reading a line and interpreting it.

Try eval(input()) and type "1+1" - this should print 2. Eval evaluates expressions.

Greenonline
  • 1,330
  • 8
  • 23
  • 31
hburde
  • 1,441
  • 11
  • 6
  • 1
    Why should I type it between quotes? Input is getting a string, and passing it to eval, not executing the code, so I'd should be fine if I simply typed 1+1... ¿? –  Apr 03 '15 at 14:00
  • The thing is that you are mixing P2.x and 3.x. In Python 2 your code works, but it does not make sense to eval twice. In python 3 it does not, and returns a string. –  Apr 03 '15 at 14:02
6

eval() evaluates the passed string as a Python expression and returns the result. For example, eval("1 + 1") interprets and executes the expression "1 + 1" and returns the result (2).

One reason you might be confused is because the code you cited involves a level of indirection. The inner function call (input) gets executed first so the user sees the "blah" prompt. Let's imagine they respond with "1 + 1" (quotes added for clarity, don't type them when running your program), the input function returns that string, which is then passed to the outer function (eval) which interprets the string and returns the result (2).

Read more about eval here.

Xantium
  • 11,201
  • 10
  • 62
  • 89
Marc Cohen
  • 3,742
  • 2
  • 19
  • 19
5

If a user enters a numeric value, input() will return a string.

>>> input('Enter a number: ')
Enter a number: 3
>>> '3'
>>> input('Enter a number: ')
Enter a number: 1+1
'1+1'

eval() will evaluate returned value (or expression) which is a string and return integer/float.

>>> eval(input('Enter a number: '))
Enter a number: 1+1
2
>>>
>>> eval(input('Enter a number: '))
Enter a number: 3.14
3.14

However, it would be better to use more specific tools here, such as int() or float() .

>>> float(input('Enter a number: '))
Enter a number: 3.14
3.14
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Calvin Kim
  • 359
  • 2
  • 6
0

The eval() function takes three parameters evaluates and returns value.
syntax: eval(expression, globals, locals)
expression #string of python3 expression
globals (optional) #dictionary
locals (optional) #dictionary
#common use case u use frequently is
x="{'name':'abhi','mydict':{'sub':'python'}}"
y=dict(x)
print(y,type(y)) # ValueError: dictionary update sequence element #0 has length 1; 2 is required
z=eval(x)
print(z,type(z)) #{'name': 'abhi', 'mydict': {'sub': 'python'}} <class 'dict'>

0

Note the difference of eval() and exec():

>>> exec("x=2")
>>> x
2
>>> eval("x=1")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    x=1
     ^
DomTomCat
  • 8,189
  • 1
  • 49
  • 64