Google 网上论坛不再支持新的 Usenet 帖子或订阅项。历史内容仍可供查看。

eval vs. exec

已查看 0 次
跳至第一个未读帖子

Simon Budig

未读,
2002年5月27日 10:20:522002/5/27
收件人
Hi.

I currently have problems to let code inside strings execute as I want
to have it. Especially the difference between "eval" and "exec"
(resp. compiling as 'eval' or as 'single') makes me headache.

I want to execute things like the three following strings:

"a = 2" ---> returns nothing
"3 * 4" ---> returns 12
"a=2; b=3; a*b" ---> returns 6

My current code looks like this:

mathenv = {"__builtins__" : {}}

def evalmath (mathstuff):
global mathenv
mathenv['__builtins__'] = {}
code = None
try:
code = compile (mathstuff, "", 'eval')
except:
try:
code = compile (mathstuff, "", 'single')
except:
return "Error, was unable to compile the code"
# actually there is some exception stuff in here.
if code:
try:
result = eval (code, mathenv)
except:
result = "Error, unable to evaluate the code."
# actually there is some exception stuff in here.
return result

the tests result in:

>>> print "result:", evalmath("a = 2")
result: None
>>> print "result:", evalmath("3 * 4")
result: 12
>>> print "result:", evalmath("a=2; b=3; a*b")
result:
6
None

While the first two things are OK, the last one is not, since the
evaluated result gets printed instead of returned. The other problem
is that the code written above is quite ugly and looks like a hack.
For the latter problem there surely is a workaround by reassigning
sys.stdout or something like this, but this is really evil.

Is there a more pythonic way to do this?

Thanks,
Simon

--
Simon...@unix-ag.org http://www.home.unix-ag.org/simon/

Alexander Schmolck

未读,
2002年5月27日 10:13:042002/5/27
收件人
Simon Budig <Simon...@unix-ag.org> writes:

> Hi.
>
> I currently have problems to let code inside strings execute as I want
> to have it. Especially the difference between "eval" and "exec"
> (resp. compiling as 'eval' or as 'single') makes me headache.
> I want to execute things like the three following strings:
>
> "a = 2" ---> returns nothing
> "3 * 4" ---> returns 12
> "a=2; b=3; a*b" ---> returns 6


The distiction is quite simple: use eval for expressions and exec for
everything else. That of course only works if you know what qualifies as an
expression in python :)

Maybe this helps: an expression is something that returns a value, so anything
you can write on the right side of an '=' is an expression (unless it already
has an "=" in it) and everything you can't isn't. So only example 2 above is
an expression. If you have an expression, use eval, else use exec, which will
execute arbitrary code in the dictionary you specify with "in" (default
globals, but I'd never use that).

This will work fine for case 2:

result = eval("3 * 4")

for the other cases a not completely horrible way to do it is:

namespace = {}
exec "a=2; b=3; result=a*b" in namespace
result = namespace["result"]

or even better, depending on how much control you have over the strings do:

result = eval("a*b", {"a" : 2, "b" : 3})

Of course using eval or exec is usually not a good idea, because it creates a
huge security risk -- A better approach would be to parse the expression or at
least make sure it only contains harmless things before you exec/eval it.


BTW: this

> except:

is almost always a bad idea. You should explicitly test for the Errors you are
expecting like so:

except SyntaxError:

because otherwise bugs you didn't expect might go unnoticed.

HTH

alex

Simon Budig

未读,
2002年5月27日 12:38:312002/5/27
收件人
Alexander Schmolck <a.sch...@gmx.net> wrote:

> Simon Budig <Simon...@unix-ag.org> writes:
>
> The distiction is quite simple: use eval for expressions and exec for
> everything else. That of course only works if you know what qualifies as an
> expression in python :)

I think I do know what an expression is - what I do not know is
how to determine the type to use from a string given by an external
source.

[...]


> for the other cases a not completely horrible way to do it is:
>
> namespace = {}
> exec "a=2; b=3; result=a*b" in namespace
> result = namespace["result"]

The strings are from an external source, so I have no control over them.

> Of course using eval or exec is usually not a good idea, because it creates a
> huge security risk -- A better approach would be to parse the expression or at
> least make sure it only contains harmless things before you exec/eval it.

I think I took quite good care of it by making sure that before I
eval/exec something the mathdict["__builtins__"] = {}. In the real code
I also have a runtime control so that "100L**(100L**100L)" would not
virtually stop the program.

