From 30bdf57ae431a9ab27fbd75833fa744c05577033 Mon Sep 17 00:00:00 2001 From: Bart Feenstra Date: Wed, 1 May 2024 11:01:40 +0100 Subject: [PATCH] Use a custom Jinja2 Context class rather than a custom Template class (#1439) --- .../entity/label--person-name.html.j2 | 2 +- .../assets/templates/event-dimensions.html.j2 | 2 +- betty/jinja2/__init__.py | 64 ++++++++++--------- 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/betty/extension/cotton_candy/assets/templates/entity/label--person-name.html.j2 b/betty/extension/cotton_candy/assets/templates/entity/label--person-name.html.j2 index 5790c6d9c..a70aac745 100644 --- a/betty/extension/cotton_candy/assets/templates/entity/label--person-name.html.j2 +++ b/betty/extension/cotton_candy/assets/templates/entity/label--person-name.html.j2 @@ -1,4 +1,4 @@ -{% import 'macro/citation.html.j2' as citation_macros %} +{% import 'macro/citation.html.j2' as citation_macros with context %} {% set person_name = person_name | default(entity) %} {% set embedded = embedded | default(False) %} {% set person_context = entity_contexts['Person'] %} diff --git a/betty/extension/cotton_candy/assets/templates/event-dimensions.html.j2 b/betty/extension/cotton_candy/assets/templates/event-dimensions.html.j2 index 8a59c820d..f3f456b1e 100644 --- a/betty/extension/cotton_candy/assets/templates/event-dimensions.html.j2 +++ b/betty/extension/cotton_candy/assets/templates/event-dimensions.html.j2 @@ -1,4 +1,4 @@ -{%- import 'macro/citation.html.j2' as citation_macros -%} +{%- import 'macro/citation.html.j2' as citation_macros with context -%} {%- set embedded = embedded | default(False) -%} {%- set citation_context = entity_contexts['Citation'] -%} {%- set place_context = entity_contexts['Place'] -%} diff --git a/betty/jinja2/__init__.py b/betty/jinja2/__init__.py index 8e5d254f1..cccebe8b0 100644 --- a/betty/jinja2/__init__.py +++ b/betty/jinja2/__init__.py @@ -6,9 +6,10 @@ import datetime from collections import defaultdict +from collections.abc import MutableMapping, Iterator from pathlib import Path from threading import Lock -from typing import Callable, Any, cast, Mapping, TypeVar +from typing import Callable, Any, cast, TypeVar import aiofiles from aiofiles import os as aiofiles_os @@ -17,9 +18,8 @@ select_autoescape, FileSystemLoader, pass_context, - Template as Jinja2Template, ) -from jinja2.runtime import StrictUndefined, Context, DebugUndefined, new_context +from jinja2.runtime import StrictUndefined, Context, DebugUndefined from betty.app import App from betty.html import CssProvider, JsProvider @@ -155,32 +155,7 @@ def tests(self) -> dict[str, Callable[..., bool]]: return {} -class Template(Jinja2Template): - environment: Environment - - def new_context( - self, - vars: dict[str, Any] | None = None, - shared: bool = False, - locals: Mapping[str, Any] | None = None, - ) -> Context: - return new_context( - self.environment, - self.name, - self.blocks, - vars, - shared, - { - "citer": _Citer(), - "breadcrumbs": _Breadcrumbs(), - **self.globals, - }, - locals, - ) - - class Environment(Jinja2Environment): - template_class = Template globals: dict[str, Any] filters: dict[str, Callable[..., Any]] tests: dict[str, Callable[..., bool]] @@ -205,6 +180,7 @@ def __init__(self, app: App): ], ) + self._context_class: type[Context] | None = None self.app = app if app.project.configuration.debug: @@ -225,6 +201,35 @@ def _init_i18n(self) -> None: ) self.policies["ext.i18n.trimmed"] = True + @property + def context_class(self) -> type[Context]: # type: ignore[override] + if self._context_class is None: + + class _Context(Context): + def __init__( + self, + environment: Environment, + parent: dict[str, Any], + name: str | None, + blocks: dict[str, Callable[[Context], Iterator[str]]], + globals: MutableMapping[str, Any] | None = None, + ): + if "citer" not in parent: + parent["citer"] = _Citer() + if "breadcrumbs" not in parent: + parent["breadcrumbs"] = _Breadcrumbs() + super().__init__( + environment, + parent, + name, + blocks, + globals, + ) + + self._context_class = _Context + + return self._context_class + @pass_context def _gettext(self, context: Context, message: str) -> str: return context_localizer(context).gettext(message) @@ -280,9 +285,6 @@ def _init_extensions(self) -> None: self.tests.update(extension.tests) -Template.environment_class = Environment - - class Jinja2Renderer(Renderer): def __init__(self, environment: Environment, configuration: ProjectConfiguration): self._environment = environment