Recipe 4.23. Ensuring a Name Is Defined in a Given Module
Credit: Steven Cummings
Problem
You want to ensure that a certain
name is defined in a given module (e.g., you want to ensure that
there is a built-in name set), and, if not, you
want to execute some code that sets the definition.
Solution
The solution
to this problem is the only good use I've yet seen
for statement exec. exec lets
us execute arbitrary Python code from a string, and thus lets us
write a very simple function to deal with this task:
import _ _builtin_ _
def ensureDefined(name, defining_code, target=_ _builtin_ _):
if not hasattr(target, name):
d = { }
exec defining_code in d
assert name in d, 'Code %r did not set name %r' % (
defining_code, name)
setattr(target, name, d[name])
Discussion
If your code supports several versions of Python (or of some
third-party package), then many of your modules must start with code
such as the following snippet (which ensures name
set is properly set in either Python 2.4, where
it's a built-in, or 2.3, where it must be obtained
from the standard library):
try:This recipe encapsulates this kind of logic directly, and by default
set
except NameError:
from sets import Set as set
works on module _ _builtin_ _, since
that's the typical module for which you need to work
around missing names in older Python versions. With this recipe, you
could ensure name set is properly defined among
the built-ins by running just once, during your
program's initialization, the single
call:
ensureDefined('set', 'from sets import Set as set')The key advantage of this recipe is that you can group all needed
calls to ensureDefined in just one place of your
application, at initialization time, rather than having several ad
hoc try/except statements at
the start of various modules. Moreover,
ensureDefined may allow more readable code because
it does only one specific job, so the purpose of calling it is
obvious, while try/except
statements could have several purposes, so that more study and
reflection might be needed to understand them. Last but not least,
using this recipe lets you avoid the warnings that the
try/except approach can trigger
from such useful checking tools as pychecker, http://pychecker.sourceforge.net/. (If you
aren't using pychecker or something like that, you
should!)The recipe takes care to avoid unintended accidental side effects on
target, by using an auxiliary dictionary
d as the target for the
exec statement and then transferring only the
requested name. This way, for example, you can use as
target an object that is not a module (a
class, say, or even a class instance), without necessarily adding to
your target an attribute named _ _builtins_ _ that
references the dictionary of Python's built-ins. If
you used less care, so that the body of the if
statement was only:
exec defining_code in vars(target)you would inevitably get such side effects, as documented at
http://www.python.org/doc/current/ref/execl.It's important to be aware that
exec can and does execute any valid string of
Python code that you give it. Therefore, make sure that the argument
defining_code that you pass to any call of function
ensureDefined does not come
from an untrusted source, such as a text file that might have been
maliciously tampered
with.
See Also
The online documentation of the exec statement in
the Python Language Reference Manual at
http://www.python.org/doc/current/ref/execl.