> BTW: this
>
>> except:
>
> is almost always a bad idea. You should explicitly test for the Errors you are
> expecting like so:
>
> except SyntaxError:
>
> because otherwise bugs you didn't expect might go unnoticed.

I do know about this, but this is definitely necessary because otherwise
it would be possible to crash the program via "raise 'foo'".

holger krekel

未读,
2002年5月27日 12:15:152002/5/27
收件人
Simon Budig wrote:
> I want to execute things like the three following strings:
>
> "a = 2" ---> returns nothing
> "3 * 4" ---> returns 12
> "a=2; b=3; a*b" ---> returns 6
> My current code looks like this:

what you can do is e.g.

def run_code(string, mathstuff={'myabs': lambda x: abs(x)}):
import code
interpreter = code.InteractiveInterpreter()
interpreter.locals = mathstuff
codeobj = interpreter.compile(string)
result = interpreter.runcode(codeobj)
return result

run_code('a=2')
run_code('3*4')
run_code('a=2;b=3;a*b')
run_code('a=-3;myabs(a*5)')

(you should be able to paste it to your interpreter).

Is this what you want? You may need to fiddle around,
of course.

have fun,

holger


Hans Nowak

未读,
2002年5月27日 12:42:402002/5/27
收件人
Alexander Schmolck wrote:

> Of course using eval or exec is usually not a good idea, because it creates a
> huge security risk -- A better approach would be to parse the expression or at
> least make sure it only contains harmless things before you exec/eval it.

I don't know why this "security risk" comes up all the time...
security is only at stake if you execute or evaluate a string
from an unknown or untrusted source. Most of the time, this
will not be the case. If you use eval/exec with a string you
made yourself, there's no more risk than, say, importing a
module, or calling an object's method. (For some reason, I
don't hear people say, "don't use import, it's a security risk,
the module might have been magically changed and do something
unintended now".)

Whether use of exec and eval is _necessary_ or whether it's
good programming style is a different bowl of soup, of course.

--
Hans (base64.decodestring('d3VybXlAZWFydGhsaW5rLm5ldA=='))
# decode for email address ;-)
The Pythonic Quarter:: http://www.awaretek.com/nowak/

Hans Nowak

未读,
2002年5月27日 12:45:572002/5/27
收件人
Simon Budig wrote:

> The strings are from an external source, so I have no control over them.

In that case, Alexander's remark about security risks with eval
and exec are on point. Don't do this.

(My other mail was merely about usage of eval/exec in general.)

Regards,

Michael Hudson

未读,
2002年5月27日 12:53:092002/5/27
收件人
Simon Budig <Simon...@unix-ag.org> writes:

> Alexander Schmolck <a.sch...@gmx.net> wrote:
> > Simon Budig <Simon...@unix-ag.org> writes:
> >
> > The distiction is quite simple: use eval for expressions and exec for
> > everything else. That of course only works if you know what qualifies as an
> > expression in python :)
>
> I think I do know what an expression is - what I do not know is
> how to determine the type to use from a string given by an external
> source.

Hmm. Maybe compile as "single" but smash the last PRINT_EXPR into a
RETURN_VALUE? Might work. You'd probably want to turn all but the
last PRINT_EXPR into POP_TOPs too...

Something like this?

import new, dis

POP_TOP = dis.opname.index("POP_TOP")
PRINT_EXPR = dis.opname.index("PRINT_EXPR")
RETURN_VALUE = dis.opname.index("RETURN_VALUE")

def super_eval(expr):
code = compile(expr, "", "single")

codestring = code.co_code
newcode = ""

last_i = 0
i = 0
while i < len(codestring):
op = ord(codestring[i])
if op > dis.HAVE_ARGUMENT:
newcode += codestring[i:i+3]
i += 3
elif op == PRINT_EXPR:
last_i = i
newcode += chr(POP_TOP)
i += 1
else:
newcode += chr(op)
i += 1

if last_i > 0:
newcode = newcode[:last_i] + chr(RETURN_VALUE) + newcode[last_i+1:]

code = new.code(code.co_argcount, code.co_nlocals, code.co_stacksize,
code.co_flags, newcode, code.co_consts, code.co_names,
code.co_varnames, code.co_filename, code.co_name,
code.co_firstlineno, code.co_lnotab)

return eval(code)

