python-fragments 0.32__py3-none-any.whl → 0.35__py3-none-any.whl

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.
fragments/ast_nodes.py CHANGED
@@ -253,6 +253,7 @@ class ASTComponent:
253
253
  name: "ASTComponentName"
254
254
  arguments: dict[str, "ASTComponentArgument"]
255
255
  children: Sequence["ASTHTMLChild"]
256
+ self_closing: bool
256
257
 
257
258
  transpiled_content: str = field(init=False)
258
259
  transpiled_start: int = field(init=False)
@@ -263,13 +264,14 @@ class ASTComponent:
263
264
  def transpile(self, transpiled_start: int) -> None:
264
265
  self.transpiled_start = transpiled_start
265
266
  self.name.transpile(self.transpiled_start)
266
- self.transpiled_content = self.name.transpiled_content + '(""'
267
+ self.transpiled_content = self.name.transpiled_content + "("
267
268
 
268
- for child in self.children:
269
- child.transpile(self.transpiled_start + len(self.transpiled_content))
270
- self.transpiled_content += "+" + child.transpiled_content
271
-
272
- self.transpiled_content += ","
269
+ if not self.self_closing:
270
+ self.transpiled_content += 'children=""'
271
+ for child in self.children:
272
+ child.transpile(self.transpiled_start + len(self.transpiled_content))
273
+ self.transpiled_content += "+" + child.transpiled_content
274
+ self.transpiled_content += ","
273
275
 
274
276
  for argument in self.arguments.values():
275
277
  argument.transpile(self.transpiled_start + len(self.transpiled_content))
@@ -464,7 +466,7 @@ class ASTHTMLAttribute:
464
466
  return
465
467
 
466
468
  if self.interpolation is None:
467
- self.transpiled_content = f'"{self.name}"'
469
+ self.transpiled_content = f"{self.name}"
468
470
  self.transpiled_end = self.transpiled_start + len(self.transpiled_content)
469
471
  return
470
472
 
