fastlifeweb 0.15.0__py3-none-any.whl → 0.16.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.
fastlife/__init__.py CHANGED
@@ -2,32 +2,35 @@ from fastapi import Response
2
2
 
3
3
  from .config import (
4
4
  Configurator,
5
- Registry,
5
+ DefaultRegistry,
6
+ GenericConfigurator,
6
7
  Settings,
7
8
  configure,
8
9
  resource,
9
10
  resource_view,
10
11
  view_config,
11
12
  )
12
- from .request import Request
13
+ from .request import GenericRequest, Registry, Request, get_request
13
14
 
14
15
  # from .request.form_data import model
15
- from .templates import Template, template
16
+ from .services.templates import TemplateParams
16
17
 
17
18
  __all__ = [
18
19
  # Config
19
20
  "configure",
21
+ "GenericConfigurator",
20
22
  "Configurator",
21
- "template",
22
- "Template",
23
- "Registry",
23
+ "DefaultRegistry",
24
+ "TemplateParams",
24
25
  "Settings",
25
26
  "view_config",
26
27
  "resource",
27
28
  "resource_view",
28
29
  # Model
29
30
  # "model",
30
- # Fast API reexport
31
31
  "Request",
32
+ "GenericRequest",
33
+ "get_request",
34
+ "Registry",
32
35
  "Response",
33
36
  ]
@@ -0,0 +1 @@
1
+ """HTML Form generation using widgets."""
@@ -1,3 +1,4 @@
1
+ """Widget base class."""
1
2
  import abc
2
3
  import secrets
3
4
  from typing import Any, Generic, Mapping, Type, TypeVar
@@ -8,6 +8,6 @@
8
8
  #}
9
9
  <tbody
10
10
  {%- if id %} id="{{id}}" {%- endif %}
11
- {%- if class %} class="{{attrs.class}}" {%- endif %}>
11
+ {%- if attrs.class %} class="{{attrs.class}}" {%- endif %}>
12
12
  {{- content -}}
13
13
  </tbody>
@@ -8,6 +8,6 @@
8
8
  #}
9
9
  <tfoot
10
10
  {%- if id %} id="{{id}}" {%- endif %}
11
- {%- if class %} class="{{attrs.class}}" {%- endif %}>
11
+ {%- if attrs.class %} class="{{attrs.class}}" {%- endif %}>
12
12
  {{- content -}}
13
13
  </tfoot>
@@ -8,6 +8,6 @@
8
8
  #}
9
9
  <thead
10
10
  {%- if id %} id="{{id}}" {%- endif %}
11
- {%- if class %} class="{{attrs.class}}" {%- endif %}>
11
+ {%- if attrs.class %} class="{{attrs.class}}" {%- endif %}>
12
12
  {{- content -}}
13
13
  </thead>
@@ -8,6 +8,6 @@
8
8
  #}
9
9
  <tr
10
10
  {%- if id %} id="{{id}}" {%- endif %}
11
- {%- if class %} class="{{attrs.class}}" {%- endif %}>
11
+ {%- if attrs.class %} class="{{attrs.class}}" {%- endif %}>
12
12
  {{- content -}}
13
13
  </tr>
@@ -1,18 +1,18 @@
1
1
  """Configure fastlife app for dependency injection."""
2
2
 
3
- from .configurator import Configurator, configure
4
- from .registry import AppRegistry, Registry
3
+ from .configurator import Configurator, GenericConfigurator, configure
4
+ from .registry import DefaultRegistry
5
5
  from .resources import resource, resource_view
6
6
  from .settings import Settings
7
7
  from .views import view_config
8
8
 
9
9
  __all__ = [
10
10
  "Configurator",
11
+ "GenericConfigurator",
11
12
  "configure",
12
13
  "view_config",
13
14
  "resource",
14
15
  "resource_view",
15
- "Registry",
16
- "AppRegistry",
16
+ "DefaultRegistry",
17
17
  "Settings",
18
18
  ]
@@ -19,7 +19,7 @@ from collections.abc import Mapping, Sequence
19
19
  from enum import Enum
20
20
  from pathlib import Path
21
21
  from types import ModuleType
22
- from typing import TYPE_CHECKING, Annotated, Any, Callable, Self, Tuple, Type
22
+ from typing import TYPE_CHECKING, Annotated, Any, Callable, Generic, Self, Tuple, Type
23
23
 
24
24
  import venusian
25
25
  from fastapi import Depends, FastAPI
@@ -38,6 +38,7 @@ from fastlife.security.csrf import check_csrf
38
38
  from fastlife.services.policy import check_permission
39
39
  from fastlife.shared_utils.resolver import resolve
40
40
 
41
+ from .registry import DefaultRegistry, TRegistry
41
42
  from .settings import Settings
42
43
 
43
44
  if TYPE_CHECKING:
@@ -46,7 +47,7 @@ if TYPE_CHECKING:
46
47
  AbstractTemplateRendererFactory, # coverage: ignore
47
48
  )
48
49
 
49
- from .registry import AppRegistry, LocaleNegociator # coverage: ignore
50
+ from fastlife.services.locale_negociator import LocaleNegociator
50
51
 
51
52
  log = logging.getLogger(__name__)
52
53
  VENUSIAN_CATEGORY = "fastlife"