>>> super_eval("a = 2")
>>> super_eval("3*4")
12
>>> super_eval("a = 2; b = 3; a*b")
6


buyer-beware-ly y'rs
M.

--
I think my standards have lowered enough that now I think ``good
design'' is when the page doesn't irritate the living fuck out of
me. -- http://www.jwz.org/gruntle/design.html

Simon Budig

未读,
2002年5月27日 14:07:302002/5/27
收件人
holger krekel <py...@devel.trillke.net> wrote:
> what you can do is e.g.

[slightly adjusted]

import code

def run_code(string, mathstuff={'myabs': lambda x: abs(x)}):

interpreter = code.InteractiveInterpreter()
interpreter.locals = mathstuff

codeobj = code.compile_command(string)


result = interpreter.runcode(codeobj)
return result

print "Result: %s" % repr (run_code('a=2'))
print "Result: %s" % repr (run_code('3*4'))
print "Result: %s" % repr (run_code('a=2;b=3;a*b'))
print "Result: %s" % repr (run_code('a=-3;myabs(a*5)'))

and this results in:

Result: None
12
Result: None
6
Result: None
15
Result: None

As you can see the return value always is "None". The real results
get printed to stdout. This is the same problem I had with the
eval/exec stuff.

Thanks for trying :-)

Alexander Schmolck

未读,
2002年5月27日 13:26:352002/5/27
收件人
Simon Budig <Simon...@unix-ag.org> writes:
Simon Budig <Simon...@unix-ag.org> writes:

> Alexander Schmolck <a.sch...@gmx.net> wrote:
> > Simon Budig <Simon...@unix-ag.org> writes:
> >
> > The distiction is quite simple: use eval for expressions and exec for
> > everything else. That of course only works if you know what qualifies as an
> > expression in python :)
>
> I think I do know what an expression is - what I do not know is
> how to determine the type to use from a string given by an external
> source.

Sorry, I didn't mean to state the obvious, but it seems to be confusing to
many people, especially since assignments can form part of expressions in many
languages.

Well, if all your stuff is exclusively math expressions which can only contain

a) variables and literals
b) operators
c) maybe some (math) function calls
d) assignments

then maybe something like:

re.search('[^=!]=[^=-+]', str)

is enough to find out?


But I don't see why you need this distinction anyway, since your current
strategy means you don't reduce everything to expressions, so you might as
well also handle the expressions with exec, or not?

Anyway, this all seems to be heading down the wrong track. I'm not completely
clear I understand your problem, but is my assumption right that it basically
is:

"I have string consisting of a series of assignment statements (possibly 0)
followed by an expression and I want to return the value of this
expression?" ?

From what I understand the proper solution would very likely involve writing a
parser (either by hand or by using something like spark). But if you'd rather
use exec and evals you can just extract the final expression from the string,
along the lines of (untested):

re.search(r'(?s)\n|(?:;\s*)(.*)$', ').group(1)

and eval that in the namespace in which you executed the rest, which should
give you the result you are after.

However, all the tricks you have to play to make this stuff safe are
presumably more of an effort than parsing it properly, especially since
parsing simple math expressions often comes as example code for parser
generators and is such a common problem that it shouldn't be that hard to find
something canned.


>
> I think I took quite good care of it by making sure that before I
> eval/exec something the mathdict["__builtins__"] = {}. In the real code

Are you sure that this is guaranteed to be safe? The reference manual would
suggest that only *additional* keys may be inserted by the implementation, but
I'd rather double-check.

>
> > BTW: this
> >
> >> except:
> >
> > is almost always a bad idea. You should explicitly test for the Errors you are
> > expecting like so:
> >
> > except SyntaxError:
> >
> > because otherwise bugs you didn't expect might go unnoticed.
>
> I do know about this, but this is definitely necessary because otherwise
> it would be possible to crash the program via "raise 'foo'".

Sure, but it might still be helpful to have *2* (or more) except clauses: one
that checks for what you reasonably expect might go wrong and a final
"except:" in the body of which you could print a warning or debugging message,
log intrusion attempts etc., at least while you are still developing the code.

>
> Thanks,
> Simon

alex

Simon Budig

未读,
2002年5月27日 15:41:302002/5/27
收件人
Alexander Schmolck <a.sch...@gmx.net> wrote:
> Simon Budig <Simon...@unix-ag.org> writes:
>> Alexander Schmolck <a.sch...@gmx.net> wrote:
>> > Simon Budig <Simon...@unix-ag.org> writes:
>
> Sorry, I didn't mean to state the obvious, but it seems to be confusing to
> many people, especially since assignments can form part of expressions in
> many languages.

Well, originally this was confusing to me. I now know this fact but
sometimes would love to be able to do something like

if (match = re.match ("spam", eggs)):
print match.group (0)

similiarily: Why does "a = b = 2" work, but making the associativity
explicit ("a = (b = 2)") does not?

But I'd guess this is kind of a holy cow I'd better avoid to touch... :-)

