Labmdas ------- Lambdas are :class:`collections.abc.Callable` values found in the template rendering scope that are called when referenced, allowing complext context-aware rendering logic to be implemented. Lambda variables ================ As part of standard Mustache lambda extension, any :class:`collections.abc.Callable` variable will be invoked when used as a variable, and its return value will be rendered as a template. This is the simplest type of lambda. .. code:: python import mstache print(mstache.render( '{{lambda}} world!', { 'lambda': lambda: '{{hello}}', 'hello': 'Hello', }, )) # Hello world! Lambda blocks ============= If a lambda is invoked as a block, the content of said block will be passed to the :class:`collections.abc.Callable` as parameter. .. code:: handlebars {{#lambda}}content{{/lambda}} There are currently two different ways of implement lambda blocks. Explicit render mode ~~~~~~~~~~~~~~~~~~~~ Also known as ``chevron-style``, current default behavior, the lambda :class:`collections.abc.Callable` will expect two parameters, ``template`` and ``render``, and its return value will be included in the template as a value. This means that any ``template`` rendering must happen by explicitly calling the received ``render`` function, which works in the current scope and inherits the configuration of the current rendering context, and then returned. .. code:: python import mstache print(mstache.render( '{{#lambda}}content{{/lambda}} world!', { 'lambda': lambda content, render: ( # explicit render: return render result render('{{' + content + '}}') ), 'content': 'Hello', }, )) # Hello world! Implicit render mode ~~~~~~~~~~~~~~~~~~~~ Also known as ``Mustache-style``, enabled by passing ``lambda_render=None``, the lambda :class:`collections.abc.Callable` will expect a single ``template`` parameter, and its return value will be as template, and rendered, before being incuded in the output. .. code:: python import mstache print(mstache.render( '{{#lambda}}content{{/lambda}} world!', { 'lambda': lambda content: ( # implicit render: return template '{{' + content + '}}' ), 'content': 'Hello', }, lambda_render=None, )) # Hello world! Virtual properties ================== Virtual properties are, essentially, generic object property lambdas. They are useful for when typical template lambdas, which receive template data, can't effectively cover operations made with objects in the rendering scope. This was traditionally done by patching JavaScript prototypes, but that kind of object manipulation is considered unsafe in Python. To mitigate this, :py:func:`mstache.default_getter` can receive a mapping of virtual properties to be made available under any scope, effectively functioning as injected property getters. While you can provide your own getter function to :py:func:`mstache.render`, it's simpler to just wrap :py:func:`mstache.default_getter` via :py:func:`functools.partial` to include extra virtual properties, extending those from :py:attr:`mstache.default_virtuals`. .. code:: python import functools import mstache print(mstache.render( '{{text.word_count}} words', {'text': 'virtual properties are cool'}, getter=functools.partial( mstache.default_getter, virtuals={ **mstache.default_virtuals, 'word_count': lambda text: len(text.split()), }, ), )) # 4 words Please note both :py:class:`AttributeError` and :py:class:`TypeError` exceptions raised from virtual property functions are appropriately ignored by by :py:func:`mstache.default_getter`.