@@ -109,14 +110,14 @@ def rebuild_router(router: Router) -> Router:
109
110
  return _router
110
111
 
111
112
 
112
- class Configurator:
113
+ class GenericConfigurator(Generic[TRegistry]):
113
114
  """
114
115
  Configure and build an application.
115
116
 
116
117
  Initialize the app from the settings.
117
118
  """
118
119
 
119
- registry: "AppRegistry"
120
+ registry: TRegistry
120
121
 
121
122
  def __init__(self, settings: Settings) -> None:
122
123
  """
@@ -135,10 +136,14 @@ class Configurator:
135
136
  self.api_version = "v1"
136
137
  self.api_description: str = ""
137
138
  self.api_summary: str | None = None
139
+ self.api_swagger_ui_url: str | None = None
140
+ self.api_redoc_url: str | None = None
138
141
 
139
142
  self._route_prefix: str = ""
140
143
  self._routers: dict[str, Router] = defaultdict(Router)
141
- self._security_policies: dict[str, "type[AbstractSecurityPolicy[Any]]"] = {}
144
+ self._security_policies: dict[
145
+ str, "type[AbstractSecurityPolicy[Any, TRegistry]]"
146
+ ] = {}
142
147
 
143
148
  self.scanner = venusian.Scanner(fastlife=self)
144
149
  self.include("fastlife.views")
@@ -170,8 +175,8 @@ class Configurator:
170
175
  description=self.api_description,
171
176
  summary=self.api_summary,
172
177
  dependencies=[Depends(check_csrf())],
173
- docs_url=self.registry.settings.api_swagger_ui_url,
174
- redoc_url=self.registry.settings.api_redocs_url,
178
+ docs_url=self.api_swagger_ui_url,
179
+ redoc_url=self.api_redoc_url,
175
180
  openapi_tags=[tag.model_dump(by_alias=True) for tag in self.tags.values()]
176
181
  if self.tags
177
182
  else None,
@@ -241,7 +246,7 @@ class Configurator:
241
246
  self._route_prefix = old
242
247
  return self
243
248
 
244
- def set_locale_negociator(self, locale_negociator: "LocaleNegociator") -> Self:
249
+ def set_locale_negociator(self, locale_negociator: LocaleNegociator) -> Self:
245
250
  """Install a locale negociator for the app."""
246
251
  self.registry.locale_negociator = locale_negociator
247
252
  return self
@@ -263,20 +268,28 @@ class Configurator:
263
268
  title: str,
264
269
  version: str,
265
270
  description: str,
271
+ *,
266
272
  summary: str | None = None,
273
+ swagger_ui_url: str | None = None,
274
+ redoc_url: str | None = None,
267
275
  ) -> Self:
268
276
  """
269
277
  Set your api documentation title for application that expose an API.
270
278
 
271
279
  :param title: OpenAPI documentation title
272
280
  :param version: OpenAPI api version
273
- :param description: OpenAPI documentation description
274
- :param summary: OpenAPI documentation summary
281
+ :param description: OpenAPI documentation description. Use markdown here.
282
+ :param summary: OpenAPI documentation summary.
283
+ A short description: text only.
284
+ :param swagger_ui_url: Endpoint for {term}`Swagger UI` served by FastAPI
285
+ :param redoc_url: Endpoint for {term}`Redoc` served by FastAPI
275
286
  """
276
287
  self.api_title = title
277
288
  self.api_version = version
278
289
  self.api_description = description
279
290
  self.api_summary = summary
291
+ self.api_swagger_ui_url = swagger_ui_url
292
+ self.api_redoc_url = redoc_url
280
293
  return self
281
294
 
282
295
  def add_open_tag(self, tag: OpenApiTag) -> Self:
@@ -296,7 +309,7 @@ class Configurator:
296
309
  return self
297
310
 
298
311
  def set_security_policy(
299
- self, security_policy: "type[AbstractSecurityPolicy[Any]]"
312
+ self, security_policy: "type[AbstractSecurityPolicy[Any, TRegistry]]"
300
313
  ) -> Self:
301
314
  """
302
315
  Set a security policy for the application.
@@ -367,7 +380,7 @@ class Configurator:
367
380
  :param endpoint: the function that will reveive the request.
368
381
  :param permission: a permission to validate by the security policy.
369
382
  :param methods: restrict route to a list of http methods.
370
- :param description:OpenAPI description for the route.
383
+ :param description:{term}`OpenAPI` description for the route.
371
384
  :param summary: OpenAPI summary for the route.
372
385
  :param response_description: OpenAPI description for the response.
373
386
  :param operation_id: OpenAPI optional unique string used to identify an
@@ -385,7 +398,7 @@ class Configurator:
385
398
  :param response_model_exclude_none: exclude fields instead of serialize to
386
399
  null value.
387
400
  :param include_in_schema: expose or not the route in the doc.
388
- :param openapi_extra: open api documentation extra fields.
401
+ :param openapi_extra: OpenAPI documentation extra fields.
389
402
 
390
403
  :return: the configurator.
391
404
  """
@@ -549,7 +562,7 @@ class Configurator:
549
562
  self.registry.renderers[f".{file_ext.lstrip('.')}"] = renderer # type: ignore
550
563
  return self
551
564
 
552
- def add_template_search_path(self, path: str) -> Self:
565
+ def add_template_search_path(self, path: str | Path) -> Self:
553
566
  """