> Well, if all your stuff is exclusively math expressions which can only

[...]

> Anyway, this all seems to be heading down the wrong track. I'm not completely
> clear I understand your problem, but is my assumption right that it basically
> is:
>
> "I have string consisting of a series of assignment statements (possibly 0)
> followed by an expression and I want to return the value of this
> expression?" ?

Sorry, my fault. Maybe I should have phrased this question better.
I try to be a bit more general.

The specific problem of math expressions is not the main problem here.
I am just astonished about the uglyness of eval/exec'ing code.
Why is it not as simple as:

>>> print repr (eval ("3*4"))
12
>>> print repr (eval ("a=2"))
None

Why is there this distinction between 'single' and 'eval'?

The interactive console is able to handle "3*4" as well as "a=2" without
having to specify the type of input, why not eval?

eval could - for example - return a list of collected return values:
eval ("[1,2,3] * 2 ; 3*4 ; 7*8 ; a=2") ---> [[1,2,3,1,2,3], 12, 56]

> However, all the tricks you have to play to make this stuff safe are
> presumably more of an effort than parsing it properly, especially since
> parsing simple math expressions often comes as example code for parser
> generators and is such a common problem that it shouldn't be that hard to
> find something canned.

This is - as always - a tradeoff thingie. I don't see a good reason
to implement something that has been solved better - and more general -
in python itself again. It is not a permanent service, it is not used
for mission critical stuff, and the people that are able to put stuff
from the external python into this environment without having references
to it probably won't bother. Maybe this is too optimistic, but It is
good enough for me. Currently the eval/exec thing is more interesting
to me :-)

Simon Budig

未读,
2002年5月27日 15:46:542002/5/27
收件人
Michael Hudson <m...@python.net> wrote:
> Hmm. Maybe compile as "single" but smash the last PRINT_EXPR into a
> RETURN_VALUE? Might work. You'd probably want to turn all but the
> last PRINT_EXPR into POP_TOPs too...
>
> Something like this?
[...]

>>>> super_eval("a = 2")
>>>> super_eval("3*4")
> 12
>>>> super_eval("a = 2; b = 3; a*b")
> 6

Whow, this is impressive. Yes, this seems to work although this is
way beyond my python-scope :-)

I am not sure if I want to do this or if the interception of
sys.stdout is a more - uhm - Simon-friendly solution... ;-)

Thanks!

Just

未读,
2002年5月27日 15:26:342002/5/27
收件人
In article <3cf2...@si-nic.hrz.uni-siegen.de>,
Simon Budig <Simon...@unix-ag.org> wrote:

> Whow, this is impressive. Yes, this seems to work although this is
> way beyond my python-scope :-)

It's defintely pretty cool, the way Michael handled this...

> I am not sure if I want to do this or if the interception of
> sys.stdout is a more - uhm - Simon-friendly solution... ;-)

Here's another solution:

import sys, __builtin__

class NullFile:
def write(self, s):
pass

def super_eval(expr):
code = compile(expr, "", "single")

try:
del __builtin__._
except AttributeError:
pass
savestdout = sys.stdout
try:
sys.stdout = NullFile()
eval(code)
finally:
sys.stdout = savestdout
return getattr(__builtin__, "_", None)


>>> super_eval("a = 2")
>>> super_eval("3*4")
12
>>> super_eval("a = 2; b = 3; a*b")
6
>>>


Just

holger krekel

未读,
2002年5月27日 15:17:102002/5/27
收件人
Simon Budig wrote:
> holger krekel <py...@devel.trillke.net> wrote:
> > what you can do is e.g.
>
> [slightly adjusted]
> (...)

> As you can see the return value always is "None". The real results
> get printed to stdout. This is the same problem I had with the
> eval/exec stuff.
>
> Thanks for trying :-)

:-)

ok. seriously now!

this is the working version (i hope you appreciate it :-)

