2

I want to read some input, which contains python assignment statements like this string:

"VARIABLE = 'something' + OTHER_VAR"

So I use one of these:

exec("VARIABLE = 'something' + OTHER_VAR")
exec("VARIABLE = 'something' + OTHER_VAR", globals(), locals())

I want to use this variable in other code, but after exec(...) it is not in current namespace. It is possible to get the variable value like this:

locals()['VARIABLE']

however, if I dont know the name of variable it is not solution for me.

So how to get that new variable into my namespace?

UPDATE: My data for exec are like this:

COMPBLOCK =  StringLeft(MyPlatform.HierarchicalName,6) +"_IADI." + CMP + "." + BLOCK ;
SetCustomPropertyValue("DEVLM2",COMPBLOCK + ".DEVLM2",false);
SetCustomPropertyValue("CRIT",COMPBLOCK + ".CRIT",false);
SetCustomPropertyValue("UNACK",COMPBLOCK + ".UNACK",false);
SetCustomPropertyValue("ALMSTA_26",COMPBLOCK + ".ALMSTA#B26",false);

I defined functions SetCustomPropertyValue and StringLeft. I want to avoid some complicated translation of this script to python with all possible inputs. Exec() seems to be very quick solution, but after reading this post - Modifying locals in python I am little bit stuck.

Community
  • 1
  • 1
Pavol Liška
  • 206
  • 2
  • 7
  • 2
    Why is this a problem in the first place? What is the context for evaluating arbitrary Python expressions here? – deceze Sep 08 '15 at 12:03
  • What do you mean by *your namespace*? `locals()` *is* the local namespace. Something like `exec("a = 5"); print(a);` works, i.e. `a` is then in the local namespace. What do you want to achieve? – dhke Sep 08 '15 at 12:04
  • 1
    `exec`ing arbitrary strings is dangerous. Are you sure you _really_ want to do this? – PM 2Ring Sep 08 '15 at 12:09
  • here is the problem: if i execute above code in console, it works (VARIABLE is define). But if I make a function like `def fn(line): exec(line) print(VARIABLE)` I got `NameError: 'VARIABLE' is not defined` – Pavol Liška Sep 08 '15 at 12:43
  • your function `fn` has a different *local* namespace (that's the point of local namespaces...) – umläute Sep 08 '15 at 12:49
  • print statement is inside this function – Pavol Liška Sep 08 '15 at 12:53

3 Answers3

1
  1. pay attention to the comments warning about how dangerous it is to execute arbitrary code from a foreign source.
  2. if the statements have a consistent format, for example like the one in the example, you could easly parse it and extract the variable name:

    varname = stmt.split('=')[0].strip()
    

    or something more sophisticated using regular expressions

  3. if the statement always introduces exactly one new variable, you could compare locals() before and after execution and check which new variable has been added:

    old_locals = set(locals().keys())
    exec(stmt)
    new_locals = set(locals().keys())
    varname = (new_locals-old_locals).pop()
    
yurib
  • 8,043
  • 3
  • 30
  • 55
  • Yes, this is good solution for this, however, I also have other types of statements, like: `SetCustomPropertyValue('DEVLM2',COMPBLOCK + '.DEVLM2',False);`. I defined function SetCustomPropertyValue, it can read the variables, but, it cannot see the resulting variable. (In this case, this could be translated to `DEVLM2 = COMPBLOCK + '.DEVLM2'`. – Pavol Liška Sep 08 '15 at 12:48
0

How about using a small domain specific language as found in configparser to declare your new variables? This way you don't need to run untrusted code, and get simple things like variable expansion (though probably in a slightly different syntax).

E.g. considering the following input

FOO = world
BAR = hello, @FOO@

as simple parser could look like:

lines=["FOO = world", "BAR = hello, @FOO@" ]
vars={}

# store variables in dictionary, with expansion
for line in lines:
  key, value = [x.strip() for x in line.split('=', 1)]
  for k in vars:
     value=value.replace('@%s@' % (k), str(vars[k]))
  vars[key]=value
# make variables available in local namespace
for k in vars:
    locals()[k]=vars[k]
## use new variable
print(BAR)
umläute
  • 28,885
  • 9
  • 68
  • 122
0

There is some issues around locals() in Python 3 (see this post) so generally speaking, changes in locals() during runtime is not allowed.

So I made some workaround, where I defined my own namespace dictonary my_dict_locals and my_dict_globals. To my_dict_globals I copied some necessary definitions from current namespace (like SetCustomPropertyValue function definition ...). Then I just called

exec(each, my_dict_globals, my_dict_locals)

where each could be like one of following:

COMPBLOCK =  StringLeft(MyPlatform.HierarchicalName,6) +"_IADI." + CMP + "." + BLOCK ;
SetCustomPropertyValue("DEVLM2",COMPBLOCK + ".DEVLM2",false);
SetCustomPropertyValue("CRIT",COMPBLOCK + ".CRIT",false);
SetCustomPropertyValue("UNACK",COMPBLOCK + ".UNACK",false);
SetCustomPropertyValue("ALMSTA_26",COMPBLOCK + ".ALMSTA#B26",false);`

This works for me as I expected and I have in my_dict_locals all variables from above script defined.

Community
  • 1
  • 1
Pavol Liška
  • 206
  • 2
  • 7