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.
- json_tstring-0.1.0/PKG-INFO +81 -0
- json_tstring-0.1.0/README.md +54 -0
- json_tstring-0.1.0/pyproject.toml +78 -0
- json_tstring-0.1.0/src/json_tstring/__init__.py +21 -0
- json_tstring-0.1.0/src/json_tstring/_errors.py +15 -0
- json_tstring-0.1.0/src/json_tstring/_runtime.py +106 -0
- json_tstring-0.1.0/src/json_tstring/_slots.py +3 -0
- json_tstring-0.1.0/src/json_tstring/py.typed +1 -0
|
@@ -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 @@
|
|
|
1
|
+
# Marker file for PEP 561 typing support.
|