Practical Usage
00:00
In the previous lesson, I showed you the security risks of eval()
. In this lesson, I’m going to show you how to use eval()
in a useful application.
00:10
Unless you’ve skipped straight to this lesson, you’ve probably already gotten tired of me warning you not to use eval()
on untrusted input. Well, when is it trusted and what does that mean?
00:20 Most command line applications can be considered somewhat trusted. What I mean by this is people are running this on their own machines and anything they do they could probably do without your program, so the consequences aren’t severe.
00:35 You should still be careful with this. Computing history is filled with just this kind of logic failing eventually. There are word processors out there that have macrolanguages in them that were considered helpful at one point in time but now are used as exploits by people sending those files to each other.
00:52 So once again, caveat emptor. It doesn’t hurt to still put some restrictions on the builtins to make sure that they don’t accidentally do something that you wouldn’t try to accomplish with the kind of program being run.
01:05
The sample tool that I’m going to show you builds on top of some of the math expressions that I’ve been evaluating up until now and creates, essentially, a fancy calculator. Trying to create this program without the use of eval()
would mean you would need to parse whatever input the user gave you, convert the parsed information into Python, convert any sort of functions in the parsed information into Python functions, identify any sort of operator tokens, loop through those tokens and apply those operators to the literals and function results. That would be a lot of work.
01:41
Or, you can just use eval()
, take the expression, and run it.
01:47
And here’s the math expression evaluator. In just 50 lines of Python, I’ve created a fully interactive calculator. And about half those lines are things like the instructions. This is the power of eval()
.
02:00
It allows you to do some things very, very succinctly. To help prevent the user from shooting themselves in the foot, I’m going to restrict the eval()
to just those functions inside of the math
library. Lines 4 through 6 map all of the functions and constants found inside of the math
library into the ALLOWED
dictionary, which will be used as part of the locals call inside of the eval()
. On line 10, I’m defining the prompt that the user will see when they run the program. Lines 12 through 16, I show the welcome message, similar to what you get inside of the REPL when you run that. In lines 18 to 24, I build a help message including all of the names of the constants and functions pulled in by math
.
02:48 Let me just scroll down.
02:51
Line 26 is the main part of the application. When the program is run, the first thing that happens is the user gets the welcome message. Then it enters a while
loop.
03:04
Each expression will be evaluated at a time and then loop back to the prompt, unless the user quits. Line 31 is where I get the input from the user, prompting them with the PROMPT
variable. input()
is sensitive to Control + C and Control + D, so I catch those and quit the application if they were sent. Line 35 and 38 look for special commands inside of the application. The first one here, 35 through 37, is the "help"
command. I run .lower()
and .strip()
on it in case they spell it with capital "H"
or put spaces before or after it.
03:40
When the input is "help"
, the usage message gets printed and then the loop goes back up to the top. Similar concept for line 38, except I’m looking for the keywords "quit"
or "exit"
and if they’re found, I finish the program. The actual eval()
happens on line 42. If you get here, then the input wasn’t "help"
, "quit"
, or "exit"
, so it’s time to evaluate. I’ve redefined "__builtins__"
in the global space so that the user can’t accidentally use arbitrary Python functions.
04:11
And I pass in the ALLOWED
dictionary in the local context so all of the things from the math
library that I grabbed at the beginning of the script are valid parts of eval()
. eval()
returns the result and then I print that to the screen. In lines 44 through 47, I check for SyntaxError
, NameError
, and ValueError
that might be raised by eval()
. In all these cases, I print some information to the screen and then allow the loop to complete and go back to the beginning. This stops the program from crashing if an exception is found. And finally, lines 49 and 50 are the standard mechanism to use in Python to make sure that your main()
function only gets executed in the case of the script running as a program and not if it’s imported as a module.
05:02 It doesn’t really matter in this case, but it’s good practice.
05:11 When I start the program, I get the welcome message.
05:17 I can type in an expression.
05:22
I have access to the sin()
and pi()
mechanisms and I can expose the fact that Python uses floats and doesn’t quite know how to round that to 0.0
.
05:32
And here’s the help message. It prints out Usage:
and then lists everything from the math
library that’s allowed.
05:44 And if I try to use a function that’s not part of the allowed list, then I get an error—a nice, friendly, readable error—and the program doesn’t crash. Not bad for 50 lines of code.
05:57 Thanks for sticking with me so far. Last up, I’ll give you a quick summary of the course.
Become a Member to join the conversation.