Using Eval in Python to create class variables - python

I wrote a class that lets me pass in a list of variable types, variable names, prompts, and default values. The class creates a wxPython panel, which is displayed in a frame that lets the user set the input values before pressing the calculate button and getting the results back as a plot. I add all of the variables to the class using exec statements. This keeps all of the variables together in one class, and I can refer to them by name.
light = Variables( frame , [ ['f','wavelength','Wavelength (nm)',632.8] ,\
['f','n','Index of Refraction',1.0],])
Inside the class I create and set the variables with statments like:
for variable in self.variable_list:
var_type,var_text_ctrl,var_name = variable
if var_type == 'f' :
exec( 'self.' + var_name + ' = ' + var_text_ctrl.GetValue() )
When I need to use the variables, I can just refer to them by name:
wl = light.wavelength
n = light.n
Then I read on SO that there is rarely a need to use exec in Python. Is there a problem with this approach? Is there a better way to create a class that holds variables that should be grouped together, that you want to be able to edit, and also has the code and wxPython calls for displaying, editing, (and also saving all the variables to a file or reading them back again)?
Curt

You can use the setattr function, which takes three arguments: the object, the name of the attribute, and it's value. For example,
setattr(self, 'wavelength', wavelength_val)
is equivalent to:
self.wavelength = wavelength_val
So you could do something like this:
for variable in self.variable_list:
var_type,var_text_ctrl,var_name = variable
if var_type == 'f' :
setattr(self, var_name, var_text_ctrl.GetValue())

I agree with mipadi's answer, but wanted to add one more answer, since the Original Post asked if there's a problem using exec. I'd like to address that.
Think like a criminal.
If your malicious adversary knew you had code that read:
exec( 'self.' + var_name + ' = ' + var_text_ctrl.GetValue() )
then he or she may try to inject values for var_name and var_text_ctrl that hacks your code.
Imagine if a malicious user could get var_name to be this value:
var_name = """
a = 1 # some bogus assignment to complete "self." statement
import os # malicious code starts here
os.rmdir('/bin') # do some evil
# end it with another var_name
# ("a" alone, on the next line)
a
"""
All of the sudden, the malicious adversary was able to get YOU to exec[ute] code to delete your /bin directory (or whatever evil they want). Now your exec statement roughly reads the equivalent of:
exec ("self.a=1 \n import os \n os.rmdir('/bin') \n\n "
"a" + ' = ' + var_text_ctrl.GetValue() )
Not good!!!
As you can imagine, it's possible to construct all sorts of malicious code injections when exec is used. This puts the burden onto the developer to think of any way that the code can be hacked - and adds unnecessary risk, when a risk-free alternative is available.

For the security conscious, there might be an acceptable alternative. There used to be a module call rexec that allowed "restricted" execution of arbitrary python code. This module was removed from recent python versions. http://pypi.python.org/pypi/RestrictedPython is another implementation by the Zope people that creates a "restricted" environment for arbitrary python code.

The module was removed because it had security issues. Very difficult to provide an environment where any code can be executed in a restricted environment, with all the introspection that Python has.
A better bet is to avoid eval and exec.
A really off-the-wall idea is to use Google App Engine, and let them worry about malicious code.

Related

Importing number into variable name in python

First of all, i am somewhat new to python coding, so this might seem like a stupid question.
Problem: I am trying to create a script that allows me to import a number into (variable?operator?) names, in order to run my python script from a bash script.
Ideally i wanted to do the following (i know the syntax is wrong, but it is from my first try and captures what i would want it to do):
replica_number = 2 #2 is only for testing. It will later be exchanged with an imported number from a bash script over many different data sheets.
t_r+replica number = md.load(data_path+'potein_+replica_number+_centered.xtc', top = data_path+'potein_+replica_number+_first.gro')[1:]
What i want this to do is to automatically create the variables named t_r2 and import the files called protein_2_centered.xtc and protein_2_first.gro. However when i do this i get: SyntaxError: can't assign to operator
Does anyone know how to get around this problem, or do i just have to make a separate script for every replica?
What you need is either a list or a dictionary.
you can keep all your results in a list (without keeping the replica_number):
t_r_list = []
t_r_list.append(md.load(...)[1:]) # Run this line for each t_r you want to load
or if you want to keep the replica_number, you can use a dict:
t_r_dict = {}
t_r_dict[replica_number] = md.load(...)[1:]
You might want to read a tutorial on these data structures and how to use them, it will greatly help you on your journey with python later on and is the basis of the basis when it comes to working with data in python.
when the name of the attributes or variables is dynamic, we can use, for example, the new way Python uses for fomat Strings (f'') and then the setattr method:
The settattr method is this, is part of the builtins library:
def setattr(x, y, v): # real signature unknown; restored from __doc__
""" Sets the named attribute on the given object to the specified value."""
Here what you can do with variables
replica_number = 2
variable_name = f't_r{replica_number }'
and then check and set the aatribute:
if not hasattr(YOUR_OBJECT, variable_name ):
raise ValueError
setattr(YOUR_OBJECT, variable_name , THE_VALUE)
Use a dictionary for such kind of operations:
replica_number = md.load(...)[1:]
your_dict = {t_r : replica_number}
And access it through
your_dict[t_r]

