fastlifeweb 0.26.2__py3-none-any.whl → 0.27.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 CHANGED
@@ -1,3 +1,6 @@
1
+ ## 0.27.0 - Released on 2025-04-24
2
+ * Add i18n support on pydantic form.
3
+
1
4
  ## 0.26.2 - Released on 2025-04-20
2
5
  * Add a RedirectResponse class that is htmx friendly and P/R/G pattern friendly.
3
6
  * Fix documentation generation.
@@ -55,7 +55,7 @@ class Widget(JinjaXTemplate, Generic[T]):
55
55
  aria_label: str | None = Field(default=None)
56
56
  "Non visible text alternative."
57
57
  token: str = Field(default="")
58
- "unique token to ensure id are unique in the DOM."
58
+ "Unique token to ensure id are unique in the DOM."
59
59
  removable: bool = Field(default=False)
60
60
  "Indicate that the widget is removable from the dom."
61
61
 
@@ -16,7 +16,7 @@ class BooleanWidget(Widget[bool]):
16
16
  <div class="flex items-center">
17
17
  <Checkbox :name="name" :id="id" :checked="value" value="1" />
18
18
  <Label :for="id" class="ms-2 text-base text-neutral-900 dark:text-white">
19
- {{title|safe}}
19
+ {{ gettext(title)|safe }}
20
20
  </Label>
21
21
  </div>
22
22
  <pydantic_form.Error :text="error" />
@@ -40,7 +40,7 @@ class ChecklistWidget(Widget[Sequence[Checkable]]):
40
40
  <div class="pt-4">
41
41
  <Details>
42
42
  <Summary :id="id + '-summary'">
43
- <H3 :class="H3_SUMMARY_CLASS">{{title}}</H3>
43
+ <H3 :class="H3_SUMMARY_CLASS">{{ gettext(title) }}</H3>
44
44
  <pydantic_form.Error :text="error" />
45
45
  </Summary>
46
46
  <div>
@@ -52,7 +52,7 @@ class ChecklistWidget(Widget[Sequence[Checkable]]):
52
52
  :checked="value.checked" />
53
53
  <Label :for="value.id"
54
54
  class="ms-2 text-base text-neutral-900 dark:text-white">
55
- {{- value.label -}}
55
+ {{- gettext(value.label) -}}
56
56
  </Label>
57
57
  <pydantic_form.Error :text="value.error" />
58
58
  </div>
@@ -17,12 +17,12 @@ class DropDownWidget(Widget[str]):
17
17
  template = """
18
18
  <pydantic_form.Widget :widget_id="id" :removable="removable">
19
19
  <div class="pt-4">
20
- <Label :for="id">{{title}}</Label>
20
+ <Label :for="id">{{ gettext(title) }}</Label>
21
21
  <Select :name="name" :id="id">
22
22
  {%- for opt in options -%}
23
23
  <Option :value="opt.value" id={{id + "-" + opt.value.replace(" ", " -")}}
24
24
  :selected="value==opt.value">
25
- {{- opt.text -}}
25
+ {{- gettext(opt.text) -}}
26
26
  </Option>
27
27
  {%- endfor -%}
28
28
  </Select>
@@ -11,7 +11,7 @@ class MFACodeWidget(Widget[str]):
11
11
  template = """
12
12
  <pydantic_form.Widget :widget_id="id" :removable="removable">
13
13
  <div class="pt-4">
14
- <Label :for="id">{{title}}</Label>
14
+ <Label :for="id">{{ gettext(title) }}</Label>
15
15
  <pydantic_form.Error :text="error" />
16
16
  <Input :name="name" type="text" :id="id" inputmode="numeric"
17
17
  autocomplete="one-time-code" :autofocus="autofocus"
@@ -17,7 +17,7 @@ class ModelWidget(Widget[Sequence[TWidget]]):
17
17
  {% if nested %}
18
18
  <Details>
19
19
  <Summary :id="id + '-summary'">