fragments/grammar.py CHANGED
@@ -180,7 +180,7 @@ def expect_component(source: Source) -> tuple[Source, ASTComponent | ASTControlN
180
180
  if source.starts_with("/>"):
181
181
  source = expect_string(source, "/>")
182
182
  return source, ASTControlNode[ASTComponent].wrap_child(
183
- ASTComponent(source_start, source.offset, name, arguments, []),
183
+ ASTComponent(source_start, source.offset, name, arguments, [], self_closing=True),
184
184
  if_argument.interpolation if if_argument is not None else None,
185
185
  for_argument.interpolation if for_argument is not None else None,
186
186
  )
@@ -196,7 +196,7 @@ def expect_component(source: Source) -> tuple[Source, ASTComponent | ASTControlN
196
196
  raise ParsingError(f"Element closed ({closing_name.name}) is not the same as currently opened element ({name.name})", source.offset)
197
197
 
198
198
  return source, ASTControlNode[ASTComponent].wrap_child(
199
- ASTComponent(source_start=source_start, source_end=source.offset, name=name, arguments=arguments, children=children),
199
+ ASTComponent(source_start=source_start, source_end=source.offset, name=name, arguments=arguments, children=children, self_closing=False),
200
200
  if_argument.interpolation if if_argument is not None else None,
201
201
  for_argument.interpolation if for_argument is not None else None,
202
202
  )
@@ -9,7 +9,7 @@ def comment(content: str) -> str:
9
9
 
10
10
  def attribute_to_string(name: str, value: Any) -> str:
11
11
  if value is None:
12
- return name
12
+ return ""
13
13
 
14
14
  if isinstance(value, tuple):
15
15
  value = list(value)
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-fragments
3
+ Version: 0.35
4
+ Summary: Modern HTML template rendering in Python
5
+ Author-email: The Running Algorithm <services@therunningalgorithm.info>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 The Running Algorithm
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://python-fragments.io
29
+ Project-URL: Documentation, https://python-fragments.io
30
+ Project-URL: Source, https://github.com/TheRunningAlgorithm2/python-fragments
31
+ Project-URL: Issues, https://github.com/TheRunningAlgorithm2/python-fragments/issues
32
+ Classifier: Development Status :: 4 - Beta
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Operating System :: OS Independent
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.12
38
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
39
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
40
+ Classifier: Topic :: Text Processing :: Markup :: HTML
41
+ Requires-Python: >=3.12
42
+ Description-Content-Type: text/markdown
43
+ License-File: LICENSE
44
+ Provides-Extra: lsp
45
+ Requires-Dist: basedpyright>=1.39.0; extra == "lsp"
46
+ Requires-Dist: lsprotocol>=2025.0.0; extra == "lsp"
47
+ Provides-Extra: dev
48
+ Requires-Dist: pytest>=8.4.1; extra == "dev"
49
+ Dynamic: license-file
50
+
51
+ <p align="center">
52
+ <img src="logo.svg" alt="Python Fragments" width="300" />
53
+ </p>
54
+
55
+ <p align="center">
56
+ <a href="https://github.com/TheRunningAlgorithm2/python-fragments/actions/workflows/test.yml"><img src="https://github.com/TheRunningAlgorithm2/python-fragments/actions/workflows/test.yml/badge.svg" alt="Tests" /></a>
57
+ <a href="https://pypi.org/project/python-fragments/"><img src="https://img.shields.io/pypi/v/python-fragments" alt="PyPI version" /></a>
58
+ <a href="https://pypi.org/project/python-fragments/"><img src="https://img.shields.io/pypi/pyversions/python-fragments" alt="Python versions" /></a>
59
+ <a href="https://github.com/TheRunningAlgorithm2/python-fragments/blob/main/LICENSE"><img src="https://img.shields.io/github/license/TheRunningAlgorithm2/python-fragments" alt="License" /></a>
60
+ <a href="https://python-fragments.io"><img src="https://img.shields.io/badge/docs-python--fragments.io-blue" alt="Documentation" /></a>
61
+ <a href="https://marketplace.visualstudio.com/items?itemName=tra-technologies-ltd.python-fragments"><img src="https://img.shields.io/badge/VS%20Code-Extension-blue?logo=visualstudiocode" alt="VS Code Extension" /></a>
62
+ </p>
63
+
64
+ > **This package is in early development and not yet stable. The API may change without notice between releases.**
65
+
66
+ Modern HTML template rendering in Python. No build step, no template files, and native HTML awareness out of the box. [Read More](https://python-fragments.io)
67
+
68
+ ```python
69
+ from fastapi import APIRouter
70
+
71
+ router = APIRouter()
72
+
73
+ @router.get("/", response_class=HTMLResponse)
74
+ async def index() -> str:
75
+ published = [p for p in POSTS if p.published]
76
+ return <>
77
+ <Layout title="My Blog">
78
+ <h1>Latest Posts</h1>
79
+ <PostCard for={{ post in published }} post={{ post }} />
80
+ </Layout>
81
+ </>
82
+ ```
83
+
84
+ ## IDE Support
85
+
86
+ Type checking, completions, hover docs, go-to-definition, rename, and semantic highlighting. All working inside fragment syntax.
87
+
88
+ Install the [Python Fragments VS Code extension](https://marketplace.visualstudio.com/items?itemName=tra-technologies-ltd.python-fragments) to get started.
89
+
90
+ ![VS Code completions demo](docs/assets/vscode.gif)
91
+
92
+ ## Installation
93
+
94
+ ```bash
95
+ pip install python-fragments
96
+ ```
97
+
98
+ Register the loader at your application's entry point, before importing any modules that contain fragments:
99
+
100
+ ```python
101
+ from fragments import loader # isort: skip
102
+ ```
103
+
104
+ Any `.py` file containing `<>` is transpiled automatically. Nothing else to configure.
105
+
106
+ ## Documentation
107
+
108
+ Full documentation is available at [python-fragments.io](https://python-fragments.io).
109
+
110
+ ## Contributing
111
+
112
+ Bug reports, feature requests, and documentation improvements are all welcome. Code contributions aren't open yet while we work toward v1. See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
@@ -1,13 +1,13 @@
1
1
  fragments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- fragments/ast_nodes.py,sha256=6dzYASPw_5fZuESKutsjtb_2SNqAiBDikWbWWYZ48w0,21004
2
+ fragments/ast_nodes.py,sha256=Tzf5kvxEYwYFTvBvmtR2gZohCZl7nYjR6t_pRP6XWoo,21125
3
3
  fragments/cli.py,sha256=9P9fvurRlROstWUh-tjneFt9IrVvgxcBEd3pn0wL_CE,1292
4
- fragments/grammar.py,sha256=zBFL6W1vRGwEh-AqvAEj9acMduKCPuwuzaLfXXHZF3M,13726
4
+ fragments/grammar.py,sha256=J_U8I1g_iHMFDqc-8s9fGCIXwSlK43fRNeTXnEYfEGg,13765
5
5
  fragments/loader.py,sha256=7nlsXVjCpRRmd939UJQqd9NWbE2-3ZtbO0aFfz3zCs8,980
6
6
  fragments/source.py,sha256=cbDzLOlFy2C6xZe-ZWcroieGG-7PLTdLJi003aIdegg,1000
7
7
  fragments/transpiler.py,sha256=O4pusCuv_Hbms2J0AP3tun-uNkXfb27RBUugr_0dIVA,316
8
8
  fragments/types.py,sha256=oYaSQI4ShCnTKyL_keYqPKT8-vrN7ptDT7S8vaBCCx8,20
9
9
  fragments/html/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- fragments/html/elements.py,sha256=kVhMlt8mTzuh835k5aciAuRmfJD4-ZAixuim97EKjsM,1037
10
+ fragments/html/elements.py,sha256=OrW5YaV3y-D95tOBdkhBp6u7UTulpf_MVpSHzWQmtfU,1035
11
11
  fragments/lsp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  fragments/lsp/based_proxy.py,sha256=pFH-70H7uvy8yqxmq2s3jOmHw_CrAUHzyqiQXmDpJEI,3746
13
13
  fragments/lsp/file_state.py,sha256=mghS-hcvZyPb8n9Ufypteu9DDZfv9Ktin-3C7GYxBh0,3292
@@ -32,9 +32,9 @@ fragments/lsp/pyright_notification_handlers/__init__.py,sha256=47DEQpj8HBSa-_TIm
32
32
  fragments/lsp/pyright_notification_handlers/capability.py,sha256=1xH6SttTtWKAovCfx8WeJApRI4rrVlZDGCWHtlyGTUE,868
33
33
  fragments/lsp/pyright_notification_handlers/configuration.py,sha256=Qomq8tCVcBg15MYDPby-PeRp4kJe7k8o6-2VRAYQ0hI,1009
34
34
  fragments/lsp/pyright_notification_handlers/diagnostics.py,sha256=n_YZ8k8PHKcpHrgmTYTU2-pKi0P_v_WC1irqtAcs_iI,1107
35
- python_fragments-0.32.dist-info/licenses/LICENSE,sha256=wYLVpaPNaiHTc2qE2jUCGFpenNr53RQ3tVuuHNmE7jc,1078
36
- python_fragments-0.32.dist-info/METADATA,sha256=MllndTLa32w6TjSuxIyIeqvfINiZkWlXaDnhVT1Ama4,3233
37
- python_fragments-0.32.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
38
- python_fragments-0.32.dist-info/entry_points.txt,sha256=9xVMCpt9OucM3W3fh0UnEeErKEyI6CNIFqfQCIuGmi8,96
39
- python_fragments-0.32.dist-info/top_level.txt,sha256=4r1SNnHOK3BVhAT_KgV8MRq9kiV23yK_rnuTAEl5oRg,10
40
- python_fragments-0.32.dist-info/RECORD,,
35
+ python_fragments-0.35.dist-info/licenses/LICENSE,sha256=wYLVpaPNaiHTc2qE2jUCGFpenNr53RQ3tVuuHNmE7jc,1078
36
+ python_fragments-0.35.dist-info/METADATA,sha256=fVE7D6tRn1tDkLijS7MEeFoCIQxrqqpF_k18QlEBy4g,5278
37
+ python_fragments-0.35.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
38
+ python_fragments-0.35.dist-info/entry_points.txt,sha256=9xVMCpt9OucM3W3fh0UnEeErKEyI6CNIFqfQCIuGmi8,96
39
+ python_fragments-0.35.dist-info/top_level.txt,sha256=4r1SNnHOK3BVhAT_KgV8MRq9kiV23yK_rnuTAEl5oRg,10
40
+ python_fragments-0.35.dist-info/RECORD,,
@@ -1,88 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: python-fragments
3
- Version: 0.32
4
- Summary: Modern HTML template rendering in Python
5
- Author-email: The Running Algorithm <services@therunningalgorithm.info>
6
- License: MIT License
7
-
8
- Copyright (c) 2026 The Running Algorithm
9
-
10
- Permission is hereby granted, free of charge, to any person obtaining a copy
11
- of this software and associated documentation files (the "Software"), to deal
12
- in the Software without restriction, including without limitation the rights
13
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
- copies of the Software, and to permit persons to whom the Software is
15
- furnished to do so, subject to the following conditions:
16
-
17
- The above copyright notice and this permission notice shall be included in all
18
- copies or substantial portions of the Software.
19
-
20
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
- SOFTWARE.
27
-
28
- Requires-Python: >=3.12
29
- Description-Content-Type: text/markdown
30
- License-File: LICENSE
31
- Provides-Extra: lsp
32
- Requires-Dist: basedpyright>=1.39.0; extra == "lsp"
33
- Requires-Dist: lsprotocol>=2025.0.0; extra == "lsp"
34
- Provides-Extra: dev
35
- Requires-Dist: pytest>=8.4.1; extra == "dev"
36
- Dynamic: license-file
37
-
38
- # Python Fragments
39
-
40
- > **This package is in early development and not yet stable. The API may change without notice between releases.**
41
-
42
- Modern HTML template rendering in Python — no build step, no template files, and native HTML awareness out of the box.
43
-
44
- ```python
45
- from fragments import loader # isort: skip
46
-
47
- from fastapi import FastAPI
48
- from fastapi.responses import HTMLResponse
49
- from components import Layout, PostCard
50
-
51
- app = FastAPI()
52
-
53
- POSTS = [...]
54
-
55
- @app.get("/", response_class=HTMLResponse)
56
- async def index() -> str:
57
- published = [p for p in POSTS if p.published]
58
- return <>
59
- <Layout title="My Blog">
60
- <h1>Latest Posts</h1>
61
- <PostCard for={{ post in published }} post={{ post }} />
62
- </Layout>
63
- </>
64
- ```
65
-
66
- ## IDE Support
67
-
68
- Type checking, completions, hover docs, go-to-definition, rename, and semantic highlighting — all working inside fragment syntax.
69
-
70
- ![VS Code completions demo](docs/assets/vscode.gif)
71
-
72
- ## Installation
73
-
74
- ```bash
75
- pip install python-fragments
76
- ```
77
-
78
- Register the loader at your application's entry point, before importing any modules that contain fragments:
79
-
80
- ```python
81
- from fragments import loader # isort: skip
82
- ```
83
-
84
- Any `.py` file containing `<>` is transpiled automatically. Nothing else to configure.
85
-
86
- ## Feedback and feature requests
87
-
88
- Bug reports and feature requests are welcome via [GitHub Issues](https://github.com/TheRunningAlgorithm2/python-fragments/issues). The project is not currently accepting code contributions.