without-html 0.0.0__tar.gz → 0.0.6__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.
- without_html-0.0.6/PKG-INFO +69 -0
- without_html-0.0.6/README.md +50 -0
- without_html-0.0.6/pyproject.toml +26 -0
- without_html-0.0.6/pyproject.toml.orig +27 -0
- without_html-0.0.6/src/without_html/__init__.py +296 -0
- without_html-0.0.6/src/without_html/elements.py +602 -0
- without_html-0.0.6/src/without_html/markup.py +91 -0
- without_html-0.0.6/src/without_html/nodes.py +564 -0
- without_html-0.0.6/src/without_html/render.py +257 -0
- without_html-0.0.0/PKG-INFO +0 -5
- without_html-0.0.0/pyproject.toml +0 -9
- without_html-0.0.0/pyproject.toml.orig +0 -9
- /without_html-0.0.0/src/without_html/__init__.py → /without_html-0.0.6/src/without_html/py.typed +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: without-html
|
|
3
|
+
Version: 0.0.6
|
|
4
|
+
Summary: HTML as immutable Python values: build a node tree, render it to a string.
|
|
5
|
+
Author: Josh Karpel
|
|
6
|
+
Author-email: Josh Karpel <josh.karpel@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Dist: markupsafe>=3
|
|
17
|
+
Requires-Python: >=3.14
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# without-html
|
|
21
|
+
|
|
22
|
+
HTML as immutable Python values. Build a node tree with plain function calls,
|
|
23
|
+
render it to a string with a pure function. No template language, no loader
|
|
24
|
+
search path, no ambient application object, and no dependency on anything else in
|
|
25
|
+
the workspace: this is a value library that happens to be useful next to
|
|
26
|
+
[`without-web`](../without_web).
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from without_asgi import Response, html_content
|
|
30
|
+
from without_html import render, table, tbody, td, th, thead, tr
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def runs_table(runs): # a component is a function that returns a node
|
|
34
|
+
return table(
|
|
35
|
+
cls=["table", "table-striped"],
|
|
36
|
+
children=[
|
|
37
|
+
thead(children=tr(children=[th(children="run"), th(children="status")])),
|
|
38
|
+
tbody(children=(run_row(run) for run in runs)),
|
|
39
|
+
],
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def run_row(run):
|
|
44
|
+
return tr(attrs={"id": f"run-{run.id}"}, children=[td(children=run.id), td(children=run.status)])
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
Response.from_content(200, html_content(render(runs_table(runs))))
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Escaping is a type rather than a setting: text is escaped on the way in, and
|
|
51
|
+
`Markup` (MarkupSafe's own, kept under its own name, so anything Jinja or tdom
|
|
52
|
+
produced is already one) is the only thing that renders verbatim. The element
|
|
53
|
+
constructors carry HTML's own constraints in their signatures, so a void element
|
|
54
|
+
with children and an unescaped `<script>` body are type errors rather than runtime
|
|
55
|
+
surprises.
|
|
56
|
+
|
|
57
|
+
`render` returns the whole string; `render_chunks` walks the same tree and yields
|
|
58
|
+
the same bytes a chunk at a time, so a large page starts reaching a client while
|
|
59
|
+
the rest of it is still being built.
|
|
60
|
+
|
|
61
|
+
The tree is the interface, not the string: a component can be rendered whole for a
|
|
62
|
+
page or on its own for a fragment, and syntax sugar can be layered on later without
|
|
63
|
+
changing anything written against it.
|
|
64
|
+
|
|
65
|
+
See the
|
|
66
|
+
[`without-html` guide](https://without.help/without-html/)
|
|
67
|
+
(with the [API reference](https://without.help/without-html/reference/))
|
|
68
|
+
for nodes and escaping, the attribute rules, custom elements, and what this
|
|
69
|
+
deliberately does not do.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# without-html
|
|
2
|
+
|
|
3
|
+
HTML as immutable Python values. Build a node tree with plain function calls,
|
|
4
|
+
render it to a string with a pure function. No template language, no loader
|
|
5
|
+
search path, no ambient application object, and no dependency on anything else in
|
|
6
|
+
the workspace: this is a value library that happens to be useful next to
|
|
7
|
+
[`without-web`](../without_web).
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from without_asgi import Response, html_content
|
|
11
|
+
from without_html import render, table, tbody, td, th, thead, tr
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def runs_table(runs): # a component is a function that returns a node
|
|
15
|
+
return table(
|
|
16
|
+
cls=["table", "table-striped"],
|
|
17
|
+
children=[
|
|
18
|
+
thead(children=tr(children=[th(children="run"), th(children="status")])),
|
|
19
|
+
tbody(children=(run_row(run) for run in runs)),
|
|
20
|
+
],
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def run_row(run):
|
|
25
|
+
return tr(attrs={"id": f"run-{run.id}"}, children=[td(children=run.id), td(children=run.status)])
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
Response.from_content(200, html_content(render(runs_table(runs))))
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Escaping is a type rather than a setting: text is escaped on the way in, and
|
|
32
|
+
`Markup` (MarkupSafe's own, kept under its own name, so anything Jinja or tdom
|
|
33
|
+
produced is already one) is the only thing that renders verbatim. The element
|
|
34
|
+
constructors carry HTML's own constraints in their signatures, so a void element
|
|
35
|
+
with children and an unescaped `<script>` body are type errors rather than runtime
|
|
36
|
+
surprises.
|
|
37
|
+
|
|
38
|
+
`render` returns the whole string; `render_chunks` walks the same tree and yields
|
|
39
|
+
the same bytes a chunk at a time, so a large page starts reaching a client while
|
|
40
|
+
the rest of it is still being built.
|
|
41
|
+
|
|
42
|
+
The tree is the interface, not the string: a component can be rendered whole for a
|
|
43
|
+
page or on its own for a fragment, and syntax sugar can be layered on later without
|
|
44
|
+
changing anything written against it.
|
|
45
|
+
|
|
46
|
+
See the
|
|
47
|
+
[`without-html` guide](https://without.help/without-html/)
|
|
48
|
+
(with the [API reference](https://without.help/without-html/reference/))
|
|
49
|
+
for nodes and escaping, the attribute rules, custom elements, and what this
|
|
50
|
+
deliberately does not do.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build>=0.12.3,<0.13"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "without-html"
|
|
7
|
+
version = "0.0.6"
|
|
8
|
+
description = "HTML as immutable Python values: build a node tree, render it to a string."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.14"
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
18
|
+
"Programming Language :: Python :: 3.14",
|
|
19
|
+
"Topic :: Software Development :: Libraries",
|
|
20
|
+
"Typing :: Typed",
|
|
21
|
+
]
|
|
22
|
+
dependencies = ["markupsafe>=3"]
|
|
23
|
+
|
|
24
|
+
[[project.authors]]
|
|
25
|
+
name = "Josh Karpel"
|
|
26
|
+
email = "josh.karpel@gmail.com"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build>=0.12.3,<0.13"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "without-html"
|
|
7
|
+
version = "0.0.6"
|
|
8
|
+
description = "HTML as immutable Python values: build a node tree, render it to a string."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Josh Karpel", email = "josh.karpel@gmail.com" },
|
|
13
|
+
]
|
|
14
|
+
requires-python = ">=3.14"
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Programming Language :: Python :: 3.14",
|
|
22
|
+
"Topic :: Software Development :: Libraries",
|
|
23
|
+
"Typing :: Typed",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"markupsafe>=3",
|
|
27
|
+
]
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
from markupsafe import Markup
|
|
2
|
+
from markupsafe import escape
|
|
3
|
+
|
|
4
|
+
from without_html.markup import DOCTYPE
|
|
5
|
+
from without_html.markup import SupportsHtml
|
|
6
|
+
from without_html.nodes import AnyElement
|
|
7
|
+
from without_html.nodes import Attributes
|
|
8
|
+
from without_html.nodes import AttributeValue
|
|
9
|
+
from without_html.nodes import Child
|
|
10
|
+
from without_html.nodes import ClassNames
|
|
11
|
+
from without_html.nodes import Element
|
|
12
|
+
from without_html.nodes import ElementConstructor
|
|
13
|
+
from without_html.nodes import Node
|
|
14
|
+
from without_html.nodes import RawTextElementConstructor
|
|
15
|
+
from without_html.nodes import VoidElement
|
|
16
|
+
from without_html.nodes import VoidElementConstructor
|
|
17
|
+
from without_html.nodes import element
|
|
18
|
+
from without_html.nodes import element_type
|
|
19
|
+
from without_html.nodes import void_element_type
|
|
20
|
+
from without_html.render import render
|
|
21
|
+
from without_html.render import render_chunks
|
|
22
|
+
|
|
23
|
+
# The element constructors are re-exported here so that `from without_html import div`
|
|
24
|
+
# works; the block is generated, and isort is held off it so that the generator and the
|
|
25
|
+
# formatter do not disagree about where it starts.
|
|
26
|
+
# isort: off
|
|
27
|
+
# [[[cog import cog; from tags import emit_imports; cog.outl(emit_imports()) ]]]
|
|
28
|
+
from without_html.elements import a
|
|
29
|
+
from without_html.elements import abbr
|
|
30
|
+
from without_html.elements import address
|
|
31
|
+
from without_html.elements import area
|
|
32
|
+
from without_html.elements import article
|
|
33
|
+
from without_html.elements import aside
|
|
34
|
+
from without_html.elements import audio
|
|
35
|
+
from without_html.elements import b
|
|
36
|
+
from without_html.elements import base
|
|
37
|
+
from without_html.elements import bdi
|
|
38
|
+
from without_html.elements import bdo
|
|
39
|
+
from without_html.elements import blockquote
|
|
40
|
+
from without_html.elements import body
|
|
41
|
+
from without_html.elements import br
|
|
42
|
+
from without_html.elements import button
|
|
43
|
+
from without_html.elements import canvas
|
|
44
|
+
from without_html.elements import caption
|
|
45
|
+
from without_html.elements import cite
|
|
46
|
+
from without_html.elements import code
|
|
47
|
+
from without_html.elements import col
|
|
48
|
+
from without_html.elements import colgroup
|
|
49
|
+
from without_html.elements import data
|
|
50
|
+
from without_html.elements import datalist
|
|
51
|
+
from without_html.elements import dd
|
|
52
|
+
from without_html.elements import del_
|
|
53
|
+
from without_html.elements import details
|
|
54
|
+
from without_html.elements import dfn
|
|
55
|
+
from without_html.elements import dialog
|
|
56
|
+
from without_html.elements import div
|
|
57
|
+
from without_html.elements import dl
|
|
58
|
+
from without_html.elements import dt
|
|
59
|
+
from without_html.elements import em
|
|
60
|
+
from without_html.elements import embed
|
|
61
|
+
from without_html.elements import fieldset
|
|
62
|
+
from without_html.elements import figcaption
|
|
63
|
+
from without_html.elements import figure
|
|
64
|
+
from without_html.elements import footer
|
|
65
|
+
from without_html.elements import form
|
|
66
|
+
from without_html.elements import h1
|
|
67
|
+
from without_html.elements import h2
|
|
68
|
+
from without_html.elements import h3
|
|
69
|
+
from without_html.elements import h4
|
|
70
|
+
from without_html.elements import h5
|
|
71
|
+
from without_html.elements import h6
|
|
72
|
+
from without_html.elements import head
|
|
73
|
+
from without_html.elements import header
|
|
74
|
+
from without_html.elements import hgroup
|
|
75
|
+
from without_html.elements import hr
|
|
76
|
+
from without_html.elements import html
|
|
77
|
+
from without_html.elements import i
|
|
78
|
+
from without_html.elements import iframe
|
|
79
|
+
from without_html.elements import img
|
|
80
|
+
from without_html.elements import input_
|
|
81
|
+
from without_html.elements import ins
|
|
82
|
+
from without_html.elements import kbd
|
|
83
|
+
from without_html.elements import label
|
|
84
|
+
from without_html.elements import legend
|
|
85
|
+
from without_html.elements import li
|
|
86
|
+
from without_html.elements import link
|
|
87
|
+
from without_html.elements import main
|
|
88
|
+
from without_html.elements import map_
|
|
89
|
+
from without_html.elements import mark
|
|
90
|
+
from without_html.elements import menu
|
|
91
|
+
from without_html.elements import meta
|
|
92
|
+
from without_html.elements import meter
|
|
93
|
+
from without_html.elements import nav
|
|
94
|
+
from without_html.elements import noembed
|
|
95
|
+
from without_html.elements import noframes
|
|
96
|
+
from without_html.elements import noscript
|
|
97
|
+
from without_html.elements import object_
|
|
98
|
+
from without_html.elements import ol
|
|
99
|
+
from without_html.elements import optgroup
|
|
100
|
+
from without_html.elements import option
|
|
101
|
+
from without_html.elements import output
|
|
102
|
+
from without_html.elements import p
|
|
103
|
+
from without_html.elements import picture
|
|
104
|
+
from without_html.elements import pre
|
|
105
|
+
from without_html.elements import progress
|
|
106
|
+
from without_html.elements import q
|
|
107
|
+
from without_html.elements import rp
|
|
108
|
+
from without_html.elements import rt
|
|
109
|
+
from without_html.elements import ruby
|
|
110
|
+
from without_html.elements import s
|
|
111
|
+
from without_html.elements import samp
|
|
112
|
+
from without_html.elements import script
|
|
113
|
+
from without_html.elements import search
|
|
114
|
+
from without_html.elements import section
|
|
115
|
+
from without_html.elements import select
|
|
116
|
+
from without_html.elements import slot
|
|
117
|
+
from without_html.elements import small
|
|
118
|
+
from without_html.elements import source
|
|
119
|
+
from without_html.elements import span
|
|
120
|
+
from without_html.elements import strong
|
|
121
|
+
from without_html.elements import style
|
|
122
|
+
from without_html.elements import sub
|
|
123
|
+
from without_html.elements import summary
|
|
124
|
+
from without_html.elements import sup
|
|
125
|
+
from without_html.elements import svg
|
|
126
|
+
from without_html.elements import table
|
|
127
|
+
from without_html.elements import tbody
|
|
128
|
+
from without_html.elements import td
|
|
129
|
+
from without_html.elements import template
|
|
130
|
+
from without_html.elements import textarea
|
|
131
|
+
from without_html.elements import tfoot
|
|
132
|
+
from without_html.elements import th
|
|
133
|
+
from without_html.elements import thead
|
|
134
|
+
from without_html.elements import time
|
|
135
|
+
from without_html.elements import title
|
|
136
|
+
from without_html.elements import tr
|
|
137
|
+
from without_html.elements import track
|
|
138
|
+
from without_html.elements import u
|
|
139
|
+
from without_html.elements import ul
|
|
140
|
+
from without_html.elements import var
|
|
141
|
+
from without_html.elements import video
|
|
142
|
+
from without_html.elements import wbr
|
|
143
|
+
from without_html.elements import xmp
|
|
144
|
+
# [[[end]]]
|
|
145
|
+
# isort: on
|
|
146
|
+
|
|
147
|
+
__all__ = [
|
|
148
|
+
# [[[cog
|
|
149
|
+
# import cog
|
|
150
|
+
# from tags import emit_names
|
|
151
|
+
# cog.outl(emit_names([
|
|
152
|
+
# "DOCTYPE", "AttributeValue", "Attributes", "Child", "ClassNames", "Element",
|
|
153
|
+
# "Node", "Markup", "SupportsHtml", "AnyElement", "VoidElement",
|
|
154
|
+
# "ElementConstructor", "RawTextElementConstructor", "VoidElementConstructor",
|
|
155
|
+
# "element", "element_type", "void_element_type",
|
|
156
|
+
# "escape", "render", "render_chunks",
|
|
157
|
+
# ]))
|
|
158
|
+
# ]]]
|
|
159
|
+
"DOCTYPE",
|
|
160
|
+
"AnyElement",
|
|
161
|
+
"AttributeValue",
|
|
162
|
+
"Attributes",
|
|
163
|
+
"Child",
|
|
164
|
+
"ClassNames",
|
|
165
|
+
"Element",
|
|
166
|
+
"ElementConstructor",
|
|
167
|
+
"Markup",
|
|
168
|
+
"Node",
|
|
169
|
+
"RawTextElementConstructor",
|
|
170
|
+
"SupportsHtml",
|
|
171
|
+
"VoidElement",
|
|
172
|
+
"VoidElementConstructor",
|
|
173
|
+
"a",
|
|
174
|
+
"abbr",
|
|
175
|
+
"address",
|
|
176
|
+
"area",
|
|
177
|
+
"article",
|
|
178
|
+
"aside",
|
|
179
|
+
"audio",
|
|
180
|
+
"b",
|
|
181
|
+
"base",
|
|
182
|
+
"bdi",
|
|
183
|
+
"bdo",
|
|
184
|
+
"blockquote",
|
|
185
|
+
"body",
|
|
186
|
+
"br",
|
|
187
|
+
"button",
|
|
188
|
+
"canvas",
|
|
189
|
+
"caption",
|
|
190
|
+
"cite",
|
|
191
|
+
"code",
|
|
192
|
+
"col",
|
|
193
|
+
"colgroup",
|
|
194
|
+
"data",
|
|
195
|
+
"datalist",
|
|
196
|
+
"dd",
|
|
197
|
+
"del_",
|
|
198
|
+
"details",
|
|
199
|
+
"dfn",
|
|
200
|
+
"dialog",
|
|
201
|
+
"div",
|
|
202
|
+
"dl",
|
|
203
|
+
"dt",
|
|
204
|
+
"element",
|
|
205
|
+
"element_type",
|
|
206
|
+
"em",
|
|
207
|
+
"embed",
|
|
208
|
+
"escape",
|
|
209
|
+
"fieldset",
|
|
210
|
+
"figcaption",
|
|
211
|
+
"figure",
|
|
212
|
+
"footer",
|
|
213
|
+
"form",
|
|
214
|
+
"h1",
|
|
215
|
+
"h2",
|
|
216
|
+
"h3",
|
|
217
|
+
"h4",
|
|
218
|
+
"h5",
|
|
219
|
+
"h6",
|
|
220
|
+
"head",
|
|
221
|
+
"header",
|
|
222
|
+
"hgroup",
|
|
223
|
+
"hr",
|
|
224
|
+
"html",
|
|
225
|
+
"i",
|
|
226
|
+
"iframe",
|
|
227
|
+
"img",
|
|
228
|
+
"input_",
|
|
229
|
+
"ins",
|
|
230
|
+
"kbd",
|
|
231
|
+
"label",
|
|
232
|
+
"legend",
|
|
233
|
+
"li",
|
|
234
|
+
"link",
|
|
235
|
+
"main",
|
|
236
|
+
"map_",
|
|
237
|
+
"mark",
|
|
238
|
+
"menu",
|
|
239
|
+
"meta",
|
|
240
|
+
"meter",
|
|
241
|
+
"nav",
|
|
242
|
+
"noembed",
|
|
243
|
+
"noframes",
|
|
244
|
+
"noscript",
|
|
245
|
+
"object_",
|
|
246
|
+
"ol",
|
|
247
|
+
"optgroup",
|
|
248
|
+
"option",
|
|
249
|
+
"output",
|
|
250
|
+
"p",
|
|
251
|
+
"picture",
|
|
252
|
+
"pre",
|
|
253
|
+
"progress",
|
|
254
|
+
"q",
|
|
255
|
+
"render",
|
|
256
|
+
"render_chunks",
|
|
257
|
+
"rp",
|
|
258
|
+
"rt",
|
|
259
|
+
"ruby",
|
|
260
|
+
"s",
|
|
261
|
+
"samp",
|
|
262
|
+
"script",
|
|
263
|
+
"search",
|
|
264
|
+
"section",
|
|
265
|
+
"select",
|
|
266
|
+
"slot",
|
|
267
|
+
"small",
|
|
268
|
+
"source",
|
|
269
|
+
"span",
|
|
270
|
+
"strong",
|
|
271
|
+
"style",
|
|
272
|
+
"sub",
|
|
273
|
+
"summary",
|
|
274
|
+
"sup",
|
|
275
|
+
"svg",
|
|
276
|
+
"table",
|
|
277
|
+
"tbody",
|
|
278
|
+
"td",
|
|
279
|
+
"template",
|
|
280
|
+
"textarea",
|
|
281
|
+
"tfoot",
|
|
282
|
+
"th",
|
|
283
|
+
"thead",
|
|
284
|
+
"time",
|
|
285
|
+
"title",
|
|
286
|
+
"tr",
|
|
287
|
+
"track",
|
|
288
|
+
"u",
|
|
289
|
+
"ul",
|
|
290
|
+
"var",
|
|
291
|
+
"video",
|
|
292
|
+
"void_element_type",
|
|
293
|
+
"wbr",
|
|
294
|
+
"xmp",
|
|
295
|
+
# [[[end]]]
|
|
296
|
+
]
|