20
- <H3 :class="H3_SUMMARY_CLASS">{{title}}</H3>
20
+ <H3 :class="H3_SUMMARY_CLASS">{{ gettext(title) }}</H3>
21
21
  <pydantic_form.Error :text="error" />
22
22
  </Summary>
23
23
  <div>
@@ -14,7 +14,7 @@ class SequenceWidget(Widget[Sequence[TWidget]]):
14
14
  <pydantic_form.Widget :widget_id="id" :removable="removable">
15
15
  <Details :id="id">
16
16
  <Summary :id="id + '-summary'">
17
- <H3 :class="H3_SUMMARY_CLASS">{{title}}</H3>
17
+ <H3 :class="H3_SUMMARY_CLASS">{{ gettext(title) }}</H3>
18
18
  <pydantic_form.Error :text="error" />
19
19
  </Summary>
20
20
  <div>
@@ -15,7 +15,7 @@ class TextWidget(Widget[Builtins]):
15
15
  template = """
16
16
  <pydantic_form.Widget :widget_id="id" :removable="removable">
17
17
  <div class="pt-4">
18
- <Label :for="id">{{title}}</Label>
18
+ <Label :for="id">{{ gettext(title) }}</Label>
19
19
  <pydantic_form.Error :text="error" />
20
20
  <Input :name="name" :value="value" :type="input_type" :id="id"
21
21
  :aria-label="aria_label" :placeholder="placeholder"
@@ -26,19 +26,22 @@ class TextWidget(Widget[Builtins]):
26
26
  """
27
27
 
28
28
  input_type: str = Field(default="text")
29
+ """type attribute for the Input component."""
29
30
  placeholder: str | None = Field(default=None)
31
+ """placeholder attribute for the Input component."""
30
32
  autocomplete: str | None = Field(default=None)
33
+ """autocomplete attribute for the Input component."""
31
34
 
32
35
 
33
36
  class PasswordWidget(Widget[SecretStr]):
34
37
  """
35
- Widget for text like field (email, ...).
38
+ Widget for password fields.
36
39
  """
37
40
 
38
41
  template = """
39
42
  <pydantic_form.Widget :widget_id="id" :removable="removable">
40
43
  <div class="pt-4">
41
- <Label :for="id">{{title}}</Label>
44
+ <Label :for="id">{{ gettext(title) }}</Label>
42
45
  <pydantic_form.Error :text="error" />
43
46
  <Password :name="name" :type="input_type" :id="id"
44
47
  autocomplete={{
@@ -51,8 +54,13 @@ class PasswordWidget(Widget[SecretStr]):
51
54
  """
52
55
 
53
56
  input_type: str = Field(default="password")
57
+ """type attribute for the Input component."""
54
58
  placeholder: str | None = Field(default=None)
59
+ """placeholder attribute for the Input component."""
55
60
  new_password: bool = Field(default=False)
61
+ """
62
+ Adapt autocomplete behavior for browsers to hint existing or generate password.
63
+ """
56
64
 
57
65
 
58
66
  class TextareaWidget(Widget[str | Sequence[str]]):
@@ -81,7 +89,7 @@ class TextareaWidget(Widget[str | Sequence[str]]):
81
89
  template = """
82
90
  <pydantic_form.Widget :widget_id="id" :removable="removable">
83
91
  <div class="pt-4">
84
- <Label :for="id">{{title}}</Label>
92
+ <Label :for="id">{{ gettext(title) }}</Label>
85
93
  <pydantic_form.Error :text="error" />
86
94
  <Textarea :name="name" :id="id" :aria-label="aria_label">
87
95
  {%- if value is string -%}
@@ -94,5 +102,3 @@ class TextareaWidget(Widget[str | Sequence[str]]):
94
102
  </div>
95
103
  </pydantic_form.Widget>
96
104
  """
97
-
98
- placeholder: str = Field(default="")
@@ -23,7 +23,7 @@ class UnionWidget(Widget[TWidget]):
23
23
  <div id="{{id}}">
24
24
  <Details>
25
25
  <Summary :id="id + '-union-summary'">
