fastlifeweb 0.27.0__py3-none-any.whl → 0.28.0__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.
- CHANGELOG.md +11 -0
- fastlife/__init__.py +2 -1
- fastlife/adapters/jinjax/renderer.py +8 -0
- fastlife/adapters/jinjax/widgets/union.py +1 -1
- fastlife/adapters/xcomponent/__init__.py +1 -0
- fastlife/adapters/xcomponent/catalog.py +11 -0
- fastlife/adapters/xcomponent/html/__init__.py +7 -0
- fastlife/adapters/xcomponent/html/collapsible.py +76 -0
- fastlife/adapters/xcomponent/html/form.py +437 -0
- fastlife/adapters/xcomponent/html/nav.py +60 -0
- fastlife/adapters/xcomponent/html/table.py +130 -0
- fastlife/adapters/xcomponent/html/text.py +30 -0
- fastlife/adapters/xcomponent/html/title.py +145 -0
- fastlife/adapters/xcomponent/icons/__init__.py +0 -0
- fastlife/adapters/xcomponent/icons/icons.py +93 -0
- fastlife/adapters/xcomponent/pydantic_form/__init__.py +0 -0
- fastlife/adapters/xcomponent/pydantic_form/components.py +121 -0
- fastlife/adapters/xcomponent/pydantic_form/widget_factory/__init__.py +1 -0
- fastlife/adapters/xcomponent/pydantic_form/widget_factory/base.py +40 -0
- fastlife/adapters/xcomponent/pydantic_form/widget_factory/bool_builder.py +45 -0
- fastlife/adapters/xcomponent/pydantic_form/widget_factory/emailstr_builder.py +50 -0
- fastlife/adapters/xcomponent/pydantic_form/widget_factory/enum_builder.py +49 -0
- fastlife/adapters/xcomponent/pydantic_form/widget_factory/factory.py +188 -0
- fastlife/adapters/xcomponent/pydantic_form/widget_factory/literal_builder.py +55 -0
- fastlife/adapters/xcomponent/pydantic_form/widget_factory/model_builder.py +66 -0
- fastlife/adapters/xcomponent/pydantic_form/widget_factory/secretstr_builder.py +48 -0
- fastlife/adapters/xcomponent/pydantic_form/widget_factory/sequence_builder.py +60 -0
- fastlife/adapters/xcomponent/pydantic_form/widget_factory/set_builder.py +85 -0
- fastlife/adapters/xcomponent/pydantic_form/widget_factory/simpletype_builder.py +48 -0
- fastlife/adapters/xcomponent/pydantic_form/widget_factory/union_builder.py +92 -0
- fastlife/adapters/xcomponent/pydantic_form/widgets/__init__.py +1 -0
- fastlife/adapters/xcomponent/pydantic_form/widgets/base.py +140 -0
- fastlife/adapters/xcomponent/pydantic_form/widgets/boolean.py +25 -0
- fastlife/adapters/xcomponent/pydantic_form/widgets/checklist.py +75 -0
- fastlife/adapters/xcomponent/pydantic_form/widgets/dropdown.py +72 -0
- fastlife/adapters/xcomponent/pydantic_form/widgets/hidden.py +25 -0
- fastlife/adapters/xcomponent/pydantic_form/widgets/mfa_code.py +25 -0
- fastlife/adapters/xcomponent/pydantic_form/widgets/model.py +49 -0
- fastlife/adapters/xcomponent/pydantic_form/widgets/sequence.py +74 -0
- fastlife/adapters/xcomponent/pydantic_form/widgets/text.py +121 -0
- fastlife/adapters/xcomponent/pydantic_form/widgets/union.py +81 -0
- fastlife/adapters/xcomponent/renderer.py +130 -0
- fastlife/assets/dist.css +4 -1
- fastlife/components/A.jinja +5 -1
- fastlife/config/configurator.py +7 -8
- fastlife/config/resources.py +9 -3
- fastlife/domain/model/template.py +6 -0
- fastlife/service/csrf.py +1 -1
- fastlife/service/templates.py +44 -2
- fastlife/template_globals.py +3 -0
- fastlife/views/pydantic_form.py +9 -9
- {fastlifeweb-0.27.0.dist-info → fastlifeweb-0.28.0.dist-info}/METADATA +6 -3
- {fastlifeweb-0.27.0.dist-info → fastlifeweb-0.28.0.dist-info}/RECORD +56 -18
- {fastlifeweb-0.27.0.dist-info → fastlifeweb-0.28.0.dist-info}/WHEEL +1 -1
- {fastlifeweb-0.27.0.dist-info → fastlifeweb-0.28.0.dist-info}/entry_points.txt +0 -0
- {fastlifeweb-0.27.0.dist-info → fastlifeweb-0.28.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,130 @@
|
|
1
|
+
"""
|
2
|
+
Template renderer based on XComponent.
|
3
|
+
|
4
|
+
More template engine can be registered using the configurator method
|
5
|
+
{meth}`add_renderer <fastlife.config.configurator.GenericConfigurator.add_renderer>`
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Any
|
9
|
+
|
10
|
+
from markupsafe import Markup
|
11
|
+
from pydantic.fields import FieldInfo
|
12
|
+
|
13
|
+
from fastlife import Request
|
14
|
+
from fastlife.adapters.xcomponent.pydantic_form.widget_factory.factory import (
|
15
|
+
WidgetFactory,
|
16
|
+
)
|
17
|
+
from fastlife.config import Configurator, configure
|
18
|
+
from fastlife.domain.model.form import FormModel
|
19
|
+
from fastlife.domain.model.template import InlineTemplate
|
20
|
+
from fastlife.service.templates import (
|
21
|
+
AbstractTemplateRenderer,
|
22
|
+
AbstractTemplateRendererFactory,
|
23
|
+
)
|
24
|
+
from fastlife.settings import Settings
|
25
|
+
from fastlife.shared_utils.resolver import resolve
|
26
|
+
|
27
|
+
from .catalog import catalog
|
28
|
+
|
29
|
+
|
30
|
+
class XTemplateRenderer(AbstractTemplateRenderer):
|
31
|
+
"""
|
32
|
+
An object that will be initialized by an AbstractTemplateRendererFactory,
|
33
|
+
passing the request to process.
|
34
|
+
"""
|
35
|
+
|
36
|
+
request: Request
|
37
|
+
"""Associated request that needs a response."""
|
38
|
+
|
39
|
+
def __init__(self, globals: dict[str, Any], request: Request) -> None:
|
40
|
+
self.request = request
|
41
|
+
self.globals: dict[str, Any] = {**globals}
|
42
|
+
self.globals["pydantic_form"] = self.pydantic_form
|
43
|
+
|
44
|
+
def pydantic_form(
|
45
|
+
self, model: FormModel[Any], *, token: str | None = None
|
46
|
+
) -> Markup:
|
47
|
+
"""
|
48
|
+
Generate HTML markup to build a form from the given form model.
|
49
|
+
|
50
|
+
:param model: the form model that will be transformed to markup.
|
51
|
+
:param token: a token used to ensure that unique identifier are unique.
|
52
|
+
:return: HTML Markup generated by composign fields widgets.
|
53
|
+
"""
|
54
|
+
return WidgetFactory(self, token).get_markup(model)
|
55
|
+
|
56
|
+
def pydantic_form_field(
|
57
|
+
self,
|
58
|
+
model: type[Any],
|
59
|
+
*,
|
60
|
+
name: str | None,
|
61
|
+
token: str | None,
|
62
|
+
removable: bool,
|
63
|
+
field: FieldInfo | None,
|
64
|
+
) -> Markup:
|
65
|
+
"""
|
66
|
+
Generate HTML for a particular field in a form.
|
67
|
+
|
68
|
+
This function is used to generate union subtypes in Ajax requests.
|
69
|
+
:param model: a pydantic or python builtin type that is requests to be rendered
|
70
|
+
:param name: name for the field
|
71
|
+
:param token: the token of the form to render unique identifiers
|
72
|
+
:param removable: add a way let the user remove the widget
|
73
|
+
:param field: only render this particular field for the model.
|
74
|
+
:return: HTML Markup.
|
75
|
+
"""
|
76
|
+
return (
|
77
|
+
WidgetFactory(self, token)
|
78
|
+
.get_widget(
|
79
|
+
model,
|
80
|
+
form_data={},
|
81
|
+
form_errors={},
|
82
|
+
prefix=(name or self.request.registry.settings.form_data_model_prefix),
|
83
|
+
removable=removable,
|
84
|
+
field=field,
|
85
|
+
)
|
86
|
+
.to_html(self)
|
87
|
+
)
|
88
|
+
|
89
|
+
@property
|
90
|
+
def route_prefix(self) -> str:
|
91
|
+
"""Used to buid pydantic form widget that do ajax requests."""
|
92
|
+
return self.request.registry.settings.fastlife_route_prefix
|
93
|
+
|
94
|
+
def render_template(self, template: InlineTemplate) -> str:
|
95
|
+
"""
|
96
|
+
Render an inline template.
|
97
|
+
|
98
|
+
:param template: the template to render.
|
99
|
+
:return: The template rendering result.
|
100
|
+
"""
|
101
|
+
params = template.model_dump()
|
102
|
+
return catalog.render(
|
103
|
+
template.template,
|
104
|
+
globals=self.globals,
|
105
|
+
**params,
|
106
|
+
)
|
107
|
+
|
108
|
+
|
109
|
+
class XRendererFactory(AbstractTemplateRendererFactory):
|
110
|
+
"""
|
111
|
+
The template render factory.
|
112
|
+
"""
|
113
|
+
|
114
|
+
def __init__(self, settings: "Settings") -> None:
|
115
|
+
self.globals = resolve(settings.jinjax_global_catalog_class)().model_dump()
|
116
|
+
|
117
|
+
def __call__(self, request: Request) -> AbstractTemplateRenderer:
|
118
|
+
"""
|
119
|
+
While processing an HTTP Request, a renderer object is created giving
|
120
|
+
isolated context per request.
|
121
|
+
|
122
|
+
:param Request: the HTTP Request to process.
|
123
|
+
:return: The renderer object that will process that request.
|
124
|
+
"""
|
125
|
+
return XTemplateRenderer(globals=self.globals, request=request)
|
126
|
+
|
127
|
+
|
128
|
+
@configure
|
129
|
+
def includeme(conf: Configurator) -> None:
|
130
|
+
conf.add_renderer("xcomponent", XRendererFactory(conf.registry.settings))
|
fastlife/assets/dist.css
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
/*! tailwindcss v4.1.
|
1
|
+
/*! tailwindcss v4.1.11 | MIT License | https://tailwindcss.com */
|
2
2
|
@layer properties;
|
3
3
|
@layer theme, base, components, utilities;
|
4
4
|
@layer theme {
|
@@ -288,6 +288,9 @@
|
|
288
288
|
.cursor-pointer {
|
289
289
|
cursor: pointer;
|
290
290
|
}
|
291
|
+
.appearance-none {
|
292
|
+
appearance: none;
|
293
|
+
}
|
291
294
|
.items-center {
|
292
295
|
align-items: center;
|
293
296
|
}
|
fastlife/components/A.jinja
CHANGED
@@ -20,13 +20,17 @@
|
|
20
20
|
"specify how the response will be swapped in relative to the target of an AJAX request."
|
21
21
|
] = "innerHTML show:body:top",
|
22
22
|
hx_push_url: Annotated[bool, "replace the browser url with the link."] = True,
|
23
|
+
hx_get: Annotated[
|
24
|
+
str | None,
|
25
|
+
"Override the target link only for htmx request for component rendering. href will be used if None."
|
26
|
+
] = None,
|
23
27
|
disable_htmx: Annotated[bool, "do not add any `hx-*` attibute to the link."] = False
|
24
28
|
#}
|
25
29
|
|
26
30
|
<a href="{{href}}"
|
27
31
|
{%- if id %} id="{{ id }}" {%- endif %}
|
28
32
|
{%- if not disable_htmx %}
|
29
|
-
hx-get="{{ href }}"
|
33
|
+
hx-get="{{ hx_get or href }}"
|
30
34
|
hx-target="{{ hx_target }}"
|
31
35
|
hx-swap="{{ hx_swap }}"
|
32
36
|
{%- if hx_push_url %} hx-push-url="true" {%- endif %}
|
fastlife/config/configurator.py
CHANGED
@@ -178,12 +178,11 @@ class GenericConfigurator(Generic[TRegistry]):
|
|
178
178
|
|
179
179
|
# register our main template renderer at then end, to ensure that
|
180
180
|
# if settings have been manipulated, everything is taken into account.
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
)
|
181
|
+
for optional_adapter in ("jinjax", "xcomponent"):
|
182
|
+
try:
|
183
|
+
self.include(f"fastlife.adapters.{optional_adapter}")
|
184
|
+
except ImportError:
|
185
|
+
pass
|
187
186
|
|
188
187
|
app = FastAPI(
|
189
188
|
title=self.api_title,
|
@@ -368,7 +367,7 @@ class GenericConfigurator(Generic[TRegistry]):
|
|
368
367
|
deprecated: bool | None = None,
|
369
368
|
methods: list[str] | None = None,
|
370
369
|
operation_id: str | None = None,
|
371
|
-
|
370
|
+
response_model: Any = None,
|
372
371
|
response_model_include: IncEx | None = None,
|
373
372
|
response_model_exclude: IncEx | None = None,
|
374
373
|
response_model_by_alias: bool = True,
|
@@ -433,7 +432,7 @@ class GenericConfigurator(Generic[TRegistry]):
|
|
433
432
|
self._current_router.add_api_route(
|
434
433
|
path,
|
435
434
|
endpoint,
|
436
|
-
|
435
|
+
response_model=response_model,
|
437
436
|
status_code=status_code,
|
438
437
|
tags=tags,
|
439
438
|
dependencies=dependencies,
|
fastlife/config/resources.py
CHANGED
@@ -99,6 +99,7 @@ def resource(
|
|
99
99
|
response_description=endpoint.response_description,
|
100
100
|
deprecated=endpoint.deprecated,
|
101
101
|
operation_id=endpoint.operation_id,
|
102
|
+
response_model=endpoint.response_model,
|
102
103
|
response_model_include=endpoint.response_model_include,
|
103
104
|
response_model_exclude=endpoint.response_model_exclude,
|
104
105
|
response_model_by_alias=endpoint.response_model_by_alias,
|
@@ -146,6 +147,7 @@ def resource_view(
|
|
146
147
|
deprecated: bool | None = None,
|
147
148
|
methods: list[str] | None = None,
|
148
149
|
operation_id: str | None = None,
|
150
|
+
response_model: type[Any] | None = None,
|
149
151
|
response_model_include: IncEx | None = None,
|
150
152
|
response_model_exclude: IncEx | None = None,
|
151
153
|
response_model_by_alias: bool = True,
|
@@ -155,7 +157,7 @@ def resource_view(
|
|
155
157
|
include_in_schema: bool = True,
|
156
158
|
openapi_extra: dict[str, Any] | None = None,
|
157
159
|
) -> Callable[..., Any]:
|
158
|
-
"""
|
160
|
+
"""
|
159
161
|
Decorator to use on a method of a class decorated with {func}`resource` in order
|
160
162
|
to add OpenAPI information.
|
161
163
|
|
@@ -177,8 +179,11 @@ def resource_view(
|
|
177
179
|
:param include_in_schema: Expose or not the route in the OpenAPI schema and
|
178
180
|
documentation.
|
179
181
|
|
180
|
-
:param
|
181
|
-
|
182
|
+
:param response_model: class used for the api documentation for streaming response.
|
183
|
+
It may also be used to validate data in case the view return dict.
|
184
|
+
See [FastAPI doc](https://fastapi.tiangolo.com/tutorial/response-model/).
|
185
|
+
:param response_model_include: customize fields list to include in response.
|
186
|
+
:param response_model_exclude: customize fields list to exclude in response.
|
182
187
|
:param response_model_by_alias: serialize fields by alias or by name if False.
|
183
188
|
:param response_model_exclude_unset: exclude fields that are not explicitly
|
184
189
|
set in response.
|
@@ -197,6 +202,7 @@ def resource_view(
|
|
197
202
|
fn.deprecated = deprecated
|
198
203
|
fn.methods = methods
|
199
204
|
fn.operation_id = operation_id
|
205
|
+
fn.response_model = response_model
|
200
206
|
fn.response_model_include = response_model_include
|
201
207
|
fn.response_model_exclude = response_model_exclude
|
202
208
|
fn.response_model_by_alias = response_model_by_alias
|
fastlife/service/csrf.py
CHANGED
@@ -11,7 +11,7 @@ to process the request, otherwise an exception
|
|
11
11
|
The cookie named is configurabllefia the settings
|
12
12
|
:attr:`fastlife.config.settings.Settings.csrf_token_name`
|
13
13
|
|
14
|
-
While using the `<Form/>` JinjaX
|
14
|
+
While using the `<Form/>` from XComponent or JinjaX, the csrf token is always sent.
|
15
15
|
|
16
16
|
The cookie is always set when you render any template. At the moment, there is
|
17
17
|
no way to prevent to set the cookie in the request.
|
fastlife/service/templates.py
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
"""
|
2
2
|
Base class to of the template renderer.
|
3
3
|
|
4
|
-
Fastlife comes with
|
5
|
-
|
4
|
+
Fastlife comes with two rendering engines:
|
5
|
+
{class}`fastlife.adapters.jinjax.renderer.JinjaxEngine`,
|
6
|
+
and {class}`fastlife.adapters.xcomponent.renderer.XRendererFactory`,
|
7
|
+
they have to be installed using python packaging extra syntax for your tool,
|
8
|
+
fastlifeweb[jinjax] or fastlifeweb[xcomponent] since those engine are not
|
9
|
+
installed by default.
|
6
10
|
|
7
11
|
More template engine can be registered using the configurator method
|
8
12
|
{meth}`add_renderer <fastlife.config.configurator.GenericConfigurator.add_renderer>`
|
@@ -12,7 +16,11 @@ import abc
|
|
12
16
|
from collections.abc import Mapping
|
13
17
|
from typing import Any
|
14
18
|
|
19
|
+
from markupsafe import Markup
|
20
|
+
from pydantic.fields import FieldInfo
|
21
|
+
|
15
22
|
from fastlife import Request, Response
|
23
|
+
from fastlife.adapters.fastapi.form import FormModel
|
16
24
|
from fastlife.domain.model.template import InlineTemplate
|
17
25
|
|
18
26
|
|
@@ -72,6 +80,40 @@ class AbstractTemplateRenderer(abc.ABC):
|
|
72
80
|
:return: The template rendering result.
|
73
81
|
"""
|
74
82
|
|
83
|
+
@abc.abstractmethod
|
84
|
+
def pydantic_form(
|
85
|
+
self, model: FormModel[Any], *, token: str | None = None
|
86
|
+
) -> Markup:
|
87
|
+
"""
|
88
|
+
Generate HTML markup to build a form from the given form model.
|
89
|
+
|
90
|
+
:param model: the form model that will be transformed to markup.
|
91
|
+
:param token: a token used to ensure that unique identifier are unique.
|
92
|
+
:return: HTML Markup generated by composign fields widgets.
|
93
|
+
"""
|
94
|
+
|
95
|
+
@abc.abstractmethod
|
96
|
+
def pydantic_form_field(
|
97
|
+
self,
|
98
|
+
model: type[Any],
|
99
|
+
*,
|
100
|
+
name: str | None,
|
101
|
+
token: str | None,
|
102
|
+
removable: bool,
|
103
|
+
field: FieldInfo | None,
|
104
|
+
) -> Markup:
|
105
|
+
"""
|
106
|
+
Generate HTML for a particular field in a form.
|
107
|
+
|
108
|
+
This function is used to generate union subtypes in Ajax requests.
|
109
|
+
:param model: a pydantic or python builtin type that is requests to be rendered
|
110
|
+
:param name: name for the field
|
111
|
+
:param token: the token of the form to render unique identifiers
|
112
|
+
:param removable: add a way let the user remove the widget
|
113
|
+
:param field: only render this particular field for the model.
|
114
|
+
:return: HTML Markup.
|
115
|
+
"""
|
116
|
+
|
75
117
|
|
76
118
|
class AbstractTemplateRendererFactory(abc.ABC):
|
77
119
|
"""
|
fastlife/template_globals.py
CHANGED
@@ -29,6 +29,7 @@ class Globals(BaseModel):
|
|
29
29
|
"""Default css class for {jinjax:component}`A`."""
|
30
30
|
|
31
31
|
BUTTON_CLASS: str = space_join(
|
32
|
+
"appearance-none",
|
32
33
|
"bg-primary-600",
|
33
34
|
"px-5",
|
34
35
|
"py-2.5",
|
@@ -52,6 +53,7 @@ class Globals(BaseModel):
|
|
52
53
|
"""Default css class for {jinjax:component}`Details`."""
|
53
54
|
|
54
55
|
SECONDARY_BUTTON_CLASS: str = space_join(
|
56
|
+
"appearance-none",
|
55
57
|
"bg-neutral-300",
|
56
58
|
"px-5",
|
57
59
|
"py-2.5",
|
@@ -79,6 +81,7 @@ class Globals(BaseModel):
|
|
79
81
|
"""
|
80
82
|
|
81
83
|
ICON_BUTTON_CLASS: str = space_join(
|
84
|
+
"appearance-none",
|
82
85
|
"bg-white",
|
83
86
|
"p-1",
|
84
87
|
"rounded-xs",
|
fastlife/views/pydantic_form.py
CHANGED
@@ -4,13 +4,12 @@ Views for pydantic form.
|
|
4
4
|
Pydantic form generate form that may contains fields that requires some ajax query.
|
5
5
|
"""
|
6
6
|
|
7
|
-
from typing import
|
7
|
+
from typing import Literal
|
8
8
|
|
9
9
|
from fastapi import Query
|
10
10
|
from pydantic.fields import FieldInfo
|
11
11
|
|
12
12
|
from fastlife import Configurator, Request, Response, configure
|
13
|
-
from fastlife.adapters.jinjax.renderer import JinjaxRenderer
|
14
13
|
from fastlife.shared_utils.resolver import resolve_extended
|
15
14
|
|
16
15
|
|
@@ -20,6 +19,7 @@ async def show_widget(
|
|
20
19
|
title: str | None = Query(None),
|
21
20
|
name: str | None = Query(None),
|
22
21
|
token: str | None = Query(None),
|
22
|
+
format: Literal["jinjax", "xcomponent"] | str = Query("jinjax"),
|
23
23
|
removable: bool = Query(False),
|
24
24
|
) -> Response:
|
25
25
|
"""
|
@@ -29,14 +29,14 @@ async def show_widget(
|
|
29
29
|
field = None
|
30
30
|
if title:
|
31
31
|
field = FieldInfo(title=title)
|
32
|
-
|
33
|
-
|
32
|
+
|
33
|
+
if format == "jinjax":
|
34
|
+
# XXX
|
35
|
+
format = request.registry.settings.jinjax_file_ext
|
36
|
+
rndr = request.registry.get_renderer(f".{format}")(request)
|
34
37
|
lczr = request.registry.localizer(request.locale_name)
|
35
|
-
|
36
|
-
|
37
|
-
**lczr.as_dict(),
|
38
|
-
}
|
39
|
-
data = renderer.pydantic_form_field(
|
38
|
+
rndr.globals.update({"request": request, **lczr.as_dict()})
|
39
|
+
data = rndr.pydantic_form_field(
|
40
40
|
model=model_cls, # type: ignore
|
41
41
|
name=name,
|
42
42
|
token=token,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fastlifeweb
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.28.0
|
4
4
|
Summary: High-level web framework
|
5
5
|
Author-Email: Guillaume Gauvrit <guillaume@gauvr.it>
|
6
6
|
License: MIT
|
@@ -19,13 +19,16 @@ Project-URL: Changelog, https://mardiros.github.io/fastlife/user/changelog.html
|
|
19
19
|
Requires-Python: >=3.11
|
20
20
|
Requires-Dist: fastapi[standard]<0.116.0,>=0.115.4
|
21
21
|
Requires-Dist: itsdangerous<3,>=2.1.2
|
22
|
-
Requires-Dist: jinjax<0.49,>=0.48
|
23
22
|
Requires-Dist: markupsafe<3,>=2.1.3
|
24
23
|
Requires-Dist: multidict<7,>=6.0.5
|
25
24
|
Requires-Dist: pydantic<3,>=2.5.3
|
26
25
|
Requires-Dist: pydantic-settings<3,>=2.0.3
|
27
26
|
Requires-Dist: python-multipart<1,>=0.0.13
|
28
27
|
Requires-Dist: venusian<4,>=3.0.0
|
28
|
+
Provides-Extra: jinjax
|
29
|
+
Requires-Dist: jinjax<0.49,>=0.48; extra == "jinjax"
|
30
|
+
Provides-Extra: xcomponent
|
31
|
+
Requires-Dist: xcomponent<0.7,>=0.6.6; extra == "xcomponent"
|
29
32
|
Provides-Extra: testing
|
30
33
|
Requires-Dist: beautifulsoup4; extra == "testing"
|
31
34
|
Provides-Extra: docs
|
@@ -77,7 +80,7 @@ to maintain and scale your project.
|
|
77
80
|
|
78
81
|
## Tests
|
79
82
|
|
80
|
-
Fastlife
|
83
|
+
Fastlife comes with [a test client](https://mardiros.github.io/fastlife/develop/fastlife/fastlife.testing.testclient.html) that can interact with html inside unit tests.
|
81
84
|
|
82
85
|
|
83
86
|
## Try it
|
@@ -1,5 +1,5 @@
|
|
1
|
-
CHANGELOG.md,sha256=
|
2
|
-
fastlife/__init__.py,sha256=
|
1
|
+
CHANGELOG.md,sha256=4y91GixHED3CEyv7z4Y4RrdnQORgWX41F-dqyQH0I68,9426
|
2
|
+
fastlife/__init__.py,sha256=XtGrcI5a3MPSl4vupeWT4ZijISiXtAhjcL6eRt9WpfE,2503
|
3
3
|
fastlife/adapters/__init__.py,sha256=imPD1hImpgrYkvUJRhHA5kVyGAua7VbP2WGkhSWKJT8,93
|
4
4
|
fastlife/adapters/fastapi/__init__.py,sha256=1goV1FGFP04TGyskJBLKZam4Gvt1yoAvLMNs4ekWSSQ,243
|
5
5
|
fastlife/adapters/fastapi/form.py,sha256=csxsDI6RK-g41pMwFhaVQCLDhF7dAZzgUp-VcrC3NFY,823
|
@@ -17,7 +17,7 @@ fastlife/adapters/jinjax/jinjax_ext/docstring.py,sha256=Zlx0oSxsRU9vQvGoyScXf9uB
|
|
17
17
|
fastlife/adapters/jinjax/jinjax_ext/inspectable_catalog.py,sha256=KHOYTT6UNA41QwyLNQVoG4trOVXYdChlRmqq_G1pv1s,2987
|
18
18
|
fastlife/adapters/jinjax/jinjax_ext/inspectable_component.py,sha256=Ye_0XIKPC2H2XAvqEc1Er3YWyvNyYS4chuVkTSY5bAk,2909
|
19
19
|
fastlife/adapters/jinjax/jinjax_ext/jinjax_doc.py,sha256=S4-JNQ2NehUdKkc6mzAWRE_oyuw3XYPkR53RArgpDFk,10218
|
20
|
-
fastlife/adapters/jinjax/renderer.py,sha256=
|
20
|
+
fastlife/adapters/jinjax/renderer.py,sha256=bCY_NBM2YbgaTPk62K20x1t38wRFBWUZo8oQ0ng4D7c,5477
|
21
21
|
fastlife/adapters/jinjax/widget_factory/__init__.py,sha256=Dy_2xr_YDAyEF9WtNpjV-aYaehRO1iKEIHVFdfFeszw,59
|
22
22
|
fastlife/adapters/jinjax/widget_factory/base.py,sha256=TLEpYdekR4AeHhIie_DICc_oSvQaUaL8GlatqNiRewg,1046
|
23
23
|
fastlife/adapters/jinjax/widget_factory/bool_builder.py,sha256=ElIDb6rlUiwuDAetC22TreoMa9HDgoZ2euVV1VurFFY,1291
|
@@ -41,10 +41,48 @@ fastlife/adapters/jinjax/widgets/mfa_code.py,sha256=dI0CKlx2jCwujfnud_uIjnBCHvlG
|
|
41
41
|
fastlife/adapters/jinjax/widgets/model.py,sha256=STojkUMfCP1kyPFzzWS-MIZOoxAEFHTIW_jxFnODPb4,1309
|
42
42
|
fastlife/adapters/jinjax/widgets/sequence.py,sha256=aL93-ytj6nlbT8vumt3br6Jq8D97iiCXU3eQXrGYuG0,2577
|
43
43
|
fastlife/adapters/jinjax/widgets/text.py,sha256=XKpoLoBNsvQhHrezKCEcXlF9iQzuclXGaeFu6uq9_5A,3390
|
44
|
-
fastlife/adapters/jinjax/widgets/union.py,sha256=
|
45
|
-
fastlife/
|
44
|
+
fastlife/adapters/jinjax/widgets/union.py,sha256=tSbiylgBOcvQl3b3SVoXRsdpVqvxVpQAlLuMk1EjTTc,2630
|
45
|
+
fastlife/adapters/xcomponent/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
46
|
+
fastlife/adapters/xcomponent/catalog.py,sha256=i-_HUmAXgtDCl7uHB6EBGncAlt8qtiYEo_zCbeQsnZs,209
|
47
|
+
fastlife/adapters/xcomponent/html/__init__.py,sha256=MliLg_8CfMP63GWnRWkia2J_xfBDY5Cr6qO5875IqCE,250
|
48
|
+
fastlife/adapters/xcomponent/html/collapsible.py,sha256=9H8JvTHvg-oJGfmk327xp2MzGKDKQDo6ZbzhDQhBcTI,2191
|
49
|
+
fastlife/adapters/xcomponent/html/form.py,sha256=TD_cvET1FYqEOJTCED0LMGm25CvZRr48w0_rvEa1oT0,11546
|
50
|
+
fastlife/adapters/xcomponent/html/nav.py,sha256=k3IlsBPny7kMlgG0ZHx3I2KVQRQs2PW0OMZvqHcQXNo,1940
|
51
|
+
fastlife/adapters/xcomponent/html/table.py,sha256=f-Lir3XxOQUVlUWfSXlPoKwSZ3nROhDqcrEN_g0TMbA,2678
|
52
|
+
fastlife/adapters/xcomponent/html/text.py,sha256=Qv1mJx1_xoAZpojiEDfKjrkOBHTa2dQo1J-l4Qpwe_E,699
|
53
|
+
fastlife/adapters/xcomponent/html/title.py,sha256=2-n4cs37medT6ovrw1-pUlNRxE3SzZNaDFo0VB5t2OM,3564
|
54
|
+
fastlife/adapters/xcomponent/icons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
55
|
+
fastlife/adapters/xcomponent/icons/icons.py,sha256=WViJhYBtpej7fs3dwbSo66s5M24bCy0qDLBrPbCjFxI,2402
|
56
|
+
fastlife/adapters/xcomponent/pydantic_form/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
57
|
+
fastlife/adapters/xcomponent/pydantic_form/components.py,sha256=tfkgWc97atVY27UFMjC50G-FqueoeQINgEmmiVlS4oA,2781
|
58
|
+
fastlife/adapters/xcomponent/pydantic_form/widget_factory/__init__.py,sha256=Dy_2xr_YDAyEF9WtNpjV-aYaehRO1iKEIHVFdfFeszw,59
|
59
|
+
fastlife/adapters/xcomponent/pydantic_form/widget_factory/base.py,sha256=iKqYXpKZubdxEh0NJJUDEY9ZZu2XqsZdyWNZ43BtS5Q,1025
|
60
|
+
fastlife/adapters/xcomponent/pydantic_form/widget_factory/bool_builder.py,sha256=nE8Q2kaYhLyTTPFn7Hi8vSTotGLdmKthM_ryzTiBiIY,1336
|
61
|
+
fastlife/adapters/xcomponent/pydantic_form/widget_factory/emailstr_builder.py,sha256=9RDlB-Lvmwp0h5XNW-dnb6rDPwx2MabrguQJ2Dtfveg,1611
|
62
|
+
fastlife/adapters/xcomponent/pydantic_form/widget_factory/enum_builder.py,sha256=wZRODjXX_LAEWbkA61XxtALctw1t94CjEDb52hJ4av0,1590
|
63
|
+
fastlife/adapters/xcomponent/pydantic_form/widget_factory/factory.py,sha256=V8QBKnz_Bx-7L5B9V0nbYPXFrFXbD4VjeSKPOt-Hg2o,6138
|
64
|
+
fastlife/adapters/xcomponent/pydantic_form/widget_factory/literal_builder.py,sha256=7_nv3vtnVxKYAuPp2CJVWQvvZLf_NJYZ4qS2l-oqJQg,1821
|
65
|
+
fastlife/adapters/xcomponent/pydantic_form/widget_factory/model_builder.py,sha256=AgT7dRzu5COns6iUBpDvz2Fq8Ob48fT0aNT-9ozQlkE,2299
|
66
|
+
fastlife/adapters/xcomponent/pydantic_form/widget_factory/secretstr_builder.py,sha256=TbeYqeHXlSQY2H5B6JsiOmdsT9202ICa4DEvQu_5btE,1627
|
67
|
+
fastlife/adapters/xcomponent/pydantic_form/widget_factory/sequence_builder.py,sha256=mpdol0o2RnNRUyIUvdkyWsX-EdnGAvxxTBuT6gpY4lw,2007
|
68
|
+
fastlife/adapters/xcomponent/pydantic_form/widget_factory/set_builder.py,sha256=4Zn9pK8vFmyVN2x6Ur5vG40L_gGFDvrwAc2KDv9RB7E,2818
|
69
|
+
fastlife/adapters/xcomponent/pydantic_form/widget_factory/simpletype_builder.py,sha256=ADQH1YogP5NHkWPoqi7aTWICAVZ33m8Q1i_dx4w4nx8,1633
|
70
|
+
fastlife/adapters/xcomponent/pydantic_form/widget_factory/union_builder.py,sha256=bRxEF75S3In2vGnHxcONEjUGeVt_5c_6CP11WOfXP4Y,2904
|
71
|
+
fastlife/adapters/xcomponent/pydantic_form/widgets/__init__.py,sha256=HERnX9xiXUbTDz3XtlnHWABTBjhIq_kkBgWs5E6ZIMY,42
|
72
|
+
fastlife/adapters/xcomponent/pydantic_form/widgets/base.py,sha256=iHcZb_9-qTiVGUiRS9aYHrQPlqcws05LMTnQlE8qIBM,4128
|
73
|
+
fastlife/adapters/xcomponent/pydantic_form/widgets/boolean.py,sha256=MVPehGxij8o1rul5cr4d4YIdtBu3tpT4Pj0jgcIFUws,586
|
74
|
+
fastlife/adapters/xcomponent/pydantic_form/widgets/checklist.py,sha256=G_1beOKHmlETg2zvAGt6zmYEqFRs2Rf9iFSPjWdtWfA,1896
|
75
|
+
fastlife/adapters/xcomponent/pydantic_form/widgets/dropdown.py,sha256=YdTGDyXVj5YxCtsO0h_c9qBnK5K6fQYzcQZ0lUIV6no,1760
|
76
|
+
fastlife/adapters/xcomponent/pydantic_form/widgets/hidden.py,sha256=p5Bun1ETOEIALjitE5TD3Ad4KfUihEIVSFNgYAkQ2zA,671
|
77
|
+
fastlife/adapters/xcomponent/pydantic_form/widgets/mfa_code.py,sha256=beQ58s47FP194MTf_9dEPtM0ELJSoin54valVbpb-vo,686
|
78
|
+
fastlife/adapters/xcomponent/pydantic_form/widgets/model.py,sha256=HnZyDVyfW0s-u9VsoWGimCQGtzzHNmbV2U042n2PEFg,1350
|
79
|
+
fastlife/adapters/xcomponent/pydantic_form/widgets/sequence.py,sha256=ANilerP-RfOO5nakIpuhmG1levPlO8O8ge-786qkGcw,2551
|
80
|
+
fastlife/adapters/xcomponent/pydantic_form/widgets/text.py,sha256=_cms11Ir_onCORmdrW_N5ZeLb-0hCM2fIwWdR_uLXQU,3513
|
81
|
+
fastlife/adapters/xcomponent/pydantic_form/widgets/union.py,sha256=MlmfEsAx9vZdtZMNSILdAV5cq5R_yzbLLppT1NE675E,2646
|
82
|
+
fastlife/adapters/xcomponent/renderer.py,sha256=zLwWpoE0OPjvBtaIRGWxMwQqxVrL0kITQBUjDaqkanI,4243
|
83
|
+
fastlife/assets/dist.css,sha256=xuD2jjSO5GMCjyr_rmhcazJQ5wQpcYUvFxKm0RcC1XY,19801
|
46
84
|
fastlife/assets/source.css,sha256=0KtDcsKHj9LOcqNR1iv9pACwNBaNWkieEDqqjkgNL_s,47
|
47
|
-
fastlife/components/A.jinja,sha256=
|
85
|
+
fastlife/components/A.jinja,sha256=hTZeYWXKLgnqPucSrF2IY7TDP4d_xNfU2Rjl-bXnpGc,1548
|
48
86
|
fastlife/components/Button.jinja,sha256=itKU-ct45XissU33yfmTekyHsNe00fr4RQL-e9cxbgU,2305
|
49
87
|
fastlife/components/Checkbox.jinja,sha256=g62A1LR8TaN60h94pE2e5l9_eMmgnhVVE9HVCQtVVMo,748
|
50
88
|
fastlife/components/CsrfToken.jinja,sha256=mS0q-3_hAevl_waWEPaN0QAYOBzMyzl-W1PSpEHUBA0,215
|
@@ -1690,10 +1728,10 @@ fastlife/components/pydantic_form/FatalError.jinja,sha256=ADtQvmo-e-NmDcFM1E6wZV
|
|
1690
1728
|
fastlife/components/pydantic_form/Hint.jinja,sha256=8leBpfMGDmalc_KAjr2paTojr_rwq-luS6m_1BGj7Tw,202
|
1691
1729
|
fastlife/components/pydantic_form/Widget.jinja,sha256=PgguUpvhG6CY9AW6H8qQMjKqjlybjDCAaFFAOHzrzVQ,418
|
1692
1730
|
fastlife/config/__init__.py,sha256=5qpuaVYqi-AS0GgsfggM6rFsSwXgrqrLBo9jH6dVroc,407
|
1693
|
-
fastlife/config/configurator.py,sha256=
|
1731
|
+
fastlife/config/configurator.py,sha256=yhwI6rDMFud931NoxyCR5muHRdnsBt-DZJXEkXsAODw,24869
|
1694
1732
|
fastlife/config/exceptions.py,sha256=9MdBnbfy-Aw-KaIFzju0Kh8Snk41-v9LqK2w48Tdy1s,1169
|
1695
1733
|
fastlife/config/openapiextra.py,sha256=rYoerrn9sni2XwnO3gIWqaz7M0aDZPhVLjzqhDxue0o,514
|
1696
|
-
fastlife/config/resources.py,sha256=
|
1734
|
+
fastlife/config/resources.py,sha256=stKCuZQGgiDW9xTrTNxKMb_JzkgkM1Ubum1b7OK4Lm4,8780
|
1697
1735
|
fastlife/config/views.py,sha256=9CZ0qNi8vKvQuGo1GgM6cwNK8WwHOxwIHqtikAOaOHY,2399
|
1698
1736
|
fastlife/domain/__init__.py,sha256=3zDDos5InVX0el9OO0lgSDGzdUNYIhlA6w4uhBh2pF8,29
|
1699
1737
|
fastlife/domain/model/__init__.py,sha256=aoBjaSpDscuFXvtknJHwiNyoJRUpE-v4X54h_wNuo2Y,27
|
@@ -1703,7 +1741,7 @@ fastlife/domain/model/form.py,sha256=JP6uumlZBYhiPxzcdxOsfsFm5BRfvkDFvlUCD6Vy8dI
|
|
1703
1741
|
fastlife/domain/model/request.py,sha256=hHtGsfVND3TSG7HQZI_I0n4Gp4KyaAtsjaZEc4lwrv0,3090
|
1704
1742
|
fastlife/domain/model/response.py,sha256=Vsd2zYGGhH0D2DlfiKz1CX9OJZ_ZYoEv_-foMZpDFZo,1294
|
1705
1743
|
fastlife/domain/model/security_policy.py,sha256=f9SLi54vvRU-KSPJ5K0unoqYpkxIyzuZjKf2Ylwf5Rg,4796
|
1706
|
-
fastlife/domain/model/template.py,sha256=
|
1744
|
+
fastlife/domain/model/template.py,sha256=xpvWb_EMdAbdx5VIG7DDjmDZMXbF7DUs0INlXvzN_wY,983
|
1707
1745
|
fastlife/domain/model/types.py,sha256=64jJKFAi5x0e3vr8naHU1m_as0Qy8MS-s9CG0z6K1qc,381
|
1708
1746
|
fastlife/middlewares/__init__.py,sha256=C3DUOzR5EhlAv5Zq7h-Abyvkd7bUsJohTRSB2wpRYQE,220
|
1709
1747
|
fastlife/middlewares/base.py,sha256=7FZE_1YU7wNew2u1qdYXjamosk4CXJmg1mJWGp6Xhc0,649
|
@@ -1715,27 +1753,27 @@ fastlife/middlewares/session/serializer.py,sha256=nbJGiCJ_ryZxkW1I28kmK6hD3U98D4
|
|
1715
1753
|
fastlife/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1716
1754
|
fastlife/service/__init__.py,sha256=SfM2eSrMjDx6safjBc2LVFty4Wy2H1ZsHQSHeDcZ7dU,31
|
1717
1755
|
fastlife/service/check_permission.py,sha256=WodiDVTnY-dZmTfbQV-YmJfPOLtE07mfqvG6jvL0xeA,1828
|
1718
|
-
fastlife/service/csrf.py,sha256=
|
1756
|
+
fastlife/service/csrf.py,sha256=hxcn4IMXLnxUVGtXHCqy5dm3APY0K8sZo-7NLrSkghc,1901
|
1719
1757
|
fastlife/service/locale_negociator.py,sha256=4HifgNkyI7DxR3_IdSUMG0UUY-JZeQsJ_MMSqiyFzgc,730
|
1720
1758
|
fastlife/service/registry.py,sha256=0r8dVCF44JUugRctL9sDQjnHDV7SepH06OfkV6KE-4s,2937
|
1721
1759
|
fastlife/service/request_factory.py,sha256=9o4B_78qrKPXQAq8A_RDhzAqCHdt6arV96Bq_JByyIM,931
|
1722
1760
|
fastlife/service/security_policy.py,sha256=qYXs4mhfz_u4x59NhUkirqKYKQbFv9YrzyRuXj7mxE0,4688
|
1723
|
-
fastlife/service/templates.py,sha256=
|
1761
|
+
fastlife/service/templates.py,sha256=MkH-fRYBYmb6kgtnmuUmhCHrYXxsvRynjyI8QVyj8OA,4146
|
1724
1762
|
fastlife/service/translations.py,sha256=UrkITvfdfw68GzWn_uFlCjjhRvwhc0aCJPnEr_Y1rK8,7151
|
1725
1763
|
fastlife/settings.py,sha256=q-rz4CEF2RQGow5-m-yZJOvdh3PPb2c1Q_ZLJGnu4VQ,3647
|
1726
1764
|
fastlife/shared_utils/__init__.py,sha256=i66ytuf-Ezo7jSiNQHIsBMVIcB-tDX0tg28-pUOlhzE,26
|
1727
1765
|
fastlife/shared_utils/infer.py,sha256=0GflLkaWJ-4LZ1Ig3moR-_o55wwJ_p_vJ4xo-yi3lyA,1406
|
1728
1766
|
fastlife/shared_utils/resolver.py,sha256=Wb9cO2MWavpti63hju15xmwFMgaD5DsQaxikRpB39E8,3713
|
1729
|
-
fastlife/template_globals.py,sha256=
|
1767
|
+
fastlife/template_globals.py,sha256=pn2fWRE5QwYii4K0XJtfCt1hUmN8VvXXwAOWSwRFe94,9102
|
1730
1768
|
fastlife/testing/__init__.py,sha256=VpxkS3Zp3t_hH8dBiLaGFGhsvt511dhBS_8fMoFXdmU,99
|
1731
1769
|
fastlife/testing/dom.py,sha256=q2GFrHWjwKMMTR0dsP3J-rXSxojZy8rOQ-07h2gfLKA,5869
|
1732
1770
|
fastlife/testing/form.py,sha256=diiGfVMfNt19JTNUxlnbGfcbskR3ZMpk0Y-A57vfShc,7871
|
1733
1771
|
fastlife/testing/session.py,sha256=LEFFbiR67_x_g-ioudkY0C7PycHdbDfaIaoo_G7GXQ8,2226
|
1734
1772
|
fastlife/testing/testclient.py,sha256=gqgHQalhrLLZ8eveN2HeuoG9ne8CwxCm-Ll4b7jo9Xo,7249
|
1735
1773
|
fastlife/views/__init__.py,sha256=zG8gveL8e2zBdYx6_9jtZfpQ6qJT-MFnBY3xXkLwHZI,22
|
1736
|
-
fastlife/views/pydantic_form.py,sha256=
|
1737
|
-
fastlifeweb-0.
|
1738
|
-
fastlifeweb-0.
|
1739
|
-
fastlifeweb-0.
|
1740
|
-
fastlifeweb-0.
|
1741
|
-
fastlifeweb-0.
|
1774
|
+
fastlife/views/pydantic_form.py,sha256=9lcS14jxddIpoN_n4VqWwU8CKhKZ8AtoD3c1Pdc-Oe4,1651
|
1775
|
+
fastlifeweb-0.28.0.dist-info/METADATA,sha256=gPnwO8lHfw5Lz0qY7hm83yc5LiuEMQxZ7C2FRMW2VUk,3821
|
1776
|
+
fastlifeweb-0.28.0.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
|
1777
|
+
fastlifeweb-0.28.0.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
1778
|
+
fastlifeweb-0.28.0.dist-info/licenses/LICENSE,sha256=JFWuiKYRXKKMEAsX0aZp3hBcju-HYflJ2rwJAGwbCJo,1080
|
1779
|
+
fastlifeweb-0.28.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|