1
0
Fork 0

Adding upstream version 0.55+dfsg.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-04-16 09:02:47 +02:00
parent 8090869090
commit 902976bf20
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
14 changed files with 197 additions and 66 deletions

View file

@ -19,3 +19,15 @@ coverage:
.PHONY: types
types:
uv run pyright src/jinjax
.PHONY: docs
docs:
cd docs && uv run python docs.py
.PHONY: docs-build
docs-build:
cd docs && uv run python docs.py build
.PHONY: docs-deploy
docs-deploy:
cd docs && uv run sh deploy.sh

View file

@ -4,7 +4,7 @@ requires = ["setuptools"]
[project]
name = "jinjax"
version = "0.54"
version = "0.55"
description = "Replace your HTML templates with Python server-Side components"
authors = [
{name = "Juan Pablo Scaletti", email = "juanpablo@jpscaletti.com"},

View file

@ -1,3 +1,4 @@
from . import utils # noqa
from .catalog import Catalog
from .component import Component
from .exceptions import (

View file

@ -9,10 +9,18 @@ import jinja2
from markupsafe import Markup
from .component import Component
from .exceptions import ComponentNotFound, InvalidArgument
from .exceptions import ComponentNotFound, InvalidArgument, UnknownPrefix
from .html_attrs import HTMLAttrs
from .jinjax import JinjaX
from .utils import DELIMITER, SLASH, get_random_id, get_url_prefix, kebab_case, logger
from .utils import (
ARGS_PREFIX,
DELIMITER,
SLASH,
get_random_id,
get_url_prefix,
kebab_case,
logger,
)
if t.TYPE_CHECKING:
@ -25,6 +33,7 @@ DEFAULT_PREFIX = ""
DEFAULT_EXTENSION = ".jinja"
ARGS_ATTRS = "attrs"
ARGS_CONTENT = "content"
PREFIX_SEP = ":"
# Create ContextVars containers at module level
collected_css: dict[int, ContextVar[list[str]]] = {}
@ -488,7 +497,6 @@ class Catalog:
f"were parsed incorrectly as:\n {str(kw)}"
) from exc
args["__prefix"] = component.prefix
args[ARGS_CONTENT] = CallerWrapper(caller=caller, content=content)
return component.render(**args)
@ -605,7 +613,7 @@ class Catalog:
def _get_component(self, cname: str, **kw) -> Component:
source = kw.pop("_source", kw.pop("__source", ""))
file_ext = kw.pop("_file_ext", kw.pop("__file_ext", "")) or self.file_ext
caller_prefix = kw.pop("__prefix", "")
caller_prefix = kw.pop(ARGS_PREFIX, "")
prefix, name = self._split_name(cname)
component = None
@ -686,18 +694,18 @@ class Catalog:
return
path, relpath = paths
component = Component(name=name, prefix=prefix, path=path, relpath=relpath)
component.tmpl = self.jinja_env.get_template(str(relpath), globals=self.tmpl_globals)
component.tmpl = self.jinja_env.get_template(str(relpath.as_posix()), globals=self.tmpl_globals)
return component
def _split_name(self, cname: str) -> tuple[str, str]:
cname = cname.strip().strip(DELIMITER)
if DELIMITER not in cname:
if PREFIX_SEP not in cname:
return DEFAULT_PREFIX, cname
for prefix in self.prefixes.keys():
_prefix = f"{prefix}{DELIMITER}"
if cname.startswith(_prefix):
return prefix, cname.removeprefix(_prefix)
return DEFAULT_PREFIX, cname
prefix, cname = cname.split(PREFIX_SEP, 1)
if prefix not in self.prefixes:
raise UnknownPrefix(prefix)
return prefix, cname
def _get_component_path(
self,

View file

@ -12,7 +12,7 @@ from .exceptions import (
InvalidArgument,
MissingRequiredArgument,
)
from .utils import get_url_prefix
from .utils import ARGS_PREFIX, get_url_prefix
if t.TYPE_CHECKING:
@ -105,11 +105,11 @@ class Component:
self.load_metadata(source)
if path is not None and relpath is not None:
default_css = str(relpath.with_suffix(".css"))
default_css = str(relpath.with_suffix(".css").as_posix())
if (path.with_suffix(".css")).is_file():
self.css.extend(self.parse_files_expr(default_css))
default_js = str(relpath.with_suffix(".js"))
default_js = str(relpath.with_suffix(".js").as_posix())
if (path.with_suffix(".js")).is_file():
self.js.extend(self.parse_files_expr(default_js))
@ -254,6 +254,7 @@ class Component:
def render(self, **kwargs):
assert self.tmpl, f"Component {self.name} has no template"
kwargs.setdefault(ARGS_PREFIX, self.prefix)
html = self.tmpl.render(**kwargs).strip()
return Markup(html)

View file

@ -35,3 +35,14 @@ class InvalidArgument(Exception):
Raised when the arguments passed to the component cannot be parsed
by JinjaX because of an invalid syntax.
"""
class UnknownPrefix(Exception):
"""
Raised when a component is used/invoked with a prefix that is
not registered.
"""
def __init__(self, name: str) -> None:
msg = f"The prefix `{name}` is not registered"
super().__init__(msg)

View file

@ -6,19 +6,23 @@ from jinja2.exceptions import TemplateSyntaxError
from jinja2.ext import Extension
from jinja2.filters import do_forceescape
from .utils import logger
from .utils import ARGS_PREFIX, logger
RENDER_CMD = "catalog.irender"
BLOCK_CALL = '{% call(_slot="") [CMD]("[TAG]", __prefix=__prefix[ATTRS]) -%}[CONTENT]{%- endcall %}'
BLOCK_CALL = BLOCK_CALL.replace("[CMD]", RENDER_CMD)
INLINE_CALL = '{{ [CMD]("[TAG]", __prefix=__prefix[ATTRS]) }}'
INLINE_CALL = INLINE_CALL.replace("[CMD]", RENDER_CMD)
BLOCK_CALL = '{% call(_slot="") [CMD]("[TAG]", [ARGS_PREFIX]=[ARGS_PREFIX][ATTRS]) -%}[CONTENT]{%- endcall %}'
BLOCK_CALL = BLOCK_CALL.replace("[CMD]", RENDER_CMD).replace("[ARGS_PREFIX]", ARGS_PREFIX)
INLINE_CALL = '{{ [CMD]("[TAG]", [ARGS_PREFIX]=[ARGS_PREFIX][ATTRS]) }}'
INLINE_CALL = INLINE_CALL.replace("[CMD]", RENDER_CMD).replace("[ARGS_PREFIX]", ARGS_PREFIX)
re_raw = r"\{%-?\s*raw\s*-?%\}.+?\{%-?\s*endraw\s*-?%\}"
RX_RAW = re.compile(re_raw, re.DOTALL)
re_tag_name = r"([0-9A-Za-z_-]+\.)*[A-Z][0-9A-Za-z_-]*"
re_tag_prefix = r"([0-9A-Za-z_-]+\:)?"
re_tag_path = r"([0-9A-Za-z_-]+\.)*[A-Z][0-9A-Za-z_-]*"
re_tag_name = rf"{re_tag_prefix}{re_tag_path}"
re_raw_attrs = r"(?P<attrs>[^\>]*)"
re_tag = rf"<(?P<tag>{re_tag_name}){re_raw_attrs}\s*/?>"
RX_TAG = re.compile(re_tag)

View file

@ -40,7 +40,7 @@ class ComponentsMiddleware(WhiteNoise): # type: ignore
stem = fingerprinted.group(1)
relpath = relpath.with_name(f"{stem}{ext}")
return super().find_file(str(relpath))
return super().find_file(str(relpath.as_posix()))
def add_file_to_dictionary(
self, url: str, path: str, stat_cache: t.Any = None

View file

@ -8,6 +8,8 @@ logger = logging.getLogger("jinjax")
DELIMITER = "."
SLASH = "/"
ARGS_PREFIX = "__prefix"
def get_url_prefix(prefix: str) -> str:
url_prefix = prefix.strip().strip(f"{DELIMITER}{SLASH}").replace(DELIMITER, SLASH)

View file

@ -1,13 +1,34 @@
import jinja2
import pytest
from markupsafe import Markup
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_prefix_namespace(catalog, folder, folder_t, autoescape):
def test_render_prefixed(catalog, folder, folder_t, autoescape, undefined):
"""Components mounted with a prefix should be able to import other components
from the same folder without specifying the prefix.
"""
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
catalog.add_folder(folder_t, prefix="ui")
(folder / "Test.jinja").write_text("<ui:Title />")
(folder_t / "Title.jinja").write_text("prefix")
html = catalog.render("Test")
assert html == Markup("prefix")
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_prefix_namespace(catalog, folder, folder_t, autoescape, undefined):
"""Components mounted with a prefix should be able to import other components
from the same folder without specifying the prefix.
"""
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
catalog.add_folder(folder_t, prefix="ui")
(folder / "Title.jinja").write_text("parent")
@ -15,54 +36,60 @@ def test_prefix_namespace(catalog, folder, folder_t, autoescape):
(folder_t / "Title.jinja").write_text("prefix")
(folder_t / "Alert.jinja").write_text("<Title />")
html = catalog.render("ui.Alert")
html = catalog.render("ui:Alert")
assert html == Markup("prefix")
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_prefix_namespace_sub(catalog, folder, folder_t, autoescape):
def test_prefix_namespace_sub(catalog, folder, folder_t, autoescape, undefined):
"""Components mounted with a prefix should be able to import other components
from the same folder without specifying the prefix, even if those components
are in a subfolder.
"""
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
catalog.add_folder(folder_t, prefix="ui")
(folder / "sub").mkdir()
(folder_t / "sub").mkdir()
(folder / "Title.jinja").write_text("parent")
(folder / "sub").mkdir()
(folder / "sub" / "Title.jinja").write_text("sub/parent")
(folder_t / "Title.jinja").write_text("sub")
(folder_t / "sub").mkdir()
(folder_t / "sub" / "Title.jinja").write_text("sub/prefix")
(folder_t / "Alert.jinja").write_text("<sub.Title />")
html = catalog.render("ui.Alert")
html = catalog.render("ui:Alert")
assert html == Markup("sub/prefix")
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_prefix_fallback(catalog, folder, folder_t, autoescape):
def test_prefix_fallback(catalog, folder, folder_t, autoescape, undefined):
"""If a component is not found in the folder with the prefix, it should
fallback to the no-prefix folders.
"""
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
catalog.add_folder(folder_t, prefix="ui")
(folder / "Title.jinja").write_text("parent")
(folder_t / "Alert.jinja").write_text("<Title />")
html = catalog.render("ui.Alert")
html = catalog.render("ui:Alert")
assert html == Markup("parent")
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_prefix_namespace_assets(catalog, folder, folder_t, autoescape):
def test_prefix_namespace_assets(catalog, folder, folder_t, autoescape, undefined):
"""Components import without specifying the prefix should also be
able to auto-import their assets.
"""
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
catalog.add_folder(folder_t, prefix="ui")
(folder_t / "Title.jinja").write_text("prefix")
@ -73,7 +100,7 @@ def test_prefix_namespace_assets(catalog, folder, folder_t, autoescape):
""")
(folder_t / "Alert.jinja").write_text("<Layout><Title /></Layout>")
html = catalog.render("ui.Alert")
html = catalog.render("ui:Alert")
assert html == Markup("""
<link rel="stylesheet" href="/static/components/ui/Title.css">
prefix

View file

@ -9,9 +9,11 @@ from markupsafe import Markup
import jinjax
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_render_simple(catalog, folder, autoescape):
def test_render_simple(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Greeting.jinja").write_text(
"""
@ -23,9 +25,11 @@ def test_render_simple(catalog, folder, autoescape):
assert html == Markup('<div class="greeting [&_a]:flex">Hello world!</div>')
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_render_source(catalog, autoescape):
def test_render_source(catalog, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
source = '{#def message #}\n<div class="greeting [&_a]:flex">{{ message }}</div>'
expected = Markup('<div class="greeting [&_a]:flex">Hello world!</div>')
@ -38,9 +42,11 @@ def test_render_source(catalog, autoescape):
assert expected == html
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_render_content(catalog, folder, autoescape):
def test_render_content(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Card.jinja").write_text("""
<section class="card">
@ -60,6 +66,7 @@ def test_render_content(catalog, folder, autoescape):
assert expected == html
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
@pytest.mark.parametrize(
"source, expected",
@ -76,8 +83,10 @@ def test_render_mix_of_contentful_and_contentless_components(
source,
expected,
autoescape,
undefined,
):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Icon.jinja").write_text('<i class="icon"></i>')
(folder / "Title.jinja").write_text("<h1>{{ content }}</h1>")
@ -87,9 +96,11 @@ def test_render_mix_of_contentful_and_contentless_components(
assert html == Markup(expected)
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_composition(catalog, folder, autoescape):
def test_composition(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Greeting.jinja").write_text(
"""
@ -138,9 +149,11 @@ def test_composition(catalog, folder, autoescape):
)
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_just_properties(catalog, folder, autoescape):
def test_just_properties(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Lorem.jinja").write_text(
"""
@ -181,9 +194,11 @@ def test_just_properties(catalog, folder, autoescape):
)
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_global_values(catalog, folder, autoescape):
def test_global_values(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Global.jinja").write_text("""{{ globalvar }}""")
message = "Hello world!"
@ -193,9 +208,11 @@ def test_global_values(catalog, folder, autoescape):
assert message in html
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_required_attr_are_required(catalog, folder, autoescape):
def test_required_attr_are_required(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Greeting.jinja").write_text(
"""
@ -208,12 +225,14 @@ def test_required_attr_are_required(catalog, folder, autoescape):
catalog.render("Greeting")
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_subfolder(catalog, folder, autoescape):
def test_subfolder(catalog, folder, autoescape, undefined):
"""Components can be organized in subfolders and called
using the dot notation.
"""
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
sub = folder / "ui"
sub.mkdir()
@ -224,9 +243,11 @@ def test_subfolder(catalog, folder, autoescape):
assert html == Markup('<div class="tab">Meh</div>')
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_default_attr(catalog, folder, autoescape):
def test_default_attr(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Greeting.jinja").write_text(
"""
@ -259,9 +280,11 @@ def test_default_attr(catalog, folder, autoescape):
)
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_raw_content(catalog, folder, autoescape):
def test_raw_content(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Code.jinja").write_text("""
<pre class="code">
@ -293,9 +316,11 @@ def test_raw_content(catalog, folder, autoescape):
)
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_multiple_raw(catalog, folder, autoescape):
def test_multiple_raw(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "C.jinja").write_text("""
<div {{ attrs.render() }}></div>
@ -325,9 +350,11 @@ def test_multiple_raw(catalog, folder, autoescape):
)
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_check_for_unclosed(catalog, folder, autoescape):
def test_check_for_unclosed(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Lorem.jinja").write_text("""
{#def ipsum=False #}
@ -348,9 +375,11 @@ def test_check_for_unclosed(catalog, folder, autoescape):
raise
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_dict_as_attr(catalog, folder, autoescape):
def test_dict_as_attr(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "CitiesList.jinja").write_text("""
{#def cities #}
@ -370,9 +399,11 @@ def test_dict_as_attr(catalog, folder, autoescape):
assert html == Markup("<p>Lima, Peru</p><p>New York, USA</p>")
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_do_not_mess_with_external_jinja_env(folder_t, folder, autoescape):
"""https://github.com/jpsca/jinjax/issues/19"""
def test_do_not_mess_with_external_jinja_env(folder_t, folder, autoescape, undefined):
"""Fix https://github.com/jpsca/jinjax/issues/19
"""
(folder_t / "greeting.html").write_text("Jinja still works")
(folder / "Greeting.jinja").write_text("JinjaX works")
@ -384,6 +415,7 @@ def test_do_not_mess_with_external_jinja_env(folder_t, folder, autoescape):
jinja_env.filters = {"fil": lambda x: x}
jinja_env.tests = {"tes": lambda x: x}
jinja_env.autoescape = autoescape
jinja_env.undefined = undefined
catalog = jinjax.Catalog(
jinja_env=jinja_env,
@ -422,9 +454,11 @@ def test_do_not_mess_with_external_jinja_env(folder_t, folder, autoescape):
assert "jinja2.ext.DebugExtension" not in jinja_env.extensions
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_auto_reload(catalog, folder, autoescape):
def test_auto_reload(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Layout.jinja").write_text("""
<html>
@ -482,9 +516,11 @@ def test_auto_reload(catalog, folder, autoescape):
)
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_subcomponents(catalog, folder, autoescape):
def test_subcomponents(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
"""Issue https://github.com/jpsca/jinjax/issues/32"""
(folder / "Page.jinja").write_text("""
@ -520,9 +556,11 @@ def test_subcomponents(catalog, folder, autoescape):
assert html == Markup(expected.strip())
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_colon_in_attrs(catalog, folder, autoescape):
def test_colon_in_attrs(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "C.jinja").write_text("""
<div {{ attrs.render() }}></div>
@ -537,9 +575,11 @@ def test_colon_in_attrs(catalog, folder, autoescape):
assert """<div hx-on:click="show = !show"></div>""" in html
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_template_globals(catalog, folder, autoescape):
def test_template_globals(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Input.jinja").write_text("""
{# def name, value #}<input type="text" name="{{name}}" value="{{value}}">
@ -563,9 +603,11 @@ def test_template_globals(catalog, folder, autoescape):
assert """<input type="hidden" name="csrft" value="abc">""" in html
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_template_globals_update_cache(catalog, folder, autoescape):
def test_template_globals_update_cache(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "CsrfToken.jinja").write_text(
"""<input type="hidden" name="csrft" value="{{csrf_token}}">"""
@ -581,9 +623,11 @@ def test_template_globals_update_cache(catalog, folder, autoescape):
assert """<input type="hidden" name="csrft" value="xyz">""" in html
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_alpine_sintax(catalog, folder, autoescape):
def test_alpine_sintax(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Greeting.jinja").write_text("""
{#def message #}
@ -595,9 +639,11 @@ def test_alpine_sintax(catalog, folder, autoescape):
assert html == Markup(expected)
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_alpine_sintax_in_component(catalog, folder, autoescape):
def test_alpine_sintax_in_component(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Button.jinja").write_text(
"""<button {{ attrs.render() }}>{{ content }}</button>"""
@ -613,9 +659,11 @@ def test_alpine_sintax_in_component(catalog, folder, autoescape):
assert html == Markup(expected)
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_autoescaped_attrs(catalog, folder, autoescape):
def test_autoescaped_attrs(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "CheckboxItem.jinja").write_text(
"""<div {{ attrs.render(class="relative") }}></div>"""
@ -708,9 +756,11 @@ def test_autoescaped_attrs(catalog, folder, autoescape):
),
],
)
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_strip_comment(catalog, folder, autoescape, template):
def test_strip_comment(catalog, folder, autoescape, template, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "A.jinja").write_text(template)
@ -767,9 +817,11 @@ def test_mixed_syntax(catalog, folder):
assert html == Markup(expected)
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_slots(catalog, folder, autoescape):
def test_slots(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Component.jinja").write_text(
"""
@ -804,9 +856,11 @@ def test_slots(catalog, folder, autoescape):
assert html == Markup(expected)
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_kebab_cased_component_names(catalog, folder, autoescape):
def test_kebab_cased_component_names(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "a_tricky-FOLDER").mkdir()
(folder / "kebab-folder").mkdir()

View file

@ -1,12 +1,15 @@
from pathlib import Path
import jinja2
import pytest
from markupsafe import Markup
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_render_assets(catalog, folder, autoescape):
def test_render_assets(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Greeting.jinja").write_text(
"""
@ -72,9 +75,11 @@ def test_render_assets(catalog, folder, autoescape):
)
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_cleanup_assets(catalog, folder, autoescape):
def test_cleanup_assets(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Layout.jinja").write_text("""
<html>
@ -122,9 +127,11 @@ def test_cleanup_assets(catalog, folder, autoescape):
)
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_fingerprint_assets(catalog, folder: Path, autoescape):
def test_fingerprint_assets(catalog, folder: Path, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Layout.jinja").write_text("""
<html>
@ -150,9 +157,11 @@ def test_fingerprint_assets(catalog, folder: Path, autoescape):
assert 'href="http://example.com/super.css' in html
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_auto_load_assets_with_same_name(catalog, folder, autoescape):
def test_auto_load_assets_with_same_name(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Layout.jinja").write_text(
"""{{ catalog.render_assets() }}\n{{ content }}"""
@ -194,9 +203,11 @@ def test_auto_load_assets_with_same_name(catalog, folder, autoescape):
assert html == Markup(expected)
@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined])
@pytest.mark.parametrize("autoescape", [True, False])
def test_auto_load_assets_for_kebab_cased_names(catalog, folder, autoescape):
def test_auto_load_assets_for_kebab_cased_names(catalog, folder, autoescape, undefined):
catalog.jinja_env.autoescape = autoescape
catalog.jinja_env.undefined = undefined
(folder / "Layout.jinja").write_text(
"""{{ catalog.render_assets() }}\n{{ content }}"""

View file

@ -26,8 +26,8 @@ class ThreadWithReturnValue(Thread):
if self._target is not None:
self._return = self._target(*self._args, **self._kwargs)
def join(self, *args):
Thread.join(self, *args)
def join(self, *args, **kwargs):
Thread.join(self, *args, **kwargs)
return self._return

2
uv.lock generated
View file

@ -215,7 +215,7 @@ wheels = [
[[package]]
name = "jinjax"
version = "0.54"
version = "0.55"
source = { editable = "." }
dependencies = [
{ name = "jinja2" },