26
- <H3 :class="H3_SUMMARY_CLASS">{{title}}</H3>
26
+ <H3 :class="H3_SUMMARY_CLASS">{{ gettext(title) }}</H3>
27
27
  <pydantic_form.Error :text="error" />
28
28
  </Summary>
29
29
  <div hx-sync="this" id="{{id}}-child">
@@ -37,7 +37,7 @@ class UnionWidget(Widget[TWidget]):
37
37
  :hx-vals="typ.params|tojson"
38
38
  :id="typ.id"
39
39
  onclick={{ "document.getElementById('" + id + "-remove-btn').hidden=false" }}
40
- :class="SECONDARY_BUTTON_CLASS">{{typ.title}}</Button>
40
+ :class="SECONDARY_BUTTON_CLASS">{{ gettext(typ.title) }}</Button>
41
41
  {% endfor %}
42
42
  {% endif %}
43
43
  </div>
@@ -42,8 +42,8 @@
42
42
 
43
43
  <input name="{{name}}" value="{{value|default('')}}" type="{{type}}"
44
44
  {%- if id %} id="{{id}}" {%- endif %}
45
- {%- if aria_label %} aria-label="{{aria_label}}" {%- endif %}
46
- {%- if placeholder %} placeholder="{{placeholder}}" {%- endif %}
45
+ {%- if aria_label %} aria-label="{{ gettext(aria_label) }}" {%- endif %}
46
+ {%- if placeholder %} placeholder="{{ gettext(placeholder) }}" {%- endif %}
47
47
  {%- if inputmode %} inputmode="{{inputmode}}" {%- endif %}
48
48
  {%- if autocomplete %} autocomplete="{{autocomplete}}" {%- endif %}
49
49
  class="{{attrs.class or INPUT_CLASS}}"
@@ -14,5 +14,5 @@
14
14
  {%- if attrs.for %} for="{{attrs.for}}" {%- endif %}
15
15
  class="{{attrs.class or LABEL_CLASS}}"
16
16
  {%- if id %} id="{{id}}" {%- endif %}>
17
- {{-content -}}
17
+ {{- content -}}
18
18
  </label>
@@ -493,14 +493,7 @@ class GenericConfigurator(Generic[TRegistry]):
493
493
  custom_globals[key] = val
494
494
  return {
495
495
  "request": request,
496
- "gettext": lczr.gettext,
497
- "ngettext": lczr.ngettext,
498
- "dgettext": lczr.dgettext,
499
- "dngettext": lczr.dngettext,
500
- "pgettext": lczr.pgettext,
501
- "dpgettext": lczr.dpgettext,
502
- "npgettext": lczr.npgettext,
503
- "dnpgettext": lczr.dnpgettext,
496
+ **lczr.as_dict(),
504
497
  **custom_globals,
505
498
  **request.renderer_globals,
506
499
  }
