I have a calculator (python 3.4.2) that can do normal operations using eval.
def calculator(user_input):
if any(c not in config.valid_cal_chars for c in user_input):
print("- Invalid Equation | Bad characters")
return
elif not any(c in user_input for c in "0123456789"):
print("- Invalid Equation | No numbers found")
return
sys.stdout.write("calculating " + "-".join(gfx.load_sequence))
time.sleep(0.1)
print (" | 100%")
try:
current_ans = eval(user_input)
except (SyntaxError, ZeroDivisionError, NameError, TypeError, ValueError):
print ("- Invalid Equation | Error")
return
config.ans = current_ans
print (current_ans)
Here is the config.py that config.ans, config.valid_cal_char is refering to:
ans = ("0.0")
valid_cal_chars = ("0123456789-+/*ansqrt() \n")
And if you are wondering what the
user_choice
variable is referring to, it is referring to a input function that I have before this function. That part works, so no need to worry there.
However, I am wondering if it is possible to do something like this:
input equation here: 4*4 #this would be saved as the user_input variable
> 16 #the output of the equation
input equation here: sqrt(ans) #this would use the previous answer saved in config.ans (ans) and the sqrt() to find the square root of the previous printed value, so:
> 4
So typing ans would result in:
input equation here: 1+1
> 2
input equation here: ans
> 2
And using sqrt() would result in:
input equation here: 2+2
> 4
input equation here: sqrt(4)
> 2
So if you still don't get it, sqrt() find the square root of the value inputted. ans uses the previous returned value. So combining the two "sqrt(ans)" would give the square root of the previous returned value.
With the back ground info out of the way, what I want to do is to allow a user to use these when calculating. While the "eval" may not work, I am happy to use "exec" too (knowing the dangers). However, here is a multitool.py (the main file) that imports the this file (functions.py) to use all the functions that I have in there, including this one.
import os, sys, glob, math, random, login, gfx, config, functions, time
path = "******" #creates path to folder (can be changed by commenting this line out and creating new one)
dirs = os.listdir( path ) #not used currently
functions.load_sequence_complete()
functions.username_login()
time.sleep(0.05)
functions.password_login()
print ("\n[credentials have been verified! proceeding to main program " + "-".join(gfx.load_sequence) + "]\n")
time.sleep(0.1)
program = True
while (program == True):
user_choice = functions.choice_selecter()
functions.validate_choice(user_choice)
If there are any other info that you need, please put that in the comments or answers below so I can edit this to help you help me :)
You can do it in the first method , you can define ans
as a global in the start of the function, and then the result of eval(user_input)
, and use ans
instead of current_ans
everywhere.
How your function would look like , assuming everything works -
def calculator(user_input):
global ans
if any(c not in config.valid_cal_chars for c in user_input):
print("- Invalid Equation | Bad characters")
return
sys.stdout.write("calculating " + "-".join(gfx.load_sequence))
time.sleep(0.1)
print (" | 100%")
try:
ans = eval(user_input, {'ans':ans,'sqrt':sqrt},{})
except (SyntaxError, ZeroDivisionError, NameError, TypeError, ValueError):
print ("- Invalid Equation | Error")
return
config.ans = ans
print (ans)
Please note I got rid of the elif
part in the function, as otherwise, it would not let an input like - ans
- go through - you may rethink how to rewrite that.
Example/Demo -
>>> def calculator(user_input):
... global ans
... if any(c not in "0123456789-+/*ansqrt() \n" for c in user_input):
... print("- Invalid Equation | Bad characters")
... return
... time.sleep(0.1)
... print (" | 100%")
... try:
... ans = eval(user_input, {'ans':ans,'sqrt':sqrt},{})
... except (SyntaxError, ZeroDivisionError, NameError, TypeError, ValueError):
... print ("- Invalid Equation | Error")
... return
... print (ans)
...
>>>
>>> calculator('1+2')
| 100%
3
>>> calculator('ans')
| 100%
3
>>> from math import sqrt
>>> calculator('sqrt(ans)')
| 100%
1.7320508075688772
Though you can also use eval
as -
ans = eval(user_input, {'ans':ans,'sqrt':sqrt},{})
This would restrict eval to only take ans
and sqrt
as that names.
But still you should reconsider using eval()
, as even after restricting the globals and locals in it, users can still cause harm. Why? Check here.