import compiler
from parser import ParserError

class MyCodeGenerator(compiler.pycodegen.InteractiveCodeGenerator):
def visitDiscard(self, node):
self.visit(node.expr)
self.emit('RETURN_VALUE') # instead of printing :-)

def get_code(string):
""" determine whether string is an expression or a statement """
try: tree = compiler.pycodegen.parse(string,'exec')
except ParserError,e:
print "no valid statement:",string
raise
tree.filename='<string>'
return MyCodeGenerator(tree).getCode()

def run_code(string, mathstuff={'myabs': lambda x: abs(x)}):

print "execing",string
codeobj = get_code(string)
return eval(codeobj,mathstuff)

print "Result: %s" % repr (run_code('a=asdlkj(-3;myabs(a*5)'))


print "Result: %s" % repr (run_code('a=2'))
print "Result: %s" % repr (run_code('3*4'))
print "Result: %s" % repr (run_code('a=2;b=3;a*b'))
print "Result: %s" % repr (run_code('a=-3;myabs(a*5)'))


I know that this has the odor of beeing 'hackish'. But i
think this is *mostly* because the compiler/parser modules
are not in wide use. There are some respectable applications
though which work with this layer of python (quixote e.g.).

Also note that with this solution you can distinguish
parsing and runtime-exceptions.

have fun,

holger


Delaney, Timothy

未读,
2002年5月27日 20:15:292002/5/27
收件人
> From: Simon Budig [mailto:Simon...@unix-ag.org]

>
> The specific problem of math expressions is not the main problem here.
> I am just astonished about the uglyness of eval/exec'ing code.
> Why is it not as simple as:
>
> >>> print repr (eval ("3*4"))
> 12
> >>> print repr (eval ("a=2"))
> None
>
> Why is there this distinction between 'single' and 'eval'?

Assignment (binding) is not an expression in Python. This is a Good
Thing(TM).

> The interactive console is able to handle "3*4" as well as
> "a=2" without
> having to specify the type of input, why not eval?

The interactive console is actually playing tricks. When it sees a plain
expression, it prints the result. This does not happen when you run a
script.

> in python itself again. It is not a permanent service, it is not used
> for mission critical stuff, and the people that are able to put stuff
> from the external python into this environment without having
> references
> to it probably won't bother. Maybe this is too optimistic, but It is
> good enough for me. Currently the eval/exec thing is more interesting

You are too optimistic. Never assume anything is not a permanent service -
everything will last longer than you intended, because people are afraid to
change things that currently work. In many cases, this fear is justified.

Any time you are using code from an untrusted source you need to be
incredibly careful. This includes third-party modules (I assume you trust
the implementors of Python and its standard libraries ...). A trusted
associate today may turn out to be a bitter ex-employee with a nasty
backdoor into your systems in the future.

If this is running on an isolated system, where you don't care if someone
(accidentally or otherwise) messes it up, then OK. Of course, it will need
to be a *real* isolated system - otherwise someone could write some
networking code, exec it, and compromise your network. Oops.

Tim Delaney


Delaney, Timothy

未读,
2002年5月28日 04:16:352002/5/28
收件人
> From: holger krekel [mailto:py...@devel.trillke.net]
>
> I've hear these type of arguments before. AFAIK bitter ex-employees
> don't need to write some exec/eval-exploit, they just use their old
> passwords or implant an easy backdoor on their last days.
>
> Todays company's practices regarding security are often a *laugh*.
> You try to close some mouses' hole but there are already several
> open six-lane highways into the core of your system. This doesn't
> neccessarily mean you don't need to care, though.

That's all true - but it's partially known as "covering one's arse". If
*you* haven't left the backdoor open, you're OK. The fewer back doors into a
place the better. Most security breaches in business are caused by employees
(both current and ex employees).

> But assume that your application wants to give the system
> administrator
> means to inspect and test the objects in a running application. I
> wouldn't see the point of investing too much time on how to make it
> 'secure' (besides killing long-running mys-typed calculations). How
> secure can using a debugger get? At least I often start
> my python programs with 'python -i progname.py' which is a
> good application of 'exec/eval'.

Yes - in which case you (a) trust the employee and (b) close everything up
tightly behind them when they're gone. Oh - and (c) you verify as much as
possible that arbitrary strings aren't going to kill your system when
execed. How many debuggers allow entering arbitrary statements? Most allow
you to inspect, and change the value/binding of existing variables, but
don't allow executing arbitrary code.