@@ -69,3 +69,13 @@ class GenericRequest(ASGIRequest, Generic[TRegistry, TIdentity, TClaimedIdentity
69
69
  )
70
70
 
71
71
  return await self.security_policy.has_permission(permission)
72
+
73
+ def url_path_for(self, name: str, /, **path_params: Any) -> str:
74
+ """
75
+ Return the url pathinfo for the given route and route parameters.
76
+
77
+ :param name: the name of the route
78
+ :param path_params: parameters for the route.
79
+ """
80
+ url_path_provider: Any = self.scope.get("router") or self.scope.get("app")
81
+ return url_path_provider.url_path_for(name, **path_params)
@@ -2,9 +2,10 @@
2
2
 
3
3
  import pathlib
4
4
  from collections import defaultdict
5
- from collections.abc import Callable, Iterator
5
+ from collections.abc import Callable, Iterator, Mapping
6
6
  from gettext import GNUTranslations
7
7
  from io import BufferedReader
8
+ from typing import Any
8
9
 
9
10
  from fastlife.shared_utils.resolver import resolve_path
10
11
 
@@ -100,10 +101,23 @@ class Localizer:
100
101
  self.translations[domain].merge(trans)
101
102
  self.global_translations.merge(trans)
102
103
 
103
- def __call__(self, message: str, mapping: dict[str, str] | None = None) -> str:
104
- return self.gettext(message, mapping)
105
-
106
- def gettext(self, message: str, mapping: dict[str, str] | None = None) -> str:
104
+ def as_dict(self) -> Mapping[str, Callable[..., str]]:
105
+ return {
106
+ "_": self.gettext,
107
+ "gettext": self.gettext,
108
+ "ngettext": self.ngettext,
109
+ "dgettext": self.dgettext,
110
+ "dngettext": self.dngettext,
111
+ "pgettext": self.pgettext,
112
+ "dpgettext": self.dpgettext,
113
+ "npgettext": self.npgettext,
114
+ "dnpgettext": self.dnpgettext,
115
+ }
116
+
117
+ def __call__(self, message: str, /, **mapping: Any) -> str:
118
+ return self.gettext(message, **mapping)
119
+
120
+ def gettext(self, message: str, /, **mapping: Any) -> str:
107
121
  if isinstance(message, TranslatableString):
108
122
  ret = self.translations[message.domain].gettext(message) # type: ignore
109
123
  else:
@@ -112,16 +126,12 @@ class Localizer:
112
126
  ret = ret.format(**mapping)
113
127
  return ret
114
128
 
115
- def ngettext(
116
- self, singular: str, plural: str, n: int, mapping: dict[str, str] | None = None
117
- ) -> str:
129
+ def ngettext(self, singular: str, plural: str, n: int, /, **mapping: Any) -> str:
118
130
  ret = self.global_translations.ngettext(singular, plural, n)
119
- mapping_num = {"num": n, **(mapping or {})}
131
+ mapping_num = {"num": n, **mapping}
120
132
  return ret.format(**mapping_num)
121
133
 
122
- def dgettext(
123
- self, domain: str, message: str, mapping: dict[str, str] | None = None
124
- ) -> str:
134
+ def dgettext(self, domain: str, message: str, /, **mapping: Any) -> str:
125
135
  ret = self.translations[domain].gettext(message)
126
136
  if mapping:
127
137
  ret = ret.format(**mapping)
@@ -133,15 +143,14 @@ class Localizer:
133
143
  singular: str,
134
144
  plural: str,
135
145
  n: int,
136
- mapping: dict[str, str] | None = None,
146
+ /,
147
+ **mapping: Any,
137
148
  ) -> str:
138
149
  ret = self.translations[domain].ngettext(singular, plural, n)
139
- mapping_num = {"num": n, **(mapping or {})}
150
+ mapping_num = {"num": n, **mapping}
140
151
  return ret.format(**mapping_num)
141
152
 
142
- def pgettext(
143
- self, context: str, message: str, mapping: dict[str, str] | None = None
144
- ) -> str:
153
+ def pgettext(self, context: str, message: str, /, **mapping: Any) -> str:
145
154
  ret = self.global_translations.pgettext(context, message)
146
155
  if mapping:
147
156
  ret = ret.format(**mapping)
@@ -152,7 +161,8 @@ class Localizer:
152
161
  domain: str,
153
162
  context: str,
154
163
  message: str,
155
- mapping: dict[str, str] | None = None,
164
+ /,
165
+ **mapping: Any,
156
166
  ) -> str:
157
167
  ret = self.translations[domain].pgettext(context, message)
158
168
  if mapping:
@@ -165,10 +175,11 @@ class Localizer:
165
175
  singular: str,
166
176
  plural: str,
167
177
  n: int,
168
- mapping: dict[str, str] | None = None,
178
+ /,
179
+ **mapping: Any,
169
180
  ) -> str:
170
181
  ret = self.global_translations.npgettext(context, singular, plural, n)
171
- mapping_num = {"num": n, **(mapping or {})}
182
+ mapping_num = {"num": n, **mapping}
172
183
  return ret.format(**mapping_num)
173
184
 
