Re: exec_code, a class for easing automatic code generation

Tim Peters (tim@ksr.com)
Mon, 28 Feb 94 23:41:38 EST

You're picking up Python in a flash, Keith! That's a nice module.

I have only a few coding tricks to pass on:

1) Instances of
self.exec_code = compile(self.exec_string,'<string>','exec')
are probably better as
self.exec_code = compile(self.exec_string,`self`,'exec')

That way tracebacks from errors during compilation will identify the
specific exec_code object that's in error.

2)
> def p(self) :
> print str(self)

'print' automatically invokes your __str__ method, so the body can be
replaced by a plain
print self

And note that, for the same reason, if x is an exec_code object,
the user gets the same output whether they do
x.p()
or
print x

This makes the .p() method marginal.

3)
> for x in range(0,self.indent_level):
> self.exec_string = self.exec_string + ' '
> self.exec_string = self.exec_string + stmt + '\n'

A) When you want a 'for' loop to execute N times, the one-argument
form of 'range' does the trick. So
for x in range(self.indent_level):
is more idiomatic here.

B) An amazing number of Python programmers don't seem to know (or
remember <grin>) that "string * int" returns string+string+...
(int catenations of "string"). So the whole sequence above can
be done in one statement:

self.exec_string = self.exec_string + ' '*self.indent_level + \
stmt + '\n'

> I would appreciate ... comments ... especially with regard to the
> namespace issues I discussed in the comments for ... execute()

The trick to this is to stare at the table in section 4.1 of the language
(not library) reference manual, until after a week or two it dawns on you
that it means exactly (& in particular, no more than) what it says <wink>.

The other trick is to realize that the rules _imply_ that, in almost all
cases (and in all non-devious cases), the global namespace used by a
chunk of code is the namespace of the module in which that code appears.
This is a good rule, because it prevents modules from stepping on each
other by accident.

So, e.g., suppose we put your module in the file eco.py. Then:

>>> import eco
>>> e = eco.exec_code()
>>> e.append_stmt('global i')
>>> e.append_stmt('i = 45')
>>> print e
<exec_code instance> with indent-level 0 and code:
--------------------------------------------------------------
global i
i = 45

>>> i = 0 # stick "i" in __main__'s global NS
>>> dir() # yup, it's there
['__name__', 'e', 'eco', 'i']
>>> dir(eco) # but "i" is not in eco's global NS
['__name__', 'exec_code']
>>> e.execute()
>>> i # *our* value of i didn't change
0
>>> dir(eco) # but eco suddenly grew one
['__name__', 'exec_code', 'i']
>>> eco.i
45
>>>

Clearer? It might help to point out that, in your

exec self.exec_code

exec's caller is the 'execute' method, not whoever _called_ execute. So
"global n.s. of caller" in table 4.1 refers here to the global NS of
function 'execute', and the global NS of a function is the global NS of
the block _containing_ the function (not of the function's caller).
That's the chain you trace thru in 4.1: it leads from the exec, to
execute, to the class defn, and ends at the module containing the class.

5.5-on-the-technical-and-5.9-for-artistic-impression-ly y'rs - tim,
the tonya harding of python

Tim Peters tim@ksr.com
not speaking for Kendall Square Research Corp