fastlifeweb 0.16.3__py3-none-any.whl → 0.16.4__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.
Files changed (37) hide show
  1. fastlife/adapters/jinjax/renderer.py +5 -10
  2. fastlife/adapters/jinjax/widgets/base.py +6 -4
  3. fastlife/adapters/jinjax/widgets/checklist.py +1 -1
  4. fastlife/adapters/jinjax/widgets/dropdown.py +7 -7
  5. fastlife/adapters/jinjax/widgets/factory.py +16 -16
  6. fastlife/adapters/jinjax/widgets/model.py +2 -1
  7. fastlife/adapters/jinjax/widgets/sequence.py +3 -2
  8. fastlife/adapters/jinjax/widgets/text.py +9 -10
  9. fastlife/adapters/jinjax/widgets/union.py +9 -7
  10. fastlife/config/configurator.py +9 -10
  11. fastlife/config/exceptions.py +2 -1
  12. fastlife/config/openapiextra.py +1 -0
  13. fastlife/config/resources.py +24 -25
  14. fastlife/config/views.py +3 -1
  15. fastlife/middlewares/reverse_proxy/x_forwarded.py +15 -7
  16. fastlife/middlewares/session/middleware.py +2 -2
  17. fastlife/middlewares/session/serializer.py +6 -5
  18. fastlife/request/form.py +7 -6
  19. fastlife/request/form_data.py +2 -6
  20. fastlife/routing/route.py +3 -1
  21. fastlife/routing/router.py +1 -0
  22. fastlife/security/csrf.py +2 -1
  23. fastlife/security/policy.py +2 -1
  24. fastlife/services/locale_negociator.py +2 -1
  25. fastlife/services/policy.py +2 -1
  26. fastlife/services/templates.py +2 -1
  27. fastlife/services/translations.py +3 -2
  28. fastlife/shared_utils/infer.py +4 -3
  29. fastlife/shared_utils/resolver.py +6 -3
  30. fastlife/templates/binding.py +2 -1
  31. fastlife/testing/__init__.py +1 -0
  32. fastlife/testing/testclient.py +7 -7
  33. fastlife/views/pydantic_form.py +4 -4
  34. {fastlifeweb-0.16.3.dist-info → fastlifeweb-0.16.4.dist-info}/METADATA +1 -1
  35. {fastlifeweb-0.16.3.dist-info → fastlifeweb-0.16.4.dist-info}/RECORD +37 -37
  36. {fastlifeweb-0.16.3.dist-info → fastlifeweb-0.16.4.dist-info}/LICENSE +0 -0
  37. {fastlifeweb-0.16.3.dist-info → fastlifeweb-0.16.4.dist-info}/WHEEL +0 -0
@@ -6,16 +6,11 @@ import ast
6
6
  import logging
7
7
  import re
8
8
  import textwrap
9
+ from collections.abc import Iterator, Mapping, MutableMapping, Sequence
9
10
  from pathlib import Path
10
11
  from typing import (
11
12
  TYPE_CHECKING,
12
13
  Any,
13
- Iterator,
14
- Mapping,
15
- MutableMapping,
16
- Optional,
17
- Sequence,
18
- Type,
19
14
  cast,
20
15
  )
21
16
 
@@ -108,7 +103,7 @@ def generate_docstring(
108
103
  kwonlyargs = func_def.args.kwonlyargs
109
104
  kw_defaults = func_def.args.kw_defaults
110
105
 
111
- for arg, default in zip(kwonlyargs, kw_defaults):
106
+ for arg, default in zip(kwonlyargs, kw_defaults, strict=False):
112
107
  process_arg(arg, default)
113
108
 
114
109
  if add_content:
@@ -342,7 +337,7 @@ class JinjaxRenderer(AbstractTemplateRenderer):
342
337
  self,
343
338
  template: str,
344
339
  *,
345
- globals: Optional[Mapping[str, Any]] = None,
340
+ globals: Mapping[str, Any] | None = None,
346
341
  **params: Any,
347
342
  ) -> str:
348
343
  # Jinja template does accept the file extention while rendering the template
@@ -360,13 +355,13 @@ class JinjaxRenderer(AbstractTemplateRenderer):
360
355
  )
361
356
 
362
357
  def pydantic_form(
363
- self, model: FormModel[Any], *, token: Optional[str] = None
358
+ self, model: FormModel[Any], *, token: str | None = None
364
359
  ) -> Markup:
365
360
  return WidgetFactory(self, token).get_markup(model)
366
361
 
