[Zope-dev] Lazy expressions appear to cause memory leaks

Geoff Davis geoff at phds.org
Fri Jun 2 12:20:09 EDT 2006


In Zope 2.8+ there is a little known but very useful TALES feature: you
can have expressions be lazily evaluated.  For example,

<span tal:define="foo lazy:python:someExpensiveMethod()" />

The "lazy:" prefix causes the python expression to not be evaluated until
foo is used somewhere.

There appears to be a fairly big problem with this setup.  The lazy:
prefix wraps the expression in a LazyExpr which stores the expression and
its context.  The LazyExpr in turn generates a LazyWrapper (that
holds similar information) which ends up getting held in the
TALInterpreter's global/local variable list.

The expression context for TAL expressions in a page template includes
things like the template itself, the context object (a fully wrapped
object), the request (chock full of complicated stuff), and so on.  It
appears that storing the expression context in the lazy wrapper creates
some kind of circular reference or something similar that is preventing
garbage collection of these lazy wrappers.  The result is a nasty memory
leak.

The problem appears to be fixable via some cleaning up in
PageTemplate.pt_render after the interpreter does its thing.  The code
snippet below is probably overkill, but something like this appears to be
what is needed:

    context = getEngine().getContext(c)
    TALInterpreter(self._v_program, self._v_macros,
                   context,
                   output,
                   tal=not source, strictinsert=0)()

    # clean up - try to eliminate circular references - this may be overkill
    context._compiler = None
    context.contexts = None
    context.repeat_vars = None
    from Products.PageTemplates.DeferExpr import LazyWrapper
    for k,v in context.global_vars.items():
        if isinstance(v, LazyWrapper):
            v._expr = None
            v._econtext = None
            v._result = None
    if context.vars:
        while len(context.vars):
            context.vars._pop()
    context.global_vars.clear()
    context.global_vars = None
    context.local_vars.clear()
    context.local_vars = None
    context.vars = None
    context._scope_stack = None



More information about the Zope-Dev mailing list