fstache 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fstache-0.1.0/LICENSE +12 -0
- fstache-0.1.0/PKG-INFO +102 -0
- fstache-0.1.0/README.md +78 -0
- fstache-0.1.0/pyproject.toml +38 -0
- fstache-0.1.0/src/fstache/__init__.py +86 -0
- fstache-0.1.0/src/fstache/_compiler.py +946 -0
- fstache-0.1.0/src/fstache/_factories.py +368 -0
- fstache-0.1.0/src/fstache/_inliner.py +116 -0
- fstache-0.1.0/src/fstache/_missing.py +66 -0
- fstache-0.1.0/src/fstache/_renderer.py +635 -0
- fstache-0.1.0/src/fstache/py.typed +1 -0
fstache-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Zero-Clause BSD
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
4
|
+
purpose with or without fee is hereby granted.
|
|
5
|
+
|
|
6
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
7
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
8
|
+
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
9
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
10
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
11
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
12
|
+
PERFORMANCE OF THIS SOFTWARE.
|
fstache-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fstache
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A fast and realistic Mustache-based HTML generator
|
|
5
|
+
Author: vkorobkov
|
|
6
|
+
License-Expression: 0BSD
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
12
|
+
Requires-Dist: ruff>=0.6.0 ; extra == 'dev'
|
|
13
|
+
Requires-Dist: chevron==0.14.0 ; extra == 'dev'
|
|
14
|
+
Requires-Dist: mstache==0.3.0 ; extra == 'dev'
|
|
15
|
+
Requires-Dist: pystache==0.6.8 ; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest ; extra == 'dev'
|
|
17
|
+
Requires-Dist: ty ; extra == 'dev'
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Project-URL: Homepage, https://github.com/servletcloud/fstache
|
|
20
|
+
Project-URL: Repository, https://github.com/servletcloud/fstache
|
|
21
|
+
Project-URL: Issues, https://github.com/servletcloud/fstache/issues
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# Fstache
|
|
26
|
+
|
|
27
|
+
## Overview
|
|
28
|
+
|
|
29
|
+
- Dependency-free [Mustache](https://mustache.github.io/) renderer for
|
|
30
|
+
[Python 3.12+](https://docs.python.org/3.12/).
|
|
31
|
+
- Supports [upstream Mustache spec fixtures](https://github.com/mustache/spec),
|
|
32
|
+
including lambdas and dynamic partial names; **inheritance is unsupported**.
|
|
33
|
+
- Built for speed; [benchmarked](#benchmarks) at 3.3x
|
|
34
|
+
[Chevron](https://github.com/noahmorrison/chevron), 3.1x
|
|
35
|
+
[mstache](https://pypi.org/project/mstache/), and 3.5x
|
|
36
|
+
[Pystache](https://github.com/defunkt/pystache).
|
|
37
|
+
- [PEP 561](https://peps.python.org/pep-0561/) typed package.
|
|
38
|
+
- Supports Python mappings, objects, sequences, and callables as render data.
|
|
39
|
+
- Static partials are preloaded and inlined, eliminating render-time boundary overhead.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
## Benchmarks
|
|
43
|
+
|
|
44
|
+
### Renderer Throughput
|
|
45
|
+
|
|
46
|
+
| Library | Renders per second | Indentation details |
|
|
47
|
+
| --- | ---: | --- |
|
|
48
|
+
| Fstache | 4066.5 | **Deviates**: `ignore_indents=True` skips standard standalone partial reindentation. |
|
|
49
|
+
| Fstache | 3102.2 | Follows standard standalone partial indentation. |
|
|
50
|
+
| mstache | 1339.0 | **Deviates**: `keep_lines=True` keeps tag-only lines instead of collapsing them, so partial indentation is not reapplied to every partial line. |
|
|
51
|
+
| mstache | 985.3 | Follows standard standalone partial indentation. |
|
|
52
|
+
| Chevron | 951.1 | Follows standard standalone partial indentation. |
|
|
53
|
+
| Pystache | 898.8 | Follows standard standalone partial indentation. |
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
### Benchmark environment
|
|
57
|
+
|
|
58
|
+
| Field | Value |
|
|
59
|
+
| --- | --- |
|
|
60
|
+
| Python | CPython 3.14.6 |
|
|
61
|
+
| OS | Fedora Linux 44 (Workstation Edition), Linux 7.0.12-201.fc44.x86_64 |
|
|
62
|
+
| CPU | AMD Ryzen 7 8845HS w/ Radeon 780M Graphics, 8 cores / 16 threads |
|
|
63
|
+
| Compared versions | Fstache 0.1.0, Chevron 0.14.0, mstache 0.3.0, Pystache 0.6.8 |
|
|
64
|
+
| Command | `RENDERER=<renderer> uv run --python 3.14 --extra dev python tests/perf_test.py`<br>`<renderer>` values: `fstache.no_indentation`, `fstache`, `mstache.no_indentation`, `mstache`, `chevron`, `pystache` |
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
### Methodology
|
|
68
|
+
|
|
69
|
+
- The benchmark renders a realistic, heavy marketing/docs HTML page from the
|
|
70
|
+
[source Mustache templates](demo/templates), [JSON data](demo/data.json), and
|
|
71
|
+
[perf script](tests/perf_test.py):
|
|
72
|
+
- About 100 KiB of [rendered HTML](https://servletcloud.github.io/fstache/)
|
|
73
|
+
([source](demo/dist/index.html)) from 15 Mustache templates, including 14
|
|
74
|
+
partial files.
|
|
75
|
+
- Tailwind CSS v4 utility-heavy markup with Alpine.js attributes, inline SVG
|
|
76
|
+
icons, responsive navigation, cards, tables, accordions, and form controls.
|
|
77
|
+
- About 15 KiB of JSON context data with nested arrays for navigation,
|
|
78
|
+
feature cards, testimonials, a recursive docs tree, comparison rows, blog
|
|
79
|
+
posts, changelog entries, FAQs, and pricing plans.
|
|
80
|
+
- 42 section, inverted-section, and partial references, including 24 section
|
|
81
|
+
tags for loops and conditionals plus recursive `node` partial rendering.
|
|
82
|
+
- The benchmark isolates render throughput from setup work:
|
|
83
|
+
- Each engine preloads the template files and partials during setup.
|
|
84
|
+
- Each engine uses the closest available precompiled, preparsed, or
|
|
85
|
+
pretokenized representation for the layout and partials.
|
|
86
|
+
- The timed render loop excludes disk I/O and one-time template preparation.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Utility Commands
|
|
91
|
+
|
|
92
|
+
### Generate Reference HTML
|
|
93
|
+
Builds and renders the templates from `./demo` into `./demo/dist/index.html` (performing file I/O):
|
|
94
|
+
```bash
|
|
95
|
+
make build-html
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Format and Lint
|
|
99
|
+
Runs ruff checks and auto-formats the project:
|
|
100
|
+
```bash
|
|
101
|
+
make post-ai-change
|
|
102
|
+
```
|
fstache-0.1.0/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Fstache
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
- Dependency-free [Mustache](https://mustache.github.io/) renderer for
|
|
6
|
+
[Python 3.12+](https://docs.python.org/3.12/).
|
|
7
|
+
- Supports [upstream Mustache spec fixtures](https://github.com/mustache/spec),
|
|
8
|
+
including lambdas and dynamic partial names; **inheritance is unsupported**.
|
|
9
|
+
- Built for speed; [benchmarked](#benchmarks) at 3.3x
|
|
10
|
+
[Chevron](https://github.com/noahmorrison/chevron), 3.1x
|
|
11
|
+
[mstache](https://pypi.org/project/mstache/), and 3.5x
|
|
12
|
+
[Pystache](https://github.com/defunkt/pystache).
|
|
13
|
+
- [PEP 561](https://peps.python.org/pep-0561/) typed package.
|
|
14
|
+
- Supports Python mappings, objects, sequences, and callables as render data.
|
|
15
|
+
- Static partials are preloaded and inlined, eliminating render-time boundary overhead.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Benchmarks
|
|
19
|
+
|
|
20
|
+
### Renderer Throughput
|
|
21
|
+
|
|
22
|
+
| Library | Renders per second | Indentation details |
|
|
23
|
+
| --- | ---: | --- |
|
|
24
|
+
| Fstache | 4066.5 | **Deviates**: `ignore_indents=True` skips standard standalone partial reindentation. |
|
|
25
|
+
| Fstache | 3102.2 | Follows standard standalone partial indentation. |
|
|
26
|
+
| mstache | 1339.0 | **Deviates**: `keep_lines=True` keeps tag-only lines instead of collapsing them, so partial indentation is not reapplied to every partial line. |
|
|
27
|
+
| mstache | 985.3 | Follows standard standalone partial indentation. |
|
|
28
|
+
| Chevron | 951.1 | Follows standard standalone partial indentation. |
|
|
29
|
+
| Pystache | 898.8 | Follows standard standalone partial indentation. |
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### Benchmark environment
|
|
33
|
+
|
|
34
|
+
| Field | Value |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| Python | CPython 3.14.6 |
|
|
37
|
+
| OS | Fedora Linux 44 (Workstation Edition), Linux 7.0.12-201.fc44.x86_64 |
|
|
38
|
+
| CPU | AMD Ryzen 7 8845HS w/ Radeon 780M Graphics, 8 cores / 16 threads |
|
|
39
|
+
| Compared versions | Fstache 0.1.0, Chevron 0.14.0, mstache 0.3.0, Pystache 0.6.8 |
|
|
40
|
+
| Command | `RENDERER=<renderer> uv run --python 3.14 --extra dev python tests/perf_test.py`<br>`<renderer>` values: `fstache.no_indentation`, `fstache`, `mstache.no_indentation`, `mstache`, `chevron`, `pystache` |
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Methodology
|
|
44
|
+
|
|
45
|
+
- The benchmark renders a realistic, heavy marketing/docs HTML page from the
|
|
46
|
+
[source Mustache templates](demo/templates), [JSON data](demo/data.json), and
|
|
47
|
+
[perf script](tests/perf_test.py):
|
|
48
|
+
- About 100 KiB of [rendered HTML](https://servletcloud.github.io/fstache/)
|
|
49
|
+
([source](demo/dist/index.html)) from 15 Mustache templates, including 14
|
|
50
|
+
partial files.
|
|
51
|
+
- Tailwind CSS v4 utility-heavy markup with Alpine.js attributes, inline SVG
|
|
52
|
+
icons, responsive navigation, cards, tables, accordions, and form controls.
|
|
53
|
+
- About 15 KiB of JSON context data with nested arrays for navigation,
|
|
54
|
+
feature cards, testimonials, a recursive docs tree, comparison rows, blog
|
|
55
|
+
posts, changelog entries, FAQs, and pricing plans.
|
|
56
|
+
- 42 section, inverted-section, and partial references, including 24 section
|
|
57
|
+
tags for loops and conditionals plus recursive `node` partial rendering.
|
|
58
|
+
- The benchmark isolates render throughput from setup work:
|
|
59
|
+
- Each engine preloads the template files and partials during setup.
|
|
60
|
+
- Each engine uses the closest available precompiled, preparsed, or
|
|
61
|
+
pretokenized representation for the layout and partials.
|
|
62
|
+
- The timed render loop excludes disk I/O and one-time template preparation.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Utility Commands
|
|
67
|
+
|
|
68
|
+
### Generate Reference HTML
|
|
69
|
+
Builds and renders the templates from `./demo` into `./demo/dist/index.html` (performing file I/O):
|
|
70
|
+
```bash
|
|
71
|
+
make build-html
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Format and Lint
|
|
75
|
+
Runs ruff checks and auto-formats the project:
|
|
76
|
+
```bash
|
|
77
|
+
make post-ai-change
|
|
78
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build>=0.11.25,<0.12"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fstache"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A fast and realistic Mustache-based HTML generator"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
license = "0BSD"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "vkorobkov" }]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
|
+
"Programming Language :: Python :: 3.13",
|
|
18
|
+
"Programming Language :: Python :: 3.14",
|
|
19
|
+
]
|
|
20
|
+
dependencies = []
|
|
21
|
+
|
|
22
|
+
[project.optional-dependencies]
|
|
23
|
+
dev = [
|
|
24
|
+
"ruff>=0.6.0",
|
|
25
|
+
"chevron==0.14.0",
|
|
26
|
+
"mstache==0.3.0",
|
|
27
|
+
"pystache==0.6.8",
|
|
28
|
+
"pytest",
|
|
29
|
+
"ty"
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[tool.uv]
|
|
33
|
+
exclude-newer = "24 hours"
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/servletcloud/fstache"
|
|
37
|
+
Repository = "https://github.com/servletcloud/fstache"
|
|
38
|
+
Issues = "https://github.com/servletcloud/fstache/issues"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Public API for compiling and rendering Fstache templates.
|
|
2
|
+
|
|
3
|
+
The common entry points are :func:`compile`, :func:`render`, and the
|
|
4
|
+
filesystem-backed renderer factories such as :func:`create_renderer`.
|
|
5
|
+
`CompiledTemplate` values are intentionally treated as opaque by the public API:
|
|
6
|
+
loaders, resolvers, and renderers pass them around, but callers should not rely
|
|
7
|
+
on the private node dataclasses unless a future release explicitly promotes
|
|
8
|
+
compiled-template introspection.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from ._compiler import (
|
|
12
|
+
DEFAULT_DELIMITERS as DEFAULT_DELIMITERS,
|
|
13
|
+
EMPTY_TEMPLATE as EMPTY_TEMPLATE,
|
|
14
|
+
CompiledTemplate as CompiledTemplate,
|
|
15
|
+
Delimiters as Delimiters,
|
|
16
|
+
InvalidDelimiterError as InvalidDelimiterError,
|
|
17
|
+
InvalidNameError as InvalidNameError,
|
|
18
|
+
SectionSyntaxError as SectionSyntaxError,
|
|
19
|
+
TemplateSyntaxError as TemplateSyntaxError,
|
|
20
|
+
UnclosedTagError as UnclosedTagError,
|
|
21
|
+
UnsupportedTagError as UnsupportedTagError,
|
|
22
|
+
compile as compile,
|
|
23
|
+
)
|
|
24
|
+
from ._factories import (
|
|
25
|
+
TemplateRenderer as TemplateRenderer,
|
|
26
|
+
create_dev_renderer as create_dev_renderer,
|
|
27
|
+
create_prod_renderer as create_prod_renderer,
|
|
28
|
+
create_renderer as create_renderer,
|
|
29
|
+
create_test_renderer as create_test_renderer,
|
|
30
|
+
)
|
|
31
|
+
from ._inliner import inline_partials as inline_partials
|
|
32
|
+
from ._missing import (
|
|
33
|
+
MissingTemplateError as MissingTemplateError,
|
|
34
|
+
MissingTemplateResolver as MissingTemplateResolver,
|
|
35
|
+
MissingVariableError as MissingVariableError,
|
|
36
|
+
MissingVariableResolver as MissingVariableResolver,
|
|
37
|
+
resolve_missing_template_as_empty as resolve_missing_template_as_empty,
|
|
38
|
+
resolve_missing_template_as_error as resolve_missing_template_as_error,
|
|
39
|
+
resolve_missing_variable_as_error as resolve_missing_variable_as_error,
|
|
40
|
+
resolve_missing_variable_as_none as resolve_missing_variable_as_none,
|
|
41
|
+
)
|
|
42
|
+
from ._renderer import (
|
|
43
|
+
EscapeFunction as EscapeFunction,
|
|
44
|
+
TemplateLoader as TemplateLoader,
|
|
45
|
+
RenderChunk as RenderChunk,
|
|
46
|
+
RenderedTemplate as RenderedTemplate,
|
|
47
|
+
TemplateCompiler as TemplateCompiler,
|
|
48
|
+
html_escape as html_escape,
|
|
49
|
+
render as render,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
__all__ = (
|
|
54
|
+
"CompiledTemplate",
|
|
55
|
+
"DEFAULT_DELIMITERS",
|
|
56
|
+
"EMPTY_TEMPLATE",
|
|
57
|
+
"Delimiters",
|
|
58
|
+
"EscapeFunction",
|
|
59
|
+
"InvalidDelimiterError",
|
|
60
|
+
"InvalidNameError",
|
|
61
|
+
"MissingTemplateError",
|
|
62
|
+
"MissingTemplateResolver",
|
|
63
|
+
"MissingVariableError",
|
|
64
|
+
"MissingVariableResolver",
|
|
65
|
+
"RenderChunk",
|
|
66
|
+
"RenderedTemplate",
|
|
67
|
+
"SectionSyntaxError",
|
|
68
|
+
"TemplateCompiler",
|
|
69
|
+
"TemplateLoader",
|
|
70
|
+
"TemplateRenderer",
|
|
71
|
+
"TemplateSyntaxError",
|
|
72
|
+
"UnclosedTagError",
|
|
73
|
+
"UnsupportedTagError",
|
|
74
|
+
"compile",
|
|
75
|
+
"create_dev_renderer",
|
|
76
|
+
"create_prod_renderer",
|
|
77
|
+
"create_renderer",
|
|
78
|
+
"create_test_renderer",
|
|
79
|
+
"html_escape",
|
|
80
|
+
"inline_partials",
|
|
81
|
+
"render",
|
|
82
|
+
"resolve_missing_template_as_empty",
|
|
83
|
+
"resolve_missing_template_as_error",
|
|
84
|
+
"resolve_missing_variable_as_error",
|
|
85
|
+
"resolve_missing_variable_as_none",
|
|
86
|
+
)
|