Pybars3
Pybars3 is a Python library that provides a templating system compatible with Handlebars.js, supporting both Python 2 and Python 3. It is a fork of the original `pybars` project, enhancing it with Python 3 compatibility and features from Handlebars.js up to version 2.0. The current version is 0.9.7, released in November 2019, indicating an infrequent release cadence and a status of maintenance.
Common errors
-
PybarsError: Template source must be a unicode string
cause This error occurs when the template source provided to `compiler.compile()` is not a Unicode string. This is particularly common in Python 2 environments if string literals are not explicitly marked with `u''` or if byte strings are passed. In Python 3, it can happen if byte strings are inadvertently passed.fixEnsure your template source string is a Unicode string. In Python 2, use `u"your template here"`. In Python 3, all standard string literals are Unicode, but verify no byte strings (`b""`) are being used as template input. -
AttributeError: 'str' object has no attribute 'get'
cause This error can occur within templates or helpers when attempting to access properties of a context variable that is a simple string, but the template expects an object (like a dictionary or an object with attributes). Older versions of Pybars3 (prior to 0.9.2) had issues where dictionary calls did not always take precedence over attributes, leading to unexpected resolution.fixEnsure the data context passed to the template matches the structure expected by the template. If accessing dictionary keys, ensure the context is a dictionary. Upgrade to Pybars3 0.9.2 or later, which improved the precedence of dictionary calls over attributes and `.get()` methods. -
TypeError: 'list' object is not callable (when using options['fn'])
cause This typically happens in a custom block helper when `options['fn']` (which represents the inner block of the Handlebars helper) is not correctly invoked. It might be due to a missing `options` argument in the helper definition or calling `options.fn` instead of `options['fn']`.fixVerify that your block helper's signature includes `options` as the second positional argument and that you are correctly invoking the inner block using `options['fn'](scope_for_inner_block)`. For example: `result.extend(options['fn'](item))`.
Warnings
- gotcha Pybars3 does not fully implement all features of Handlebars.js 2.0 or even 1.1. Specific features like `@root`, `_parent`, `../` accessors, `each` helper with `@index`, `@key`, `@first`, `@last`, dynamic partials, and raw blocks are supported, but full compatibility with the latest Handlebars.js features should not be assumed.
- gotcha Custom helper functions in Pybars3 have different calling conventions than their JavaScript counterparts. Block helpers must accept `this, options, *args, **kwargs`. Other helpers and closures in the context should accept `this, *args, **kwargs`.
- gotcha When passing keyword arguments to helpers (e.g., `{{foo bar quux=1}}`), the keyword names in Handlebars templates must not be Python reserved words. Using a reserved word like `print` as a keyword argument name will result in a Python syntax error.
- gotcha For efficiency and control over HTML escaping, Pybars3 uses a `strlist` class. When a helper returns a `strlist` instance, its content will NOT be escaped, similar to Handlebars.js `SafeString`. Helpers in inner loops are recommended to return `list` or `strlist` for performance.
Install
-
pip install pybars3
Imports
- Compiler
from pybars import Compiler
Quickstart
from pybars import Compiler
# Get a compiler
compiler = Compiler()
# Define a helper function
def _list_helper(this, options, items):
result = ['<ul>']
for item in items:
result.append('<li>')
result.extend(options['fn'](item)) # Render the inner block
result.append('</li>')
result.append('</ul>')
return result
# Register helpers and partials
helpers = {'list': _list_helper}
header_template = compiler.compile(u'<h1>{{title}}</h1>')
partials = {'header': header_template}
# Compile the main template source
source = u"""
{{>header}}
<p>Hello, {{name}}!</p>
{{#list people}}{{firstName}} {{lastName}}{{/list}}
"""
template = compiler.compile(source)
# Data to render
data = {
'title': 'People List',
'name': 'World',
'people': [
{'firstName': 'Yehuda', 'lastName': 'Katz'},
{'firstName': 'Carl', 'lastName': 'Lerche'},
{'firstName': 'Alan', 'lastName': 'Johnson'}
]
}
# Render the template
output = template(data, helpers=helpers, partials=partials)
print(output)