Do eval() and exec not accept a function definition? (like 'def foo: pass) ?

V

vasudevram

Hi group,

Question: Do eval() and exec not accept a function definition? (like
'def foo: pass) ?

I wrote a function to generate other functions using something like
eval("def foo: ....")
but it gave a syntax error ("Invalid syntax") with caret pointing to
the 'd' of the def keyword.

Details (sorry for the slight long post but thought it better to give
more details in this case - wording is pretty clear, though, I think,
so shouldn't take long to read):

While working on a personal project for creating a server for a
certain protocol (which I may make into an open source project later
if it turns out to be any good), I wrote some simple functions to
generate HTML start and end tags like <html>, <body>, </html>, </
body>, etc. - just to simplify/shorten my code a little. (I'm aware
that there are HTML generation libraries out there, but don't think I
need the overhead, since my needs are very simple, and anyway wanted
to roll my own just for fun. For a bigger/more serious project I would
probably use the existing libraries after doing a proper evaluation).
So, this question is not about HTML generation but about Python's
eval() function and exec statement.

Started by writing functions like this:

def start_html():
return '<html>\r\n'

def end_html():
return '</html>\r\n'

.... and similarly for the 'body', 'p', etc. HTML tags.
(I used '\r\n' at the end because the server will send this output to
the browser over HTTP, so that my code conforms to Internet protocols,
and also so that while debugging, my output would have only one tag
per line, for readability. Not showing the '\r\n in the rest of the
code below)

Then I realized that all these functions are very similar - only
differ in the return value of the tag.
So (being interested in metaprogramming of late), thought of writing a
function that would generate these functions, when passed the
appropriate tag name argument.

[ Digression: I could of course have used another simple approach such
as this:

def start_tag(tag_name):
return '<' + tag_name + '>'

# called like this:
# print start_tag('html')
# print start_tag('body')

# and

def end_tag(tag_name):
return '</' + tag_name + '>'

# called like this:
# print end_tag('body')
# print end_tag('html')

# and called similarly for the other HTML tags.

While the above would work, it still would involve a bit more typing
than I'd like to do, since I'[d have to pass in the tag name as an
argument each time. I'd prefer having functions that I could call like
this:

print start_html()
# which would print "<html>"
print start_body()
# which would print "<body>"

# and so on ... just to make the code a little shorter and more
readable.

End of Digression]

So, I wrote this code generation function:

# AAAA
import string
def generate_html_tag_function(tag_name, start_or_end):
start_or_end.lower()
assert(start_or_end in ('start', 'end'))
if start_or_end == 'start':
func_def = "def start_" + tag_name + ":()\n" + \
"return '<' + tag_name + '>'
else:
func_def = "def end_" + tag_name + ":()\n" + \
"return '</' + tag_name + '>'
function = eval(func_def)
return function

# meant to be called like this:

start_html = generate_html_tag_function('html', 'start')
start_body = generate_html_tag_function('body', 'start')
end_html = generate_html_tag_function('html', 'end')
end_body = generate_html_tag_function('body', 'end')

# and the generated functions would be called like this:

print start_html()
print start_body()
print end_body()
print end_html()
# BBBB

# giving the output:
<html>
<body>
</body>
</html>

But when I ran the above code (between the lines marked #AAAA and
#BBBB), I got an error "Invalid syntax" with the caret pointing at the
"d" of the def statement.
I had used eval a few times earlier for somewhat similar uses, so
thought this would work.
I then looked up the Python Language Reference help, and saw that eval
is used to evaluate Python expressions, not statements, and def is a
statement. So looked up the exec statement of Python and saw that its
syntax seemed to allow what I wanted.
So replaced the line containing eval in the above with:
exec(func_def)
But that too gave the same error (I think so - I tried this yesterday
and forgot to save the error messages, sorry about that, so can't be
sure, but do think this was the case - if not, I'll save the exact
code and output/errors later and repost here - not at my PC right
now.)

Thanks for any suggestions,

Vasudev Ram
Bize site: http://www.dancingbison.com
PDF creation / conversion toolkit: http://sourceforge.net/projects/xtopdf
Blog on software innovation: http://jugad.livejournal.com
 
E

Eduardo Dobay

Hey,

I think you could use lambda functions for that matter (Ever heard of
them?). You could write something like:

def generate_html_tag_function(tag_name, start_or_end):
start_or_end.lower()
assert(start_or_end in ('start', 'end'))
if start_or_end == 'start':
function = lambda: '<' + tag_name + '>'
else:
function = lambda: '</' + tag_name + '>'
return function

Then you would create the functions using the same code you had
written before:

start_html = generate_html_tag_function('html', 'start')
start_body = generate_html_tag_function('body', 'start')
end_html = generate_html_tag_function('html', 'end')
end_body = generate_html_tag_function('body', 'end')


That seems to do what you want.

Eduardo
 
V

vasudevram

Hey,

I think you could use lambda functions for that matter (Ever heard of
them?). You could write something like:

def generate_html_tag_function(tag_name, start_or_end):
start_or_end.lower()
assert(start_or_end in ('start', 'end'))
if start_or_end == 'start':
function = lambda: '<' + tag_name + '>'
else:
function = lambda: '</' + tag_name + '>'
return function

Then you would create the functions using the same code you had
written before:

start_html = generate_html_tag_function('html', 'start')
start_body = generate_html_tag_function('body', 'start')
end_html = generate_html_tag_function('html', 'end')
end_body = generate_html_tag_function('body', 'end')

That seems to do what you want.

Eduardo

Thanks to all the repliers.

@Eduardo: Yep, I had heard of lambdas, but didn't think to use them
here.
Will try this out - thanks!

- Vasudev
 
S

Scott David Daniels

vasudevram said:
Hi group,

Question: Do eval() and exec not accept a function definition? (like
'def foo: pass) ?

def is the first keyword in a _statement_, not an expression.

exec executes statements, eval evaluates expressions.

try this:

exec "def foolish(x):\n y= x * 2\n print x, y"
foolish(2.4)
 
S

Steven D'Aprano

Hi group,

Question: Do eval() and exec not accept a function definition? (like
'def foo: pass) ?

eval() is a function, and it only evaluates EXPRESSIONS, not code blocks.

eval("2+3") # works
eval("x - 4") # works, if x exists
eval("print x") # doesn't work

exec is a statement, and it executes entire code blocks, including
function definitions, but don't use it. Seriously.

ESPECIALLY don't use it if you are exec'ing data collected from untrusted
users, e.g. from a web form.

I wrote a function to generate other functions using something like
eval("def foo: ....")
but it gave a syntax error ("Invalid syntax") with caret pointing to
the 'd' of the def keyword.

You don't need eval or exec to write a function that generates other
functions. What you need is the factory-function design pattern:


def factory(arg):
def func(x):
return x + arg
return func

And here it is in action:
7
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,053
Latest member
BrodieSola

Latest Threads

Top