367
362
  def pydantic_form_field(
368
363
  self,
369
- model: Type[Any],
364
+ model: type[Any],
370
365
  *,
371
366
  name: str | None,
372
367
  token: str | None,
@@ -1,7 +1,9 @@
1
1
  """Widget base class."""
2
+
2
3
  import abc
3
4
  import secrets
4
- from typing import Any, Generic, Mapping, Type, TypeVar
5
+ from collections.abc import Mapping
6
+ from typing import Any, Generic, TypeVar
5
7
 
6
8
  from markupsafe import Markup
7
9
 
@@ -11,7 +13,7 @@ from fastlife.shared_utils.infer import is_union
11
13
  T = TypeVar("T")
12
14
 
13
15
 
14
- def get_title(typ: Type[Any]) -> str:
16
+ def get_title(typ: type[Any]) -> str:
15
17
  return getattr(
16
18
  getattr(typ, "__meta__", None),
17
19
  "title",
@@ -81,7 +83,7 @@ class Widget(abc.ABC, Generic[T]):
81
83
  return Markup(renderer.render_template(self.get_template(), widget=self))
82
84
 
83
85
 
84
- def _get_fullname(typ: Type[Any]) -> str:
86
+ def _get_fullname(typ: type[Any]) -> str:
85
87
  if is_union(typ):
86
88
  typs = [_get_fullname(t) for t in typ.__args__] # type: ignore
87
89
  return "|".join(typs) # type: ignore
@@ -102,7 +104,7 @@ class TypeWrapper:
102
104
 
103
105
  def __init__(
104
106
  self,
105
- typ: Type[Any],
107
+ typ: type[Any],
106
108
  route_prefix: str,
107
109
  name: str,
108
110
  token: str,
@@ -2,7 +2,7 @@
2
2
  Widget for field of type Set.
3
3
  """
4
4
 
5
- from typing import Sequence
5
+ from collections.abc import Sequence
6
6
 
7
7
  from pydantic import BaseModel, Field
8
8
 
@@ -2,7 +2,7 @@
2
2
  Widget for field of type Enum or Literal.
3
3
  """
4
4
 
5
- from typing import Optional, Sequence, Tuple
5
+ from collections.abc import Sequence
6
6
 
7
7
  from .base import Widget
8
8
 
@@ -26,14 +26,14 @@ class DropDownWidget(Widget[str]):
26
26
  self,
27
27
  name: str,
28
28
  *,
29
- title: Optional[str],
30
- hint: Optional[str] = None,
31
- aria_label: Optional[str] = None,
32
- value: Optional[str] = None,
29
+ title: str | None,
30
+ hint: str | None = None,
31
+ aria_label: str | None = None,
32
+ value: str | None = None,
33
33
  error: str | None = None,
34
- options: Sequence[Tuple[str, str]] | Sequence[str],
34
+ options: Sequence[tuple[str, str]] | Sequence[str],
35
35
  removable: bool = False,
36
- token: Optional[str] = None,
36
+ token: str | None = None,
37
37
  ) -> None:
38
38
  super().__init__(
39
39
  name,
@@ -3,12 +3,12 @@ Transform.
3
3
  """
4
4
 
5
5
  import secrets
6
- from collections.abc import MutableSequence, Sequence
6
+ from collections.abc import Mapping, MutableSequence, Sequence
7
7
  from decimal import Decimal
8
8
  from enum import Enum
9
9
  from inspect import isclass
10
10
  from types import NoneType
11
- from typing import Any, Literal, Mapping, Type, cast, get_origin
11
+ from typing import Any, Literal, cast, get_origin
12
12
  from uuid import UUID
13
13
 
14
14
  from markupsafe import Markup
@@ -59,7 +59,7 @@ class WidgetFactory:
59
59
 
60
60
  def get_widget(
61
61
  self,
62
- base: Type[Any],
62
+ base: type[Any],
63
63
  form_data: Mapping[str, Any],
64
64
  form_errors: Mapping[str, Any],
65
65
  *,
@@ -78,7 +78,7 @@ class WidgetFactory:
78
78
 
79
79
  def build(
80
80
  self,
81
- typ: Type[Any],
81
+ typ: type[Any],
82
82
  *,
83
83
  name: str = "",
84
84
  value: Any,
@@ -152,7 +152,7 @@ class WidgetFactory:
152
152
  name, typ, field, value or "", form_errors, removable
153
153
  )
154
154
 
155
- if issubclass(typ, (int, str, float, Decimal, UUID)):
155
+ if issubclass(typ, int | str | float | Decimal | UUID):
156
156
  return self.build_simpletype(
157
157
  name, typ, field, value or "", form_errors, removable
158
158
  )
@@ -162,7 +162,7 @@ class WidgetFactory:
162
162
  def build_model(
163
163
  self,
164
164
  field_name: str,
165
- typ: Type[BaseModel],
165
+ typ: type[BaseModel],
166
166
  field: FieldInfo | None,
167
167
  value: Mapping[str, Any],
168
168
  form_errors: Mapping[str, Any],
@@ -204,13 +204,13 @@ class WidgetFactory:
204
204
  def build_union(
205
205
  self,
206
206
  field_name: str,
207
- field_type: Type[Any],
207
+ field_type: type[Any],
208
208
  field: FieldInfo | None,
209
209
  value: Any,
210
210
  form_errors: Mapping[str, Any],
211
211
  removable: bool,
212
212
  ) -> Widget[Any]:
213
- types: list[Type[Any]] = []
213
+ types: list[type[Any]] = []
214
214
  # required = True
215
215
  for typ in field_type.__args__: # type: ignore
216
216
  if typ is NoneType:
@@ -271,7 +271,7 @@ class WidgetFactory:
271
271
  def build_sequence(
272
272
  self,
273
273
  field_name: str,
274
- field_type: Type[Any],
274
+ field_type: type[Any],
275
275
  field: FieldInfo | None,
276
276
  value: Sequence[Any] | None,
277
277
  form_errors: Mapping[str, Any],
@@ -309,7 +309,7 @@ class WidgetFactory:
309
309
  def build_set(
310
310
  self,
311
311
  field_name: str,
312
- field_type: Type[Any],
312
+ field_type: type[Any],
313
313
  field: FieldInfo | None,
314
314
  value: Sequence[Any] | None,
315
315
  form_errors: Mapping[str, Any],
@@ -368,7 +368,7 @@ class WidgetFactory:
368
368
  def build_boolean(
369
369
  self,
370
370
  field_name: str,
371
- field_type: Type[Any],
371
+ field_type: type[Any],
372
372
  field: FieldInfo | None,
373
373
  value: bool,
374
374
  form_errors: Mapping[str, Any],
@@ -392,7 +392,7 @@ class WidgetFactory:
392
392
  def build_emailtype(
393
393
  self,
394
394
  field_name: str,
395
- field_type: Type[Any],
395
+ field_type: type[Any],
396
396
  field: FieldInfo | None,
397
397
  value: str | int | float,
398
398
  form_errors: Mapping[str, Any],
@@ -418,7 +418,7 @@ class WidgetFactory:
418
418
  def build_secretstr(
419
419
  self,
420
420
  field_name: str,
421
- field_type: Type[Any],
421
+ field_type: type[Any],
422
422
  field: FieldInfo | None,
423
423
  value: SecretStr | str,
424
424
  form_errors: Mapping[str, Any],
@@ -444,7 +444,7 @@ class WidgetFactory:
444
444
  def build_literal(
445
445
  self,
446
446
  field_name: str,
447
- field_type: Type[Any], # a literal actually
447
+ field_type: type[Any], # a literal actually
448
448
  field: FieldInfo | None,
449
449
  value: str | int | float,
450
450
  form_errors: Mapping[str, Any],
@@ -476,7 +476,7 @@ class WidgetFactory:
476
476
  def build_enum(
477
477
  self,
478
478
  field_name: str,
479
- field_type: Type[Any], # an enum subclass
479
+ field_type: type[Any], # an enum subclass
480
480
  field: FieldInfo | None,
481
481
  value: str | int | float,
482
482
  form_errors: Mapping[str, Any],
@@ -502,7 +502,7 @@ class WidgetFactory:
502
502
  def build_simpletype(
503
503
  self,
504
504
  field_name: str,
505
- field_type: Type[Any],
505
+ field_type: type[Any],
506
506
  field: FieldInfo | None,
507
507
  value: str | int | float,
508
508
  form_errors: Mapping[str, Any],
@@ -1,4 +1,5 @@
1
- from typing import Any, Sequence
1
+ from collections.abc import Sequence
2
+ from typing import Any
2
3
 
3
4
  from markupsafe import Markup
4
5
 
@@ -1,4 +1,5 @@
1
- from typing import Any, Sequence, Type
1
+ from collections.abc import Sequence
2
+ from typing import Any
2
3
 
3
4
  from markupsafe import Markup
4
5
 
@@ -17,7 +18,7 @@ class SequenceWidget(Widget[Sequence[Widget[Any]]]):
17
18
  aria_label: str | None = None,
18
19
  value: Sequence[Widget[Any]] | None,
19
20
  error: str | None = None,
20
- item_type: Type[Any],
21
+ item_type: type[Any],
21
22
  token: str,
22
23
  removable: bool,
23
24
  ):
@@ -1,5 +1,4 @@
1
1
  from collections.abc import Sequence
2
- from typing import Optional
3
2
 
4
3
  from .base import Widget
5
4
 
@@ -23,10 +22,10 @@ class TextWidget(Widget[str]):
23
22
  self,
24
23
  name: str,
25
24
  *,
26
- title: Optional[str],
27
- hint: Optional[str] = None,
28
- aria_label: Optional[str] = None,
29
- placeholder: Optional[str] = None,
25
+ title: str | None,
26
+ hint: str | None = None,
27
+ aria_label: str | None = None,
28
+ placeholder: str | None = None,
30
29
  error: str | None = None,
31
30
  value: str = "",
32
31
  input_type: str = "text",
@@ -87,12 +86,12 @@ class TextareaWidget(Widget[Sequence[str]]):
87
86
  self,
88
87
  name: str,
89
88
  *,
90
- title: Optional[str],
91
- hint: Optional[str] = None,
92
- aria_label: Optional[str] = None,
93
- placeholder: Optional[str] = None,
89
+ title: str | None,
90
+ hint: str | None = None,
91
+ aria_label: str | None = None,
92
+ placeholder: str | None = None,
94
93
  error: str | None = None,
95
- value: Optional[Sequence[str]] = None,
94
+ value: Sequence[str] | None = None,
96
95
  removable: bool = False,
97
96
  token: str,
98
97
  ) -> None:
@@ -1,7 +1,9 @@
1
1
  """
2
2
  Widget for field of type Union.
3
3
  """
4
- from typing import Any, Optional, Sequence, Type, Union
4
+
5
+ from collections.abc import Sequence
6
+ from typing import Any, Union
5
7
 
6
8
  from markupsafe import Markup
7
9
  from pydantic import BaseModel
@@ -31,12 +33,12 @@ class UnionWidget(Widget[Widget[Any]]):
31
33
  self,
32
34
  name: str,
33
35
  *,
34
- title: Optional[str],
35
- hint: Optional[str] = None,
36
- aria_label: Optional[str] = None,
37
- value: Optional[Widget[Any]],
36
+ title: str | None,
37
+ hint: str | None = None,
38
+ aria_label: str | None = None,
39
+ value: Widget[Any] | None,
38
40
  error: str | None = None,
39
- children_types: Sequence[Type[BaseModel]],
41
+ children_types: Sequence[type[BaseModel]],
40
42
  removable: bool = False,
41
43
  token: str,
42
44
  ):
@@ -72,7 +74,7 @@ class UnionWidget(Widget[Widget[Any]]):
72
74
  widget=self,
73
75
  types=self.build_types(renderer.route_prefix),
74
76
  parent_type=TypeWrapper(
75
- Union[tuple(self.children_types)], # type: ignore
77
+ Union[tuple(self.children_types)], # type: ignore # noqa: UP007
76
78
  renderer.route_prefix,
77
79
  self.parent_name,
78
80
  self.token,
@@ -15,16 +15,15 @@ import importlib
15
15
  import inspect
16
16
  import logging
17
17
  from collections import defaultdict
18
- from collections.abc import Mapping, Sequence
18
+ from collections.abc import Callable, 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, Generic, Self, Tuple, Type
22
+ from typing import TYPE_CHECKING, Annotated, Any, Generic, Self
23
23
 
24
24
  import venusian
25
- from fastapi import Depends, FastAPI
25
+ from fastapi import Depends, FastAPI, Response
26
26
  from fastapi import Request as BaseRequest
27
- from fastapi import Response
28
27
  from fastapi.params import Depends as DependsType
29
28
  from fastapi.staticfiles import StaticFiles
30
29
  from fastapi.types import IncEx
@@ -127,9 +126,9 @@ class GenericConfigurator(Generic[TRegistry]):
127
126
  self.registry = registry_cls(settings)
128
127
  Route._registry = self.registry # type: ignore
129
128
 
130
- self.middlewares: list[Tuple[Type[AbstractMiddleware], Any]] = []
131
- self.exception_handlers: list[Tuple[int | Type[Exception], Any]] = []
132
- self.mounts: list[Tuple[str, Path, str]] = []
129
+ self.middlewares: list[tuple[type[AbstractMiddleware], Any]] = []
130
+ self.exception_handlers: list[tuple[int | type[Exception], Any]] = []
131
+ self.mounts: list[tuple[str, Path, str]] = []
133
132
  self.tags: dict[str, OpenApiTag] = {}
134
133
 
135
134
  self.api_title = "OpenAPI"
@@ -142,7 +141,7 @@ class GenericConfigurator(Generic[TRegistry]):
142
141
  self._route_prefix: str = ""
143
142
  self._routers: dict[str, Router] = defaultdict(Router)
144
143
  self._security_policies: dict[
145
- str, "type[AbstractSecurityPolicy[Any, TRegistry]]"
144
+ str, type[AbstractSecurityPolicy[Any, TRegistry]]
146
145
  ] = {}
147
146
 
148
147
  self._registered_permissions: set[str] = set()
@@ -310,7 +309,7 @@ class GenericConfigurator(Generic[TRegistry]):
310
309
  return self
311
310
 
312
311
  def add_middleware(
313
- self, middleware_class: Type[AbstractMiddleware], **options: Any
312
+ self, middleware_class: type[AbstractMiddleware], **options: Any
314
313
  ) -> Self:
315
314
  """
316
315
  Add a starlette middleware to the FastAPI app.
@@ -522,7 +521,7 @@ class GenericConfigurator(Generic[TRegistry]):
522
521
 
523
522
  def add_exception_handler(
524
523
  self,
525
- status_code_or_exc: int | Type[Exception],
524
+ status_code_or_exc: int | type[Exception],
526
525
  handler: Any,
527
526
  *,
528
527
  template: str | None = None,
@@ -1,4 +1,5 @@
1
- from typing import Any, Callable
1
+ from collections.abc import Callable
2
+ from typing import Any
2
3
 
3
4
  import venusian
4
5
 
@@ -1,4 +1,5 @@
1
1
  """Types for OpenAPI documentation."""
2
+
2
3
  from pydantic import BaseModel, Field
3
4
 
4
5
 
@@ -8,7 +8,8 @@ API Resources declaration using a decorator.
8
8
 
9
9
  """
10
10
 
11
- from typing import Any, Callable
11
+ from collections.abc import Callable
12
+ from typing import Any
12
13
 
13
14
  import venusian
14
15
  from fastapi.types import IncEx
@@ -91,29 +92,21 @@ def resource(
91
92
  endpoint=endpoint,
92
93
  tags=[tag],
93
94
  methods=[method.split("_").pop()],
94
- permission=getattr(endpoint, "permission"),
95
- status_code=getattr(endpoint, "status_code"),
96
- summary=getattr(endpoint, "summary"),
97
- description=getattr(endpoint, "description"),
98
- response_description=getattr(endpoint, "response_description"),
99
- deprecated=getattr(endpoint, "deprecated"),
100
- operation_id=getattr(endpoint, "operation_id"),
101
- response_model_include=getattr(endpoint, "response_model_include"),
102
- response_model_exclude=getattr(endpoint, "response_model_exclude"),
103
- response_model_by_alias=getattr(
104
- endpoint, "response_model_by_alias"
105
- ),
106
- response_model_exclude_unset=getattr(
107
- endpoint, "response_model_exclude_unset"
108
- ),
109
- response_model_exclude_defaults=getattr(
110
- endpoint, "response_model_exclude_defaults"
111
- ),
112
- response_model_exclude_none=getattr(
113
- endpoint, "response_model_exclude_none"
114
- ),
115
- include_in_schema=getattr(endpoint, "include_in_schema"),
116
- openapi_extra=getattr(endpoint, "openapi_extra"),
95
+ permission=endpoint.permission,
96
+ status_code=endpoint.status_code,
97
+ summary=endpoint.summary,
98
+ description=endpoint.description,
99
+ response_description=endpoint.response_description,
100
+ deprecated=endpoint.deprecated,
101
+ operation_id=endpoint.operation_id,
102
+ response_model_include=endpoint.response_model_include,
103
+ response_model_exclude=endpoint.response_model_exclude,
104
+ response_model_by_alias=endpoint.response_model_by_alias,
105
+ response_model_exclude_unset=endpoint.response_model_exclude_unset,
106
+ response_model_exclude_defaults=endpoint.response_model_exclude_defaults,
107
+ response_model_exclude_none=endpoint.response_model_exclude_none,
108
+ include_in_schema=endpoint.include_in_schema,
109
+ openapi_extra=endpoint.openapi_extra,
117
110
  )
118
111
 
119
112
  for method in dir(ob):
@@ -131,7 +124,13 @@ def resource(
131
124
  config, method, collection_path, getattr(api, method)
132
125
  )
133
126
  case (
134
- "get" | "post" | "put" | "patch" | "delete" | "head" | "options"
127
+ "get"
128
+ | "post"
129
+ | "put"
130
+ | "patch"
131
+ | "delete"
132
+ | "head"
133
+ | "options"
135
134
  ):
136
135
  bind_config(config, method, path, getattr(api, method))
137
136
  case _:
fastlife/config/views.py CHANGED
@@ -17,7 +17,9 @@ async def hello_world(
17
17
  return template()
18
18
  ```
19
19
  """
20
- from typing import Any, Callable
20
+
21
+ from collections.abc import Callable
22
+ from typing import Any
21
23
 
22
24
  import venusian
23
25
 
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import Optional, Sequence, Tuple
2
+ from collections.abc import Sequence
3
3
 
4
4
  from starlette.types import ASGIApp, Receive, Scope, Send
5
5
 
@@ -8,14 +8,14 @@ from fastlife.middlewares.base import AbstractMiddleware
8
8
  log = logging.getLogger(__name__)
9
9
 
10
10
 
11
- def get_header(headers: Sequence[Tuple[bytes, bytes]], key: bytes) -> Optional[str]:
11
+ def get_header(headers: Sequence[tuple[bytes, bytes]], key: bytes) -> str | None:
12
12
  for hdr in headers:
13
13
  if hdr[0].lower() == key:
14
14
  return hdr[1].decode("latin1")
15
15
  return None
16
16
 
17
17
 
18
- def get_header_int(headers: Sequence[Tuple[bytes, bytes]], key: bytes) -> Optional[int]:
18
+ def get_header_int(headers: Sequence[tuple[bytes, bytes]], key: bytes) -> int | None:
19
19
  for hdr in headers:
20
20
  if hdr[0].lower() == key:
21
21
  ret = hdr[1].decode("latin1")
@@ -36,11 +36,19 @@ class XForwardedStar(AbstractMiddleware):
36
36
  async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
37
37
  if scope["type"] in ("http", "websocket"):
38
38
  headers = scope["headers"]
39
+ x_real_ip = get_header(headers, b"x-real-ip")
40
+ client = (
41
+ (
42
+ x_real_ip,
43
+ get_header_int(headers, b"x-real-port")
44
+ or get_header_int(headers, b"x-forwarded-port")
45
+ or 0,
46
+ )
47
+ if x_real_ip
48
+ else None
49
+ )
39
50
  new_vals = {
40
- "client": (
41
- get_header(headers, b"x-real-ip"),
42
- get_header_int(headers, b"x-forwarded-port"),
43
- ),
51
+ "client": client,
44
52
  "host": get_header(headers, b"x-forwarded-host"),
45
53
  "scheme": get_header(headers, b"x-forwarded-proto"),
46
54
  }
@@ -1,7 +1,7 @@
1
1
  """Deal with http session."""
2
2
 
3
3
  from datetime import timedelta
4
- from typing import Literal, Type
4
+ from typing import Literal
5
5
 
6
6
  from starlette.datastructures import MutableHeaders
7
7
  from starlette.requests import HTTPConnection
@@ -25,7 +25,7 @@ class SessionMiddleware(AbstractMiddleware):
25
25
  cookie_same_site: Literal["lax", "strict", "none"] = "lax",
26
26
  cookie_secure: bool = False,
27
27
  cookie_domain: str = "",
28
- serializer: Type[AbsractSessionSerializer] = SignedSessionSerializer,
28
+ serializer: type[AbsractSessionSerializer] = SignedSessionSerializer,
29
29
  ) -> None:
30
30
  self.app = app
31
31
  self.max_age = int(duration.total_seconds())
@@ -1,8 +1,10 @@
1
1
  """Serialize session."""
2
+
2
3
  import abc
3
4
  import json
4
5
  from base64 import b64decode, b64encode
5
- from typing import Any, Mapping, Tuple
6
+ from collections.abc import Mapping
7
+ from typing import Any
6
8
 
7
9
  import itsdangerous
8
10
 
@@ -11,8 +13,7 @@ class AbsractSessionSerializer(abc.ABC):
11
13
  """Session serializer base class"""
12
14
 
13
15
  @abc.abstractmethod
14
- def __init__(self, secret_key: str, max_age: int) -> None:
15
- ...
16
+ def __init__(self, secret_key: str, max_age: int) -> None: ...
16
17
 
17
18
  @abc.abstractmethod
18
19
  def serialize(self, data: Mapping[str, Any]) -> bytes:
@@ -20,7 +21,7 @@ class AbsractSessionSerializer(abc.ABC):
20
21
  ...
21
22
 
22
23
  @abc.abstractmethod
23
- def deserialize(self, data: bytes) -> Tuple[Mapping[str, Any], bool]:
24
+ def deserialize(self, data: bytes) -> tuple[Mapping[str, Any], bool]:
24
25
  """Derialize the session raw bytes content and return it as a mapping."""
25
26
  ...
26
27
 
@@ -47,7 +48,7 @@ class SignedSessionSerializer(AbsractSessionSerializer):
47
48
  signed = self.signer.sign(encoded)
48
49
  return signed
49
50
 
50
- def deserialize(self, data: bytes) -> Tuple[Mapping[str, Any], bool]:
51
+ def deserialize(self, data: bytes) -> tuple[Mapping[str, Any], bool]:
51
52
  """Deserialize the session.
52
53
 
53
54
  If the signature is incorect, the session restart from the begining.
fastlife/request/form.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """HTTP Form serialization."""
2
2
 
3
- from typing import Any, Callable, Generic, Mapping, Type, TypeVar, get_origin
3
+ from collections.abc import Callable, Mapping
4
+ from typing import Any, Generic, TypeVar, get_origin
4
5
 
5
6
  from fastapi import Depends
6
7
  from pydantic import BaseModel, ValidationError
@@ -28,7 +29,7 @@ class FormModel(Generic[T]):
28
29
  self.is_valid = is_valid
29
30
 
30
31
  @classmethod
31
- def default(cls, prefix: str, pydantic_type: Type[T]) -> "FormModel[T]":
32
+ def default(cls, prefix: str, pydantic_type: type[T]) -> "FormModel[T]":
32
33
  return cls(prefix, pydantic_type.model_construct(), {})
33
34
 
34
35
  def edit(self, pydantic_type: T) -> None:
@@ -47,7 +48,7 @@ class FormModel(Generic[T]):
47
48
 
48
49
  @classmethod
49
50
  def from_payload(
50
- cls, prefix: str, pydantic_type: Type[T], data: Mapping[str, Any]
51
+ cls, prefix: str, pydantic_type: type[T], data: Mapping[str, Any]
51
52
  ) -> "FormModel[T]":
52
53
  try:
53
54
  return cls(prefix, pydantic_type(**data.get(prefix, {})), {}, True)
@@ -68,12 +69,12 @@ class FormModel(Generic[T]):
68
69
  continue
69
70
 
70
71
  else:
71
- raise NotImplementedError
72
+ raise NotImplementedError from exc # coverage: ignore
72
73
  elif issubclass(typ, BaseModel):
73
74
  typ = typ.model_fields[part].annotation
74
75
  loc = f"{loc}.{part}"
75
76
  else:
76
- raise NotImplementedError
77
+ raise NotImplementedError from exc # coverage: ignore
77
78
 
78
79
  else:
79
80
  # it is an integer and it part of the list
@@ -88,7 +89,7 @@ class FormModel(Generic[T]):
88
89
 
89
90
 
90
91
  def form_model(
91
- cls: Type[T], name: str | None = None
92
+ cls: type[T], name: str | None = None
92
93
  ) -> Callable[[Mapping[str, Any]], FormModel[T]]:
93
94
  """
94
95
  Build a model, a class of type T based on Pydandic Base Model from a form payload.
@@ -2,14 +2,10 @@
2
2
  Set of functions to unserialize www-form-urlencoded format to python simple types.
3
3
  """
4
4
 
5
+ from collections.abc import Mapping, MutableMapping, MutableSequence, Sequence
5
6
  from typing import (
6
7
  Annotated,
7
8
  Any,
8
- Mapping,
9
- MutableMapping,
10
- MutableSequence,
11
- Optional,
12
- Sequence,
13
9
  )
14
10
 
15
11
  from fastapi import Depends
@@ -22,7 +18,7 @@ def unflatten_struct(
22
18
  unflattened_output: MutableMapping[str, Any] | MutableSequence[Any],
23
19
  level: int = 0,
24
20
  *,
25
- csrf_token_name: Optional[str] = None,
21
+ csrf_token_name: str | None = None,
26
22
  ) -> Mapping[str, Any] | Sequence[Any]:
27
23
  """
28
24
  Take a flatten_input map, with key segmented by `.` and build a nested dict.
fastlife/routing/route.py CHANGED
@@ -1,5 +1,7 @@
1
1
  """HTTP Route."""
2
- from typing import TYPE_CHECKING, Any, Callable, Coroutine
2
+
3
+ from collections.abc import Callable, Coroutine
4
+ from typing import TYPE_CHECKING, Any
3
5
 
4
6
  from fastapi.routing import APIRoute
5
7
  from starlette.requests import Request as StarletteRequest
@@ -4,6 +4,7 @@ FastApi router for fastlife application.
4
4
  The aim of this router is get {class}`fastlife.routing.route.Route`
5
5
  available in the FastApi request depency injection.
6
6
  """
7
+
7
8
  from typing import Any
8
9
 
9
10
  from fastapi import APIRouter
fastlife/security/csrf.py CHANGED
@@ -19,7 +19,8 @@ no way to prevent to set the cookie in the request.
19
19
  """
20
20
 
21
21
  import secrets
22
- from typing import Any, Callable, Coroutine
22
+ from collections.abc import Callable, Coroutine
23
+ from typing import Any
23
24
 
24
25
  from fastlife.request import Request
25
26
 
@@ -2,7 +2,8 @@
2
2
 
3
3
  import abc
4
4
  import logging
5
- from typing import Annotated, Any, Callable, Coroutine, Generic, Literal, TypeVar
5
+ from collections.abc import Callable, Coroutine
6
+ from typing import Annotated, Any, Generic, Literal, TypeVar
6
7
  from uuid import UUID
7
8
 
8
9
  from fastapi import Depends, HTTPException
@@ -1,6 +1,7 @@
1
1
  """Find the localization gor the given request."""
2
2
 
3
- from typing import TYPE_CHECKING, Any, Callable
3
+ from collections.abc import Callable
4
+ from typing import TYPE_CHECKING, Any
4
5
 
5
6
  from fastlife.config.settings import Settings
6
7
 
@@ -1,6 +1,7 @@
1
1
  """Security policy."""
2
2
 
3
- from typing import Any, Callable, Coroutine
3
+ from collections.abc import Callable, Coroutine
4
+ from typing import Any
4
5
 
5
6
  CheckPermissionHook = Callable[..., Coroutine[Any, Any, None]] | Callable[..., None]
6
7
  CheckPermission = Callable[[str], CheckPermissionHook]
@@ -10,7 +10,8 @@ In that case, those base classes have to be implemented.
10
10
  """
11
11
 
12
12
  import abc
13
- from typing import Any, Callable, Mapping
13
+ from collections.abc import Callable, Mapping
14
+ from typing import Any
14
15
 
15
16
  from fastlife import Request, Response
16
17
  from fastlife.security.csrf import create_csrf_token
@@ -1,5 +1,6 @@
1
1
  import pathlib
2
- from typing import TYPE_CHECKING, Iterator, Tuple
2
+ from collections.abc import Iterator
3
+ from typing import TYPE_CHECKING
3
4
 
4
5
  from babel.support import NullTranslations, Translations
5
6
 
@@ -11,7 +12,7 @@ if TYPE_CHECKING:
11
12
  locale_name = str
12
13
 
13
14
 
14
- def find_mo_files(root_path: str) -> Iterator[Tuple[str, str, pathlib.Path]]:
15
+ def find_mo_files(root_path: str) -> Iterator[tuple[str, str, pathlib.Path]]:
15
16
  root = pathlib.Path(root_path)
16
17
 
17
18
  # Walk through the directory structure and match the pattern
@@ -1,11 +1,12 @@
1
1
  """Type inference."""
2
+
2
3
  from types import UnionType
3
- from typing import Any, Type, Union, get_origin
4
+ from typing import Any, Union, get_origin
4
5
 
5
6
  from pydantic import BaseModel
6
7
 
7
8
 
8
- def is_complex_type(typ: Type[Any]) -> bool:
9
+ def is_complex_type(typ: type[Any]) -> bool:
9
10
  """
10
11
  Used to detect complex type such as Mapping, Sequence and pydantic BaseModel.
11
12
 
@@ -14,7 +15,7 @@ def is_complex_type(typ: Type[Any]) -> bool:
14
15
  return bool(get_origin(typ) or issubclass(typ, BaseModel))
15
16
 
16
17
 
17
- def is_union(typ: Type[Any]) -> bool:
18
+ def is_union(typ: type[Any]) -> bool:
18
19
  """Used to detect unions like Optional[T], Union[T, U] or T | U."""
19
20
  type_origin = get_origin(typ)
20
21
  if type_origin:
@@ -1,4 +1,5 @@
1
1
  """Resolution of python objects for dependency injection and more."""
2
+
2
3
  import importlib.util
3
4
  from pathlib import Path
4
5
  from types import UnionType
@@ -20,8 +21,10 @@ def resolve(value: str) -> Any:
20
21
 
21
22
  try:
22
23
  attr = getattr(module, attr_name)
23
- except AttributeError:
24
- raise ValueError(f"Attribute {attr_name} not found in module {module_name}")
24
+ except AttributeError as exc:
25
+ raise ValueError(
26
+ f"Attribute {attr_name} not found in module {module_name}"
27
+ ) from exc
25
28
 
26
29
  return attr
27
30
 
@@ -32,7 +35,7 @@ def resolve_extended(value: str) -> UnionType:
32
35
  if len(values) == 1:
33
36
  return resolve(value)
34
37
  types = [resolve(t) for t in values if t != "builtins:NoneType"]
35
- return Union[tuple(types)] # type: ignore
38
+ return Union[tuple(types)] # type: ignore # noqa: UP007
36
39
 
37
40
 
38
41
  def resolve_path(value: str) -> str:
@@ -2,7 +2,8 @@
2
2
  Bind template to the view in order to build an html response.
3
3
  """
4
4
 
5
- from typing import Any, Callable
5
+ from collections.abc import Callable
6
+ from typing import Any
6
7
 
7
8
  from fastapi import Depends, Response
8
9
 
@@ -1,4 +1,5 @@
1
1
  """Testing fastlife client."""
2
+
2
3
  from .testclient import WebTestClient
3
4
 
4
5
  __all__ = ["WebTestClient"]
@@ -2,9 +2,9 @@
2
2
 
3
3
  import re
4
4
  import time
5
- from collections.abc import MutableMapping
5
+ from collections.abc import Iterator, Mapping, MutableMapping, Sequence
6
6
  from http.cookiejar import Cookie
7
- from typing import Any, Iterator, Literal, Mapping, Optional, Sequence
7
+ from typing import Any, Literal
8
8
  from urllib.parse import urlencode
9
9
 
10
10
  import bs4
@@ -76,7 +76,7 @@ class Element:
76
76
  return Element(self._client, self._tag.form) if self._tag.form else None
77
77
 
78
78
  @property
79
- def hx_target(self) -> Optional[str]:
79
+ def hx_target(self) -> str | None:
80
80
  """
81
81
  Return the hx-target of the element.
82
82
 
@@ -251,7 +251,7 @@ class WebForm:
251
251
  raise ValueError(f'"{fieldname}" does not exists')
252
252
  field = self._formfields[fieldname]
253
253
  if field.node_name != "select":
254
- raise ValueError(f"{fieldname} is a {repr(field)}, " "use set() instead")
254
+ raise ValueError(f"{fieldname} is a {field!r}, " "use set() instead")
255
255
 
256
256
  for option in field.by_node_name("option"):
257
257
  if option.text == value.strip():
@@ -273,7 +273,7 @@ class WebForm:
273
273
 
274
274
  if field.node_name != "select":
275
275
  raise ValueError(
276
- f"{fieldname} is a {repr(self._formfields[fieldname])}, "
276
+ f"{fieldname} is a {self._formfields[fieldname]!r}, "
277
277
  "use unset() for checkbox instead"
278
278
  )
279
279
  if "multiple" not in field.attrs:
@@ -392,7 +392,7 @@ class WebResponse:
392
392
  def html_body(self) -> Element:
393
393
  """The body element of the html response."""
394
394
  body = self.html.by_node_name("body")
395
- assert len(body) == 1
395
+ assert len(body) == 1, "body element not found or multiple body found"
396
396
  return body[0]
397
397
 
398
398
  @property
@@ -400,7 +400,7 @@ class WebResponse:
400
400
  """The form element of the html response."""
401
401
  if self._form is None:
402
402
  form = self.html.form
403
- assert form is not None
403
+ assert form is not None, "form element not found"
404
404
  self._form = WebForm(self._client, self._origin, form)
405
405
  return self._form
406
406
 
@@ -1,4 +1,4 @@
1
- from typing import Optional, cast
1
+ from typing import cast
2
2
 
3
3
  from fastapi import Query
4
4
  from pydantic.fields import FieldInfo
@@ -11,9 +11,9 @@ from fastlife.shared_utils.resolver import resolve_extended
11
11
  async def show_widget(
12
12
  typ: str,
13
13
  request: Request,
14
- title: Optional[str] = Query(None),
15
- name: Optional[str] = Query(None),
16
- token: Optional[str] = Query(None),
14
+ title: str | None = Query(None),
15
+ name: str | None = Query(None),
16
+ token: str | None = Query(None),
17
17
  removable: bool = Query(False),
18
18
  ) -> Response:
19
19
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastlifeweb
3
- Version: 0.16.3
3
+ Version: 0.16.4
4
4
  Summary: High-level web framework
5
5
  Home-page: https://github.com/mardiros/fastlife
6
6
  License: BSD-derived
@@ -1,18 +1,18 @@
1
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
- fastlife/adapters/jinjax/renderer.py,sha256=rj2KI4GB8tZstJwImdDYHuswxjdbGri2f8H6YuByaLo,13046
4
+ fastlife/adapters/jinjax/renderer.py,sha256=_8k4a4R4ExjJrKiG5m5bFOxYqOFa3_TtmZ7iU1rd-p8,13041
5
5
  fastlife/adapters/jinjax/widgets/__init__.py,sha256=HERnX9xiXUbTDz3XtlnHWABTBjhIq_kkBgWs5E6ZIMY,42
6
- fastlife/adapters/jinjax/widgets/base.py,sha256=JoB1bLI97ZW2ycfBcHgrPxiOszE9t_SFFK5L1bR-cSo,4015
6
+ fastlife/adapters/jinjax/widgets/base.py,sha256=3bBThRMnsdCi6Q_Dm73ep5pNOqgpSXsvAIBbHshfY7I,4037
7
7
  fastlife/adapters/jinjax/widgets/boolean.py,sha256=w4hZMo_8xDoThStlIUR4eVfLm8JwUp0-TaGCjGSyCbA,1145
8
- fastlife/adapters/jinjax/widgets/checklist.py,sha256=VIIfJ8JB7ZAISwFFiGZ7jfGuNJ9sjkhRSVFqDTr-RR4,1655
9
- fastlife/adapters/jinjax/widgets/dropdown.py,sha256=x2Y9BOfHfSuzWD_HNrvCkiJtKDxl8Vs05Uk8QKvRxyY,1622
10
- fastlife/adapters/jinjax/widgets/factory.py,sha256=XH-S1aucdPiujCWHdP_YyP1Tks7XzVKughMcL8eQhxk,17558
8
+ fastlife/adapters/jinjax/widgets/checklist.py,sha256=8fgOrdxy1xpyQ6p3_mbRMd2vx6EU2WT5jI7QF27Y5EQ,1664
9
+ fastlife/adapters/jinjax/widgets/dropdown.py,sha256=3Kc7i0z-7d6HrQchSHFCO5-xOh3bSEePo_pjXrIkvSE,1599
10
+ fastlife/adapters/jinjax/widgets/factory.py,sha256=42cadaEzQb5vrwdOJRpv8JATz97EHa6YZo2EIbDx36o,17554
11
11
  fastlife/adapters/jinjax/widgets/hidden.py,sha256=pkMKxKhBKSGNf1Su81Jr-n8BJ45X5Qjsd1xXnJ7prPI,699
12
- fastlife/adapters/jinjax/widgets/model.py,sha256=XAvf125LAGmWOyIf7jMFzmGb6oRW_fb6Hg90tR_irTw,1272
13
- fastlife/adapters/jinjax/widgets/sequence.py,sha256=Xs3qic1pJLq-w7P7EXpa5taWnt5gWciK80gbhYG4rZM,1512
14
- fastlife/adapters/jinjax/widgets/text.py,sha256=fmbMwksgUpEImD8AExKCSp4g7LBD9F77dDU-2zWC8Qg,3260
15
- fastlife/adapters/jinjax/widgets/union.py,sha256=9u6AG9KzSQAT8ae9MmCniz0FJ8yGF12oRhwH5oWpWxE,2526
12
+ fastlife/adapters/jinjax/widgets/model.py,sha256=xdgY--K4GNo5IIWTLjSAnNRDHq2bt81mh9O5J23y0gg,1299
13
+ fastlife/adapters/jinjax/widgets/sequence.py,sha256=60rgz4LgE_TQQwajiZhn6EhY-s-HXOiIdQiQoKlUCvQ,1533
14
+ fastlife/adapters/jinjax/widgets/text.py,sha256=KtUieF-q_BigG5AcL-4Sdr6LrIOQdWPwlaVW-2p-KPQ,3205
15
+ fastlife/adapters/jinjax/widgets/union.py,sha256=CO6Q4_U8DieVsS5NzMp6TAbVXrBljfcjSARycEKYPDY,2540
16
16
  fastlife/components/A.jinja,sha256=rjnOFNpM2SUeJ0P8FDe3jzc_1WlsL65Kv-tb6KMCEpw,1389
17
17
  fastlife/components/Button.jinja,sha256=COtCjDpzGLNqtBUYsHw7gdUay4kff3KdLJFAzrEnMmo,2310
18
18
  fastlife/components/Checkbox.jinja,sha256=47_E9uPdr3QKUvRVhNQA7VE0uh5FVslQM26cdF0WCtY,753
@@ -1666,48 +1666,48 @@ fastlife/components/pydantic_form/Textarea.jinja,sha256=NzfCi5agRUSVcb5RXw0QamM8
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
1668
  fastlife/config/__init__.py,sha256=ThosRIPZ_fpD0exZu-kUC_f8ZNa5KyDlleWMmEHkjEo,448
1669
- fastlife/config/configurator.py,sha256=-U-se932t95YiIfPD6N-CElWuuqbKP2AKTjJXoajCKE,22385
1670
- fastlife/config/exceptions.py,sha256=2MS2MFgb3mDbtdHEwnC-QYubob3Rl0v8O8y615LY0ds,1180
1671
- fastlife/config/openapiextra.py,sha256=_9rBYeTqB7nVuzvUHMwZU387bTfYFHYLlP05NP0vEDs,513
1669
+ fastlife/config/configurator.py,sha256=keVPDTiCVko8Ncn1yYAnhG0x9Zp5J9yxMKEeyXZYP1E,22351
1670
+ fastlife/config/exceptions.py,sha256=M45w7ZNAj4KPPHNYiCtRHulRRb08rkOGtJ2I8WoXNHI,1207
1671
+ fastlife/config/openapiextra.py,sha256=rYoerrn9sni2XwnO3gIWqaz7M0aDZPhVLjzqhDxue0o,514
1672
1672
  fastlife/config/registry.py,sha256=dGcNm7E6WY0x5HZNzo1gBFvGFCWeJj6JFXsJtLax5NU,1347
1673
- fastlife/config/resources.py,sha256=pM0j5VKVbVak4Z5mFRHBjAjUqORP4TAtCnZM3res5o8,8776
1673
+ fastlife/config/resources.py,sha256=XJIJxNtB5DAxXvDOU3xbJFa7_n-rAy9MXXjwekvj6p8,8583
1674
1674
  fastlife/config/settings.py,sha256=7oggPOucyJwQYI97q8vs3kPXjFIVpQu1q6BK25h-uFs,3789
1675
- fastlife/config/views.py,sha256=Dxi6lO4gFs6GriAW7Rh5GDvebwbrpS2HzYhf30pXJiE,2058
1675
+ fastlife/config/views.py,sha256=V-P53GSnvqEPzkvEWNuI4ofcdbFur2Dl-s6BeKXObwI,2086
1676
1676
  fastlife/middlewares/__init__.py,sha256=C3DUOzR5EhlAv5Zq7h-Abyvkd7bUsJohTRSB2wpRYQE,220
1677
1677
  fastlife/middlewares/base.py,sha256=9OYqByRuVoIrLt353NOedPQTLdr7LSmxhb2BZcp20qk,638
1678
1678
  fastlife/middlewares/reverse_proxy/__init__.py,sha256=g1SoVDmenKzpAAPYHTEsWgdBByOxtLg9fGx6RV3i0ok,846
1679
- fastlife/middlewares/reverse_proxy/x_forwarded.py,sha256=aq_80V0Vrb4M6RQfk8CtQnhfwZGzJyeTYYQ16In8DaQ,1510
1679
+ fastlife/middlewares/reverse_proxy/x_forwarded.py,sha256=sGX7b4hOAgsG2KmtL3xlrZt61_IpF1exf6x_33F_QXE,1733
1680
1680
  fastlife/middlewares/session/__init__.py,sha256=3XgXcIO6yQls5G7x8K2T8b7a_enA_7rQptWZcp3j2Ak,1400
1681
- fastlife/middlewares/session/middleware.py,sha256=AlRIFXfn3JesKJzMAFUHDOo22mfuwDHkyecDHo9jCdA,3172
1682
- fastlife/middlewares/session/serializer.py,sha256=fTdZCop0y4VkCMyOIo6GEbo5fVWrwsBXaSWfConPL8E,2144
1681
+ fastlife/middlewares/session/middleware.py,sha256=R48x3MJ-tu8siy8G12hDHa83sMcZz6E1eEb0xwk77E4,3166
1682
+ fastlife/middlewares/session/serializer.py,sha256=wpaktDP5v1spmbD-D3Q68EK9A0KInE4DT8mkogBJ3Fc,2157
1683
1683
  fastlife/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1684
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
1685
+ fastlife/request/form.py,sha256=BiuvbV85NkvnJECSxqabfq3PhMfdKMPVobS35PhxSFA,3595
1686
+ fastlife/request/form_data.py,sha256=JZmKbKZz8nnspvCHYHQrz-xsFVJFaWHkTilUWk7Fx-M,4448
1687
1687
  fastlife/request/localizer.py,sha256=9MXAcsod-Po5qeg4lttD3dyumiI0y5vGHCwSSmt9or8,349
1688
1688
  fastlife/request/request.py,sha256=h4Ji0GElWJ3W2hHgOwjSKHQcxUjjPuOk09v-oyfjEd0,2568
1689
1689
  fastlife/routing/__init__.py,sha256=8EMnQE5n8oA4J9_c3nxzwKDVt3tefZ6fGH0d2owE8mo,195
1690
- fastlife/routing/route.py,sha256=O0gwPtP7ur2EHRf76kBASgoLMQciGHXcrJkW8zEPFJA,1413
1691
- fastlife/routing/router.py,sha256=bLZ4k2aDs4L423znwGnw-X2LnM36O8fjhDWc8q1WewI,481
1690
+ fastlife/routing/route.py,sha256=vqjfMsHAVO0l2B8fuB8t19CKMtE7WoBkG4kvi4lUonM,1441
1691
+ fastlife/routing/router.py,sha256=ho9TvTkX2iUW6GEh99FgclZVFKkCCCxYG4pPHeUtGn8,482
1692
1692
  fastlife/security/__init__.py,sha256=QYDcJ3oXQzqXQxoDD_6biGAtercFrtePttoifiL1j34,25
1693
- fastlife/security/csrf.py,sha256=PvC9Fqdb6c0IzzsnaMx2quQdjjKrb-nOPoAHfcwoAe8,2141
1694
- fastlife/security/policy.py,sha256=8FWtjcOjkK5UvnjKluVkJIZdec2bfXbkKx7ouiiyjBs,5115
1693
+ fastlife/security/csrf.py,sha256=PIKG83LPqKz4kDALnZxIyPdYVwbNqsIryi7JPqRPQag,2168
1694
+ fastlife/security/policy.py,sha256=ECNEyZXjizK2kz61v5eU7xFNd_M6tIlr9JEwcdyjuj8,5142
1695
1695
  fastlife/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1696
- fastlife/services/locale_negociator.py,sha256=b1Fsx-r_zIZe0GD8WH1gmslMb0mNrvxuacxD4LNX63o,819
1697
- fastlife/services/policy.py,sha256=ZK4K3fVGT1fUeBdo5sSWnEcJg49CZuaI3z2gyg3KguQ,1653
1698
- fastlife/services/templates.py,sha256=7gPJxGWD-XqputbZpy_Icsz3WHKJaWg2JgkVOeKrjfA,3840
1699
- fastlife/services/translations.py,sha256=Bo5CIjdbQ3g_ihbv4Bz60hzd8VOtqEEPOyhJEbGcvP4,4363
1696
+ fastlife/services/locale_negociator.py,sha256=Np2O8s7xnYTpf5eCG7LvcfFJ2LV7p_k86NNrU9Lju88,846
1697
+ fastlife/services/policy.py,sha256=bHe49EiD2ExQpFJxOsno6WyjxNrn8pf0wWDixkToHn0,1680
1698
+ fastlife/services/templates.py,sha256=-dIt8zrgiRsjMblS174Rx_2xRZkQQRIATYhaA2vbIAk,3867
1699
+ fastlife/services/translations.py,sha256=0MD6R4xCJM40JxvLyCg5laXEPJlyKvzjhVRkjE5l010,4383
1700
1700
  fastlife/shared_utils/__init__.py,sha256=i66ytuf-Ezo7jSiNQHIsBMVIcB-tDX0tg28-pUOlhzE,26
1701
- fastlife/shared_utils/infer.py,sha256=CJjsL_F5OQRG7-0_89MQiZhyd7IcMGyirlQhjtcaIT4,728
1702
- fastlife/shared_utils/resolver.py,sha256=BRU88Ej4oA1iDIyG4Z6T7Q9WFvPHiMm6zuSh623312A,1725
1701
+ fastlife/shared_utils/infer.py,sha256=3G_u6q2aWzeiVlAyGaWIlnAcz90m4bFNwpPYd5JIqfE,723
1702
+ fastlife/shared_utils/resolver.py,sha256=Nnva8D_BM_REFxH1sXZYKUZ5Ryx6o6vjqBSgvA7qWLY,1778
1703
1703
  fastlife/templates/__init__.py,sha256=QrP_5UAOgxqC-jOu5tcjd-l6GOYrS4dka6vmWMxWqfo,184
1704
- fastlife/templates/binding.py,sha256=00rAbPHttCOgY-Nl9TbwiM2iFtSoH7xcBlj9kbNLzrE,1437
1704
+ fastlife/templates/binding.py,sha256=0pE2btOwLf4xOEgBXVOyz_dIX9tBCYCaJ7RhZI3knbs,1464
1705
1705
  fastlife/templates/constants.py,sha256=MGdUjkF9hsPMN8rOS49eWbAApcb8FL-FAeFvJU8k90M,8387
1706
- fastlife/testing/__init__.py,sha256=vuqwoNUd3BuIp3fm7nkvmYkIGjIimf5zUGhDkeWrg2s,98
1707
- fastlife/testing/testclient.py,sha256=BC7lLQ_jc59UmknAKzgRtW9a3cpX_V_QLp9Mg2ScLA8,20546
1706
+ fastlife/testing/__init__.py,sha256=VpxkS3Zp3t_hH8dBiLaGFGhsvt511dhBS_8fMoFXdmU,99
1707
+ fastlife/testing/testclient.py,sha256=3s0CguxV3AePBRQ5XccutS0sMZtUkxvkt3C7z6IJbz0,20600
1708
1708
  fastlife/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1709
- fastlife/views/pydantic_form.py,sha256=ZYOXKudmSqtRvFn5ZY75DOXZVunGXJBKpjh9FJcqu6k,1386
1710
- fastlifeweb-0.16.3.dist-info/LICENSE,sha256=F75xSseSKMwqzFj8rswYU6NWS3VoWOc_gY3fJYf9_LI,1504
1711
- fastlifeweb-0.16.3.dist-info/METADATA,sha256=oKBtNXGfKC8ya3PoQYclQuJVP_UPZs4GzKmWUzt3_t8,3345
1712
- fastlifeweb-0.16.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1713
- fastlifeweb-0.16.3.dist-info/RECORD,,
1709
+ fastlife/views/pydantic_form.py,sha256=4dv37JORLpvkgCgMGZfUN_qy7wme040GLZAzOTFqdnU,1367
1710
+ fastlifeweb-0.16.4.dist-info/LICENSE,sha256=F75xSseSKMwqzFj8rswYU6NWS3VoWOc_gY3fJYf9_LI,1504
1711
+ fastlifeweb-0.16.4.dist-info/METADATA,sha256=pFRnHJnvWXf7-ZucfCFAqkdPQ_VPE9u3MdjeH5RQSJo,3345
1712
+ fastlifeweb-0.16.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1713
+ fastlifeweb-0.16.4.dist-info/RECORD,,