Evaluating python with my own variable bindings
I would like to bind symbols to values, e.g, Q1=True, Q2=False.... and then allow the user to specify arbitary logical expressions like Q3 and Q4 or (given different bindings) Q2 == 5 It would be fine if they could execute arbitrary python scraps. It looks to me as if this might be a job for a PythonScript. I was going to evaluate an arbitrary string, but since PythonScript has a security and binding framework it seems better to use it. So I have two questions: 1) Is PythonScript the most appropriate tool? In particular, I am concerned because I am not calling it from the web, but from some code in my product. The API reference does not refer to this case as a possible use. 2) Exactly how do I bind the symbols for use within the script? In case you are wondering, I'm doing this for rules like "only answer question 5 if you said yes to questions 3 or 4". Thanks.
Ross Boylan wrote:
I would like to bind symbols to values, e.g, Q1=True, Q2=False.... and then allow the user to specify arbitary logical expressions like Q3 and Q4
or (given different bindings) Q2 == 5
If you only want to allow expressions (not statements), you should probably look at lib/python/Products/PageTemplates/ZRPythonExpr.py, which defines encapsulated Python expressions and allows you to pass arbitrary context to them. Cheers, Evan @ 4-am
On Mon, Aug 05, 2002 at 05:16:46PM -0500, Evan Simpson wrote:
Ross Boylan wrote:
I would like to bind symbols to values, e.g, Q1=True, Q2=False.... and then allow the user to specify arbitary logical expressions like Q3 and Q4
or (given different bindings) Q2 == 5
If you only want to allow expressions (not statements), you should probably look at lib/python/Products/PageTemplates/ZRPythonExpr.py, which defines encapsulated Python expressions and allows you to pass arbitrary context to them.
Cheers,
Evan @ 4-am
This is a repeat of a previous message that seems not to have gotten through. Thanks, that looks like what I need. I have a few questions about it, though. Is there any through the web interface for this, either for creating one or using it? Or is the intent that it be just used on the fly for, say, a string that you get otherwise? What is the deal with 'engine' that appears in the __init__ arguments and is never used or referenced? Similarly, 'name' is an argument that's never used. Is the intended useage via the __call__ interface, or the modules call_with_ns? In either case, an example of useage would be helpful to me. In particular, how do I get my namespace to the function? Here's my guess at how to use it: c= globals().copy() c.update({'Q2':0, 'Q3':1}) e= PythonExpr('','Q2 or Q3', None) result = e(c) Is that right? (I want to evaluate Q2 or Q3 with those symbols bound to false and true respectively). I've never seen a class be a subclass of itself! The definition is class PythonExpr(PythonExpr): I take it the base class refers to the name imported from the module PythonExpr, while the class definition is in this namespace (and will hide the other one). Are you the creator of this module? I think I remember that you developed some machinery for secure python execution. If so, thanks--it's very handy.
On Sun, Aug 11, 2002 at 09:36:05AM -0700, Ross Boylan wrote: [snip]
In particular, how do I get my namespace to the function? Here's my guess at how to use it: c= globals().copy() c.update({'Q2':0, 'Q3':1}) e= PythonExpr('','Q2 or Q3', None) result = e(c)
Is that right? (I want to evaluate Q2 or Q3 with those symbols bound to false and true respectively).
No, it doesn't work. Here's what happened: ross@wheat:/usr/lib/zope/lib/python$ python Python 2.1.3 (#1, Jul 29 2002, 22:34:51) [GCC 2.95.4 20011002 (Debian prerelease)] on linux2 Type "copyright", "credits" or "license" for more information.
from Products.PageTemplates.ZRPythonExpr import PythonExpr e=PythonExpr('','Q1 or Q2', None) c=globals().copy() c.update({'Q1':1, 'Q2':0}) c {'__doc__': None, '__name__': '__main__', '__builtins__': <module '__builtin__' (built-in)>, 'PythonExpr': <class Products.PageTemplates.ZRPythonExpr.PythonExpr at 0x82b19ec>, 'e': <PythonExpr Q1 or Q2>, 'Q2': 0, 'Q1': 1} e(c) Traceback (most recent call last): File "<stdin>", line 1, in ? File "/usr/lib/zope/lib/python/Products/PageTemplates/ZRPythonExpr.py", line 47, in __call__ g = self._bind_used_names(econtext) File "/usr/lib/zope/lib/python/Products/PageTemplates/PythonExpr.py", line 50, in _bind_used_names vars = econtext.vars AttributeError: vars
The solution is not immediately obvious to me. I see the econtext passed in (where i put c) needs not only .vars but ._engine and some other stuff (in PythonExpr.py).
After looking around in the code, I think I've figured out how to get this to work. Here it is, for the benefit of those who follow, and for the benefit of myself if anyone has any corrections or insights: The key is that a lot of the machinery is in PageTemplates/TALES.py, which sort of explains things. In particular it defines Engine and Context. ross@wheat:/usr/lib/zope/lib/python$ python Python 2.1.3 (#1, Jul 29 2002, 22:34:51) [GCC 2.95.4 20011002 (Debian prerelease)] on linux2 Type "copyright", "credits" or "license" for more information.
from Products.PageTemplates import Expressions myvars = {'q1':0, 'q2':1} e = Expressions.getEngine() c = e.getContext(myvars) from Products.PageTemplates.ZRPythonExpr import PythonExpr # the last argument in the next line should be an engine # but it's ignored anyway, as far as I can tell. pe = PythonExpr('python', 'q1 or q2', c) pe(c) 1 pe = PythonExpr('python', 'q1 and q2', e) pe(c) 0
An alternate interface appears to be pe = e.compile('python: q1 or q2') but this may pull in the ZRPythonExpr version of PythonExpr or the ZPythonExpr version depending on some other settings whose values I'm not sure of (search for those strings in Expressions.py). The direct route shown above still has a potential glitch, because the version of PythonExpr that is directly instantiated may not match the one defined in the context's engine. I don't know if that can cause trouble.
Ross Boylan wrote:
from Products.PageTemplates import Expressions myvars = {'q1':0, 'q2':1} e = Expressions.getEngine() c = e.getContext(myvars) from Products.PageTemplates.ZRPythonExpr import PythonExpr
You're better off skipping this last line and using "Expressions.PythonExpr" directly. That way you'll get the correct implementation for whichever version of Zope you're using.
# the last argument in the next line should be an engine # but it's ignored anyway, as far as I can tell.
pe = PythonExpr('python', 'q1 or q2', c)
Yep, all of the expression type constructors take the same parameters, but only some of them use the engine. StringExpr does, for example.
An alternate interface appears to be pe = e.compile('python: q1 or q2')
This is probably the best method if you want to allow general TALES expressions. In any case, you will typically want to instantiate a single engine to use for all your expressions and make a new context object every time you evaluate an expression object. Sorry I wasn't more help earlier. Cheers, Evan @ 4-am
On Wed, Aug 21, 2002 at 05:36:56PM -0500, Evan Simpson wrote:
Ross Boylan wrote:
from Products.PageTemplates import Expressions myvars = {'q1':0, 'q2':1} e = Expressions.getEngine() c = e.getContext(myvars) from Products.PageTemplates.ZRPythonExpr import PythonExpr
You're better off skipping this last line and using "Expressions.PythonExpr" directly. That way you'll get the correct implementation for whichever version of Zope you're using.
# the last argument in the next line should be an engine # but it's ignored anyway, as far as I can tell.
pe = PythonExpr('python', 'q1 or q2', c)
Yep, all of the expression type constructors take the same parameters, but only some of them use the engine. StringExpr does, for example.
An alternate interface appears to be pe = e.compile('python: q1 or q2')
This is probably the best method if you want to allow general TALES expressions. In any case, you will typically want to instantiate a single engine to use for all your expressions and make a new context object every time you evaluate an expression object.
Sorry I wasn't more help earlier.
Cheers,
Evan @ 4-am
Thanks for the info. Can you say whether it is safe to persist either the engine or the PythonExpr instance?
Ross Boylan wrote:
Thanks for the info. Can you say whether it is safe to persist either the engine or the PythonExpr instance?
I wouldn't recommend it, and I suspect that a PythonExpr can't be pickled. Python Script objects play deep games in order to allow persistence of compiled Python code. Cheers, Evan @ 4-am
participants (2)
-
Evan Simpson -
Ross Boylan