tstr 0.2.0__tar.gz → 0.3.1.dev1__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.
- tstr-0.3.1.dev1/.github/workflows/build.yml +58 -0
- {tstr-0.2.0 → tstr-0.3.1.dev1}/.github/workflows/codeql.yml +4 -4
- {tstr-0.2.0 → tstr-0.3.1.dev1}/.github/workflows/testing.yaml +13 -4
- {tstr-0.2.0 → tstr-0.3.1.dev1}/.vscode/settings.json +5 -1
- {tstr-0.2.0 → tstr-0.3.1.dev1}/PKG-INFO +20 -64
- {tstr-0.2.0 → tstr-0.3.1.dev1}/README.md +18 -62
- {tstr-0.2.0 → tstr-0.3.1.dev1}/pyproject.toml +6 -0
- {tstr-0.2.0 → tstr-0.3.1.dev1}/src/tstr/__init__.py +13 -8
- {tstr-0.2.0 → tstr-0.3.1.dev1}/src/tstr/_compat.py +8 -2
- tstr-0.3.1.dev1/src/tstr/_interpolation_tools.py +141 -0
- {tstr-0.2.0 → tstr-0.3.1.dev1}/src/tstr/_template.py +3 -3
- tstr-0.3.1.dev1/src/tstr/_template.pyi +20 -0
- tstr-0.2.0/src/tstr/_utils.py → tstr-0.3.1.dev1/src/tstr/_template_tools.py +169 -104
- tstr-0.3.1.dev1/src/tstr/ext/__init__.py +0 -0
- {tstr-0.2.0/src/tstr → tstr-0.3.1.dev1/src/tstr/ext}/_html.py +17 -9
- {tstr-0.2.0/src/tstr → tstr-0.3.1.dev1/src/tstr/ext}/_logging.py +42 -11
- {tstr-0.2.0/src/tstr → tstr-0.3.1.dev1/src/tstr/ext}/_sqlite.py +32 -12
- tstr-0.3.1.dev1/src/tstr/ext/py.typed +0 -0
- tstr-0.3.1.dev1/src/tstr/py.typed +0 -0
- {tstr-0.2.0 → tstr-0.3.1.dev1}/tests/_compat1.py +76 -8
- {tstr-0.2.0 → tstr-0.3.1.dev1}/tests/_compat2.py +31 -51
- {tstr-0.2.0 → tstr-0.3.1.dev1}/tests/test_generate_template1.py +88 -16
- {tstr-0.2.0 → tstr-0.3.1.dev1}/tests/test_generate_template2.py +51 -41
- {tstr-0.2.0 → tstr-0.3.1.dev1}/tests/test_html.py +11 -2
- {tstr-0.2.0 → tstr-0.3.1.dev1}/tests/test_logging.py +6 -3
- tstr-0.3.1.dev1/tests/test_sqlite.py +37 -0
- tstr-0.3.1.dev1/tests/test_utils.py +819 -0
- tstr-0.3.1.dev1/uv.lock +227 -0
- tstr-0.2.0/.devcontainer/Dockerfile +0 -20
- tstr-0.2.0/.devcontainer/devcontainer.json +0 -44
- tstr-0.2.0/.github/workflows/build.yml +0 -61
- tstr-0.2.0/docs/api.md +0 -125
- tstr-0.2.0/src/tstr/_template.pyi +0 -72
- tstr-0.2.0/tests/test_sqlite.py +0 -16
- tstr-0.2.0/tests/test_utils.py +0 -157
- tstr-0.2.0/uv.lock +0 -212
- {tstr-0.2.0 → tstr-0.3.1.dev1}/.github/FUNDING.yml +0 -0
- {tstr-0.2.0 → tstr-0.3.1.dev1}/.gitignore +0 -0
- {tstr-0.2.0 → tstr-0.3.1.dev1}/LICENSE +0 -0
- {tstr-0.2.0 → tstr-0.3.1.dev1}/tests/_support.py +0 -0
- {tstr-0.2.0 → tstr-0.3.1.dev1}/tests/test_compat.py +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# The following build script copied from hatch/.github/workflows/build-hatchling.yml (MIT License)
|
|
2
|
+
name: build a package
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
tags:
|
|
7
|
+
- v*
|
|
8
|
+
|
|
9
|
+
env:
|
|
10
|
+
PYTHON_VERSION: "3.12"
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
name: Build wheels and source distribution
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
24
|
+
|
|
25
|
+
- name: Install UV
|
|
26
|
+
uses: astral-sh/setup-uv@v3
|
|
27
|
+
|
|
28
|
+
- name: Install build dependencies
|
|
29
|
+
run: uv pip install --system --upgrade build
|
|
30
|
+
|
|
31
|
+
- name: Build source distribution
|
|
32
|
+
run: python -m build .
|
|
33
|
+
|
|
34
|
+
- uses: actions/upload-artifact@v4
|
|
35
|
+
with:
|
|
36
|
+
name: artifacts
|
|
37
|
+
path: dist
|
|
38
|
+
if-no-files-found: error
|
|
39
|
+
|
|
40
|
+
publish:
|
|
41
|
+
name: Publish release
|
|
42
|
+
needs:
|
|
43
|
+
- build
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
|
|
46
|
+
permissions:
|
|
47
|
+
id-token: write
|
|
48
|
+
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/download-artifact@v4
|
|
51
|
+
with:
|
|
52
|
+
name: artifacts
|
|
53
|
+
path: dist
|
|
54
|
+
|
|
55
|
+
- name: Push build artifacts to PyPI
|
|
56
|
+
uses: pypa/gh-action-pypi-publish@v1.12.3
|
|
57
|
+
with:
|
|
58
|
+
skip-existing: true
|
|
@@ -41,11 +41,11 @@ jobs:
|
|
|
41
41
|
|
|
42
42
|
steps:
|
|
43
43
|
- name: Checkout repository
|
|
44
|
-
uses: actions/checkout@
|
|
44
|
+
uses: actions/checkout@v5
|
|
45
45
|
|
|
46
46
|
# Initializes the CodeQL tools for scanning.
|
|
47
47
|
- name: Initialize CodeQL
|
|
48
|
-
uses: github/codeql-action/init@
|
|
48
|
+
uses: github/codeql-action/init@v3
|
|
49
49
|
with:
|
|
50
50
|
languages: ${{ matrix.language }}
|
|
51
51
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
@@ -59,7 +59,7 @@ jobs:
|
|
|
59
59
|
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
|
60
60
|
# If this step fails, then you should remove it and run the build manually (see below)
|
|
61
61
|
- name: Autobuild
|
|
62
|
-
uses: github/codeql-action/autobuild@
|
|
62
|
+
uses: github/codeql-action/autobuild@v3
|
|
63
63
|
|
|
64
64
|
# ℹ️ Command-line programs to run using the OS shell.
|
|
65
65
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
|
@@ -72,6 +72,6 @@ jobs:
|
|
|
72
72
|
# ./location_of_script_within_repo/buildscript.sh
|
|
73
73
|
|
|
74
74
|
- name: Perform CodeQL Analysis
|
|
75
|
-
uses: github/codeql-action/analyze@
|
|
75
|
+
uses: github/codeql-action/analyze@v3
|
|
76
76
|
with:
|
|
77
77
|
category: "/language:${{matrix.language}}"
|
|
@@ -12,22 +12,31 @@ jobs:
|
|
|
12
12
|
matrix:
|
|
13
13
|
os: [ubuntu-latest]
|
|
14
14
|
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14-dev']
|
|
15
|
+
|
|
15
16
|
runs-on: ${{ matrix.os }}
|
|
16
17
|
steps:
|
|
17
18
|
- uses: actions/checkout@v4
|
|
19
|
+
|
|
18
20
|
- name: Set up Python
|
|
19
21
|
uses: actions/setup-python@v5
|
|
20
22
|
with:
|
|
21
23
|
python-version: ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Install UV
|
|
26
|
+
uses: astral-sh/setup-uv@v3
|
|
27
|
+
|
|
22
28
|
- name: Install dependencies
|
|
23
29
|
run: |
|
|
24
|
-
python -m pip install pytest pytest-cov
|
|
25
|
-
python -m pip install -e .
|
|
30
|
+
# python -m pip install pytest pytest-cov
|
|
31
|
+
# python -m pip install -e .
|
|
32
|
+
uv sync
|
|
33
|
+
|
|
26
34
|
- name: Test with pytest
|
|
27
35
|
run: |
|
|
28
36
|
python -m pytest --cov --cov-report lcov
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
|
|
38
|
+
- name: Upload coverage information to Coveralls
|
|
39
|
+
uses: coverallsapp/github-action@v2
|
|
31
40
|
with:
|
|
32
41
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
33
42
|
path-to-lcov: coverage.lcov
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"cSpell.words": [
|
|
3
|
+
"dedented",
|
|
3
4
|
"fetchone",
|
|
5
|
+
"fstring",
|
|
4
6
|
"interp",
|
|
5
7
|
"intp",
|
|
6
8
|
"intrp",
|
|
@@ -8,7 +10,9 @@
|
|
|
8
10
|
"renderable",
|
|
9
11
|
"repr",
|
|
10
12
|
"templatelib",
|
|
11
|
-
"tstr"
|
|
13
|
+
"tstr",
|
|
14
|
+
"tstring",
|
|
15
|
+
"tstrings"
|
|
12
16
|
],
|
|
13
17
|
"python.analysis.typeCheckingMode": "standard"
|
|
14
18
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tstr
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1.dev1
|
|
4
4
|
Summary: Template string utilities and backports
|
|
5
5
|
Project-URL: Repository, https://github.com/ilotoki0804/tstr
|
|
6
6
|
Author-email: ilotoki0804 <ilotoki0804@gmail.com>
|
|
7
7
|
License-Expression: Apache-2.0
|
|
8
8
|
License-File: LICENSE
|
|
9
|
-
Keywords: backport,string,template,utility
|
|
9
|
+
Keywords: backport,pep 750,pep-750,pep750,string,t-string,template,template string,tstring,utility
|
|
10
10
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
11
|
Classifier: Operating System :: OS Independent
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -43,70 +43,23 @@ pip install tstr
|
|
|
43
43
|
- `render` (alias: `f`): Render a template to a string, mimicking f-string behavior.
|
|
44
44
|
- `generate_template` (alias: `t`): Create a Template object from a string and context.
|
|
45
45
|
This function is especially useful on Python versions that do not support template strings natively.
|
|
46
|
-
- `bind`: Apply a function to all interpolations
|
|
46
|
+
- `bind`: Apply a function to all interpolations and join all the parts.
|
|
47
47
|
- `binder`: Decorator to create template processors from an interpolation processor.
|
|
48
|
-
- `normalize`:
|
|
49
|
-
- `
|
|
50
|
-
- `convert`: Apply f-string-style conversion to a value.
|
|
48
|
+
- `normalize` / `normalize_str`: Apply conversion and format to value.
|
|
49
|
+
- `convert`: Apply conversion to a value.
|
|
51
50
|
- `template_eq`: Check if two templates are equivalent.
|
|
51
|
+
- `interpolation_replace`: Create a new `Interpolation` by selectively replacing attributes of an existing one.
|
|
52
|
+
- `dedent`: `textwrap.dedent` for template strings
|
|
53
|
+
- `template_from_parts`: Construct template strings from iterable
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
- `
|
|
57
|
-
- `
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
```python
|
|
62
|
-
from tstr import Template, Interpolation, generate_template, render, bind, binder, f, normalize, normalize_str, convert, template_eq
|
|
63
|
-
|
|
64
|
-
# Rendering templates
|
|
65
|
-
x = 12
|
|
66
|
-
template = t"Value: {x}"
|
|
67
|
-
print(render(template)) # "Value: 12"
|
|
68
|
-
print(f(template)) # "Value: 12"
|
|
69
|
-
|
|
70
|
-
# Generating templates
|
|
71
|
-
template = generate_template("Hello, {name}!", {"name": "Alice"}) # with explicit context
|
|
72
|
-
print(f(template)) # "Hello, Alice!"
|
|
73
|
-
|
|
74
|
-
name = "Bob"
|
|
75
|
-
template = generate_template("Nice to meet you, {name}!") # without explicit context
|
|
76
|
-
print(f(template)) # "Nice to meet you, Bob!"
|
|
77
|
-
|
|
78
|
-
template = t("Nice to meet you, {name}!") # with an alias `t`
|
|
79
|
-
print(f(template)) # "Nice to meet you, Bob!"
|
|
80
|
-
|
|
81
|
-
# Binding all interpolations
|
|
82
|
-
def double(i: Interpolation):
|
|
83
|
-
return str(i.value * 2)
|
|
84
|
-
n = 10
|
|
85
|
-
template = t"Double: {n}"
|
|
86
|
-
print(bind(template, double)) # "Double: 20"
|
|
87
|
-
|
|
88
|
-
@binder
|
|
89
|
-
def upper(i):
|
|
90
|
-
return normalize_str(i).upper()
|
|
91
|
-
name = 'bob'
|
|
92
|
-
template = t"Hi, {name}!"
|
|
93
|
-
print(upper(template)) # "Hi, BOB!"
|
|
94
|
-
|
|
95
|
-
# Applying conversion and formatting
|
|
96
|
-
age = 20
|
|
97
|
-
template = t"Age: {age:04d}"
|
|
98
|
-
intp = template.interpolations[0]
|
|
99
|
-
print(normalize(intp)) # "0020"
|
|
100
|
-
|
|
101
|
-
# Applying conversion
|
|
102
|
-
print(convert(42, "r")) # e.g., "42"
|
|
103
|
-
|
|
104
|
-
# Template equivalence
|
|
105
|
-
x = 123
|
|
106
|
-
t1 = t"A: {x}"
|
|
107
|
-
t2 = t"A: {x}"
|
|
108
|
-
print(template_eq(t1, t2)) # True
|
|
109
|
-
```
|
|
55
|
+
This library also provides several useful extensions where template strings can be effectively utilized.
|
|
56
|
+
These extensions are available in the `tstr.ext` submodule, and below is a list with brief descriptions:
|
|
57
|
+
|
|
58
|
+
- `ext._html`: Render templates with HTML escaping.
|
|
59
|
+
- `ext._sqlite`: Safely execute SQL with templates, preventing SQL injection attacks.
|
|
60
|
+
- `ext._logging`: Enable Python's logging module to accept template strings.
|
|
61
|
+
|
|
62
|
+
For more details, see the [API documentation](/docs/api.md).
|
|
110
63
|
|
|
111
64
|
## Compatibility
|
|
112
65
|
|
|
@@ -117,15 +70,18 @@ print(template_eq(t1, t2)) # True
|
|
|
117
70
|
|
|
118
71
|
Use the `TEMPLATE_STRING_SUPPORTED` constant to check if template strings are natively supported in your Python version.
|
|
119
72
|
|
|
73
|
+
For details on how the compatible backport of template string works and what similarities and differences it has with native template strings, see the [compatible template strings](/docs/compat.md) documentation.
|
|
74
|
+
|
|
120
75
|
# Contributing
|
|
121
76
|
|
|
122
77
|
This project welcomes contributions of all kinds from anyone willing to help improve it! Whether you're fixing a typo in documentation, reporting a bug, proposing a new feature, or implementing code changes - every contribution matters and is highly appreciated.
|
|
123
78
|
|
|
124
79
|
## Releases
|
|
125
80
|
|
|
81
|
+
* 0.3.0: Revamp various things
|
|
126
82
|
* 0.2.0: Rename html_render to render_html, add `_logging` module, fix various bugs and improve documentation
|
|
127
83
|
* 0.1.1.post1: Initial release
|
|
128
84
|
|
|
129
85
|
## License
|
|
130
86
|
|
|
131
|
-
Apache License 2.0
|
|
87
|
+
This library is licensed under the Apache License 2.0.
|
|
@@ -23,70 +23,23 @@ pip install tstr
|
|
|
23
23
|
- `render` (alias: `f`): Render a template to a string, mimicking f-string behavior.
|
|
24
24
|
- `generate_template` (alias: `t`): Create a Template object from a string and context.
|
|
25
25
|
This function is especially useful on Python versions that do not support template strings natively.
|
|
26
|
-
- `bind`: Apply a function to all interpolations
|
|
26
|
+
- `bind`: Apply a function to all interpolations and join all the parts.
|
|
27
27
|
- `binder`: Decorator to create template processors from an interpolation processor.
|
|
28
|
-
- `normalize`:
|
|
29
|
-
- `
|
|
30
|
-
- `convert`: Apply f-string-style conversion to a value.
|
|
28
|
+
- `normalize` / `normalize_str`: Apply conversion and format to value.
|
|
29
|
+
- `convert`: Apply conversion to a value.
|
|
31
30
|
- `template_eq`: Check if two templates are equivalent.
|
|
31
|
+
- `interpolation_replace`: Create a new `Interpolation` by selectively replacing attributes of an existing one.
|
|
32
|
+
- `dedent`: `textwrap.dedent` for template strings
|
|
33
|
+
- `template_from_parts`: Construct template strings from iterable
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- `
|
|
37
|
-
- `
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
```python
|
|
42
|
-
from tstr import Template, Interpolation, generate_template, render, bind, binder, f, normalize, normalize_str, convert, template_eq
|
|
43
|
-
|
|
44
|
-
# Rendering templates
|
|
45
|
-
x = 12
|
|
46
|
-
template = t"Value: {x}"
|
|
47
|
-
print(render(template)) # "Value: 12"
|
|
48
|
-
print(f(template)) # "Value: 12"
|
|
49
|
-
|
|
50
|
-
# Generating templates
|
|
51
|
-
template = generate_template("Hello, {name}!", {"name": "Alice"}) # with explicit context
|
|
52
|
-
print(f(template)) # "Hello, Alice!"
|
|
53
|
-
|
|
54
|
-
name = "Bob"
|
|
55
|
-
template = generate_template("Nice to meet you, {name}!") # without explicit context
|
|
56
|
-
print(f(template)) # "Nice to meet you, Bob!"
|
|
57
|
-
|
|
58
|
-
template = t("Nice to meet you, {name}!") # with an alias `t`
|
|
59
|
-
print(f(template)) # "Nice to meet you, Bob!"
|
|
60
|
-
|
|
61
|
-
# Binding all interpolations
|
|
62
|
-
def double(i: Interpolation):
|
|
63
|
-
return str(i.value * 2)
|
|
64
|
-
n = 10
|
|
65
|
-
template = t"Double: {n}"
|
|
66
|
-
print(bind(template, double)) # "Double: 20"
|
|
67
|
-
|
|
68
|
-
@binder
|
|
69
|
-
def upper(i):
|
|
70
|
-
return normalize_str(i).upper()
|
|
71
|
-
name = 'bob'
|
|
72
|
-
template = t"Hi, {name}!"
|
|
73
|
-
print(upper(template)) # "Hi, BOB!"
|
|
74
|
-
|
|
75
|
-
# Applying conversion and formatting
|
|
76
|
-
age = 20
|
|
77
|
-
template = t"Age: {age:04d}"
|
|
78
|
-
intp = template.interpolations[0]
|
|
79
|
-
print(normalize(intp)) # "0020"
|
|
80
|
-
|
|
81
|
-
# Applying conversion
|
|
82
|
-
print(convert(42, "r")) # e.g., "42"
|
|
83
|
-
|
|
84
|
-
# Template equivalence
|
|
85
|
-
x = 123
|
|
86
|
-
t1 = t"A: {x}"
|
|
87
|
-
t2 = t"A: {x}"
|
|
88
|
-
print(template_eq(t1, t2)) # True
|
|
89
|
-
```
|
|
35
|
+
This library also provides several useful extensions where template strings can be effectively utilized.
|
|
36
|
+
These extensions are available in the `tstr.ext` submodule, and below is a list with brief descriptions:
|
|
37
|
+
|
|
38
|
+
- `ext._html`: Render templates with HTML escaping.
|
|
39
|
+
- `ext._sqlite`: Safely execute SQL with templates, preventing SQL injection attacks.
|
|
40
|
+
- `ext._logging`: Enable Python's logging module to accept template strings.
|
|
41
|
+
|
|
42
|
+
For more details, see the [API documentation](/docs/api.md).
|
|
90
43
|
|
|
91
44
|
## Compatibility
|
|
92
45
|
|
|
@@ -97,15 +50,18 @@ print(template_eq(t1, t2)) # True
|
|
|
97
50
|
|
|
98
51
|
Use the `TEMPLATE_STRING_SUPPORTED` constant to check if template strings are natively supported in your Python version.
|
|
99
52
|
|
|
53
|
+
For details on how the compatible backport of template string works and what similarities and differences it has with native template strings, see the [compatible template strings](/docs/compat.md) documentation.
|
|
54
|
+
|
|
100
55
|
# Contributing
|
|
101
56
|
|
|
102
57
|
This project welcomes contributions of all kinds from anyone willing to help improve it! Whether you're fixing a typo in documentation, reporting a bug, proposing a new feature, or implementing code changes - every contribution matters and is highly appreciated.
|
|
103
58
|
|
|
104
59
|
## Releases
|
|
105
60
|
|
|
61
|
+
* 0.3.0: Revamp various things
|
|
106
62
|
* 0.2.0: Rename html_render to render_html, add `_logging` module, fix various bugs and improve documentation
|
|
107
63
|
* 0.1.1.post1: Initial release
|
|
108
64
|
|
|
109
65
|
## License
|
|
110
66
|
|
|
111
|
-
Apache License 2.0
|
|
67
|
+
This library is licensed under the Apache License 2.0.
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
from ._interpolation_tools import (
|
|
2
|
+
convert,
|
|
3
|
+
interpolation_replace,
|
|
4
|
+
normalize,
|
|
5
|
+
normalize_str,
|
|
6
|
+
)
|
|
1
7
|
from ._template import (
|
|
2
8
|
TEMPLATE_STRING_SUPPORTED,
|
|
3
9
|
Conversion,
|
|
@@ -5,15 +11,13 @@ from ._template import (
|
|
|
5
11
|
StringOrTemplate,
|
|
6
12
|
Template,
|
|
7
13
|
)
|
|
8
|
-
from .
|
|
9
|
-
CONVERTERS,
|
|
10
|
-
TemplateGenerationError,
|
|
14
|
+
from ._template_tools import (
|
|
11
15
|
bind,
|
|
12
16
|
binder,
|
|
13
|
-
|
|
17
|
+
dedent,
|
|
14
18
|
f,
|
|
19
|
+
template_from_parts,
|
|
15
20
|
generate_template,
|
|
16
|
-
normalize,
|
|
17
21
|
normalize_str,
|
|
18
22
|
render,
|
|
19
23
|
t,
|
|
@@ -21,7 +25,6 @@ from ._utils import (
|
|
|
21
25
|
)
|
|
22
26
|
|
|
23
27
|
__all__ = [
|
|
24
|
-
"CONVERTERS",
|
|
25
28
|
"bind",
|
|
26
29
|
"binder",
|
|
27
30
|
"f",
|
|
@@ -34,9 +37,11 @@ __all__ = [
|
|
|
34
37
|
"Conversion",
|
|
35
38
|
"generate_template",
|
|
36
39
|
"t",
|
|
37
|
-
"TemplateGenerationError",
|
|
38
40
|
"TEMPLATE_STRING_SUPPORTED",
|
|
39
41
|
"template_eq",
|
|
40
42
|
"StringOrTemplate",
|
|
43
|
+
"interpolation_replace",
|
|
44
|
+
"template_from_parts",
|
|
45
|
+
"dedent",
|
|
41
46
|
]
|
|
42
|
-
__version__ = "0.
|
|
47
|
+
__version__ = "0.3.1.dev1"
|
|
@@ -3,7 +3,10 @@ from __future__ import annotations
|
|
|
3
3
|
import typing
|
|
4
4
|
from itertools import zip_longest
|
|
5
5
|
|
|
6
|
+
__all__ = ["Template", "Interpolation"]
|
|
6
7
|
|
|
8
|
+
|
|
9
|
+
@typing.final
|
|
7
10
|
class Template:
|
|
8
11
|
__strings: tuple[str, ...]
|
|
9
12
|
__interpolations: tuple[Interpolation, ...]
|
|
@@ -80,7 +83,8 @@ class Template:
|
|
|
80
83
|
|
|
81
84
|
def __add__(self, other: str | Template) -> Template:
|
|
82
85
|
if isinstance(other, str):
|
|
83
|
-
|
|
86
|
+
raise TypeError(
|
|
87
|
+
'can only concatenate tstr.Template (not "str") to tstr.Template')
|
|
84
88
|
elif isinstance(other, Template):
|
|
85
89
|
return Template(*self, *other)
|
|
86
90
|
else:
|
|
@@ -88,7 +92,8 @@ class Template:
|
|
|
88
92
|
|
|
89
93
|
def __radd__(self, other: str | Template) -> Template:
|
|
90
94
|
if isinstance(other, str):
|
|
91
|
-
|
|
95
|
+
raise TypeError(
|
|
96
|
+
'can only concatenate str (not "tstr.Template") to str')
|
|
92
97
|
elif isinstance(other, Template):
|
|
93
98
|
return Template(*other, *self)
|
|
94
99
|
else:
|
|
@@ -98,6 +103,7 @@ class Template:
|
|
|
98
103
|
return f"Template(strings={self.strings!r}, interpolations={self.interpolations!r})"
|
|
99
104
|
|
|
100
105
|
|
|
106
|
+
@typing.final
|
|
101
107
|
class Interpolation:
|
|
102
108
|
__match_args__ = ("value", "expression", "conversion", "format_spec")
|
|
103
109
|
__value: object
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from ._template import Conversion, Interpolation
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"convert",
|
|
9
|
+
"normalize",
|
|
10
|
+
"normalize_str",
|
|
11
|
+
"interpolation_replace",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
T = typing.TypeVar("T")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@typing.overload
|
|
18
|
+
def convert(
|
|
19
|
+
value: typing.Any,
|
|
20
|
+
conversion: Conversion,
|
|
21
|
+
) -> str: ...
|
|
22
|
+
|
|
23
|
+
@typing.overload
|
|
24
|
+
def convert(
|
|
25
|
+
value: typing.Any,
|
|
26
|
+
conversion: str,
|
|
27
|
+
) -> str: ...
|
|
28
|
+
|
|
29
|
+
@typing.overload
|
|
30
|
+
def convert(
|
|
31
|
+
value: T,
|
|
32
|
+
conversion: Conversion | None,
|
|
33
|
+
) -> T | str: ...
|
|
34
|
+
|
|
35
|
+
@typing.overload
|
|
36
|
+
def convert(
|
|
37
|
+
value: T,
|
|
38
|
+
conversion: str | None,
|
|
39
|
+
) -> T | str: ...
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def convert(
|
|
43
|
+
value: T,
|
|
44
|
+
conversion: str | None,
|
|
45
|
+
) -> T | str:
|
|
46
|
+
"""
|
|
47
|
+
Applies a conversion to a value, similar to how f-strings handle conversions.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
value (T): The value to convert, typically from an Interpolation.value.
|
|
51
|
+
conversion (Conversion | None): The conversion specifier ('a', 'r', or 's'), or None.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
T | str: The value converted according to the specified conversion;
|
|
55
|
+
if 'conversion' is None, returns the original value unchanged.
|
|
56
|
+
"""
|
|
57
|
+
if conversion is None:
|
|
58
|
+
return value
|
|
59
|
+
if conversion == "s":
|
|
60
|
+
return str(value)
|
|
61
|
+
if conversion == "r":
|
|
62
|
+
return repr(value)
|
|
63
|
+
if conversion == "a":
|
|
64
|
+
return ascii(value)
|
|
65
|
+
raise ValueError(f"Invalid conversion: {conversion}")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def normalize(interp: Interpolation) -> str | object:
|
|
69
|
+
"""
|
|
70
|
+
Normalizes a PEP 750 Interpolation, preserving its type when possible.
|
|
71
|
+
|
|
72
|
+
This is a more flexible version of normalize_str() that preserves the original
|
|
73
|
+
value's type when no conversion is specified.
|
|
74
|
+
|
|
75
|
+
If neither a conversion nor a format spec is specified, the original value
|
|
76
|
+
is returned without any modification, ensuring that the value's type is preserved.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
interp (Interpolation): The interpolation to normalize.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
str | object: The normalized string if conversion or format spec is specified, otherwise
|
|
83
|
+
the original value.
|
|
84
|
+
"""
|
|
85
|
+
if interp.conversion or interp.format_spec:
|
|
86
|
+
return normalize_str(interp)
|
|
87
|
+
else:
|
|
88
|
+
return interp.value
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def normalize_str(interp: Interpolation) -> str:
|
|
92
|
+
"""
|
|
93
|
+
Normalizes a PEP 750 Interpolation to a formatted string.
|
|
94
|
+
|
|
95
|
+
This processes an Interpolation object similarly to how f-strings process
|
|
96
|
+
interpolated expressions: it applies conversion and format specification.
|
|
97
|
+
Unlike normalize(), this always returns a string.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
interp (Interpolation): The interpolation to normalize.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
str: The formatted string representation of the interpolation.
|
|
104
|
+
"""
|
|
105
|
+
converted = convert(interp.value, interp.conversion)
|
|
106
|
+
return format(converted, interp.format_spec)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
_NOTSET = object()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def interpolation_replace(
|
|
113
|
+
interp: Interpolation,
|
|
114
|
+
*,
|
|
115
|
+
value: object = _NOTSET,
|
|
116
|
+
expression: str = _NOTSET, # type: ignore
|
|
117
|
+
conversion: typing.Literal["a", "r", "s"] | None = _NOTSET, # type: ignore
|
|
118
|
+
format_spec: str = _NOTSET, # type: ignore
|
|
119
|
+
) -> Interpolation:
|
|
120
|
+
"""
|
|
121
|
+
Creates a new Interpolation by selectively replacing attributes of an existing one.
|
|
122
|
+
|
|
123
|
+
This function allows you to create a modified copy of an Interpolation object
|
|
124
|
+
by specifying which attributes to replace. Any attribute not explicitly provided
|
|
125
|
+
will retain its original value from the input interpolation.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
interp (Interpolation): The original interpolation object.
|
|
129
|
+
value (object, optional): New value to use instead of the original.
|
|
130
|
+
expression (str, optional): New expression to use instead of the original.
|
|
131
|
+
conversion (Literal["a", "r", "s"] | None, optional): New conversion to use.
|
|
132
|
+
format_spec (str, optional): New format specification to use.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Interpolation: A new Interpolation with the specified replacements.
|
|
136
|
+
"""
|
|
137
|
+
value = interp.value if value is _NOTSET else value
|
|
138
|
+
expression = interp.expression if expression is _NOTSET else expression
|
|
139
|
+
conversion = interp.conversion if conversion is _NOTSET else conversion
|
|
140
|
+
format_spec = interp.format_spec if format_spec is _NOTSET else format_spec
|
|
141
|
+
return Interpolation(value, expression, conversion, format_spec) # type: ignore
|
|
@@ -8,12 +8,12 @@ Conversion: typing.TypeAlias = typing.Literal["a", "r", "s"]
|
|
|
8
8
|
|
|
9
9
|
try:
|
|
10
10
|
from string.templatelib import Interpolation, Template # type: ignore
|
|
11
|
+
|
|
12
|
+
TEMPLATE_STRING_SUPPORTED = True
|
|
13
|
+
StringOrTemplate: typing.TypeAlias = typing.LiteralString | Template # type: ignore
|
|
11
14
|
except Exception:
|
|
12
15
|
# Fallback to compatible implementation if template strings are not supported
|
|
13
16
|
from ._compat import Interpolation, Template
|
|
14
17
|
|
|
15
18
|
TEMPLATE_STRING_SUPPORTED = False
|
|
16
19
|
StringOrTemplate: typing.TypeAlias = str | Template # type: ignore
|
|
17
|
-
else:
|
|
18
|
-
TEMPLATE_STRING_SUPPORTED = True
|
|
19
|
-
StringOrTemplate: typing.TypeAlias = typing.LiteralString | Template # type: ignore
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
__all__ = ["Template", "Interpolation", "Conversion"]
|
|
7
|
+
|
|
8
|
+
Conversion: typing.TypeAlias = typing.Literal["a", "r", "s"]
|
|
9
|
+
|
|
10
|
+
if sys.version_info >= (3, 14):
|
|
11
|
+
TEMPLATE_STRING_SUPPORTED = True
|
|
12
|
+
StringOrTemplate: typing.TypeAlias = typing.LiteralString | Template
|
|
13
|
+
|
|
14
|
+
from string.templatelib import Interpolation, Template
|
|
15
|
+
|
|
16
|
+
else:
|
|
17
|
+
TEMPLATE_STRING_SUPPORTED = False
|
|
18
|
+
StringOrTemplate: typing.TypeAlias = str | Template
|
|
19
|
+
|
|
20
|
+
from ._compat import Interpolation, Template
|