[Tutor] Named-value formatting fails
Steven D'Aprano
steve at pearwood.info
Sun Jan 9 23:14:10 CET 2011
Tim Johnson wrote:
> * Steven D'Aprano <steve at pearwood.info> [110108 19:46]:
>> A more detailed response.
>>
>> Dear me... messing with globals and locals. That's always a bad sign.
>> But let's assume this is that one time in 100 that it is actually
>> justified...
>>
>>> if localvals is None: self.locals =
>>> sys._getframe(1).f_locals
>> Are you aware that this is non-portable and subject to change without
>> notice?
> No!
> 1)Can you explain further?
The convention in Python is that any name that starts with a single
leading underscore is a private implementation detail, and therefore
subject to change. _getframe starts with a single underscore.
Of course, it's been around effectively forever -- it was introduced in
Python 2.1, and is unlikely to be removed any time soon. But it *could*
be, and it could happen without notice, so you have to weigh up the
benefit you get from using an implementation detail against the risk of
it disappearing.
On the plus side, it is a *documented* internal detail, which suggests
that it won't disappear without notice.
http://docs.python.org/library/sys.html#sys._getframe
But notice that it is specific to CPython (the version you are using).
Other Pythons, such as Jython, IronPython, PyPy, Stackless, and many
others, may not include this function. So code using it is non-portable.
(E.g. IronPython only supports frames if you pass the commandline switch
-X:Frames or -X:FullFrames.)
(Also note that PyPy is now about twice as fast as CPython. I would
expect that over the next few years, PyPy should start to take off as
the preferred Python implementation.)
> And you have convinced me, and furthermore given me enough to
> convince someone else that a different approach would be safer.
> 2)Can you recommend a *simple* templatting module?
You haven't actually told us what you're trying to do, so I have to
guess. That means I may have misunderstood something. But it looks like
you want to be able to use templating of arbitrary variable names.
Perhaps your Evalx class is overkill. Maybe this is all you need:
>>> a = 42
>>> mydict = globals().copy()
>>> mydict.update(locals())
>>> "value = %(a)s" % mydict
'value = 42'
You might also like to look at the string.Template class, which is for
substituting $ templates.
If you want to stick with Evalx, the approach I'd consider is:
* support globals() and locals() if they are explicitly given (*reading*
is safer than *writing*);
* support automatic lookup of globals and locals only when supported,
otherwise fail gracefully;
* don't use eval unless you really need to;
* if you think you need to, you probably don't :)
That means that instead of:
if localvals is None:
self.locals = sys._getframe(1).f_locals
do something like this:
if localvals is None:
try:
self.locals = sys._getframe(1).f_locals
except AttributeError:
self.locals = {}
(and similarly for globals).
Then get rid of the eval in your __getitem__. You probably don't
*really* need to support cases like this, no matter how tempting it
might be:
a = 42
b = 23
"value = %(a+b)s" % Evalx()
=> "value 75"
Instead:
def __getitem__(self, key):
if key in self.locals: return self.locals[key]
elif key in self.globals: return self.globals[key]
else: return "Key? What key?"
# Or raise KeyError
--
Steven
More information about the Tutor
mailing list