How can I avoid retyping long variable names in calling functions

How can I avoid lines like:
this_long_variable_name = this_long_variable_name.replace('a', 'b')
I thought I could avoid it by making a function, repl,
def repl(myfind, myreplace, s):
s = s.replace(myfind, myreplace)
print(s) # for testing purposes
return s
but because of stuff about the local vs. global namespaces that I don't understand, I can't get the function to return a changed value for this_long_variable_name. Here's what I've tried:
this_long_variable_name = 'abbbc'
repl('b', 'x', this_long_variable_name)
print('after call to repl, this_long_variable_name =', this_long_variable_name)
The internal print statement shows the expected: axxxc
The print statement after the call to repl show the unchanged: abbbbc
Of course, it works if I give up and accept the redundant typing:
this_long_variable_name = repl('b', 'x', this_long_variable_name)
BTW, it's not just about the length of what has to be retyped, even if the variable's name were 'a,' I would not like retyping a = a.replace(...)
Since in the function s is a parameter, I can't do:
global s
I even tried:
this_long_variable_name.repl('b', 'x')
which shows you both how little I understand and how desperate I am.
The issue you're running into is that Python strings are immutable. str.replace() returns an entirely new string, so in s = s.replace(myfind, myreplace), the name s no longer refers to the original string, which is why you don't see any change on the outside of the function's namespace.
There probably isn't a great solution to your problem. I recommend using a modern IDE or Python REPL with autocompletion to alleviate it. Trying to abuse the standard way of writing things like this may feel good to you, but it will confuse anyone else looking at your code.
Harry it does not work because inside your repl function you actually have a local copy of the content of your this_long_variable_name. This is called "pass by copy" which means python hands over a copy to the function. Unfortunately this is how python does it. Check also here:
Python: How do I pass a string by reference?
Also strings are immutable in python so if you wanna change them you always create a new modified version. Check here:
Aren't Python strings immutable?
Question would be why should you need long variable names in the first place?

Create a scope for temporary variables in Python

I searched around but I did not found the answer to this question.
Is there any aesthetic, pythonic way to create temporary variables in an arbitrary scope so I am sure they won't ever appear outside this scope ?
For exemple in OCaml, I can write :
let x = 3 and y = 5 in z = x + y
I'd love to do some stuff like this in Python. For exemple, let's imagine I create a module talker.py with a lot of useful functions, and each of them uses a variable that I don't want to copy again and again.
# talker.py
sentance = ( 'Has provinciae has dictione emergunt sorte aptae'
'iuris navigerum nusquam iuris in natura sorte aptae visitur'
'in verum dictione flumen.' )
def talk() :
print sentance
def talk_loudly() :
print sentance.upper()
def any_other_useful_function :
# do stuff using sentance
Then I want to use all of these wonderful functions in another file, but :
I want to import all these functions at once (without naming each of them)
I don't want to have access to sentance : this variable has done it's job, now I want it to fall in darkness and to be forgotten.
But if I call this module with import talk or from talk import *, this second condition won't be respected.
This is an example of where I could use a "special scope for temporary variables", even if it is not the only situation in which I wish I had this at range.
I thought about using the with ... : statement but I wasn't satisfied with the result.
Any idea is welcomed, but I am looking for the most aesthetic and least wordy manner to proceed.
You can define the __all__ name on your module, to control the names which will be available when you are using from talk import *.
Documented here.
Firstly, sentance isn't temporary. Those functions are relying on its existence. You could del sentance and it would be gone but the functions would throw an error.
A common Python convention is to prefix the name with an underscore. This signals that the variable is 'protected'. It doesn't do anything, but programmers know that they probably shouldn't access it from outside the module (or class).

Why would people use globals() to define variables