> If your strings are coming from a public web-application
> it's a different matter, of course. But not everything is
> a web-application.

In the particular case we are talking about, the strings are coming from an
external source - maybe not a public web application, but external
nonetheless.

There is always a tradeoff of convenience vs security. Security is not just
malicious - it's also about accidents. A classic case is teaching. Students
tend to make more mistakes than professionals. It is very important to limit
the amount of damage they can do. A large part of this is giving them an
environment which is "safe" - limited disk quotas and privilege levels. If
they *cannot* cause damage, no matter what they do, then by all means give
them unlimited eval/exec access. Just don't be surprised when you find
someone accidentally DOSing your mission-critical server because you forgot
to unplug the student server from the network.

Tim Delaney


holger krekel

未读,
2002年5月28日 04:30:152002/5/28
收件人
Delaney, Timothy wrote:
> > If your strings are coming from a public web-application
> > it's a different matter, of course. But not everything is
> > a web-application.
>
> In the particular case we are talking about, the strings are coming from an
> external source - maybe not a public web application, but external
> nonetheless.
>
> There is always a tradeoff of convenience vs security. Security is not just
> malicious - it's also about accidents. A classic case is teaching. Students
> tend to make more mistakes than professionals. It is very important to limit
> the amount of damage they can do. A large part of this is giving them an
> environment which is "safe" - limited disk quotas and privilege levels. If
> they *cannot* cause damage, no matter what they do, then by all means give
> them unlimited eval/exec access. Just don't be surprised when you find
> someone accidentally DOSing your mission-critical server because you forgot
> to unplug the student server from the network.

This reminds of an accident on the german stock market last year.
The all important DAX index dropped by IIRC 16% in a few minutes.
They had a 'teaching' system which the students could use
for practicing buying/selling stocks. It turned out that this teaching
system was connected to the real thing and someone had justed ordered
5.000.000 stocks...

Couldn't have been prevented by better exec/eval-handling, i guess :-)

Seriously, i don't think we have much of a disagreement:
If you are working on anything remotely mission critical,
take care of (and probably avoid) execing/evaling stuff.
Otherwise it's still good practice but maybe not worth it.

regards,

holger


Michael Hudson

未读,
2002年5月28日 08:47:212002/5/28
收件人
Simon Budig <Simon...@unix-ag.org> writes:

> Michael Hudson <m...@python.net> wrote:
> > Hmm. Maybe compile as "single" but smash the last PRINT_EXPR into a
> > RETURN_VALUE? Might work. You'd probably want to turn all but the
> > last PRINT_EXPR into POP_TOPs too...
> >
> > Something like this?
> [...]
> >>>> super_eval("a = 2")
> >>>> super_eval("3*4")
> > 12
> >>>> super_eval("a = 2; b = 3; a*b")
> > 6
>
> Whow, this is impressive. Yes, this seems to work although this is
> way beyond my python-scope :-)
>
> I am not sure if I want to do this or if the interception of
> sys.stdout is a more - uhm - Simon-friendly solution... ;-)

Here's another crack. It works much the same way as the last one, but
is somewhat more sensible...

from compiler.pycodegen import InteractiveCodeGenerator, \
AbstractCompileMode

class SuperICG(InteractiveCodeGenerator):
def visitDiscard(self, node):
self.visit(node.expr)
self.storeName('$ output')

class SuperI(AbstractCompileMode):
mode = "single"
def compile(self):
tree = self._get_tree()
gen = SuperICG(tree)
self.code = gen.getCode()

def super_eval2(expr):
gen = SuperI(expr, "")
gen.compile()
code = gen.code
d = {}
eval(code,d,d)
return d.get('$ output')

Just's solution is quite nice too.

Cheers,
M.

--
In that case I suggest that to get the correct image you look at
the screen from inside the monitor whilst standing on your head.
-- James Bonfield, http://www.ioccc.org/2000/rince.hint

Michael Hudson

未读,
2002年5月28日 09:52:132002/5/28
收件人
Michael Hudson <m...@python.net> writes:

> Simon Budig <Simon...@unix-ag.org> writes:

[super_eval bytecodehack-lite-stylee]

> > I am not sure if I want to do this or if the interception of
> > sys.stdout is a more - uhm - Simon-friendly solution... ;-)
>
> Here's another crack. It works much the same way as the last one, but
> is somewhat more sensible...

