diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..05e8271 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: jpsca diff --git a/MIT-LICENSE b/MIT-LICENSE index ae168dc..eec0ba1 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2022 Juan-Pablo Scaletti +Copyright (c) Juan-Pablo Scaletti Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/benchmark/benchmark.py b/benchmark/benchmark.py index b939952..349be28 100644 --- a/benchmark/benchmark.py +++ b/benchmark/benchmark.py @@ -1,3 +1,7 @@ +""" +JinjaX Benchmark +Copyright (c) Juan-Pablo Scaletti +""" import timeit from pathlib import Path diff --git a/benchmark/profile.py b/benchmark/profile.py index 8c88ad0..aa5f398 100644 --- a/benchmark/profile.py +++ b/benchmark/profile.py @@ -1,3 +1,7 @@ +""" +JinjaX Benchmark +Copyright (c) Juan-Pablo Scaletti +""" from pathlib import Path from jinjax import Catalog, Component diff --git a/pyproject.toml b/pyproject.toml index 8e56b9b..5ff208c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ requires = ["setuptools"] [project] name = "jinjax" -version = "0.55" +version = "0.56" description = "Replace your HTML templates with Python server-Side components" authors = [ {name = "Juan Pablo Scaletti", email = "juanpablo@jpscaletti.com"}, @@ -45,7 +45,7 @@ documentation = "https://jinjax.scaletti.dev/guides/" [dependency-groups] dev = [ "ipdb >= 0.13", - "pyright >= 1.1", + "pyright >= 1.1.400", "pre-commit", "ruff >= 0.2.0", "tox-uv", diff --git a/src/jinjax/__init__.py b/src/jinjax/__init__.py index 390ccd6..95592e9 100644 --- a/src/jinjax/__init__.py +++ b/src/jinjax/__init__.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" from . import utils # noqa from .catalog import Catalog from .component import Component diff --git a/src/jinjax/catalog.py b/src/jinjax/catalog.py index 514576f..777de48 100644 --- a/src/jinjax/catalog.py +++ b/src/jinjax/catalog.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" import os import typing as t from collections import UserString @@ -40,6 +44,8 @@ collected_css: dict[int, ContextVar[list[str]]] = {} collected_js: dict[int, ContextVar[list[str]]] = {} tmpl_globals: dict[int, ContextVar[dict[str, t.Any]]] = {} +RelPath = Path + class CallerWrapper(UserString): _content = "" @@ -590,7 +596,7 @@ class Catalog: html_js.append(f'') rendered_urls.add(full_url) - return Markup("\n".join(html_css + html_js)) + return Markup("\n".join(sorted(html_css) + sorted(html_js))) # Private @@ -712,7 +718,7 @@ class Catalog: prefix: str, name: str, file_ext: str, - ) -> tuple[Path, Path] | None: + ) -> tuple[Path, RelPath] | None: root_paths = self.prefixes[prefix].searchpath name = name.replace(DELIMITER, SLASH) @@ -724,11 +730,21 @@ class Catalog: root_path, topdown=False, followlinks=True ): relfolder = os.path.relpath(curr_folder, root_path).strip(".") - if relfolder and not ( - name.startswith(relfolder) - or kebab_name.startswith(relfolder) - ): - continue + if relfolder: + if not ( + name.startswith(relfolder) + or kebab_name.startswith(relfolder) + ): + continue + + # Allow for index.jinja files in subfolders + # to be used as the folder name + if relfolder in (name, kebab_name): + filename = f"index{file_ext}" + fullpath = Path(curr_folder) / filename + if fullpath.is_file(): + relpath = Path(f"{relfolder}/{filename}") + return fullpath, relpath for filename in files: if relfolder: @@ -737,7 +753,10 @@ class Catalog: filepath = filename if filepath.startswith(dot_names) and filepath.endswith(file_ext): - return Path(curr_folder) / filename, Path(filepath) + fullpath = Path(curr_folder) / filename + relpath = Path(filepath) + if fullpath.is_file(): + return fullpath, relpath def _render_attrs(self, attrs: dict[str, t.Any]) -> Markup: html_attrs = [] diff --git a/src/jinjax/component.py b/src/jinjax/component.py index b2b5afd..1df6f10 100644 --- a/src/jinjax/component.py +++ b/src/jinjax/component.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" import ast import re import typing as t diff --git a/src/jinjax/exceptions.py b/src/jinjax/exceptions.py index 7f0fffe..366c765 100644 --- a/src/jinjax/exceptions.py +++ b/src/jinjax/exceptions.py @@ -1,3 +1,8 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" + class ComponentNotFound(Exception): """ Raised when JinjaX can't find a component by name in none of the diff --git a/src/jinjax/html_attrs.py b/src/jinjax/html_attrs.py index 7f0f47a..70c1db5 100644 --- a/src/jinjax/html_attrs.py +++ b/src/jinjax/html_attrs.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" import re import typing as t from collections import UserString diff --git a/src/jinjax/jinjax.py b/src/jinjax/jinjax.py index 65e225d..5352d03 100644 --- a/src/jinjax/jinjax.py +++ b/src/jinjax/jinjax.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" import re import typing as t from uuid import uuid4 diff --git a/src/jinjax/middleware.py b/src/jinjax/middleware.py index d83e60b..f7470c9 100644 --- a/src/jinjax/middleware.py +++ b/src/jinjax/middleware.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" import re import typing as t from pathlib import Path diff --git a/src/jinjax/utils.py b/src/jinjax/utils.py index 3bfe148..9547aa1 100644 --- a/src/jinjax/utils.py +++ b/src/jinjax/utils.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" import logging import re import uuid diff --git a/tests/conftest.py b/tests/conftest.py index 6256373..557ee35 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" import pytest import jinjax diff --git a/tests/test_catalog.py b/tests/test_catalog.py index d74e0dc..51d9452 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" import pytest import jinjax diff --git a/tests/test_component.py b/tests/test_component.py index 9961b21..9a538f9 100644 --- a/tests/test_component.py +++ b/tests/test_component.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" import pytest from jinjax import Component, DuplicateDefDeclaration, InvalidArgument diff --git a/tests/test_html_attrs.py b/tests/test_html_attrs.py index bc1a68f..63326f4 100644 --- a/tests/test_html_attrs.py +++ b/tests/test_html_attrs.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" import pytest from jinjax.html_attrs import HTMLAttrs diff --git a/tests/test_middleware.py b/tests/test_middleware.py index b1fa9a9..275799d 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" import typing as t from pathlib import Path diff --git a/tests/test_prefix.py b/tests/test_prefix.py index a4b6cf8..a0ba934 100644 --- a/tests/test_prefix.py +++ b/tests/test_prefix.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" import jinja2 import pytest from markupsafe import Markup diff --git a/tests/test_render.py b/tests/test_render.py index 0550028..5395569 100644 --- a/tests/test_render.py +++ b/tests/test_render.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" import time from textwrap import dedent @@ -243,6 +247,24 @@ def test_subfolder(catalog, folder, autoescape, undefined): assert html == Markup('
Meh
') +@pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined]) +@pytest.mark.parametrize("autoescape", [True, False]) +def test_subfolder_index_file(catalog, folder, autoescape, undefined): + """Components named "index.jinja" in subfolders can be called + using the subfolder names. + """ + catalog.jinja_env.autoescape = autoescape + catalog.jinja_env.undefined = undefined + + sub = folder / "tab" + sub.mkdir() + (sub / "index.jinja").write_text("Hello") + (sub / "panel.jinja").write_text("World") + + assert catalog.render("Tab") == Markup("Hello") + assert catalog.render("Tab.Panel") == Markup("World") + + @pytest.mark.parametrize("undefined", [jinja2.Undefined, jinja2.StrictUndefined]) @pytest.mark.parametrize("autoescape", [True, False]) def test_default_attr(catalog, folder, autoescape, undefined): diff --git a/tests/test_render_assets.py b/tests/test_render_assets.py index af1f862..8db1da8 100644 --- a/tests/test_render_assets.py +++ b/tests/test_render_assets.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" from pathlib import Path import jinja2 @@ -57,14 +61,14 @@ def test_render_assets(catalog, folder, autoescape, undefined): assert ( """ - - - + + +
Hello
@@ -195,8 +199,8 @@ def test_auto_load_assets_with_same_name(catalog, folder, autoescape, undefined) - +
""".strip() diff --git a/tests/test_thread_safety.py b/tests/test_thread_safety.py index d210be0..3ef907b 100644 --- a/tests/test_thread_safety.py +++ b/tests/test_thread_safety.py @@ -1,3 +1,7 @@ +""" +JinjaX +Copyright (c) Juan-Pablo Scaletti +""" from threading import Thread from markupsafe import Markup diff --git a/uv.lock b/uv.lock index fcff473..c3b9507 100644 --- a/uv.lock +++ b/uv.lock @@ -215,7 +215,7 @@ wheels = [ [[package]] name = "jinjax" -version = "0.55" +version = "0.56" source = { editable = "." } dependencies = [ { name = "jinja2" }, @@ -254,7 +254,7 @@ provides-extras = ["whitenoise"] dev = [ { name = "ipdb", specifier = ">=0.13" }, { name = "pre-commit" }, - { name = "pyright", specifier = ">=1.1" }, + { name = "pyright", specifier = ">=1.1.400" }, { name = "ruff", specifier = ">=0.2.0" }, { name = "tox-uv" }, ] @@ -451,15 +451,15 @@ wheels = [ [[package]] name = "pyright" -version = "1.1.396" +version = "1.1.400" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bd/73/f20cb1dea1bdc1774e7f860fb69dc0718c7d8dea854a345faec845eb086a/pyright-1.1.396.tar.gz", hash = "sha256:142901f5908f5a0895be3d3befcc18bedcdb8cc1798deecaec86ef7233a29b03", size = 3814400 } +sdist = { url = "https://files.pythonhosted.org/packages/6c/cb/c306618a02d0ee8aed5fb8d0fe0ecfed0dbf075f71468f03a30b5f4e1fe0/pyright-1.1.400.tar.gz", hash = "sha256:b8a3ba40481aa47ba08ffb3228e821d22f7d391f83609211335858bf05686bdb", size = 3846546 } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/be/ecb7cfb42d242b7ee764b52e6ff4782beeec00e3b943a3ec832b281f9da6/pyright-1.1.396-py3-none-any.whl", hash = "sha256:c635e473095b9138c471abccca22b9fedbe63858e0b40d4fc4b67da041891844", size = 5689355 }, + { url = "https://files.pythonhosted.org/packages/c8/a5/5d285e4932cf149c90e3c425610c5efaea005475d5f96f1bfdb452956c62/pyright-1.1.400-py3-none-any.whl", hash = "sha256:c80d04f98b5a4358ad3a35e241dbf2a408eee33a40779df365644f8054d2517e", size = 5563460 }, ] [[package]]