json-tstring 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.
@@ -0,0 +1,81 @@
1
+ Metadata-Version: 2.3
2
+ Name: json-tstring
3
+ Version: 0.1.0
4
+ Summary: JSON renderer for t-string structured data templates
5
+ Keywords: pep750,t-strings,json,template-strings,structured-data
6
+ Author: Koudai Aono
7
+ Author-email: Koudai Aono <koxudaxi@gmail.com>
8
+ License: MIT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
+ Requires-Dist: tstring-bindings>=0.1.0
17
+ Requires-Dist: tstring-core>=0.1.0
18
+ Maintainer: Koudai Aono
19
+ Maintainer-email: Koudai Aono <koxudaxi@gmail.com>
20
+ Requires-Python: >=3.14
21
+ Project-URL: Homepage, https://github.com/koxudaxi/tstring-structured-data
22
+ Project-URL: Repository, https://github.com/koxudaxi/tstring-structured-data
23
+ Project-URL: Documentation, https://github.com/koxudaxi/tstring-structured-data/blob/main/json-tstring/README.md
24
+ Project-URL: Changelog, https://github.com/koxudaxi/tstring-structured-data/blob/main/CHANGELOG.md
25
+ Project-URL: Issues, https://github.com/koxudaxi/tstring-structured-data/issues
26
+ Description-Content-Type: text/markdown
27
+
28
+ # json-tstring
29
+
30
+ JSON rendering for PEP 750 t-strings. Parsing and rendering happen in Rust;
31
+ this package is the Python wrapper.
32
+
33
+ Requires Python 3.14+.
34
+
35
+ This package depends on `tstring-bindings`, a native PyO3 extension. On
36
+ supported platforms, install from prebuilt wheels. Other environments require a
37
+ local Rust 1.94.0 toolchain build.
38
+
39
+ ## API
40
+
41
+ ```python
42
+ render_data(template, profile="rfc8259") # -> Python data
43
+ render_text(template, profile="rfc8259") # -> JSON text
44
+ render_result(template, profile="rfc8259") # -> RenderResult (.text + .data)
45
+ ```
46
+
47
+ Type alias: `JsonProfile = Literal["rfc8259"]`
48
+
49
+ Parsed template structure is cached per process using `template.strings` +
50
+ profile as the key.
51
+
52
+ ## How it works
53
+
54
+ The Python `Template` is converted to a Rust token stream, parsed into JSON
55
+ nodes (keeping interpolation visible in values, keys, and string fragments),
56
+ and rendered back to text or Python data. `serde_json` handles normalization.
57
+
58
+ ## Supported positions
59
+
60
+ - whole-value, object-key, quoted-key-fragment, and string-fragment interpolation
61
+ - bare fragments promoted to JSON strings
62
+ - nested arrays and objects, top-level values
63
+
64
+ ## Limits
65
+
66
+ - object keys must be `str`
67
+ - non-finite floats rejected
68
+ - values must be JSON-representable
69
+ - integers keep exact Python text (no silent `float` coercion)
70
+
71
+ ## Verify
72
+
73
+ ```bash
74
+ uv sync --group dev
75
+ uv run --group dev pytest
76
+ ```
77
+
78
+ ## See also
79
+
80
+ - [Project README](https://github.com/koxudaxi/tstring-structured-data#readme)
81
+ - [Backend support matrix](https://github.com/koxudaxi/tstring-structured-data/blob/main/docs/backend-support-matrix.md)
@@ -0,0 +1,54 @@
1
+ # json-tstring
2
+
3
+ JSON rendering for PEP 750 t-strings. Parsing and rendering happen in Rust;
4
+ this package is the Python wrapper.
5
+
6
+ Requires Python 3.14+.
7
+
8
+ This package depends on `tstring-bindings`, a native PyO3 extension. On
9
+ supported platforms, install from prebuilt wheels. Other environments require a
10
+ local Rust 1.94.0 toolchain build.
11
+
12
+ ## API
13
+
14
+ ```python
15
+ render_data(template, profile="rfc8259") # -> Python data
16
+ render_text(template, profile="rfc8259") # -> JSON text
17
+ render_result(template, profile="rfc8259") # -> RenderResult (.text + .data)
18
+ ```
19
+
20
+ Type alias: `JsonProfile = Literal["rfc8259"]`
21
+
22
+ Parsed template structure is cached per process using `template.strings` +
23
+ profile as the key.
24
+
25
+ ## How it works
26
+
27
+ The Python `Template` is converted to a Rust token stream, parsed into JSON
28
+ nodes (keeping interpolation visible in values, keys, and string fragments),
29
+ and rendered back to text or Python data. `serde_json` handles normalization.
30
+
31
+ ## Supported positions
32
+
33
+ - whole-value, object-key, quoted-key-fragment, and string-fragment interpolation
34
+ - bare fragments promoted to JSON strings
35
+ - nested arrays and objects, top-level values
36
+
37
+ ## Limits
38
+
39
+ - object keys must be `str`
40
+ - non-finite floats rejected
41
+ - values must be JSON-representable
42
+ - integers keep exact Python text (no silent `float` coercion)
43
+
44
+ ## Verify
45
+
46
+ ```bash
47
+ uv sync --group dev
48
+ uv run --group dev pytest
49
+ ```
50
+
51
+ ## See also
52
+
53
+ - [Project README](https://github.com/koxudaxi/tstring-structured-data#readme)
54
+ - [Backend support matrix](https://github.com/koxudaxi/tstring-structured-data/blob/main/docs/backend-support-matrix.md)
@@ -0,0 +1,78 @@
1
+ [project]
2
+ name = "json-tstring"
3
+ version = "0.1.0"
4
+ description = "JSON renderer for t-string structured data templates"
5
+ readme = "README.md"
6
+ license = { text = "MIT" }
7
+ authors = [{ name = "Koudai Aono", email = "koxudaxi@gmail.com" }]
8
+ maintainers = [{ name = "Koudai Aono", email = "koxudaxi@gmail.com" }]
9
+ requires-python = ">=3.14"
10
+ dependencies = [
11
+ "tstring-bindings>=0.1.0",
12
+ "tstring-core>=0.1.0",
13
+ ]
14
+ keywords = ["pep750", "t-strings", "json", "template-strings", "structured-data"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3 :: Only",
21
+ "Programming Language :: Python :: 3.14",
22
+ "Topic :: Software Development :: Libraries :: Python Modules",
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://github.com/koxudaxi/tstring-structured-data"
27
+ Repository = "https://github.com/koxudaxi/tstring-structured-data"
28
+ Documentation = "https://github.com/koxudaxi/tstring-structured-data/blob/main/json-tstring/README.md"
29
+ Changelog = "https://github.com/koxudaxi/tstring-structured-data/blob/main/CHANGELOG.md"
30
+ Issues = "https://github.com/koxudaxi/tstring-structured-data/issues"
31
+
32
+ [build-system]
33
+ requires = ["uv_build>=0.10.9,<0.11.0"]
34
+ build-backend = "uv_build"
35
+
36
+ [dependency-groups]
37
+ dev = [
38
+ "coverage[toml]>=7.8.0",
39
+ "inline-snapshot>=0.32.4",
40
+ "pytest>=8.4.0",
41
+ "ruff>=0.11.0",
42
+ "ty>=0.0.23",
43
+ ]
44
+
45
+ [tool.uv.sources]
46
+ tstring-bindings = { workspace = true }
47
+ tstring-core = { workspace = true }
48
+
49
+ [tool.uv.build-backend]
50
+ source-exclude = [".DS_Store", "src/json_tstring/core"]
51
+ wheel-exclude = [".DS_Store", "src/json_tstring/core"]
52
+
53
+ [tool.pytest.ini_options]
54
+ testpaths = ["tests"]
55
+
56
+ [tool.inline-snapshot]
57
+ format-command = "ruff format --stdin-filename {filename}"
58
+
59
+ [tool.coverage.run]
60
+ branch = true
61
+ source_pkgs = ["json_tstring", "tstring_core"]
62
+
63
+ [tool.coverage.report]
64
+ show_missing = true
65
+ fail_under = 100
66
+
67
+ [tool.ty.environment]
68
+ extra-paths = [
69
+ "../rust/python-bindings/python",
70
+ "../tstring-core/src",
71
+ "../examples",
72
+ ]
73
+
74
+ [tool.ruff]
75
+ target-version = "py314"
76
+
77
+ [tool.ruff.lint]
78
+ select = ["B", "E", "F", "I"]
@@ -0,0 +1,21 @@
1
+ from tstring_core import (
2
+ RenderResult,
3
+ TemplateError,
4
+ TemplateParseError,
5
+ TemplateSemanticError,
6
+ UnrepresentableValueError,
7
+ )
8
+
9
+ from ._runtime import JsonProfile, render_data, render_result, render_text
10
+
11
+ __all__ = [
12
+ "JsonProfile",
13
+ "RenderResult",
14
+ "TemplateError",
15
+ "TemplateParseError",
16
+ "TemplateSemanticError",
17
+ "UnrepresentableValueError",
18
+ "render_data",
19
+ "render_result",
20
+ "render_text",
21
+ ]
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ from tstring_core import (
4
+ TemplateError,
5
+ TemplateParseError,
6
+ TemplateSemanticError,
7
+ UnrepresentableValueError,
8
+ )
9
+
10
+ __all__ = [
11
+ "TemplateError",
12
+ "TemplateParseError",
13
+ "TemplateSemanticError",
14
+ "UnrepresentableValueError",
15
+ ]
@@ -0,0 +1,106 @@
1
+ from __future__ import annotations
2
+
3
+ from string.templatelib import Template
4
+ from typing import Literal, Protocol, TypeIs, cast
5
+
6
+ from tstring_bindings import tstring_bindings as _bindings
7
+ from tstring_core import JsonValue, RenderResult
8
+
9
+ type JsonProfile = Literal["rfc8259"]
10
+
11
+ _CONTRACT_VERSION = 1
12
+ _REQUIRED_SYMBOLS = {
13
+ "render_json",
14
+ "render_json_text",
15
+ "_render_json_result_payload",
16
+ }
17
+
18
+
19
+ class _RenderJson(Protocol):
20
+ def __call__(self, template: Template, *, profile: JsonProfile) -> JsonValue: ...
21
+
22
+
23
+ class _RenderJsonText(Protocol):
24
+ def __call__(self, template: Template, *, profile: JsonProfile) -> str: ...
25
+
26
+
27
+ class _RenderJsonResultPayload(Protocol):
28
+ def __call__(
29
+ self, template: Template, *, profile: JsonProfile
30
+ ) -> tuple[str, JsonValue]: ...
31
+
32
+
33
+ def _bind_extension() -> tuple[_RenderJson, _RenderJsonText, _RenderJsonResultPayload]:
34
+ version = getattr(_bindings, "__contract_version__", None)
35
+ if version != _CONTRACT_VERSION:
36
+ raise ImportError(
37
+ "json_tstring extension contract mismatch: "
38
+ f"expected version {_CONTRACT_VERSION}, got {version!r}."
39
+ )
40
+
41
+ exported = set(getattr(_bindings, "__contract_symbols__", ()))
42
+ missing = sorted(_REQUIRED_SYMBOLS - exported)
43
+ if missing:
44
+ raise ImportError(
45
+ "json_tstring could not bind required extension symbols: "
46
+ + ", ".join(missing)
47
+ )
48
+
49
+ return (
50
+ cast(_RenderJson, _bindings.render_json),
51
+ cast(_RenderJsonText, _bindings.render_json_text),
52
+ cast(_RenderJsonResultPayload, _bindings._render_json_result_payload),
53
+ )
54
+
55
+
56
+ _render_json, _render_json_text, _render_json_result_payload = _bind_extension()
57
+
58
+
59
+ def _is_template(value: object) -> TypeIs[Template]:
60
+ return isinstance(value, Template)
61
+
62
+
63
+ def _validate_template(template: object, api_name: str) -> Template:
64
+ if _is_template(template):
65
+ return template
66
+ raise TypeError(
67
+ f"{api_name} requires a PEP 750 Template object. "
68
+ f"Got {type(template).__name__} instead."
69
+ )
70
+
71
+
72
+ def _resolve_profile(profile: JsonProfile | str | None) -> JsonProfile:
73
+ if profile is None or profile == "rfc8259":
74
+ return "rfc8259"
75
+ raise ValueError(
76
+ f"Unsupported JSON profile {profile!r}. Supported profiles: 'rfc8259'."
77
+ )
78
+
79
+
80
+ def render_data(
81
+ template: Template, *, profile: JsonProfile | str | None = None
82
+ ) -> JsonValue:
83
+ checked = _validate_template(template, "render_data")
84
+ return _render_json(checked, profile=_resolve_profile(profile))
85
+
86
+
87
+ def render_text(template: Template, *, profile: JsonProfile | str | None = None) -> str:
88
+ checked = _validate_template(template, "render_text")
89
+ return _render_json_text(checked, profile=_resolve_profile(profile))
90
+
91
+
92
+ def render_result(
93
+ template: Template, *, profile: JsonProfile | str | None = None
94
+ ) -> RenderResult[JsonValue]:
95
+ checked = _validate_template(template, "render_result")
96
+ text, data = _render_json_result_payload(checked, profile=_resolve_profile(profile))
97
+ return RenderResult(text=text, data=data)
98
+
99
+
100
+ __all__ = [
101
+ "JsonProfile",
102
+ "RenderResult",
103
+ "render_data",
104
+ "render_result",
105
+ "render_text",
106
+ ]
@@ -0,0 +1,3 @@
1
+ from tstring_core import FragmentGroup, Slot, SlotContext
2
+
3
+ __all__ = ["FragmentGroup", "Slot", "SlotContext"]
@@ -0,0 +1 @@
1
+ # Marker file for PEP 561 typing support.