[version using the compiler module]

Actually, the two versions I posted share a piece of (probably)
undesired behaviour. Can anyone spot it?

Cheers,
M.

--
This proposal, if accepted, will probably mean a heck of a lot of
work for somebody. But since I don't want it accepted, I don't
care. -- Laura Creighton, PEP 666

Kragen Sitaker

未读,
2002年5月28日 18:22:432002/5/28
收件人
Hans Nowak <wu...@earthlink.net> writes:
> Simon Budig wrote:
> > The strings are from an external source, so I have no control over them.
>
> In that case, Alexander's remark about security risks with eval
> and exec are on point. Don't do this.

Whether or not your program can trust input is a separate issue from
whether or not it can control that input. Not all user input is
untrusted; in particular, if your program has no privileges not also
possessed by the sender of the input, it is safe to trust that input.

Be aware that the sender of the input may not be who you think it is,
though. A PostScript viewer might have input supplied by anybody who
wrote a PostScript document the user thinks they want to read, for
example.

Michael Hudson

未读,
2002年5月29日 07:51:132002/5/29
收件人
Michael Hudson <m...@python.net> writes:

> Michael Hudson <m...@python.net> writes:
>
> > Simon Budig <Simon...@unix-ag.org> writes:
>
> [super_eval bytecodehack-lite-stylee]
>
> > > I am not sure if I want to do this or if the interception of
> > > sys.stdout is a more - uhm - Simon-friendly solution... ;-)
> >
> > Here's another crack. It works much the same way as the last one, but
> > is somewhat more sensible...
> [version using the compiler module]
>
> Actually, the two versions I posted share a piece of (probably)
> undesired behaviour. Can anyone spot it?

It seems noone did. Here it is:

>>> super_eval("1;a=1")
1

That should probably have returned None.

Here's my final word on the subject:

from compiler.pycodegen import InteractiveCodeGenerator, \
AbstractCompileMode

class SuperICG(InteractiveCodeGenerator):
def visitDiscard(self, node):
self.visit(node.expr)

if self.return_val:
self.emit('RETURN_VALUE')
else:
self.emit('POP_TOP')
def visitStmt(self, node):
self.return_val = 0
children = node.getChildNodes()
for child in children[:-1]:
self.visit(child)
self.return_val = 1
self.visit(children[-1])



class SuperI(AbstractCompileMode):
mode = "single"
def compile(self):
tree = self._get_tree()

print tree


gen = SuperICG(tree)
self.code = gen.getCode()

def super_eval2(expr, d=None):
if d is None: d = {}


gen = SuperI(expr, "")
gen.compile()
code = gen.code

return eval(code, d)

This is my first play with the compiler module... I think I like it :)

Cheers,
M.

--
While preceding your entrance with a grenade is a good tactic in
Quake, it can lead to problems if attempted at work. -- C Hacking
-- http://home.xnet.com/~raven/Sysadmin/ASR.Quotes.html

Andrei Kulakov

未读,
2002年5月29日 23:46:292002/5/29
收件人
In article <yfsadql...@black132.ex.ac.uk>, Alexander Schmolck wrote:
> Simon Budig <Simon...@unix-ag.org> writes:
>
>> Hi.
>>
>> I currently have problems to let code inside strings execute as I want
>> to have it. Especially the difference between "eval" and "exec"
>> (resp. compiling as 'eval' or as 'single') makes me headache.
>> I want to execute things like the three following strings:
>>
>> "a = 2" ---> returns nothing
>> "3 * 4" ---> returns 12
>> "a=2; b=3; a*b" ---> returns 6
>
>
> The distiction is quite simple: use eval for expressions and exec for
> everything else. That of course only works if you know what qualifies as an
> expression in python :)
>
> Maybe this helps: an expression is something that returns a value, so anything
> you can write on the right side of an '=' is an expression (unless it already
> has an "=" in it) and everything you can't isn't. So only example 2 above is
> an expression. If you have an expression, use eval, else use exec, which will
> execute arbitrary code in the dictionary you specify with "in" (default
> globals, but I'd never use that).
>
Thanks! I knew the difference between expressions and
statements but I'd always forget it. Now I'll remember it goes on
the right side of an equal sign. The simplest explanation is
often the best and most memorable :-).

- Andrei

--
Cymbaline: intelligent learning mp3 player - python, linux, console.
get it at: cy.silmarill.org

0 个新帖子