Every time I see this code, I have to relearn it. These notes will hopefully make this a little easier. :)
The important module is RCompile. The entry points are the compile_restricted_* functions.
compile_restricted_function is used by Python scripts.
compile_restricted_eval is used by ZPT
and by DTML indirectly through Eval.RestrictionCapableEval.
OK, so lets see how this works by following the logic of compile_restricted_eval.
First, we create an RExpression, passing the source and a "file name", to be used in tracebacks.
Now, an RExpression is just:
a subclass of RestrictedCompileMode and Expression.
Expression is a subclass of AbstractCompileMode that sets it's mode to 'eval' and everided compile. Sigh.
RestrictedCompileMode is a subclass of AbstractCompileMode that changes a bunch of things. :) These include compile, so we can ignore the compile we got from Expression. It would have been simpler to just set the dang mode in RExpression. Sigh.
RestrictedCompileMode seem to be the interestng base class. I assume it implements the interesting functionality. We'll see below...
Next, we call compileAndTuplize.
The compile method provided by RestrictedCompileMode is interesting.
First it calls _get_tree.
It uses compiler.parse to parse the source
it uses MutatingWalker.walk to mutaate the tree using the RestrictedCompileMode's 'rm' attr, which is a RestrictionMutator.
The RestrictionMutator has the recipies for mutating the parse tree. (Note, for comparison, that Zope3's zope.security.untrustedpython.rcompile module an alternative RestrictionMutator that provides a much smaller set of changes.)
A mutator has visit method for different kinds of AST nodes. These visit methods may mutate nodes or return new nodes that replace the originally visited nodes. There is a default visitor that visits a node's children and replaces the children who's visitors returned new nodes.
The walk function just calls the visitor for the root node of the given tree. Note _get_tree ignores the walk return value, thus assuming that the visitor for the root node doesn't return a new node. This is a theoretical bug that we can ignore.
Second, it generates the code. This too is boring.
So this seems simple enough. ;) When we want to add a check, we need to update or add a visit function in RestrictionMutator.
How does a visit function work.
First, we usually call walker.defaultVisitNode(node). This transforms the node's child nodes.
Then we hack the node, or possibly return the node. To do this, we have to know how the node works.
The hack often involved changing the code to call some checker function. These have names like _name_. These are names that would be illegal in the input source.
If this is a new function, we have to provide it in AccessControl.ZopeGuards._safe_globals.
Don't forget to add a test case to tests.before_and_after.