Recipe4.23.Ensuring a Name Is Defined in a Given Module


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:     set except NameError:     from sets import Set as set

This recipe encapsulates this kind of logic directly, and by default 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/exec.html.

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/exec.html.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net