I've come across recently a number of places in our code which do things like this
...
globals()['machine'] = otherlib.Machine()
globals()['logger'] = otherlib.getLogger()
globals()['logfile'] = datetime.datetime.now().strftim('logfiles_%Y_%m_%d.log')
and I am more than a little confused as to why people would do that, rather than doing
global machine
machine = otherlib.Machine()
and so on.
Here is a slightly anonymised function which does this, in full:
def openlog(num)
log_file = '/log_dir/thisprogram.' + num
if os.path.exists(log_file):
os.rename(log_file, log_file + '.old')
try:
globals()["log"] = open(log_file, 'w')
return log
except:
print 'Unable to open ' + log_file
sys.exit(1)
It confuses the hell out of pylint (0.25) as well me.
Is there any reason for coding it that way? There's minimal usage of eval in our code, and this isn't in a library
PS I checked Reason for globals() in python but it doesn't really answer as to why you'd use this for setting globals in a program
Maybe the function uses a local variable with the same name as the global one, and the programmer didn't want to bother changing the variable name?
def foo(bar):
global bar # SyntaxError
bar = bar + 1
def foo(bar):
globals()['bar'] = bar + 1
foo(1)
print(bar) # prints 2
Another use case, albeit still a bit specious (and clearly not the case in the example function you gave), is for defining variable names dynamically. This is rarely, if ever, a good idea, but it does come up a lot in questions on this site, at least. For example:
>>> def new_variable():
... name = input("Give your new variable a name! ")
... value = input("Give your new variable a value! ")
... globals()[name] = value
...
>>> new_variable()
Give your new variable a name! foo
Give your new variable a value! bar
>>> print(foo)
bar
Otherwise, I can think of only one reason to do this: perhaps some supervising entity requires that all global variables be set this way, e.g. "in order to make it really, really clear that these variables are global". Or maybe that same supervising entity has placed a blanket ban on the global keyword, or docks programmer pay for each line.
I'm not saying that any of these would be a good reason, but then again, I truly can't conceive of a good reason to define variables this way if not for scoping purposes (and even then, it seems questionable...).
Just in case, I did a timing check, to see if maybe the globals() call is faster than using the keyword. I'd expect the function call + dictionary access to be significantly slower, and it is.
>>> import timeit
>>> timeit.timeit('foo()', 'def foo():\n\tglobals()["bar"] = 1',number=10000000)
2.733132876863408
>>> timeit.timeit('foo()', 'def foo():\n\tglobal bar\n\tbar = 1',number=10000000)
1.6613818077011615
Given the code you posted and my timing results, I can think of no legitimate reason for the code you're looking at to be written like this. Looks like either misguided management requirement, or simple incompetence.
Are the authors PHP converts? This is a valid code in PHP:
$GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b'];
See this for more examples. If someone was used to this way of writing the code, maybe they just used the closest matching way of doing it in Python and didn't bother to check for alternatives.
You'd sometimes use a superglobal $GLOBAL variable to define something, because although global keyword exists in PHP, it will only import existing variables - it cannot create a new variable as far as I know.

does python 2.5 have an equivalent to Tcl's uplevel command?

Does python have an equivalent to Tcl's uplevel command? For those who don't know, the "uplevel" command lets you run code in the context of the caller. Here's how it might look in python:
def foo():
answer = 0
print "answer is", answer # should print 0
bar()
print "answer is", answer # should print 42
def bar():
uplevel("answer = 42")
It's more than just setting variables, however, so I'm not looking for a solution that merely alters a dictionary. I want to be able to execute any code.
In general, what you ask is not possible (with the results you no doubt expect). E.g., imagine the "any code" is x = 23. Will this add a new variable x to your caller's set of local variables, assuming you do find a black-magical way to execute this code "in the caller"? No it won't -- the crucial optimization performed by the Python compiler is to define once and for all, when def executes, the exact set of local variables (all the barenames that get assigned, or otherwise bound, in the function's body), and turn every access and setting to those barenames into very fast indexing into the stackframe. (You could systematically defeat that crucial optimization e.g. by having an exec '' at the start of every possible caller -- and see your system's performance crash through the floor in consequence).
Except for assigning to the caller's local barenames, exec thecode in thelocals, theglobals may do roughly what you want, and the inspect module lets you get the locals and globals of the caller in a semi-reasonable way (in as far as deep black magic -- which would make me go postal on any coworker suggesting it be perpetrated in production code -- can ever be honored with the undeserved praise of calling it "semi-reasonable", that is;-).
But you do specify "I want to be able to execute any code." and the only solution to that unambiguous specification (and thanks for being so precise, as it makes answering easier!) is: then, use a different programming language.
Is the third party library written in Python? If yes, you could rewrite and rebind the function "foo" at runtime with your own implementation. Like so:
import third_party
original_foo = third_party.foo
def my_foo(*args, **kwds):
# do your magic...
original_foo(*args, **kwds)
third_party.foo = my_foo
I guess monkey-patching is slighly better than rewriting frame locals. ;)

Categories