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
# ...
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: ar(handlebars_ grammar, {}, 'handlebars') ar(compile_ grammar, {'builder': _builder})
"""A handlebars template compiler.
The compiler is not threadsafe: you need one per thread because of the
state in CodeBuilder.
"""
_handlebars = OMeta.makeGramm
_builder = CodeBuilder()
_compiler = OMeta.makeGramm
# ...
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 self._local, 'handlebars', None) is None:
self. _local. handlebars = OMeta.makeGramm ar(handlebars_ grammar, {}, 'handlebars') handlebars
def _handlebars(self):
if getattr(
return self._local.
@property self._local, 'builder', None) is None:
self. _local. builder = CodeBuilder()
def _builder(self):
if getattr(
return self._local.builder
@property self._local, 'compiler', None) is None:
self. _local. compiler = OMeta.makeGramm ar(compile_ grammar, {'builder': self._builder}) compiler
def _compiler(self):
if getattr(
return self._local.
# ...