554
567
  Add a template search path directly from the code.
555
568
 
@@ -561,9 +574,17 @@ class Configurator:
561
574
  return self
562
575
 
563
576
 
577
+ class Configurator(GenericConfigurator[DefaultRegistry]):
578
+ """
579
+ Configure and build an application.
580
+
581
+ Initialize the app from the settings.
582
+ """
583
+
584
+
564
585
  def configure(
565
586
  wrapped: Callable[[Configurator], None],
566
- ) -> Callable[[Configurator], None]:
587
+ ) -> Callable[[Any], None]:
567
588
  """
568
589
  Decorator used to attach route in a submodule while using the configurator.include.
569
590
  """
@@ -1,10 +1,7 @@
1
1
  from collections.abc import Mapping
2
- from typing import TYPE_CHECKING, Annotated, Callable
2
+ from typing import TYPE_CHECKING, TypeVar
3
3
 
4
- from fastapi import Depends
5
- from fastapi import Request as FastAPIRequest
6
-
7
- from fastlife.request.request import Request
4
+ from fastlife.services.locale_negociator import LocaleNegociator, default_negociator
8
5
  from fastlife.services.translations import LocalizerFactory
9
6
 
10
7
  if TYPE_CHECKING:
@@ -14,17 +11,8 @@ if TYPE_CHECKING:
14
11
 
15
12
  from .settings import Settings
16
13
 
17
- LocaleNegociator = Callable[[Request], str]
18
-
19
-
20
- def _default_negociator(settings: Settings) -> LocaleNegociator:
21
- def locale_negociator(request: FastAPIRequest) -> str:
22
- return settings.default_locale
23
-
24
- return locale_negociator
25
14
 
26
-
27
- class AppRegistry:
15
+ class DefaultRegistry:
28
16
  """
29
17
  The application registry got fastlife dependency injection.
30
18
  It is initialized by the configurator and accessed by the `fastlife.Registry`.
@@ -37,7 +25,7 @@ class AppRegistry:
37
25
 
38
26
  def __init__(self, settings: Settings) -> None:
39
27
  self.settings = settings
40
- self.locale_negociator = _default_negociator(self.settings)
28
+ self.locale_negociator = default_negociator(self.settings)
41
29
  self.renderers = {}
42
30
  self.localizer = LocalizerFactory()
43
31
 
@@ -48,9 +36,4 @@ class AppRegistry:
48
36
  raise RuntimeError(f"No renderer registered for template {template}")
49
37
 
50
38
 
51
- def get_registry(request: Request) -> AppRegistry:
52
- return request.registry
53
-
54
-
55
- Registry = Annotated[AppRegistry, Depends(get_registry)]
56
- """FastAPI dependency to access to the registry."""
39
+ TRegistry = TypeVar("TRegistry", bound=DefaultRegistry, covariant=True)
@@ -29,7 +29,7 @@ class Settings(BaseSettings):
29
29
  a python module name. for instance `fastlife:components` is the directory components
30
30
  found in the fastlife package.
31
31
  """
32
- registry_class: str = Field(default="fastlife.config.registry:AppRegistry")
32
+ registry_class: str = Field(default="fastlife.config.registry:DefaultRegistry")
33
33
  """Implementation class for the application regitry."""