174
185
  def dnpgettext(
@@ -178,10 +189,11 @@ class Localizer:
178
189
  singular: str,
179
190
  plural: str,
180
191
  n: int,
181
- mapping: dict[str, str] | None = None,
192
+ /,
193
+ **mapping: Any,
182
194
  ) -> str:
183
195
  ret = self.translations[domain].npgettext(context, singular, plural, n)
184
- mapping_num = {"num": n, **(mapping or {})}
196
+ mapping_num = {"num": n, **mapping}
185
197
  return ret.format(**mapping_num)
186
198
 
187
199
 
@@ -31,6 +31,11 @@ async def show_widget(
31
31
  field = FieldInfo(title=title)
32
32
  # FIXME: .jinja should not be hardcoded
33
33
  renderer = cast(JinjaxRenderer, request.registry.get_renderer(".jinja")(request))
34
+ lczr = request.registry.localizer(request.locale_name)
35
+ renderer.globals = {
36
+ "request": request,
37
+ **lczr.as_dict(),
38
+ }
34
39
  data = renderer.pydantic_form_field(
35
40
  model=model_cls, # type: ignore
36
41
  name=name,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastlifeweb
3
- Version: 0.26.2
3
+ Version: 0.27.0
4
4
  Summary: High-level web framework
5
5
  Author-Email: Guillaume Gauvrit <guillaume@gauvr.it>
6
6
  License: MIT
@@ -1,4 +1,4 @@
1
- CHANGELOG.md,sha256=NqlUm-TificrXs2dKAL5-1_6i3Eo74MRksoaa0RBS1k,8830
1
+ CHANGELOG.md,sha256=SaMUOfsgJvjHaEzgvgmtggz0FY0YEqltXogCMACKrvc,8904
2
2
  fastlife/__init__.py,sha256=Fe8JiQyKIN1WGagUGFct-QBW8-Ku5vXhc_7BkFUGcWk,2475
3
3
  fastlife/adapters/__init__.py,sha256=imPD1hImpgrYkvUJRhHA5kVyGAua7VbP2WGkhSWKJT8,93
4
4
  fastlife/adapters/fastapi/__init__.py,sha256=1goV1FGFP04TGyskJBLKZam4Gvt1yoAvLMNs4ekWSSQ,243
@@ -32,16 +32,16 @@ fastlife/adapters/jinjax/widget_factory/set_builder.py,sha256=kwtVLATkoOFTcKBKHk
32
32
  fastlife/adapters/jinjax/widget_factory/simpletype_builder.py,sha256=olP66B9AMY1X8fgEAxhMdozWN_w1TtcIAIW6uPJRSng,1570
33
33
  fastlife/adapters/jinjax/widget_factory/union_builder.py,sha256=qQOK3Y4I0Tg0XOzU_DwseaaKRmqQ7ORMfFyIHd6oysU,2841
34
34
  fastlife/adapters/jinjax/widgets/__init__.py,sha256=HERnX9xiXUbTDz3XtlnHWABTBjhIq_kkBgWs5E6ZIMY,42
35
- fastlife/adapters/jinjax/widgets/base.py,sha256=jU-oklWuM6MPlNzBvYO0HtxUrUteb-Dh96OudPLFJx4,4120
36
- fastlife/adapters/jinjax/widgets/boolean.py,sha256=GFvyO2Lz-vTJ8lpX2UrkJk7ymu0vU-xnBBEmyBKamJg,613
37
- fastlife/adapters/jinjax/widgets/checklist.py,sha256=xsr_edMu1nTcNnMEs1AAS3U-ugK95pEqn5NV5hjTeI0,1747
38
- fastlife/adapters/jinjax/widgets/dropdown.py,sha256=ou8U48txShr8IXdwF4_pBvTn1VcR9SazZKzbLcsN3_0,1458
35
+ fastlife/adapters/jinjax/widgets/base.py,sha256=M1Mu4ArU_YCxRrhisaMMzjKMNCw7npX5DRfdZyVKuks,4120
36
+ fastlife/adapters/jinjax/widgets/boolean.py,sha256=gHErI8tp_6J3kuqjL4-i1dw-UuR65zDsOCcVKcg_s4E,624
37
+ fastlife/adapters/jinjax/widgets/checklist.py,sha256=lbt2HL8-ie8FxHEXIx9etxfnnDWi-hfM6NXddLlS1fY,1767
38
+ fastlife/adapters/jinjax/widgets/dropdown.py,sha256=zcm1M7EQLISWr4sbZDinvUR_mgmdezrLHdC7wA0dQsA,1478
39
39
  fastlife/adapters/jinjax/widgets/hidden.py,sha256=IKcVYs6NjN8YjW-UTr3DRBong6Wrc0QLgcp8U9JoQmE,638
40
- fastlife/adapters/jinjax/widgets/mfa_code.py,sha256=8HHaEHyUEQT6xzJ0Y4O0eA1SU3wSfzXljx8XY73o-Rc,733
41
- fastlife/adapters/jinjax/widgets/model.py,sha256=YBIEWa_6mnmrBnesXjLTrpJ4drUS2CIorNmhK24cz7Q,1298
42
- fastlife/adapters/jinjax/widgets/sequence.py,sha256=dVoHQmHloaRuU1Sd82b2jnO8WDfdwM2FaZlLCJCps1o,2566
43
- fastlife/adapters/jinjax/widgets/text.py,sha256=TfmlJU233aZWIl-4cmm-f-pFxp6ycHWHnbiluOvRDgM,3040
44
- fastlife/adapters/jinjax/widgets/union.py,sha256=roCoFA82dLjF1XFW6UYaV7SCQWdFsSAT8Ux7KEB6_Us,2602
40
+ fastlife/adapters/jinjax/widgets/mfa_code.py,sha256=dI0CKlx2jCwujfnud_uIjnBCHvlG_gtapL1Iuan5wVg,744
41
+ fastlife/adapters/jinjax/widgets/model.py,sha256=STojkUMfCP1kyPFzzWS-MIZOoxAEFHTIW_jxFnODPb4,1309
42
+ fastlife/adapters/jinjax/widgets/sequence.py,sha256=aL93-ytj6nlbT8vumt3br6Jq8D97iiCXU3eQXrGYuG0,2577
43
+ fastlife/adapters/jinjax/widgets/text.py,sha256=XKpoLoBNsvQhHrezKCEcXlF9iQzuclXGaeFu6uq9_5A,3390
44
+ fastlife/adapters/jinjax/widgets/union.py,sha256=fNWmAXNEPkDbP14q6HmlD65L7q5_JmspZ_IG_SqB_NM,2624
45
45
  fastlife/assets/dist.css,sha256=d2ez-igscOaeYtJQc2FQuXlN17cYX13sUansFdg_kdA,19753
46
46
  fastlife/assets/source.css,sha256=0KtDcsKHj9LOcqNR1iv9pACwNBaNWkieEDqqjkgNL_s,47
47
47
  fastlife/components/A.jinja,sha256=MDNJ2auIeYbpNeErvJdlGid4nIKfbi85ArmMgChsCJU,1384
@@ -57,8 +57,8 @@ fastlife/components/H4.jinja,sha256=w-n0bFqR_38oIuju_Bs_8OwYCtLP0gIfSsoi5u3U8GM,
57
57
  fastlife/components/H5.jinja,sha256=bpphjO54yrKLKt64voR5wCvxwFpxQRfkZg_OqhCPAcA,370
58
58
  fastlife/components/H6.jinja,sha256=9qzd6LpaLk5oTLdKw3utSVyX-uq7fn837mm22zG23Ko,370
59
59
  fastlife/components/Hidden.jinja,sha256=-D74wZ7qp2n5l_8HKmDhX5v_M2sAJ5l-w_z9m5d5KvA,283
60
- fastlife/components/Input.jinja,sha256=bg2YCVGQsXwNF5zPauNg0VYbhf3qzEW12t6Xz7bWYVI,1950
61
- fastlife/components/Label.jinja,sha256=t50MUGyNSFB4LK-J0D0fM2xMT_vCsG3fnO0OapvzPJQ,532
60
+ fastlife/components/Input.jinja,sha256=Orp_gTo40KfICLZECc8oLWnaBFixYzmzLRVzoAUe8hc,1972
61
+ fastlife/components/Label.jinja,sha256=5cYezFHNh5Nuytxbgo60fCeCeiiGS-Cs_fVToj7znx0,533
62
62
  fastlife/components/Option.jinja,sha256=x6t7uUQsI1YSRstD4bSVlgK2wm8NJUXnzOWfNWGlk_Y,448
63
63
  fastlife/components/P.jinja,sha256=Jumlwu9Wix8E2K7QwwimgWTrMdrFDAEfdLHlkz_Mp-g,371
64
64
  fastlife/components/Password.jinja,sha256=dSjPzzgBJM1K1hg_9UURPLpvUcwnna8hf6lH0nsYEps,1903
@@ -1690,7 +1690,7 @@ fastlife/components/pydantic_form/FatalError.jinja,sha256=ADtQvmo-e-NmDcFM1E6wZV
1690
1690
  fastlife/components/pydantic_form/Hint.jinja,sha256=8leBpfMGDmalc_KAjr2paTojr_rwq-luS6m_1BGj7Tw,202
1691
1691
  fastlife/components/pydantic_form/Widget.jinja,sha256=PgguUpvhG6CY9AW6H8qQMjKqjlybjDCAaFFAOHzrzVQ,418
1692
1692
  fastlife/config/__init__.py,sha256=5qpuaVYqi-AS0GgsfggM6rFsSwXgrqrLBo9jH6dVroc,407
1693
- fastlife/config/configurator.py,sha256=2LkPXoarMTk40S7AVlxODPAckDllE0G8u27jtPmvGM8,25188
1693
+ fastlife/config/configurator.py,sha256=82YEVRoI6VoyFPIbkWgnqlLIVNSVP9TUkbKl6qksWs8,24898
1694
1694
  fastlife/config/exceptions.py,sha256=9MdBnbfy-Aw-KaIFzju0Kh8Snk41-v9LqK2w48Tdy1s,1169
1695
1695
  fastlife/config/openapiextra.py,sha256=rYoerrn9sni2XwnO3gIWqaz7M0aDZPhVLjzqhDxue0o,514
1696
1696
  fastlife/config/resources.py,sha256=EcPTM25pnHcGFTtXjeZnWn5Mo_-8rhJ72HJ6rxnjPg8,8389
@@ -1700,7 +1700,7 @@ fastlife/domain/model/__init__.py,sha256=aoBjaSpDscuFXvtknJHwiNyoJRUpE-v4X54h_wN
1700
1700
  fastlife/domain/model/asgi.py,sha256=Cz45TZOtrh2pBVZr37aJ9jpnJH9BeNHrsvk9bq1nBc0,526
1701
1701
  fastlife/domain/model/csrf.py,sha256=BUiWK-S7rVciWHO1qTkM8e_KxzpF6gGC4MMJK1v6iDo,414
1702
1702
  fastlife/domain/model/form.py,sha256=JP6uumlZBYhiPxzcdxOsfsFm5BRfvkDFvlUCD6Vy8dI,3275
1703
- fastlife/domain/model/request.py,sha256=HgUSnUu3q18e07y57PadN3pPQwYrIZS1YEhYkBZ_Zfg,2674
1703
+ fastlife/domain/model/request.py,sha256=hHtGsfVND3TSG7HQZI_I0n4Gp4KyaAtsjaZEc4lwrv0,3090
1704
1704
  fastlife/domain/model/response.py,sha256=Vsd2zYGGhH0D2DlfiKz1CX9OJZ_ZYoEv_-foMZpDFZo,1294
1705
1705
  fastlife/domain/model/security_policy.py,sha256=f9SLi54vvRU-KSPJ5K0unoqYpkxIyzuZjKf2Ylwf5Rg,4796
1706
1706
  fastlife/domain/model/template.py,sha256=z9oxdKme1hMPuvk7mBiKR_tuVY8TqH77aTYqMgvEGl8,876
@@ -1721,7 +1721,7 @@ fastlife/service/registry.py,sha256=0r8dVCF44JUugRctL9sDQjnHDV7SepH06OfkV6KE-4s,
1721
1721
  fastlife/service/request_factory.py,sha256=9o4B_78qrKPXQAq8A_RDhzAqCHdt6arV96Bq_JByyIM,931
1722
1722
  fastlife/service/security_policy.py,sha256=qYXs4mhfz_u4x59NhUkirqKYKQbFv9YrzyRuXj7mxE0,4688
1723
1723
  fastlife/service/templates.py,sha256=xNMKH-jNkEoCscO04H-QlzTqg-0pYbF_fc65xG-2rzs,2575
1724
- fastlife/service/translations.py,sha256=cAfvUlLM3KcgQjlD9PtEpZpTMctXKM_CUAmUeKw9n4M,6901
1724
+ fastlife/service/translations.py,sha256=UrkITvfdfw68GzWn_uFlCjjhRvwhc0aCJPnEr_Y1rK8,7151
1725
1725
  fastlife/settings.py,sha256=q-rz4CEF2RQGow5-m-yZJOvdh3PPb2c1Q_ZLJGnu4VQ,3647
1726
1726
  fastlife/shared_utils/__init__.py,sha256=i66ytuf-Ezo7jSiNQHIsBMVIcB-tDX0tg28-pUOlhzE,26
1727
1727
  fastlife/shared_utils/infer.py,sha256=0GflLkaWJ-4LZ1Ig3moR-_o55wwJ_p_vJ4xo-yi3lyA,1406
@@ -1733,9 +1733,9 @@ fastlife/testing/form.py,sha256=diiGfVMfNt19JTNUxlnbGfcbskR3ZMpk0Y-A57vfShc,7871
1733
1733
  fastlife/testing/session.py,sha256=LEFFbiR67_x_g-ioudkY0C7PycHdbDfaIaoo_G7GXQ8,2226
1734
1734
  fastlife/testing/testclient.py,sha256=gqgHQalhrLLZ8eveN2HeuoG9ne8CwxCm-Ll4b7jo9Xo,7249
1735
1735
  fastlife/views/__init__.py,sha256=zG8gveL8e2zBdYx6_9jtZfpQ6qJT-MFnBY3xXkLwHZI,22
1736
- fastlife/views/pydantic_form.py,sha256=o7EUItciAGL1OSaGNHo-3BTrYAk34GuWE7zGikjiAGY,1486
1737
- fastlifeweb-0.26.2.dist-info/METADATA,sha256=btOasL9jb9IiIrj9yPnrSW60e3T8Xpxy2Abu0hBBcb8,3690
1738
- fastlifeweb-0.26.2.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
1739
- fastlifeweb-0.26.2.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
1740
- fastlifeweb-0.26.2.dist-info/licenses/LICENSE,sha256=JFWuiKYRXKKMEAsX0aZp3hBcju-HYflJ2rwJAGwbCJo,1080
1741
- fastlifeweb-0.26.2.dist-info/RECORD,,
1736
+ fastlife/views/pydantic_form.py,sha256=M4uGP-QiDuSyrkYAsvSVJYZzdBUPOmCghQdwtR28K5E,1630
1737
+ fastlifeweb-0.27.0.dist-info/METADATA,sha256=5cvmsfd1YF7lhRZtH53Ip9Qxd7EwE54AQK4pFV2IChI,3690
1738
+ fastlifeweb-0.27.0.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
1739
+ fastlifeweb-0.27.0.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
1740
+ fastlifeweb-0.27.0.dist-info/licenses/LICENSE,sha256=JFWuiKYRXKKMEAsX0aZp3hBcju-HYflJ2rwJAGwbCJo,1080
1741
+ fastlifeweb-0.27.0.dist-info/RECORD,,