Often you'll wish to customize the restricted environment in various ways, most commonly by adding or subtracting variables or functions from the modules available. At a more advanced level, you might wish to write replacements for existing functions; for example, a Web browser that executes Python applets would have an import function that allows retrieving modules via HTTP and importing them.
An easy way to add or remove functions is to create the RExec instance, get the namespace dictionary for the desired module, and add or delete the desired function. For example, the RExec class provides a restricted open() that allows opening files for reading. If you wish to disallow this, you can simply delete 'open' from the RExec instance's __builtin__ module.
module = r_env.add_module('__builtin__') mod_dict = module.__dict__ del mod_dict['open']
(This isn't enough to prevent code from accessing the filesystem; the RExec class also allows access via some of the functions in the posix module, which is usually aliased to the os module. See below for how to change this.)
This is fine if only a single function is being added or removed, but for more complicated changes, subclassing the RExec class is a better idea.
Subclassing can potentially be quite simple. The RExec class defines some class attributes that are used to initialize the restricted versions of modules such as os and sys. Changing the environment's policy then requires just changing the class attribute in your subclass. For example, the default environment allows restricted code to use the posix module to get its process and group ID. If you decide to disallow this, you can do it with the following custom class:
class MyRExec(rexec.RExec): ok_posix_names = ('error', 'fstat', 'listdir', 'lstat', 'readlink', 'stat', 'times', 'uname')
More elaborate customizations may require overriding one of the methods called to create the corresponding module. The functions to be overridden are make_builtin, make_main, make_osname, and make_sys. The r_import, r_open, and r_reload methods are made available to restricted code, so by overriding these functions, you can change the capabilities available.
For example, defining a new import function requires overriding r_import:
class MyRExec(rexec.RExec): def r_import(self, mname, globals={}, locals={}, fromlist=[]): raise ImportError, "No imports allowed--ever"
Obviously, a less trivial function could import modules using HTTP or whatever.