htmy 0.6.0__tar.gz → 0.7.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: htmy
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: Async, pure-Python rendering engine.
5
5
  License: MIT
6
6
  Author: Peter Volf
@@ -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
+ """
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "htmy"
3
- version = "0.6.0"
3
+ version = "0.7.0"
4
4
  description = "Async, pure-Python rendering engine."
5
5
  authors = ["Peter Volf <do.volfp@gmail.com>"]
6
6
  license = "MIT"
@@ -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
File without changes
File without changes