UnboundLocalError when called a template within block helper

Bug #1316201 reported by Alek Kowalczyk
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
pybars
New
Undecided
Unassigned

Bug Description

The error is intermittent, a few occurences up to now.

It seems to be an error in parser-generated code, so I'm submitting it.

Relevant part of stacktrace:
```
  File "mails/hbs.py", line 391, in render
    html = unicode(wrapper_template(data, partials=partials, helpers=helpers))
  File "/pymeta_generated_code/pymeta_grammar__render__83.py", line 14, in render
    result.grow(inner(scope, helpers=helpers, partials=partials))
  File "/pymeta_generated_code/pymeta_grammar__render__71.py", line 17, in render
    value = value(this, options, )
  File "mails/hbs.py", line 102, in subject
    subject_html = options['fn'](this)
  File "/pymeta_generated_code/pymeta_grammar__render__69.py", line 7, in render
    if callable(value):
UnboundLocalError: local variable 'value' referenced before assignment
```

the error occured in 'subject' block helper which is defined like below:

```
    def subject(self, this, options):
        """{{#subject}} block helper to configure the email's subject."""
        logging.info("HandlebarsHelpers.subject start")
        subject_html = options['fn'](this)
        self.mail.set_rendered_subject(subject_html)
        logging.info("HandlebarsHelpers.subject end")
        return ""
```

the relevant template fragment is as following:

{{#subject}} {{trans "[AppName] Activity in the project:" }} {{ project.project_name }} {{/subject}}

Revision history for this message
Alek Kowalczyk (alek-kowalczyk) wrote :

I've found the root cause. Is there anyone who could checkin the fix?

The bug is caused by Compiler thread unsafety.

pybars._compiler.Compiler docstring says "The compiler is not threadsafe: you need one per thread".
It is not true - even one instance per thread is not threadsafe, because class-level fields are initialized in module load time
so creating separate instances won't do anyway:

class Compiler:
    """A handlebars template compiler.
    The compiler is not threadsafe: you need one per thread because of the
    state in CodeBuilder.
    """
    _handlebars = OMeta.makeGrammar(handlebars_grammar, {}, 'handlebars')
    _builder = CodeBuilder()
    _compiler = OMeta.makeGrammar(compile_grammar, {'builder': _builder})
    # ...

Here is the fixed version of the above code fragment which works fine in multithreading code:

class Compiler:
    """The really thread-safe handlebars template compiler."""

    _local = threading.local()

    @property
    def _handlebars(self):
        if getattr(self._local, 'handlebars', None) is None:
            self._local.handlebars = OMeta.makeGrammar(handlebars_grammar, {}, 'handlebars')
        return self._local.handlebars

    @property
    def _builder(self):
        if getattr(self._local, 'builder', None) is None:
            self._local.builder = CodeBuilder()
        return self._local.builder

    @property
    def _compiler(self):
        if getattr(self._local, 'compiler', None) is None:
            self._local.compiler = OMeta.makeGrammar(compile_grammar, {'builder': self._builder})
        return self._local.compiler
    # ...

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.