Available since Appy 1.0.13.
By default, when using pod, expressions and statement parts found in pod templates (be it ODT or ODS) are evaluated using the standard Python eval method.
You may consider it as a security problem. Indeed, some organizations using pod let advanced users or third party companies create or modify pod templates.
This is why it is possible to use pod in conjunction with an alternate expression evaluator like RestrictedPython.
Using RestrictedPython with pod
The Renderer's constructor (in appy/pod/renderer.py) has an argument named evaluator. By default, it is set to None. In that case, a standard, eval-based evaluator will be used (from appy/pod/__init__.py).
Alternately, you can place, in this arg, an instance of class Evaluator, from module appy/pod/restricted.py. This class' constructor looks as follows (basic RestrictedPython knowlegde is a prerequisite to understand it).
def __init__(self, builtins=None, write=None, item=None, unpack=None,
iterUnpack=None, attr=None, custom=None):
if not installed:
raise Exception(RP_N_INS)
self.builtins = builtins or rp.Guards.safe_builtins
self.write = write or rp.Guards.full_write_guard
self.item = item or rp.Eval.default_guarded_getitem
try:
self.unpack = unpack or rp.Guards.guarded_unpack_sequence
except AttributeError:
self.unpack = unpack # Default may not be available
try:
self.iterUnpack = iterUnpack or \
rp.Guards.guarded_iter_unpack_sequence
except AttributeError:
self.iterUnpack = None
self.safePrint = rp.PrintCollector
self.attr = attr
self.custom = custom
Basically, constructor's arguments allow to provide alternate, secure elements, that will be injected in the evaluation context of any expression that will be evaluated by pod. Then, every time pod will need to evaluate a Python expression, the evaluator will call RestrictedPython, that will evaluate it in this updated, secured context. Arg builtins, for example, defines a sub-set of standard Python __builtins__. Because, for every element for which a secure replacement may be used, several functions may be proposed (either by RestrictedPython itself or by third-party frameworks), the Evaluator constructor proposes args allowing to provide specific values, fallbacking to default ones if no value is passed. For example, RestrictedPython provides several variants for the __builtins__ sub-set: safe_builtins, limited_builtins or utility_builtins. appy.pod.restricted.Evaluator's default one is safe_builtins.
An exemple: using RestrictedPython from Zope
For those using the Zope application framework, here is an example of how to configure Zope-specific RestrictedPython rules.
By default, an instance of class appy.pod.restricted.Evaluator builds, from its attributes, a dict of all the RestrictedPython-specific entries that will need to be injected in the context of any expression to be evaluated via pod. The problem is that Zope already provides a function that builds such dict: AccessControl.ZopeGuards.get_safe_globals. This is why Evaluator's custom attribute has been foreseen: if not empty, all other Evaluator attributes will be ignored: the custom dict being used instead. Moreover, Zope also provides his own function for replacing the standard getattr function, in AccessControl.ZopeGuards.guarded_getattr. Consequently, here is how to define an Evaluator instance to be used from Zope.
from AccessControl.ZopeGuards import get_safe_globals
from AccessControl.ZopeGuards import guarded_getattr
from appy.pod.renderer import Renderer
from appy.pod.restricted import Evaluator
rpEvaluator = Evaluator(attr=guarded_getattr, custom=get_safe_globals())
# Then, pass the evaluator to the Renderer
renderer = Renderer(..., evaluator=rpEvaluator, ...)