34
34
  template_renderer_class: str = Field(
35
35
  default="fastlife.templates.renderer:JinjaxTemplateRenderer"
@@ -108,9 +108,3 @@ class Settings(BaseSettings):
108
108
 
109
109
  decode_reverse_proxy_headers: bool = Field(default=True)
110
110
  """Ensure that the request object has information based on http proxy headers."""
111
-
112
- api_swagger_ui_url: str | None = Field(default=None)
113
- """Path to the automatic API documentation using Swagger UI."""
114
-
115
- api_redocs_url: str | None = Field(default=None)
116
- """Path to the automatic API documentation using ReDoc."""
@@ -12,6 +12,7 @@ Note that uvicorn or hypercorn offer the same kind middleware.
12
12
 
13
13
  Norw, every website is in https, so, this middleware is active by default.
14
14
  """
15
+
15
16
  from fastlife import Configurator, configure
16
17
 
17
18
  from .x_forwarded import XForwardedStar
@@ -1,5 +1,5 @@
1
1
  """HTTP Request."""
2
2
 
3
- from .request import Request
3
+ from .request import GenericRequest, Registry, Request, get_request
4
4
 
5
- __all__ = ["Request"]
5
+ __all__ = ["Request", "Registry", "GenericRequest", "get_request"]
fastlife/request/form.py CHANGED
@@ -5,7 +5,7 @@ from typing import Any, Callable, Generic, Mapping, Type, TypeVar, get_origin
5
5
  from fastapi import Depends
6
6
  from pydantic import BaseModel, ValidationError
7
7
 
8
- from fastlife.config.registry import Registry
8
+ from fastlife import Registry
9
9
  from fastlife.request.form_data import MappingFormData
10
10
  from fastlife.shared_utils.infer import is_union
11
11
 
@@ -12,9 +12,9 @@ from typing import (
12
12
  Sequence,
13
13
  )
14
14
 
15
- from fastapi import Depends, Request
15
+ from fastapi import Depends
16
16
 
17
- from fastlife.config.registry import Registry
17
+ from fastlife import Request
18
18
 
19
19
 
20
20
  def unflatten_struct(
@@ -80,13 +80,11 @@ def unflatten_struct(
80
80
  return unflattened_output
81
81
 
82
82
 
83
- async def unflatten_mapping_form_data(
84
- request: Request, registry: Registry
85
- ) -> Mapping[str, Any]:
83
+ async def unflatten_mapping_form_data(request: Request) -> Mapping[str, Any]:
86
84
  """
87
85
  Parse the {meth}`fastlife.request.request.form` and build a nested structure.
88
86
  """
89
-
87
+ registry = request.registry
90
88
  form_data = await request.form()
91
89
  form_data_decode_list: MutableMapping[str, Any] = {}
92
90
  for key, val in form_data.multi_items():
@@ -109,17 +107,16 @@ async def unflatten_mapping_form_data(
109
107
  return ret # type: ignore
110
108
 
111
109
 
112
- async def unflatten_sequence_form_data(
113
- request: Request, reg: Registry
114
- ) -> Sequence[str]:
110
+ async def unflatten_sequence_form_data(request: Request) -> Sequence[str]:
115
111
  """
116
112
  Parse the {meth}`fastlife.request.request.form` and build a list of structure.
117
113
  """
114
+ registry = request.registry
118
115
  form_data = await request.form()
119
116
  # Could raise a value error !
120
- return unflatten_struct(
121
- form_data, [], csrf_token_name=reg.settings.csrf_token_name
122
- ) # type: ignore
117
+ return unflatten_struct( # type: ignore
118
+ form_data, [], csrf_token_name=registry.settings.csrf_token_name
119
+ )
123
120
 
124
121
 
125
122
  MappingFormData = Annotated[Mapping[str, Any], Depends(unflatten_mapping_form_data)]
@@ -3,27 +3,30 @@
3
3
  from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from fastapi import Request as FastAPIRequest
6
+ from fastapi.params import Depends
7
+ from typing_extensions import Annotated, Generic
8
+
9
+ from fastlife.config.registry import DefaultRegistry, TRegistry
6
10
 
7
11
  if TYPE_CHECKING:
8
- from fastlife.config.registry import AppRegistry # coverage: ignore
9
12
  from fastlife.security.policy import ( # coverage: ignore
10
13
  AbstractSecurityPolicy,
11
14
  HasPermission,
12
15
  )
13
16
 
14
17
 
15
- class Request(FastAPIRequest):
18
+ class GenericRequest(FastAPIRequest, Generic[TRegistry]):
16
19
  """HTTP Request representation."""
17
20
 
18
- registry: "AppRegistry"
21
+ registry: TRegistry
19
22
  """Direct access to the application registry."""
20
23
  locale_name: str
21
24
  """Request locale used for the i18n of the response."""
22
25
 
23
- security_policy: "AbstractSecurityPolicy[Any] | None"
26
+ security_policy: "AbstractSecurityPolicy[Any, TRegistry] | None"
24
27
  """Request locale used for the i18n of the response."""
25
28
 
26
- def __init__(self, registry: "AppRegistry", request: FastAPIRequest) -> None:
29
+ def __init__(self, registry: TRegistry, request: FastAPIRequest) -> None:
27
30
  super().__init__(request.scope, request.receive)
28
31
  self.registry = registry
29
32
  self.locale_name = registry.locale_negociator(self)
@@ -47,3 +50,25 @@ class Request(FastAPIRequest):
47
50
  )
48
51
 
49
52
  return await self.security_policy.has_permission(permission)
53
+
54
+
55
+ def get_request(request: FastAPIRequest) -> GenericRequest[Any]:
56
+ return request # type: ignore
57
+
58
+
59
+ Request = Annotated[GenericRequest[DefaultRegistry], Depends(get_request)]
60
+ """A request that is associated to the default registry."""
61
+ # FastAPI handle its Request objects using a lenient_issubclass,
62
+ # basically a issubclass(Request), doe to the Generic[T], it does not work.
63
+
64
+
65
+ AnyRequest = Annotated[GenericRequest[Any], Depends(get_request)]
66
+ """A request version that is associated to the any registry."""
67
+
68
+
69
+ def get_registry(request: Request) -> DefaultRegistry:
70
+ return request.registry
71
+
72
+
73
+ Registry = Annotated[DefaultRegistry, Depends(get_registry)]
74
+ """FastAPI dependency to access to the registry."""
fastlife/routing/route.py CHANGED
@@ -8,7 +8,7 @@ from starlette.responses import Response
8
8
  from fastlife.request.request import Request
9
9
 
10
10
  if TYPE_CHECKING:
11
- from fastlife.config.registry import AppRegistry # coverage: ignore
11
+ from fastlife.config.registry import DefaultRegistry # coverage: ignore
12
12
 
13
13
 
14
14
  class Route(APIRoute):
@@ -19,7 +19,7 @@ class Route(APIRoute):
19
19
  have the registry property available in every received request.
20
20
  """
21
21
 
22
- _registry: "AppRegistry"
22
+ _registry: "DefaultRegistry"
23
23
  """
24
24
  The application registry.
25
25
 
@@ -2,14 +2,16 @@
2
2
 
3
3
  import abc
4
4
  import logging
5
- from typing import Any, Callable, Coroutine, Literal, TypeVar
5
+ from typing import Annotated, Any, Callable, Coroutine, Literal, TypeVar
6
6
  from uuid import UUID
7
7
 
8
- from fastapi import HTTPException
8
+ from fastapi import Depends, HTTPException
9
9
  from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
10
10
  from typing_extensions import Generic
11
11
 
12
- from fastlife import Request
12
+ from fastlife import get_request
13
+ from fastlife.config.registry import TRegistry
14
+ from tests.fastlife_app.config import GenericRequest
13
15
 
14
16
  CheckPermissionHook = Callable[..., Coroutine[Any, Any, None]] | Callable[..., None]
15
17
  CheckPermission = Callable[[str], CheckPermissionHook]
@@ -93,7 +95,7 @@ class Denied(HasPermission):
93
95
  reason = "Access denied to this resource"
94
96
 
95
97
 
96
- class AbstractSecurityPolicy(abc.ABC, Generic[TUser]):
98
+ class AbstractSecurityPolicy(abc.ABC, Generic[TUser, TRegistry]):
97
99
  """Security policy base classe."""
98
100
 
99
101
  Forbidden = Forbidden
@@ -101,7 +103,12 @@ class AbstractSecurityPolicy(abc.ABC, Generic[TUser]):
101
103
  Unauthorized = Unauthorized
102
104
  """The exception raised if no user has been identified."""
103
105
 
104
- def __init__(self, request: Request):
106
+ request: GenericRequest[TRegistry]
107
+ """Request where the security policy is applied."""
108
+
109
+ def __init__(
110
+ self, request: Annotated[GenericRequest[TRegistry], Depends(get_request)]
111
+ ):
105
112
  """
106
113
  Build the security policy.
107
114
 
@@ -141,7 +148,7 @@ class AbstractSecurityPolicy(abc.ABC, Generic[TUser]):
141
148
  """Destroy the request session."""
142
149
 
143
150
 
144
- class InsecurePolicy(AbstractSecurityPolicy[None]):
151
+ class InsecurePolicy(AbstractSecurityPolicy[None, Any]):
145
152
  """
146
153
  An implementation of the security policy made for explicit unsecured access.
147
154
 
@@ -0,0 +1,25 @@
1
+ """Find the localization gor the given request."""
2
+
3
+ from typing import TYPE_CHECKING, Any, Callable
4
+
5
+ from fastlife.config.settings import Settings
6
+
7
+ LocaleName = str
8
+ """The LocaleName is a locale such as en, fr that will be consume for translations."""
9
+
10
+ if TYPE_CHECKING:
11
+ from fastlife.request.request import GenericRequest # coverage: ignore
12
+
13
+ LocaleNegociator = Callable[[GenericRequest[Any]], LocaleName] # coverage: ignore
14
+ """Interface to implement to negociate a locale""" # coverage: ignore
15
+ else:
16
+ LocaleNegociator = Any
17
+
18
+
19
+ def default_negociator(settings: Settings) -> LocaleNegociator:
20
+ """The default local negociator return the locale set in the conf."""
21
+
22
+ def locale_negociator(request: "GenericRequest[Any]") -> str:
23
+ return settings.default_locale
24
+
25
+ return locale_negociator
@@ -5,13 +5,11 @@ from pydantic.fields import FieldInfo
5
5
 
6
6
  from fastlife import Configurator, Request, Response, configure
7
7
  from fastlife.adapters.jinjax.renderer import JinjaxRenderer
8
- from fastlife.config.registry import Registry
9
8
  from fastlife.shared_utils.resolver import resolve_extended
10
9
 
11
10
 
12
11
  async def show_widget(
13
12
  typ: str,
14
- reg: Registry,
15
13
  request: Request,
16
14
  title: Optional[str] = Query(None),
17
15
  name: Optional[str] = Query(None),
@@ -26,7 +24,7 @@ async def show_widget(
26
24
  if title:
27
25
  field = FieldInfo(title=title)
28
26
  # FIXME: .jinja should not be hardcoded
29
- renderer = cast(JinjaxRenderer, reg.get_renderer(".jinja")(request))
27
+ renderer = cast(JinjaxRenderer, request.registry.get_renderer(".jinja")(request))
30
28
  data = renderer.pydantic_form_field(
31
29
  model=model_cls, # type: ignore
32
30
  name=name,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastlifeweb
3
- Version: 0.15.0
3
+ Version: 0.16.0
4
4
  Summary: High-level web framework
5
5
  Home-page: https://github.com/mardiros/fastlife
6
6
  License: BSD-derived
@@ -17,8 +17,9 @@ Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Topic :: Internet :: WWW/HTTP
19
19
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Provides-Extra: testing
20
21
  Requires-Dist: babel (>=2.16.0,<3.0.0)
21
- Requires-Dist: beautifulsoup4[testing] (>=4.12.2,<5.0.0)
22
+ Requires-Dist: beautifulsoup4 (>=4.12.2,<5.0.0) ; extra == "testing"
22
23
  Requires-Dist: fastapi[standard] (>=0.115.0,<0.116.0)
23
24
  Requires-Dist: itsdangerous (>=2.1.2,<3.0.0)
24
25
  Requires-Dist: jinjax (>=0.44,<0.45)
@@ -42,12 +43,12 @@ Description-Content-Type: text/markdown
42
43
  > Please note that this project is still in active development. Features and APIs may change frequently.
43
44
  > Even the name is not definitive.
44
45
 
45
- An opinionated framework Python web framework (based on FastAPI).
46
+ An opinionated Python web framework (based on FastAPI).
46
47
 
47
48
  ## Purpose
48
49
 
49
- Fastlife helps at building Web Application with html form generated from pydantic schema
50
- using customizable widget.
50
+ Fastlife helps at building Web Application with session, security, html test client,
51
+ and html form generated from pydantic schema using customizable widget.
51
52
 
52
53
  Templates are made using [JinjaX](https://jinjax.scaletti.dev/) and an extensible [set of
53
54
  component](https://mardiros.github.io/fastlife/components/index.html) is available
@@ -1,9 +1,9 @@
1
- fastlife/__init__.py,sha256=qjWjYd07qVhyG-WVMZqnBTJy_AV2jC7CHHFzAuTdv98,543
1
+ fastlife/__init__.py,sha256=fokakuhI0fdAjHP5w6GWi-YfCx7iTnrVzjSyZ11Cdgg,676
2
2
  fastlife/adapters/__init__.py,sha256=WYjEN8gp4r7LCHqmIO5VzzvsT8QGRE3w4G47UwYDtAo,94
3
3
  fastlife/adapters/jinjax/__init__.py,sha256=jy88zyqk7nFlaY-0lmgAoe0HyO5r_NKckQb3faQiUv4,137
4
4
  fastlife/adapters/jinjax/renderer.py,sha256=tKX_C0fGnooMcMXBz4_8Ym2VLCKEkRk9_AXq6HrN11I,13040
5
- fastlife/adapters/jinjax/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- fastlife/adapters/jinjax/widgets/base.py,sha256=idw9rVYfY-J9ASkxUFyH2EHtDraYORLd586Noo0-5I4,3990
5
+ fastlife/adapters/jinjax/widgets/__init__.py,sha256=HERnX9xiXUbTDz3XtlnHWABTBjhIq_kkBgWs5E6ZIMY,42
6
+ fastlife/adapters/jinjax/widgets/base.py,sha256=JoB1bLI97ZW2ycfBcHgrPxiOszE9t_SFFK5L1bR-cSo,4015
7
7
  fastlife/adapters/jinjax/widgets/boolean.py,sha256=w4hZMo_8xDoThStlIUR4eVfLm8JwUp0-TaGCjGSyCbA,1145
8
8
  fastlife/adapters/jinjax/widgets/checklist.py,sha256=VIIfJ8JB7ZAISwFFiGZ7jfGuNJ9sjkhRSVFqDTr-RR4,1655
9
9
  fastlife/adapters/jinjax/widgets/dropdown.py,sha256=x2Y9BOfHfSuzWD_HNrvCkiJtKDxl8Vs05Uk8QKvRxyY,1622
@@ -34,13 +34,13 @@ fastlife/components/Radio.jinja,sha256=ohucEGNofDsC4-Hp6Ovexi5XW812-kfAzsFCcOyVF
34
34
  fastlife/components/Select.jinja,sha256=aPqdPrCh6ooxj3XkeptefwvzFqC5XC4FLtW8XLj3CpM,556
35
35
  fastlife/components/Summary.jinja,sha256=EFVG2d7JJu7j5RRLNqKHxXcWAEJmv3Wif1aI5HXq1Zc,853
36
36
  fastlife/components/Table.jinja,sha256=X87pJkqp-fl4Uno52Ijj1Lw4MFYsqr4Jc6Oi4KGQVhM,390
37
- fastlife/components/Tbody.jinja,sha256=LG6B41wnKc3My3LoCQJPYiLnd7pz3JmRAJ2o7a_m1hs,325
37
+ fastlife/components/Tbody.jinja,sha256=FqldzLFhV6dfG0UsB55kDtALixpnECdIISXGWXiXxEM,331
38
38
  fastlife/components/Td.jinja,sha256=NTV86bdtm8t_2_2Pj1X8vsCqm4NSE4H5NPUoSt1i8cA,374
39
39
  fastlife/components/Textarea.jinja,sha256=0WsTi-yxVwYXndmJJN93WblQVqM0h0NCSMUDwoC3kdc,636
40
- fastlife/components/Tfoot.jinja,sha256=hxdYkc-3P27Qm9ZobSxhpjugOFJifVia20uHzTXHvIk,325
40
+ fastlife/components/Tfoot.jinja,sha256=Hw_eFhmbHcOx0dCHrNkyi4q1lrKye5RpihHBwjaT3zY,331
41
41
  fastlife/components/Th.jinja,sha256=tuIlLlwtvZiBHhQD_J7p8V-An-EeA4mzm_nRxUoGDzQ,375
42
- fastlife/components/Thead.jinja,sha256=FB0ZaYwQR1KEJun5XpuO4sXsUuyPabXxf8M5qkeD-xI,324
43
- fastlife/components/Tr.jinja,sha256=tWMonYy0XPCaGCeheo6aqqSXo7wxqxz6hPUBGAWrHNI,316
42
+ fastlife/components/Thead.jinja,sha256=TpnnbtbgYe97e0QDjeJulbh0_-Cbu3z_Qh9dsraQNb4,330
43
+ fastlife/components/Tr.jinja,sha256=kWy-jx4ShT2dv9vuHqJtMnc3O2pZ3y8KCWEAifMpjrU,322
44
44
  fastlife/components/icons/AcademicCap.jinja,sha256=0yJiwNF09SXGmssEHFkEE_2GPb2LFN1BIR4DChDRBls,528
45
45
  fastlife/components/icons/AdjustmentsHorizontal.jinja,sha256=eaAOfrTqfmkJmNaFeCiBSzSCohu6_kqNSuFIB1Vz1RE,568
46
46
  fastlife/components/icons/AdjustmentsVertical.jinja,sha256=n7jBi400_q6KnhMABtUwKBBM_7n1IXFtdtaNwamrfxs,560
@@ -1665,34 +1665,35 @@ fastlife/components/pydantic_form/Text.jinja,sha256=2f_3Q32GySHTLFt-YO8gEJNCY-3X
1665
1665
  fastlife/components/pydantic_form/Textarea.jinja,sha256=NzfCi5agRUSVcb5RXw0QamM8P1lZ-CdNI6P30zb2948,1155
1666
1666
  fastlife/components/pydantic_form/Union.jinja,sha256=czTska54z9KCZKu-FaycLmOvtH6y6CGUFQ8DHnkjrJk,1461
1667
1667
  fastlife/components/pydantic_form/Widget.jinja,sha256=EXskDqt22D5grpGVwlZA3ndve2Wr_6yQH4qVE9c31Og,397
1668
- fastlife/config/__init__.py,sha256=O_Mw2XOxo55SArHdGKRhlTrroRN8ymwfzYKlHG0eV_s,418
1669
- fastlife/config/configurator.py,sha256=e0PeDn6S0swsXAJ79-E6zpnonrYwoFHsfEIEzVaCzls,21222
1668
+ fastlife/config/__init__.py,sha256=ThosRIPZ_fpD0exZu-kUC_f8ZNa5KyDlleWMmEHkjEo,448
1669
+ fastlife/config/configurator.py,sha256=5In_ciAxqORR47gKIbZKTd1IOkkMO0a0iB4q5x7ltaI,21955
1670
1670
  fastlife/config/exceptions.py,sha256=2MS2MFgb3mDbtdHEwnC-QYubob3Rl0v8O8y615LY0ds,1180
1671
1671
  fastlife/config/openapiextra.py,sha256=_9rBYeTqB7nVuzvUHMwZU387bTfYFHYLlP05NP0vEDs,513
1672
- fastlife/config/registry.py,sha256=Zm3i9ZfQZOP3vNKNKQ8nar7XevI91bqy2tUXX492Fuk,1749
1672
+ fastlife/config/registry.py,sha256=dGcNm7E6WY0x5HZNzo1gBFvGFCWeJj6JFXsJtLax5NU,1347
1673
1673
  fastlife/config/resources.py,sha256=pM0j5VKVbVak4Z5mFRHBjAjUqORP4TAtCnZM3res5o8,8776
1674
- fastlife/config/settings.py,sha256=FepHFZVHPLy3yA3dQux79GiH65MkipqWyT-zgXUBOKE,4028
1674
+ fastlife/config/settings.py,sha256=7oggPOucyJwQYI97q8vs3kPXjFIVpQu1q6BK25h-uFs,3789
1675
1675
  fastlife/config/views.py,sha256=Dxi6lO4gFs6GriAW7Rh5GDvebwbrpS2HzYhf30pXJiE,2058
1676
1676
  fastlife/middlewares/__init__.py,sha256=C3DUOzR5EhlAv5Zq7h-Abyvkd7bUsJohTRSB2wpRYQE,220
1677
1677
  fastlife/middlewares/base.py,sha256=9OYqByRuVoIrLt353NOedPQTLdr7LSmxhb2BZcp20qk,638
1678
- fastlife/middlewares/reverse_proxy/__init__.py,sha256=XTG9_Djw92wlyYh1dUDq8kkO8Oq61kBMqd_jNCsLfaQ,845
1678
+ fastlife/middlewares/reverse_proxy/__init__.py,sha256=g1SoVDmenKzpAAPYHTEsWgdBByOxtLg9fGx6RV3i0ok,846
1679
1679
  fastlife/middlewares/reverse_proxy/x_forwarded.py,sha256=WC4xV3i6_Ogqsf_Zgt1ESml8zfnPbJJJkPlC2gTEqW8,1095
1680
1680
  fastlife/middlewares/session/__init__.py,sha256=3XgXcIO6yQls5G7x8K2T8b7a_enA_7rQptWZcp3j2Ak,1400
1681
1681
  fastlife/middlewares/session/middleware.py,sha256=AlRIFXfn3JesKJzMAFUHDOo22mfuwDHkyecDHo9jCdA,3172
1682
1682
  fastlife/middlewares/session/serializer.py,sha256=fTdZCop0y4VkCMyOIo6GEbo5fVWrwsBXaSWfConPL8E,2144
1683
1683
  fastlife/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1684
- fastlife/request/__init__.py,sha256=C5lZAsUZJDw0TfTegRyN5BEhzgfiqvfc8YXu2vp0siU,73
1685
- fastlife/request/form.py,sha256=FucGua79LCKqNBP6Ycle7-5JU6EMI6SrHCgoJBcvGY4,3532
1686
- fastlife/request/form_data.py,sha256=yoP-AYF-dSClpCQuZNRTY-c1OnDga5MoTjBKIzgpTs8,4459
1684
+ fastlife/request/__init__.py,sha256=-wrh12uWM7ysmIUE6FszBit2H6iI5LWVEnHxQJ_23ZE,157
1685
+ fastlife/request/form.py,sha256=Ln9TySvAccYhFY9zc49P6YGE9cQSi_o3mByxvcuCIKY,3516
1686
+ fastlife/request/form_data.py,sha256=DNKpXUxeDMYb43zSBnSPTBjAB8OhsYlNjERSsLgFdvI,4454
1687
1687
  fastlife/request/localizer.py,sha256=9MXAcsod-Po5qeg4lttD3dyumiI0y5vGHCwSSmt9or8,349
1688
- fastlife/request/request.py,sha256=NiRtEV_iw8BuESQPf7gqXxiHsQwLiyOu1yzQy7Ck9eM,1779
1688
+ fastlife/request/request.py,sha256=bJydiRLkhRaA2Y5OCYN6p1rIlhSRsRicCFNVeM-RdgI,2597
1689
1689
  fastlife/routing/__init__.py,sha256=8EMnQE5n8oA4J9_c3nxzwKDVt3tefZ6fGH0d2owE8mo,195
1690
- fastlife/routing/route.py,sha256=8RMnAqun8GTfWp6PFIWmWu-q-K89M3SiVSi2A-DYqyM,1405
1690
+ fastlife/routing/route.py,sha256=O0gwPtP7ur2EHRf76kBASgoLMQciGHXcrJkW8zEPFJA,1413
1691
1691
  fastlife/routing/router.py,sha256=bLZ4k2aDs4L423znwGnw-X2LnM36O8fjhDWc8q1WewI,481
1692
1692
  fastlife/security/__init__.py,sha256=QYDcJ3oXQzqXQxoDD_6biGAtercFrtePttoifiL1j34,25
1693
1693
  fastlife/security/csrf.py,sha256=PvC9Fqdb6c0IzzsnaMx2quQdjjKrb-nOPoAHfcwoAe8,2141
1694
- fastlife/security/policy.py,sha256=3aENmA_plxHbnUKzsIWtCAboT3oyVew0dcJKDrrILqE,4880
1694
+ fastlife/security/policy.py,sha256=BYGXmtEFlJgQSydFd88huHWJNLEScbmnjO--eVNBMok,5181
1695
1695
  fastlife/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1696
+ fastlife/services/locale_negociator.py,sha256=b1Fsx-r_zIZe0GD8WH1gmslMb0mNrvxuacxD4LNX63o,819
1696
1697
  fastlife/services/policy.py,sha256=ZK4K3fVGT1fUeBdo5sSWnEcJg49CZuaI3z2gyg3KguQ,1653
1697
1698
  fastlife/services/templates.py,sha256=7gPJxGWD-XqputbZpy_Icsz3WHKJaWg2JgkVOeKrjfA,3840
1698
1699
  fastlife/services/translations.py,sha256=Bo5CIjdbQ3g_ihbv4Bz60hzd8VOtqEEPOyhJEbGcvP4,4363
@@ -1705,8 +1706,8 @@ fastlife/templates/constants.py,sha256=MGdUjkF9hsPMN8rOS49eWbAApcb8FL-FAeFvJU8k9
1705
1706
  fastlife/testing/__init__.py,sha256=vuqwoNUd3BuIp3fm7nkvmYkIGjIimf5zUGhDkeWrg2s,98
1706
1707
  fastlife/testing/testclient.py,sha256=BC7lLQ_jc59UmknAKzgRtW9a3cpX_V_QLp9Mg2ScLA8,20546
1707
1708
  fastlife/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1708
- fastlife/views/pydantic_form.py,sha256=fvjk_5-JAugpBPxwD5GkxxsQiz9eAxzWeCSU9kiyc6s,1438
1709
- fastlifeweb-0.15.0.dist-info/LICENSE,sha256=F75xSseSKMwqzFj8rswYU6NWS3VoWOc_gY3fJYf9_LI,1504
1710
- fastlifeweb-0.15.0.dist-info/METADATA,sha256=_1D_y-_GWIJCaw4ZbZs9LT7O4nfhwdQ4p9Kzlo0BAY4,3278
1711
- fastlifeweb-0.15.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1712
- fastlifeweb-0.15.0.dist-info/RECORD,,
1709
+ fastlife/views/pydantic_form.py,sha256=ZYOXKudmSqtRvFn5ZY75DOXZVunGXJBKpjh9FJcqu6k,1386
1710
+ fastlifeweb-0.16.0.dist-info/LICENSE,sha256=F75xSseSKMwqzFj8rswYU6NWS3VoWOc_gY3fJYf9_LI,1504
1711
+ fastlifeweb-0.16.0.dist-info/METADATA,sha256=Q875Q1ELgr_qbBTGf5huMPCQwpwqovev2_OU0CNX4yU,3345
1712
+ fastlifeweb-0.16.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1713
+ fastlifeweb-0.16.0.dist-info/RECORD,,