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.
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.
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
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
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
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
.strip() on it in case they spell it with capital
"H" or put spaces before or after it.
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
"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
"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.
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() returns the result and then I print that to the screen. In lines 44 through 47, I check for
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.
Become a Member to join the conversation.