htmy 0.6.0__tar.gz → 0.7.1__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.
Potentially problematic release.
This version of htmy might be problematic. Click here for more details.
- {htmy-0.6.0 → htmy-0.7.1}/PKG-INFO +2 -2
- {htmy-0.6.0 → htmy-0.7.1}/README.md +1 -1
- htmy-0.7.1/htmy/function_component.py +367 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/renderer/default.py +3 -2
- {htmy-0.6.0 → htmy-0.7.1}/pyproject.toml +1 -1
- htmy-0.6.0/htmy/function_component.py +0 -243
- {htmy-0.6.0 → htmy-0.7.1}/LICENSE +0 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/__init__.py +0 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/core.py +0 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/etree.py +0 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/html.py +0 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/i18n.py +0 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/io.py +0 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/md/__init__.py +0 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/md/core.py +0 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/md/typing.py +0 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/py.typed +0 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/renderer/__init__.py +0 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/renderer/baseline.py +0 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/snippet.py +0 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/typing.py +0 -0
- {htmy-0.6.0 → htmy-0.7.1}/htmy/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: htmy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: Async, pure-Python rendering engine.
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Peter Volf
|
|
@@ -116,7 +116,7 @@ user_table = html.table(
|
|
|
116
116
|
)
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-
`htmy` also provides a `@component` decorator that can be used on sync or async `my_component(props: MyProps, context: Context) -> Component` functions to convert them into components (preserving the `props` typing).
|
|
119
|
+
`htmy` also provides a powerful `@component` decorator that can be used on sync or async `my_component(props: MyProps, context: Context) -> Component` functions and methods to convert them into components (preserving the `props` typing). You can find out more about this feature in the [Function components](https://volfpeter.github.io/htmy/function-components/) guide.
|
|
120
120
|
|
|
121
121
|
Here is the same example as above, but with function components:
|
|
122
122
|
|
|
@@ -97,7 +97,7 @@ user_table = html.table(
|
|
|
97
97
|
)
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
`htmy` also provides a `@component` decorator that can be used on sync or async `my_component(props: MyProps, context: Context) -> Component` functions to convert them into components (preserving the `props` typing).
|
|
100
|
+
`htmy` also provides a powerful `@component` decorator that can be used on sync or async `my_component(props: MyProps, context: Context) -> Component` functions and methods to convert them into components (preserving the `props` typing). You can find out more about this feature in the [Function components](https://volfpeter.github.io/htmy/function-components/) guide.
|
|
101
101
|
|
|
102
102
|
Here is the same example as above, but with function components:
|
|
103
103
|
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from collections.abc import Callable, Coroutine
|
|
5
|
+
from typing import Any, Protocol, TypeAlias, overload
|
|
6
|
+
|
|
7
|
+
from .typing import AsyncComponent, Component, Context, SyncComponent
|
|
8
|
+
from .typing import T as TProps
|
|
9
|
+
from .typing import U as TSelf
|
|
10
|
+
|
|
11
|
+
# -- Typing for "full" function components and context only method components.
|
|
12
|
+
|
|
13
|
+
_SyncFunctionComponent: TypeAlias = Callable[[TProps, Context], Component]
|
|
14
|
+
"""
|
|
15
|
+
Protocol definition for sync function components that have both a properties and a context argument.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
_AsyncFunctionComponent: TypeAlias = Callable[[TProps, Context], Coroutine[Any, Any, Component]]
|
|
19
|
+
"""
|
|
20
|
+
Protocol definition for async function components that have both a properties and a context argument.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
# -- Typing for context-only function components.
|
|
24
|
+
|
|
25
|
+
_ContextOnlySyncFunctionComponent: TypeAlias = Callable[[Context], Component]
|
|
26
|
+
"""
|
|
27
|
+
Protocol definition for sync function components that only have a context argument.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class _DecoratedContextOnlySyncFunctionComponent(SyncComponent, Protocol):
|
|
32
|
+
"""
|
|
33
|
+
Protocol definition for sync components that are also callable, and return a sync
|
|
34
|
+
component when called.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __call__(self) -> SyncComponent: ...
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
_ContextOnlyAsyncFunctionComponent: TypeAlias = Callable[[Context], Coroutine[Any, Any, Component]]
|
|
41
|
+
"""
|
|
42
|
+
Protocol definition for async function components that only have a context argument.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class _DecoratedContextOnlyAsyncFunctionComponent(SyncComponent, Protocol):
|
|
47
|
+
"""
|
|
48
|
+
Protocol definition for async components that are also callable, and return an async
|
|
49
|
+
component when called.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __call__(self) -> SyncComponent: ...
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# -- Typing for "full" method components.
|
|
56
|
+
|
|
57
|
+
_SyncMethodComponent: TypeAlias = Callable[[TSelf, TProps, Context], Component]
|
|
58
|
+
"""
|
|
59
|
+
Protocol definition for sync method components that have both a properties and a context argument.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
_AsyncMethodComponent: TypeAlias = Callable[[TSelf, TProps, Context], Coroutine[Any, Any, Component]]
|
|
63
|
+
"""
|
|
64
|
+
Protocol definition for async method components that have both a properties and a context argument.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# -- Component decorators.
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ComponentDecorators:
|
|
72
|
+
"""
|
|
73
|
+
Function component decorators.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
__slots__ = ()
|
|
77
|
+
|
|
78
|
+
# -- Function component decorator.
|
|
79
|
+
|
|
80
|
+
@overload
|
|
81
|
+
def __call__(self, func: _SyncFunctionComponent[TProps]) -> Callable[[TProps], SyncComponent]: ...
|
|
82
|
+
|
|
83
|
+
@overload
|
|
84
|
+
def __call__(self, func: _AsyncFunctionComponent[TProps]) -> Callable[[TProps], AsyncComponent]: ...
|
|
85
|
+
|
|
86
|
+
def __call__(
|
|
87
|
+
self,
|
|
88
|
+
func: _SyncFunctionComponent[TProps] | _AsyncFunctionComponent[TProps],
|
|
89
|
+
) -> Callable[[TProps], SyncComponent] | Callable[[TProps], AsyncComponent]:
|
|
90
|
+
"""
|
|
91
|
+
Decorator that converts the decorated function into one that must be called with
|
|
92
|
+
the function component's properties and returns a component instance.
|
|
93
|
+
|
|
94
|
+
If used on an async function, the resulting component will also be async;
|
|
95
|
+
otherwise it will be sync.
|
|
96
|
+
|
|
97
|
+
Example:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
@component
|
|
101
|
+
def my_component(props: int, context: Context) -> Component:
|
|
102
|
+
return html.p(f"Value: {props}")
|
|
103
|
+
|
|
104
|
+
async def render() -> str:
|
|
105
|
+
return await Renderer().render(
|
|
106
|
+
my_component(42)
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Arguments:
|
|
111
|
+
func: The decorated function.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
A function that must be called with the function component's properties and
|
|
115
|
+
returns a component instance. (Or loosly speaking, an `HTMYComponentType` which
|
|
116
|
+
can be "instantiated" with the function component's properties.)
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
if asyncio.iscoroutinefunction(func):
|
|
120
|
+
|
|
121
|
+
def async_wrapper(props: TProps) -> AsyncComponent:
|
|
122
|
+
# This function must be async, in case the renderer inspects it to decide how to handle it.
|
|
123
|
+
async def component(context: Context) -> Component:
|
|
124
|
+
return await func(props, context) # type: ignore[no-any-return]
|
|
125
|
+
|
|
126
|
+
component.htmy = component # type: ignore[attr-defined]
|
|
127
|
+
return component # type: ignore[return-value]
|
|
128
|
+
|
|
129
|
+
return async_wrapper
|
|
130
|
+
else:
|
|
131
|
+
|
|
132
|
+
def sync_wrapper(props: TProps) -> SyncComponent:
|
|
133
|
+
def component(context: Context) -> Component:
|
|
134
|
+
return func(props, context) # type: ignore[return-value]
|
|
135
|
+
|
|
136
|
+
component.htmy = component # type: ignore[attr-defined]
|
|
137
|
+
return component # type: ignore[return-value]
|
|
138
|
+
|
|
139
|
+
return sync_wrapper
|
|
140
|
+
|
|
141
|
+
@overload
|
|
142
|
+
def function(self, func: _SyncFunctionComponent[TProps]) -> Callable[[TProps], SyncComponent]: ...
|
|
143
|
+
|
|
144
|
+
@overload
|
|
145
|
+
def function(self, func: _AsyncFunctionComponent[TProps]) -> Callable[[TProps], AsyncComponent]: ...
|
|
146
|
+
|
|
147
|
+
def function(
|
|
148
|
+
self,
|
|
149
|
+
func: _SyncFunctionComponent[TProps] | _AsyncFunctionComponent[TProps],
|
|
150
|
+
) -> Callable[[TProps], SyncComponent] | Callable[[TProps], AsyncComponent]:
|
|
151
|
+
"""
|
|
152
|
+
Decorator that converts the decorated function into one that must be called with
|
|
153
|
+
the function component's properties and returns a component instance.
|
|
154
|
+
|
|
155
|
+
If used on an async function, the resulting component will also be async;
|
|
156
|
+
otherwise it will be sync.
|
|
157
|
+
|
|
158
|
+
This function is just an alias for `__call__()`.
|
|
159
|
+
|
|
160
|
+
Example:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
@component.function
|
|
164
|
+
def my_component(props: int, context: Context) -> Component:
|
|
165
|
+
return html.p(f"Value: {props}")
|
|
166
|
+
|
|
167
|
+
async def render() -> str:
|
|
168
|
+
return await Renderer().render(
|
|
169
|
+
my_component(42)
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
Arguments:
|
|
173
|
+
func: The decorated function.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
A function that must be called with the function component's properties and
|
|
177
|
+
returns a component instance. (Or loosly speaking, an `HTMYComponentType` which
|
|
178
|
+
can be "instantiated" with the function component's properties.)
|
|
179
|
+
"""
|
|
180
|
+
return self(func)
|
|
181
|
+
|
|
182
|
+
# -- Context-only function component decorator.
|
|
183
|
+
|
|
184
|
+
@overload
|
|
185
|
+
def context_only(
|
|
186
|
+
self, func: _ContextOnlySyncFunctionComponent
|
|
187
|
+
) -> _DecoratedContextOnlySyncFunctionComponent: ...
|
|
188
|
+
|
|
189
|
+
@overload
|
|
190
|
+
def context_only(
|
|
191
|
+
self, func: _ContextOnlyAsyncFunctionComponent
|
|
192
|
+
) -> _DecoratedContextOnlyAsyncFunctionComponent: ...
|
|
193
|
+
|
|
194
|
+
def context_only(
|
|
195
|
+
self,
|
|
196
|
+
func: _ContextOnlySyncFunctionComponent | _ContextOnlyAsyncFunctionComponent,
|
|
197
|
+
) -> _DecoratedContextOnlySyncFunctionComponent | _DecoratedContextOnlyAsyncFunctionComponent:
|
|
198
|
+
"""
|
|
199
|
+
Decorator that converts the decorated function into a component.
|
|
200
|
+
|
|
201
|
+
If used on an async function, the resulting component will also be async;
|
|
202
|
+
otherwise it will be sync.
|
|
203
|
+
|
|
204
|
+
Example:
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
@component.context_only
|
|
208
|
+
def my_component(ctx):
|
|
209
|
+
return "Context only function component."
|
|
210
|
+
|
|
211
|
+
async def render() -> str:
|
|
212
|
+
return await Renderer().render(
|
|
213
|
+
my_component()
|
|
214
|
+
)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Arguments:
|
|
218
|
+
func: The decorated function.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
The created component.
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
def wrapper() -> SyncComponent | AsyncComponent:
|
|
225
|
+
func.htmy = func # type: ignore[union-attr]
|
|
226
|
+
return func # type: ignore[return-value]
|
|
227
|
+
|
|
228
|
+
# This assignment adds support for context-only function components without call signature.
|
|
229
|
+
wrapper.htmy = func # type: ignore[attr-defined]
|
|
230
|
+
return wrapper # type: ignore[return-value]
|
|
231
|
+
|
|
232
|
+
# -- Method component decorator.
|
|
233
|
+
|
|
234
|
+
@overload
|
|
235
|
+
def method(
|
|
236
|
+
self, func: _SyncMethodComponent[TSelf, TProps]
|
|
237
|
+
) -> Callable[[TSelf, TProps], SyncComponent]: ...
|
|
238
|
+
|
|
239
|
+
@overload
|
|
240
|
+
def method(
|
|
241
|
+
self, func: _AsyncMethodComponent[TSelf, TProps]
|
|
242
|
+
) -> Callable[[TSelf, TProps], AsyncComponent]: ...
|
|
243
|
+
|
|
244
|
+
def method(
|
|
245
|
+
self,
|
|
246
|
+
func: _SyncMethodComponent[TSelf, TProps] | _AsyncMethodComponent[TSelf, TProps],
|
|
247
|
+
) -> Callable[[TSelf, TProps], SyncComponent] | Callable[[TSelf, TProps], AsyncComponent]:
|
|
248
|
+
"""
|
|
249
|
+
Decorator that converts the decorated method into one that must be called with
|
|
250
|
+
the method component's properties and returns a component instance.
|
|
251
|
+
|
|
252
|
+
If used on an async method, the resulting component will also be async;
|
|
253
|
+
otherwise it will be sync.
|
|
254
|
+
|
|
255
|
+
Example:
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
@dataclass
|
|
259
|
+
class MyBusinessObject:
|
|
260
|
+
message: str
|
|
261
|
+
|
|
262
|
+
@component.method
|
|
263
|
+
def paragraph(self, props: int, context: Context) -> Component:
|
|
264
|
+
return html.p(f"{self.message} {props}")
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
async def render() -> str:
|
|
268
|
+
return await Renderer().render(
|
|
269
|
+
MyBusinessObject("Hi!").paragraph(42)
|
|
270
|
+
)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Arguments:
|
|
274
|
+
func: The decorated method.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
A method that must be called with the method component's properties and
|
|
278
|
+
returns a component instance. (Or loosly speaking, an `HTMYComponentType` which
|
|
279
|
+
can be "instantiated" with the method component's properties.)
|
|
280
|
+
"""
|
|
281
|
+
if asyncio.iscoroutinefunction(func):
|
|
282
|
+
|
|
283
|
+
def async_wrapper(self: TSelf, props: TProps) -> AsyncComponent:
|
|
284
|
+
# This function must be async, in case the renderer inspects it to decide how to handle it.
|
|
285
|
+
async def component(context: Context) -> Component:
|
|
286
|
+
return await func(self, props, context) # type: ignore[no-any-return]
|
|
287
|
+
|
|
288
|
+
component.htmy = component # type: ignore[attr-defined]
|
|
289
|
+
return component # type: ignore[return-value]
|
|
290
|
+
|
|
291
|
+
return async_wrapper
|
|
292
|
+
else:
|
|
293
|
+
|
|
294
|
+
def sync_wrapper(self: TSelf, props: TProps) -> SyncComponent:
|
|
295
|
+
def component(context: Context) -> Component:
|
|
296
|
+
return func(self, props, context) # type: ignore[return-value]
|
|
297
|
+
|
|
298
|
+
component.htmy = component # type: ignore[attr-defined]
|
|
299
|
+
return component # type: ignore[return-value]
|
|
300
|
+
|
|
301
|
+
return sync_wrapper
|
|
302
|
+
|
|
303
|
+
# -- Context-only function component decorator.
|
|
304
|
+
|
|
305
|
+
@overload
|
|
306
|
+
def context_only_method(
|
|
307
|
+
self, func: _SyncFunctionComponent[TSelf]
|
|
308
|
+
) -> Callable[[TSelf], SyncComponent]: ...
|
|
309
|
+
|
|
310
|
+
@overload
|
|
311
|
+
def context_only_method(
|
|
312
|
+
self, func: _AsyncFunctionComponent[TSelf]
|
|
313
|
+
) -> Callable[[TSelf], AsyncComponent]: ...
|
|
314
|
+
|
|
315
|
+
def context_only_method(
|
|
316
|
+
self,
|
|
317
|
+
func: _SyncFunctionComponent[TSelf] | _AsyncFunctionComponent[TSelf],
|
|
318
|
+
) -> Callable[[TSelf], SyncComponent] | Callable[[TSelf], AsyncComponent]:
|
|
319
|
+
"""
|
|
320
|
+
Decorator that converts the decorated method into one that must be called
|
|
321
|
+
without any arguments and returns a component instance.
|
|
322
|
+
|
|
323
|
+
If used on an async method, the resulting component will also be async;
|
|
324
|
+
otherwise it will be sync.
|
|
325
|
+
|
|
326
|
+
Example:
|
|
327
|
+
|
|
328
|
+
```python
|
|
329
|
+
@dataclass
|
|
330
|
+
class MyBusinessObject:
|
|
331
|
+
message: str
|
|
332
|
+
|
|
333
|
+
@component.context_only_method
|
|
334
|
+
def paragraph(self, context: Context) -> Component:
|
|
335
|
+
return html.p(f"{self.message} Goodbye!")
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
async def render() -> str:
|
|
339
|
+
return await Renderer().render(
|
|
340
|
+
MyBusinessObject("Hello!").paragraph()
|
|
341
|
+
)
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Arguments:
|
|
345
|
+
func: The decorated method.
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
A method that must be called without any arguments and returns a component instance.
|
|
349
|
+
(Or loosly speaking, an `HTMYComponentType` which can be "instantiated" by calling
|
|
350
|
+
the method.)
|
|
351
|
+
"""
|
|
352
|
+
# A context only method component must be implemented in the same way as
|
|
353
|
+
# a function component. The self argument replaces the props argument
|
|
354
|
+
# and it is added automatically by Python when the method is called.
|
|
355
|
+
# Even the type hint must be the same.
|
|
356
|
+
# This implementation doesn't make the function itself a component though,
|
|
357
|
+
# so the call signature is always necessary (unlike for context-only function
|
|
358
|
+
# components).
|
|
359
|
+
return self(func)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
component = ComponentDecorators()
|
|
363
|
+
"""
|
|
364
|
+
Decorators for converting functions into components
|
|
365
|
+
|
|
366
|
+
This is an instance of `ComponentDecorators`.
|
|
367
|
+
"""
|
|
@@ -209,8 +209,9 @@ class _ComponentRenderer:
|
|
|
209
209
|
process_node_result(node, result, child_context)
|
|
210
210
|
|
|
211
211
|
if async_todos:
|
|
212
|
-
|
|
213
|
-
async_todos
|
|
212
|
+
current_async_todos = async_todos
|
|
213
|
+
self._async_todos = async_todos = deque()
|
|
214
|
+
await asyncio.gather(*(process_async_node(n, ctx) for n, ctx in current_async_todos))
|
|
214
215
|
|
|
215
216
|
if self._error_boundary_todos:
|
|
216
217
|
await asyncio.gather(
|
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import asyncio
|
|
4
|
-
from collections.abc import Callable, Coroutine
|
|
5
|
-
from typing import Any, Protocol, TypeAlias, overload
|
|
6
|
-
|
|
7
|
-
from .typing import AsyncComponent, Component, Context, SyncComponent, T
|
|
8
|
-
|
|
9
|
-
# -- Typing for "full" function components.
|
|
10
|
-
|
|
11
|
-
_SyncFunctionComponent: TypeAlias = Callable[[T, Context], Component]
|
|
12
|
-
"""
|
|
13
|
-
Protocol definition for sync function components that have both a properties and a context argument.
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
_AsyncFunctionComponent: TypeAlias = Callable[[T, Context], Coroutine[Any, Any, Component]]
|
|
17
|
-
"""
|
|
18
|
-
Protocol definition for async function components that have both a properties and a context argument.
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
_FunctionComponent: TypeAlias = _SyncFunctionComponent[T] | _AsyncFunctionComponent[T]
|
|
22
|
-
"""
|
|
23
|
-
Function component type that has both a properties and a context argument.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
# -- Typing for context-only function components.
|
|
27
|
-
|
|
28
|
-
_ContextOnlySyncFunctionComponent: TypeAlias = Callable[[Context], Component]
|
|
29
|
-
"""
|
|
30
|
-
Protocol definition for sync function components that only have a context argument.
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class _DecoratedContextOnlySyncFunctionComponent(SyncComponent, Protocol):
|
|
35
|
-
"""
|
|
36
|
-
Protocol definition for sync components that are also callable, and return a sync
|
|
37
|
-
component when called.
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
def __call__(self) -> SyncComponent: ...
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
_ContextOnlyAsyncFunctionComponent: TypeAlias = Callable[[Context], Coroutine[Any, Any, Component]]
|
|
44
|
-
"""
|
|
45
|
-
Protocol definition for async function components that only have a context argument.
|
|
46
|
-
"""
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class _DecoratedContextOnlyAsyncFunctionComponent(SyncComponent, Protocol):
|
|
50
|
-
"""
|
|
51
|
-
Protocol definition for async components that are also callable, and return an async
|
|
52
|
-
component when called.
|
|
53
|
-
"""
|
|
54
|
-
|
|
55
|
-
def __call__(self) -> SyncComponent: ...
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
_ContextOnlyFunctionComponent: TypeAlias = (
|
|
59
|
-
_ContextOnlySyncFunctionComponent | _ContextOnlyAsyncFunctionComponent
|
|
60
|
-
)
|
|
61
|
-
"""
|
|
62
|
-
Function component type that only accepts a context argument.
|
|
63
|
-
"""
|
|
64
|
-
|
|
65
|
-
_DecoratedContextOnlyFunction: TypeAlias = (
|
|
66
|
-
_DecoratedContextOnlySyncFunctionComponent | _DecoratedContextOnlyAsyncFunctionComponent
|
|
67
|
-
)
|
|
68
|
-
"""
|
|
69
|
-
Protocol definition for sync or async components that are also callable, and return a sync
|
|
70
|
-
or async component when called.
|
|
71
|
-
"""
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
class ComponentDecorators:
|
|
75
|
-
"""
|
|
76
|
-
Function component decorators.
|
|
77
|
-
"""
|
|
78
|
-
|
|
79
|
-
__slots__ = ()
|
|
80
|
-
|
|
81
|
-
# -- FunctionComponent decorator.
|
|
82
|
-
|
|
83
|
-
@overload
|
|
84
|
-
def __call__(self, func: _SyncFunctionComponent[T]) -> Callable[[T], SyncComponent]: ...
|
|
85
|
-
|
|
86
|
-
@overload
|
|
87
|
-
def __call__(self, func: _AsyncFunctionComponent[T]) -> Callable[[T], AsyncComponent]: ...
|
|
88
|
-
|
|
89
|
-
def __call__(
|
|
90
|
-
self,
|
|
91
|
-
func: _FunctionComponent[T],
|
|
92
|
-
) -> Callable[[T], SyncComponent] | Callable[[T], AsyncComponent]:
|
|
93
|
-
"""
|
|
94
|
-
Decorator that converts the decorated function into one that must be called with
|
|
95
|
-
the function component's properties and returns a component instance.
|
|
96
|
-
|
|
97
|
-
If used on an async function, the resulting component will also be async;
|
|
98
|
-
otherwise it will be sync.
|
|
99
|
-
|
|
100
|
-
Example:
|
|
101
|
-
|
|
102
|
-
```python
|
|
103
|
-
@component
|
|
104
|
-
def my_component(props: int, context: Context) -> Component:
|
|
105
|
-
return html.p(f"Value: {props}")
|
|
106
|
-
|
|
107
|
-
async def render():
|
|
108
|
-
return await Renderer().render(
|
|
109
|
-
my_component(42)
|
|
110
|
-
)
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
Arguments:
|
|
114
|
-
func: The decorated function.
|
|
115
|
-
|
|
116
|
-
Returns:
|
|
117
|
-
A function that must be called with the function component's properties and
|
|
118
|
-
returns a component instance. (Or loosly speaking, an `HTMYComponentType` which
|
|
119
|
-
can be "instantiated" with the function component's properties.)
|
|
120
|
-
"""
|
|
121
|
-
|
|
122
|
-
if asyncio.iscoroutinefunction(func):
|
|
123
|
-
|
|
124
|
-
def async_wrapper(props: T) -> AsyncComponent:
|
|
125
|
-
# This function must be async, in case the renderer inspects it to decide how to handle it.
|
|
126
|
-
async def component(context: Context) -> Component:
|
|
127
|
-
return await func(props, context) # type: ignore[no-any-return]
|
|
128
|
-
|
|
129
|
-
component.htmy = component # type: ignore[attr-defined]
|
|
130
|
-
return component # type: ignore[return-value]
|
|
131
|
-
|
|
132
|
-
return async_wrapper
|
|
133
|
-
else:
|
|
134
|
-
|
|
135
|
-
def sync_wrapper(props: T) -> SyncComponent:
|
|
136
|
-
def component(context: Context) -> Component:
|
|
137
|
-
return func(props, context) # type: ignore[return-value]
|
|
138
|
-
|
|
139
|
-
component.htmy = component # type: ignore[attr-defined]
|
|
140
|
-
return component # type: ignore[return-value]
|
|
141
|
-
|
|
142
|
-
return sync_wrapper
|
|
143
|
-
|
|
144
|
-
@overload
|
|
145
|
-
def function(self, func: _SyncFunctionComponent[T]) -> Callable[[T], SyncComponent]: ...
|
|
146
|
-
|
|
147
|
-
@overload
|
|
148
|
-
def function(self, func: _AsyncFunctionComponent[T]) -> Callable[[T], AsyncComponent]: ...
|
|
149
|
-
|
|
150
|
-
def function(
|
|
151
|
-
self,
|
|
152
|
-
func: _FunctionComponent[T],
|
|
153
|
-
) -> Callable[[T], SyncComponent] | Callable[[T], AsyncComponent]:
|
|
154
|
-
"""
|
|
155
|
-
Decorator that converts the decorated function into one that must be called with
|
|
156
|
-
the function component's properties and returns a component instance.
|
|
157
|
-
|
|
158
|
-
If used on an async function, the resulting component will also be async;
|
|
159
|
-
otherwise it will be sync.
|
|
160
|
-
|
|
161
|
-
This function is just an alias for `__call__()`.
|
|
162
|
-
|
|
163
|
-
Example:
|
|
164
|
-
|
|
165
|
-
```python
|
|
166
|
-
@component.function
|
|
167
|
-
def my_component(props: int, context: Context) -> Component:
|
|
168
|
-
return html.p(f"Value: {props}")
|
|
169
|
-
|
|
170
|
-
async def render():
|
|
171
|
-
return await Renderer().render(
|
|
172
|
-
my_component(42)
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
Arguments:
|
|
176
|
-
func: The decorated function.
|
|
177
|
-
|
|
178
|
-
Returns:
|
|
179
|
-
A function that must be called with the function component's properties and
|
|
180
|
-
returns a component instance. (Or loosly speaking, an `HTMYComponentType` which
|
|
181
|
-
can be "instantiated" with the function component's properties.)
|
|
182
|
-
"""
|
|
183
|
-
return self(func)
|
|
184
|
-
|
|
185
|
-
# -- ContextOnlyFunctionComponent decorator.
|
|
186
|
-
|
|
187
|
-
@overload
|
|
188
|
-
def context_only(
|
|
189
|
-
self, func: _ContextOnlySyncFunctionComponent
|
|
190
|
-
) -> _DecoratedContextOnlySyncFunctionComponent: ...
|
|
191
|
-
|
|
192
|
-
@overload
|
|
193
|
-
def context_only(
|
|
194
|
-
self, func: _ContextOnlyAsyncFunctionComponent
|
|
195
|
-
) -> _DecoratedContextOnlyAsyncFunctionComponent: ...
|
|
196
|
-
|
|
197
|
-
def context_only(
|
|
198
|
-
self,
|
|
199
|
-
func: _ContextOnlyFunctionComponent,
|
|
200
|
-
) -> _DecoratedContextOnlySyncFunctionComponent | _DecoratedContextOnlyAsyncFunctionComponent:
|
|
201
|
-
"""
|
|
202
|
-
Decorator that converts the decorated function into a component.
|
|
203
|
-
|
|
204
|
-
If used on an async function, the resulting component will also be async;
|
|
205
|
-
otherwise it will be sync.
|
|
206
|
-
|
|
207
|
-
The decorated function will be both a component object and a callable that returns a
|
|
208
|
-
component object, so it can be used in the component tree both with and without the
|
|
209
|
-
call signature:
|
|
210
|
-
|
|
211
|
-
```python
|
|
212
|
-
@component.context_only
|
|
213
|
-
def my_component(ctx):
|
|
214
|
-
return "Context only function component."
|
|
215
|
-
|
|
216
|
-
async def render():
|
|
217
|
-
return await Renderer().render(
|
|
218
|
-
my_component(), # With call signature.
|
|
219
|
-
my_component, # Without call signature.
|
|
220
|
-
)
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
Arguments:
|
|
224
|
-
func: The decorated function.
|
|
225
|
-
|
|
226
|
-
Returns:
|
|
227
|
-
The created component.
|
|
228
|
-
"""
|
|
229
|
-
|
|
230
|
-
def wrapper() -> SyncComponent | AsyncComponent:
|
|
231
|
-
func.htmy = func # type: ignore[union-attr]
|
|
232
|
-
return func # type: ignore[return-value]
|
|
233
|
-
|
|
234
|
-
wrapper.htmy = func # type: ignore[attr-defined]
|
|
235
|
-
return wrapper # type: ignore[return-value]
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
component = ComponentDecorators()
|
|
239
|
-
"""
|
|
240
|
-
Decorators for converting functions into components
|
|
241
|
-
|
|
242
|
-
This is an instance of `ComponentDecorators`.
|
|
243
|
-
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|