fastlifeweb 0.3.0__py3-none-any.whl → 0.4.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.
Files changed (47) hide show
  1. fastlife/configurator/registry.py +2 -2
  2. fastlife/configurator/settings.py +3 -0
  3. fastlife/templates/A.jinja +30 -4
  4. fastlife/templates/Button.jinja +31 -17
  5. fastlife/templates/Checkbox.jinja +20 -2
  6. fastlife/templates/CsrfToken.jinja +0 -1
  7. fastlife/templates/Form.jinja +9 -3
  8. fastlife/templates/H1.jinja +15 -4
  9. fastlife/templates/H2.jinja +15 -4
  10. fastlife/templates/H3.jinja +14 -0
  11. fastlife/templates/H4.jinja +14 -0
  12. fastlife/templates/H5.jinja +14 -0
  13. fastlife/templates/H6.jinja +14 -0
  14. fastlife/templates/Input.jinja +29 -6
  15. fastlife/templates/Label.jinja +10 -2
  16. fastlife/templates/Option.jinja +4 -2
  17. fastlife/templates/P.jinja +9 -0
  18. fastlife/templates/Radio.jinja +29 -7
  19. fastlife/templates/Select.jinja +25 -6
  20. fastlife/templates/pydantic_form/Boolean.jinja +1 -1
  21. fastlife/templates/pydantic_form/Dropdown.jinja +4 -6
  22. fastlife/templates/pydantic_form/Hint.jinja +4 -0
  23. fastlife/templates/pydantic_form/Model.jinja +1 -1
  24. fastlife/templates/pydantic_form/Sequence.jinja +3 -3
  25. fastlife/templates/pydantic_form/Text.jinja +1 -3
  26. fastlife/templates/pydantic_form/Union.jinja +5 -5
  27. fastlife/templating/__init__.py +2 -2
  28. fastlife/templating/binding.py +4 -22
  29. fastlife/templating/renderer/__init__.py +5 -2
  30. fastlife/templating/renderer/abstract.py +7 -5
  31. fastlife/templating/renderer/jinjax.py +51 -23
  32. fastlife/templating/renderer/widgets/base.py +12 -2
  33. fastlife/templating/renderer/widgets/boolean.py +4 -3
  34. fastlife/templating/renderer/widgets/dropdown.py +7 -6
  35. fastlife/templating/renderer/widgets/factory.py +38 -21
  36. fastlife/templating/renderer/widgets/hidden.py +2 -3
  37. fastlife/templating/renderer/widgets/model.py +7 -6
  38. fastlife/templating/renderer/widgets/sequence.py +8 -9
  39. fastlife/templating/renderer/widgets/text.py +9 -5
  40. fastlife/templating/renderer/widgets/union.py +7 -6
  41. fastlife/testing/testclient.py +116 -55
  42. fastlife/views/pydantic_form.py +5 -2
  43. {fastlifeweb-0.3.0.dist-info → fastlifeweb-0.4.0.dist-info}/METADATA +6 -5
  44. fastlifeweb-0.4.0.dist-info/RECORD +69 -0
  45. fastlifeweb-0.3.0.dist-info/RECORD +0 -63
  46. {fastlifeweb-0.3.0.dist-info → fastlifeweb-0.4.0.dist-info}/LICENSE +0 -0
  47. {fastlifeweb-0.3.0.dist-info → fastlifeweb-0.4.0.dist-info}/WHEEL +0 -0
@@ -1,18 +1,23 @@
1
1
  import re
2
2
  import time
3
3
  from collections.abc import MutableMapping
4
+ from http.cookiejar import Cookie
4
5
  from typing import Any, Iterator, Literal, Mapping, Optional, Sequence
5
6
  from urllib.parse import urlencode
6
7
 
7
8
  import bs4
8
9
  import httpx
9
10
  from fastapi.testclient import TestClient
11
+ from multidict import MultiDict
10
12
  from starlette.types import ASGIApp
11
13
 
12
14
  from fastlife.configurator.settings import Settings
13
15
  from fastlife.session.serializer import AbsractSessionSerializer
14
16
  from fastlife.shared_utils.resolver import resolve
15
17
 
18
+ CookieTypes = httpx._types.CookieTypes # type: ignore
19
+ Cookies = httpx._models.Cookies # type: ignore
20
+
16
21
 
17
22
  class Element:
18
23
  def __init__(self, client: "WebTestClient", tag: bs4.Tag):
@@ -34,10 +39,6 @@ class Element:
34
39
  def text(self) -> str:
35
40
  return self._tag.text.strip()
36
41
 
37
- @property
38
- def form(self) -> "Element | None":
39
- return Element(self._client, self._tag.form) if self._tag.form else None
40
-
41
42
  @property
42
43
  def h1(self) -> "Element":
43
44
  nodes = self.by_node_name("h1")
@@ -48,6 +49,10 @@ class Element:
48
49
  def h2(self) -> Sequence["Element"]:
49
50
  return self.by_node_name("h2")
50
51
 
52
+ @property
53
+ def form(self) -> "Element | None":
54
+ return Element(self._client, self._tag.form) if self._tag.form else None
55
+
51
56
  @property
52
57
  def hx_target(self) -> Optional[str]:
53
58
  el: bs4.Tag | None = self._tag
@@ -115,57 +120,130 @@ class WebForm:
115
120
  self._form = form
116
121
  self._origin = origin
117
122
  self._formfields: dict[str, Element] = {}
118
- self._formdata: dict[str, str] = {}
123
+ self._formdata: MultiDict[str] = MultiDict()
119
124
  inputs = self._form.by_node_name("input")
120
125
  for input in inputs:
121
126
  self._formfields[input.attrs["name"]] = input
122
127
  if input.attrs.get("type") == "checkbox" and "checked" not in input.attrs:
123
128
  continue
124
- self._formdata[input.attrs["name"]] = input.attrs.get("value", "")
125
-
126
- inputs = self._form.by_node_name("select")
127
- for input in inputs:
128
- self._formfields[input.attrs["name"]] = input
129
- for option in input.by_node_name("options"):
130
- if "selected" in option.attrs:
131
- self._formdata[input.attrs["name"]] = option.attrs.get(
132
- "value", option.text
129
+ self._formdata.add(input.attrs["name"], input.attrs.get("value", ""))
130
+
131
+ selects = self._form.by_node_name("select")
132
+ for select in selects:
133
+ fieldname = select.attrs["name"]
134
+ self._formfields[fieldname] = select
135
+ options = select.by_node_name("option")
136
+ if "multiple" in select.attrs:
137
+ for option in options:
138
+ if "selected" in option.attrs:
139
+ self._formdata.add(
140
+ fieldname, option.attrs.get("value", option.text)
141
+ )
142
+ else:
143
+ if options:
144
+ self._formdata[fieldname] = options[0].attrs.get(
145
+ "value", options[0].text
133
146
  )
147
+ for option in options:
148
+ if "selected" in option.attrs:
149
+ self._formdata[fieldname] = option.attrs.get(
150
+ "value", option.text
151
+ )
152
+ break
153
+
134
154
  # field textearea...
135
155
 
136
156
  def set(self, fieldname: str, value: str) -> Any:
137
157
  if fieldname not in self._formfields:
138
- raise ValueError(f"{fieldname} does not exists")
158
+ raise ValueError(f'"{fieldname}" does not exists')
139
159
  if self._formfields[fieldname].node_name == "select":
140
- raise RuntimeError(f"{fieldname} is a <select>, use select() instead")
160
+ raise ValueError(f'"{fieldname}" is a <select>, use select() instead')
161
+
162
+ if self._formfields[fieldname].attrs.get("type") == "checkbox":
163
+ self._formdata.add(fieldname, value)
164
+ return
165
+
166
+ if self._formfields[fieldname].attrs.get("type") == "radio":
167
+ radio = self._form.by_node_name(
168
+ "input", attrs={"type": "radio", "value": value}
169
+ )
170
+ if not radio:
171
+ raise ValueError(
172
+ f'radio "{fieldname}" does not contains {value} option'
173
+ )
174
+
141
175
  self._formdata[fieldname] = value
142
176
 
177
+ def unset(self, fieldname: str, value: str) -> Any:
178
+ if fieldname not in self._formfields:
179
+ raise ValueError(f'"{fieldname}" does not exists')
180
+ if self._formfields[fieldname].node_name != "input":
181
+ raise ValueError(f'"{fieldname}" is not a checkbox')
182
+ if self._formfields[fieldname].attrs.get("type") != "checkbox":
183
+ raise ValueError(f'"{fieldname}" is not a checkbox')
184
+ values = self._formdata.popall(fieldname)
185
+ if value not in values:
186
+ raise ValueError(f'"{value}" not in "{fieldname}"')
187
+ for val in values:
188
+ if val != value:
189
+ self._formdata[fieldname] = val
190
+
143
191
  def select(self, fieldname: str, value: str) -> Any:
144
192
  if fieldname not in self._formfields:
145
- raise ValueError(f"{fieldname} does not exists")
146
- if self._formfields[fieldname].node_name != "select":
147
- raise RuntimeError(
148
- f"{fieldname} is a <{self._formfields[fieldname]}>, use set() instead"
193
+ raise ValueError(f'"{fieldname}" does not exists')
194
+ field = self._formfields[fieldname]
195
+ if field.node_name != "select":
196
+ raise ValueError(f"{fieldname} is a {repr(field)}, " "use set() instead")
197
+
198
+ for option in field.by_node_name("option"):
199
+ if option.text == value.strip():
200
+ if "multiple" in field.attrs:
201
+ self._formdata.add(fieldname, value)
202
+ else:
203
+ self._formdata[fieldname] = option.attrs.get("value", option.text)
204
+ break
205
+ else:
206
+ raise ValueError(f'No option {value} in <select name="{fieldname}">')
207
+
208
+ def unselect(self, fieldname: str, value: str) -> Any:
209
+ if fieldname not in self._formfields:
210
+ raise ValueError(f'"{fieldname}" does not exists')
211
+ field = self._formfields[fieldname]
212
+
213
+ if field.node_name != "select":
214
+ raise ValueError(
215
+ f"{fieldname} is a {repr(self._formfields[fieldname])}, "
216
+ "use unset() for checkbox instead"
149
217
  )
150
- if "multiple" in self._formfields[fieldname].attrs:
151
- raise NotImplementedError
218
+ if "multiple" not in field.attrs:
219
+ raise ValueError("only <select multiple> support unselect")
220
+
152
221
  for option in self._formfields[fieldname].by_node_name("option"):
153
222
  if option.text == value.strip():
154
- self._formdata[fieldname] = option.attrs.get("value", option.text)
223
+ values = self._formdata.popall(fieldname)
224
+ if value not in values:
225
+ raise ValueError(f'"{value}" not selected in "{fieldname}"')
226
+ for val in values:
227
+ if val != value:
228
+ self._formdata[fieldname] = val
155
229
  break
156
230
  else:
157
231
  raise ValueError(f'No option {value} in <select name="{fieldname}">')
158
232
 
159
233
  def button(self, text: str, position: int = 0) -> "WebForm":
160
234
  buttons = self._form.get_all_by_text(text, node_name="button")
161
- assert len(buttons) > position, f'Button "{text}" not found'
235
+ if position >= len(buttons):
236
+ pos = ""
237
+ if position > 0:
238
+ pos = f" at position {position}"
239
+ raise ValueError(f'Button "{text}" not found{pos}')
162
240
  button = buttons[position]
163
241
  if "name" in button.attrs:
164
242
  self._formdata[button.attrs["name"]] = button.attrs.get("value", "")
165
243
  return self
166
244
 
167
245
  def submit(self, follow_redirects: bool = True) -> "WebResponse":
168
- headers = {}
246
+ headers: dict[str, str] = {}
169
247
  target = (
170
248
  self._form.attrs.get("hx-post")
171
249
  or self._form.attrs.get("post")
@@ -199,12 +277,12 @@ class WebResponse:
199
277
  return self._response.status_code
200
278
 
201
279
  @property
202
- def is_redirect(self) -> int:
280
+ def is_redirect(self) -> bool:
203
281
  return 300 <= self._response.status_code < 400
204
282
 
205
283
  @property
206
284
  def content_type(self) -> str:
207
- return self._response.headers["content-type"]
285
+ return self._response.headers.get("content-type", "").split(";").pop(0)
208
286
 
209
287
  @property
210
288
  def headers(self) -> httpx.Headers:
@@ -246,28 +324,17 @@ class WebResponse:
246
324
  return self.html.by_node_name(node_name, attrs=attrs)
247
325
 
248
326
 
249
- CookieTypes = httpx._types.CookieTypes # type: ignore
250
- Cookies = httpx._models.Cookies # type: ignore
251
-
252
-
253
327
  class Session(dict[str, Any]):
254
328
  def __init__(self, client: "WebTestClient"):
255
329
  self.client = client
256
- if client.session_serializer is None:
257
- raise RuntimeError(
258
- "WebTestClient has not been initialize with the app settings, "
259
- "can't decode session"
260
- )
261
-
262
330
  self.srlz = client.session_serializer
263
- settings = self.client.settings
264
- assert settings is not None
265
- self.settings = settings
331
+ self.settings = self.client.settings
266
332
  data: Mapping[str, Any]
267
- self.has_session = settings.session_cookie_name in self.client.cookies
333
+ cookie_name = self.settings.session_cookie_name
334
+ self.has_session = cookie_name in self.client.cookies
268
335
  if self.has_session:
269
336
  data, error = self.srlz.deserialize(
270
- self.client.cookies[settings.session_cookie_name].encode("utf-8")
337
+ self.client.cookies[cookie_name].encode("utf-8")
271
338
  )
272
339
  if error:
273
340
  self.has_session = False
@@ -279,8 +346,6 @@ class Session(dict[str, Any]):
279
346
  super().__setitem__(__key, __value)
280
347
  settings = self.settings
281
348
  data = self.serialize()
282
- from http.cookiejar import Cookie
283
-
284
349
  self.client.cookies.jar.set_cookie(
285
350
  Cookie(
286
351
  version=0,
@@ -330,21 +395,17 @@ class WebTestClient:
330
395
  app, base_url=f"http://{settings.domain_name}", cookies=cookies or {}
331
396
  )
332
397
  self.settings = settings
333
- self.session_serializer: AbsractSessionSerializer | None = None
334
- if settings:
335
- self.session_serializer = resolve(settings.session_serializer)(
336
- settings.session_secret_key,
337
- int(settings.session_duration.total_seconds()),
338
- )
398
+ self.session_serializer: AbsractSessionSerializer = resolve(
399
+ settings.session_serializer
400
+ )(
401
+ settings.session_secret_key,
402
+ int(settings.session_duration.total_seconds()),
403
+ )
339
404
 
340
405
  @property
341
406
  def cookies(self) -> Cookies:
342
407
  return self.testclient.cookies
343
408
 
344
- @cookies.setter
345
- def cookies(self, value: Cookies) -> None:
346
- self.testclient.cookies = value
347
-
348
409
  @property
349
410
  def session(self) -> MutableMapping[str, Any]:
350
411
  return Session(self)
@@ -403,7 +464,7 @@ class WebTestClient:
403
464
  def post(
404
465
  self,
405
466
  url: str,
406
- data: Mapping[str, Any],
467
+ data: MultiDict[str],
407
468
  *,
408
469
  headers: Mapping[str, Any] | None = None,
409
470
  follow_redirects: bool = True,
@@ -1,6 +1,6 @@
1
1
  from typing import Optional
2
2
 
3
- from fastapi import Query, Response
3
+ from fastapi import Query, Request, Response
4
4
  from pydantic.fields import FieldInfo
5
5
 
6
6
  from fastlife import Configurator, configure
@@ -11,6 +11,7 @@ from fastlife.shared_utils.resolver import resolve_extended
11
11
  async def show_widget(
12
12
  typ: str,
13
13
  reg: Registry,
14
+ request: Request,
14
15
  title: Optional[str] = Query(None),
15
16
  name: Optional[str] = Query(None),
16
17
  token: Optional[str] = Query(None),
@@ -20,7 +21,9 @@ async def show_widget(
20
21
  field = None
21
22
  if title:
22
23
  field = FieldInfo(title=title)
23
- data = reg.renderer.pydantic_form(model_cls, None, name, token, removable, field)
24
+ data = reg.renderer(request).pydantic_form(
25
+ model_cls, None, name, token, removable, field
26
+ )
24
27
  return Response(data, headers={"Content-Type": "text/html"})
25
28
 
26
29
 
@@ -1,7 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastlifeweb
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: High-level web framework
5
+ Home-page: https://github.com/mardiros/fastlife
5
6
  License: BSD-derived
6
7
  Author: Guillaume Gauvrit
7
8
  Author-email: guillaume@gauvr.it
@@ -18,14 +19,16 @@ Classifier: Topic :: Internet :: WWW/HTTP
18
19
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
20
  Requires-Dist: beautifulsoup4[testing] (>=4.12.2,<5.0.0)
20
21
  Requires-Dist: behave (>=1.2.6,<2.0.0)
21
- Requires-Dist: fastapi (>=0.108.0,<0.109.0)
22
+ Requires-Dist: fastapi (>=0.110.0,<0.111.0)
22
23
  Requires-Dist: itsdangerous (>=2.1.2,<3.0.0)
23
- Requires-Dist: jinjax (>=0.31,<0.32)
24
+ Requires-Dist: jinjax (>=0.32,<0.33)
24
25
  Requires-Dist: markupsafe (>=2.1.3,<3.0.0)
26
+ Requires-Dist: multidict (>=6.0.5,<7.0.0)
25
27
  Requires-Dist: pydantic (>=2.3.0,<3.0.0)
26
28
  Requires-Dist: pydantic-settings (>=2.0.3,<3.0.0)
27
29
  Requires-Dist: python-multipart (>=0.0.6,<0.0.7)
28
30
  Requires-Dist: venusian (>=3.0.0,<4.0.0)
31
+ Project-URL: Repository, https://github.com/mardiros/fastlife
29
32
  Description-Content-Type: text/markdown
30
33
 
31
34
  # Fastlife
@@ -44,5 +47,3 @@ Under heavy development.
44
47
 
45
48
  The package is available on pypi with the name fastlifeweb.
46
49
 
47
- The name fastlife is squatted and I am tired of PEP 541 requests.
48
-
@@ -0,0 +1,69 @@
1
+ fastlife/__init__.py,sha256=ewfV5kR3Pq7Bzan_Wnq__LYGzXgk2CJaamxhyMyhaCI,312
2
+ fastlife/configurator/__init__.py,sha256=2EPjM1o5iHJIViPwgJjaPQS3pMhE-9dik_mm53eX2DY,91
3
+ fastlife/configurator/base.py,sha256=2ahvTudLmD99YQjnIeGN5JDPCSl3k-mauu7bsSEB5RE,216
4
+ fastlife/configurator/configurator.py,sha256=6BaB7SR24Q4Qvs8NrCpatRkkZiPXf9mKLID6RxOKxDg,5740
5
+ fastlife/configurator/registry.py,sha256=1sOicKvwIvLbrzRk9z8yb65YUXxxagJK9AK-2gGDpOQ,1463
6
+ fastlife/configurator/settings.py,sha256=ftv5MkNXeyBrvcqxnt2WKtLuzo7ge2_BNx1gX4CcSOE,1489
7
+ fastlife/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ fastlife/request/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ fastlife/request/form_data.py,sha256=gKXo92ZwsMX6UpMkf4BlNYDiOfwxyzDzItc_lpplAzY,3642
10
+ fastlife/security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ fastlife/security/csrf.py,sha256=47epJVJtr5X6j-Hog54WCGOoiRLQQHvgBU71iqR1N0A,1025
12
+ fastlife/security/policy.py,sha256=5jV5nypy5O8XPFFRJ_bzG8Ltk5xcPWclkz23qiG1_I8,509
13
+ fastlife/session/__init__.py,sha256=OnzRCYRzc1lw9JB0UdKi-aRLPNT2n8mM8kwY1P4w7uU,838
14
+ fastlife/session/middleware.py,sha256=JgXdBlxlm9zIEgXcidbBrMAp5wJVPsZWtvCLVDk5h2s,3049
15
+ fastlife/session/serializer.py,sha256=qpVnHQjYTxw3aOnoEOKIjOFJg2z45KjiX5sipWk2gws,1458
16
+ fastlife/shared_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ fastlife/shared_utils/infer.py,sha256=_hmGzu84VlZAkdw_owkW8eHknULqH3MLDBlXj7LkEsc,466
18
+ fastlife/shared_utils/resolver.py,sha256=wXQQTB4jf86m4qENhMOkHkWpLJj_T4-_eND_ItTLnTE,1410
19
+ fastlife/templates/A.jinja,sha256=q71nu4Rq874LG6SykCKv8W-EZeX13NMF0AsLc9FFAP0,677
20
+ fastlife/templates/Button.jinja,sha256=535UIK4Prunj4f0YZXBmCI0rfOiTr5GJTQkM0XDmtNA,1137
21
+ fastlife/templates/Checkbox.jinja,sha256=EbKJ9KmfexegcocxeMtoG1kt-x4bP8Tq3odh8prbJoQ,366
22
+ fastlife/templates/CsrfToken.jinja,sha256=X2-H0VmukyUV448blh9M8rhr_gN82ar51TnRAwfV3OY,59
23
+ fastlife/templates/Form.jinja,sha256=cfXWtFWCcnxdIOayR3_XWoY1PpQATiyQDqkY0Vg91g0,329
24
+ fastlife/templates/H1.jinja,sha256=fetjIhZvgw5gm6xJhl_-cnHvrPwuLYE4S1rVYgbqNl8,196
25
+ fastlife/templates/H2.jinja,sha256=z022lvv3PO_g-ZxWuAiKmBOaFZ3u8Kbzd0jkuWNsOS0,196
26
+ fastlife/templates/H3.jinja,sha256=BmSznvHOCAT8569hf-lOh0qlK3yHAPdO3KCOLD7BaPE,195
27
+ fastlife/templates/H4.jinja,sha256=5H31nKNB-_QgYs-aupTVx-hamcHhWTNns8iXW4IWqqE,195
28
+ fastlife/templates/H5.jinja,sha256=bmqrG0I9VOTbK9iq0jQhAuoYuy_vlPiRqnyA1TGvuyo,193
29
+ fastlife/templates/H6.jinja,sha256=PI1jAlgmEuEoAeoSy0bYCs0LT-8j_XTqauMTOWRGlgU,191
30
+ fastlife/templates/Hidden.jinja,sha256=nOGfkWwhXckJE2O4wL5IXNKB5dvU0HbmdWCTECxEFHs,120
31
+ fastlife/templates/Input.jinja,sha256=YlImqjxjCJjjHAekYmUDUyHloNWEVlddeF0Ea7A_C6w,638
32
+ fastlife/templates/Label.jinja,sha256=m71vGDUi2HzzA5d9L3TQhdEXYKHcp6LXP6PE0-bx44w,170
33
+ fastlife/templates/Option.jinja,sha256=YlgBayJj94kSXK0mlGkMxTmHjTBr8T-UzvQmTcANK0Q,121
34
+ fastlife/templates/P.jinja,sha256=xEcHIv9dJRpELu_SdqQcftvKrU8z1i_BHTEVO5Mu5dU,164
35
+ fastlife/templates/Radio.jinja,sha256=51I5n1yjWQ_uLZfzuUUf3C8ngo-KT4dPw29-pjP9uSU,723
36
+ fastlife/templates/Select.jinja,sha256=6Rw9A51JbSTprgNLz8A8E_UKfsLFf26eqdhwLnaMms4,427
37
+ fastlife/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
+ fastlife/templates/pydantic_form/Boolean.jinja,sha256=XE3NM4vhwQwRUIbjv10t9dI70MzWhicyQ_l6FAOs8-k,298
39
+ fastlife/templates/pydantic_form/Dropdown.jinja,sha256=JBkQp7LYtz6vvW6MyAXyW-6UDZ_xY0Fgd4mpDCQA-wM,500
40
+ fastlife/templates/pydantic_form/Hidden.jinja,sha256=n6CbTSwZr2E_oY8TO2WPbnrLHBaWfe_CXVCYOYmCfts,83
41
+ fastlife/templates/pydantic_form/Hint.jinja,sha256=O0ZsAQnATcG0a_qLQfrwM6VZHmAw3k1W33WYlEBUas8,123
42
+ fastlife/templates/pydantic_form/Model.jinja,sha256=wxEKEO8Kktxkrz-LZ9m_v_NPjKV-C8RFQ9wyEyrRKsI,449
43
+ fastlife/templates/pydantic_form/Sequence.jinja,sha256=eA8GYhJC1KokWbU3zqmqCIuG8mR-yhvlEAzt0ds2RTM,1380
44
+ fastlife/templates/pydantic_form/Text.jinja,sha256=t8fZTGm7EOrc8JsVlnqRAkAfQfKDDZQk4xXlLAgXCAQ,402
45
+ fastlife/templates/pydantic_form/Union.jinja,sha256=Ss4dwNoHqmdAa4O7cvS2vwuxBPspQINBxq4hnkm_GVY,950
46
+ fastlife/templates/pydantic_form/Widget.jinja,sha256=8raoMjtO4ARBfbz8EG-HKT112KkrWG82BbUfbXpAmZs,287
47
+ fastlife/templating/__init__.py,sha256=QdrTmxt-K7UcJ_o9LY1n-QZAvk5foct5MNFt02zXr2w,234
48
+ fastlife/templating/binding.py,sha256=n-MDg98Bne49BmWWWCopZsI6ef7h6PIdcszM_pRutYA,1378
49
+ fastlife/templating/renderer/__init__.py,sha256=UJUX3T9VYjPQUhS5Enz3P6OtwftimKoGGpoQEpewVFA,181
50
+ fastlife/templating/renderer/abstract.py,sha256=eS4ArmR2hc5-svrzDbRz7eeXpUbXf7wklPZT6VDsgVM,818
51
+ fastlife/templating/renderer/jinjax.py,sha256=y-CN5qvAj86zKQIj6nUtNw_WyGqH5b9Je9Ro3R9ZT1I,3206
52
+ fastlife/templating/renderer/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
+ fastlife/templating/renderer/widgets/base.py,sha256=_MMtrhYvtQgX1EQNku0NSBYwhCDvQ5y8TjnCC2b4CAw,2769
54
+ fastlife/templating/renderer/widgets/boolean.py,sha256=bk9bExUDp73is6tvbpC28ploIVjyMecFsxPN8NxnOd8,468
55
+ fastlife/templating/renderer/widgets/dropdown.py,sha256=o3BfD9gDiRPsCPB93xCZSYRo50LRAb_tZOmK4x7zkzY,901
56
+ fastlife/templating/renderer/widgets/factory.py,sha256=uEYaxHYygLQ-5eWWjsOlvPM6GmhHW7Nu_w-1l2HRihA,10335
57
+ fastlife/templating/renderer/widgets/hidden.py,sha256=2fsbTQKsACV0JVYpCjXaQAV7VnQTIBPCi4lJPdWCRHc,308
58
+ fastlife/templating/renderer/widgets/model.py,sha256=obZNgUiTWsMf24ge-KPSay7S3rkFn3kdQlEBbR-r-14,950
59
+ fastlife/templating/renderer/widgets/sequence.py,sha256=x6o1L_hOh3WRlW16ZQaeO6H_qtnIitALuK-w7ck0RAw,1347
60
+ fastlife/templating/renderer/widgets/text.py,sha256=mlW8LrRmj-aEMp8QzZOURhns6mfbV7jsOMQrsYZcU3Q,819
61
+ fastlife/templating/renderer/widgets/union.py,sha256=GntHssMjcgpD6S5h0JVr9_aWy3pSbMhYZ9QU-ugBH-Y,1689
62
+ fastlife/testing/__init__.py,sha256=KgTlRI0g8z7HRpL7mD5QgI__LT9Y4QDSzKMlxJG3wNk,67
63
+ fastlife/testing/testclient.py,sha256=JhWgruKDeIn0QFmEAXKhUXXsZpTpPi_JWIHhWbWIXg0,16404
64
+ fastlife/views/__init__.py,sha256=nn4B_8YTbTmhGPvSd20yyKK_9Dh1Pfh_Iq7z6iK8-CE,154
65
+ fastlife/views/pydantic_form.py,sha256=TyZbVfhOGy3en1a7o4wcRp11War7sqVHxyEjOsRMht0,1092
66
+ fastlifeweb-0.4.0.dist-info/LICENSE,sha256=F75xSseSKMwqzFj8rswYU6NWS3VoWOc_gY3fJYf9_LI,1504
67
+ fastlifeweb-0.4.0.dist-info/METADATA,sha256=w-g1uz8vo-bHjLyfqKzeQI6tZL2pJEJPaXSMp4X4-RY,1833
68
+ fastlifeweb-0.4.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
69
+ fastlifeweb-0.4.0.dist-info/RECORD,,
@@ -1,63 +0,0 @@
1
- fastlife/__init__.py,sha256=ewfV5kR3Pq7Bzan_Wnq__LYGzXgk2CJaamxhyMyhaCI,312
2
- fastlife/configurator/__init__.py,sha256=2EPjM1o5iHJIViPwgJjaPQS3pMhE-9dik_mm53eX2DY,91
3
- fastlife/configurator/base.py,sha256=2ahvTudLmD99YQjnIeGN5JDPCSl3k-mauu7bsSEB5RE,216
4
- fastlife/configurator/configurator.py,sha256=6BaB7SR24Q4Qvs8NrCpatRkkZiPXf9mKLID6RxOKxDg,5740
5
- fastlife/configurator/registry.py,sha256=VBwKWPZROzE0aQgQdqVjurLAwkh9VxpwvaKzain6Nxk,1449
6
- fastlife/configurator/settings.py,sha256=RrTE-T7CieXwg-WI5Q1eGZA8PbawJLmLxX7puX4dHbQ,1387
7
- fastlife/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- fastlife/request/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- fastlife/request/form_data.py,sha256=gKXo92ZwsMX6UpMkf4BlNYDiOfwxyzDzItc_lpplAzY,3642
10
- fastlife/security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- fastlife/security/csrf.py,sha256=47epJVJtr5X6j-Hog54WCGOoiRLQQHvgBU71iqR1N0A,1025
12
- fastlife/security/policy.py,sha256=5jV5nypy5O8XPFFRJ_bzG8Ltk5xcPWclkz23qiG1_I8,509
13
- fastlife/session/__init__.py,sha256=OnzRCYRzc1lw9JB0UdKi-aRLPNT2n8mM8kwY1P4w7uU,838
14
- fastlife/session/middleware.py,sha256=JgXdBlxlm9zIEgXcidbBrMAp5wJVPsZWtvCLVDk5h2s,3049
15
- fastlife/session/serializer.py,sha256=qpVnHQjYTxw3aOnoEOKIjOFJg2z45KjiX5sipWk2gws,1458
16
- fastlife/shared_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- fastlife/shared_utils/infer.py,sha256=_hmGzu84VlZAkdw_owkW8eHknULqH3MLDBlXj7LkEsc,466
18
- fastlife/shared_utils/resolver.py,sha256=wXQQTB4jf86m4qENhMOkHkWpLJj_T4-_eND_ItTLnTE,1410
19
- fastlife/templates/A.jinja,sha256=sQov2Hze4NfpmQ3FOYHeXugOb_SZw6ncvDZNyCxy0HI,481
20
- fastlife/templates/Button.jinja,sha256=KK3AztRIvBXZh2jUXMrBiz80XEt9s9STfBiVKR_1FvA,1064
21
- fastlife/templates/Checkbox.jinja,sha256=SZJYgijSr9rGBSLZC4DXyHpoFNLnC1QyvgpUUkbi9xE,98
22
- fastlife/templates/CsrfToken.jinja,sha256=CZrkuKJNIpcsZ3yBvQ4csUcBSHu2xJ13q11DI5VAgcI,80
23
- fastlife/templates/Form.jinja,sha256=Z-YrJioBlxSkh-xKDcXmBA7sV2vkzSzMqSjQFEe5AGU,147
24
- fastlife/templates/H1.jinja,sha256=jQb4mtR_kipxrwN7cH6Ha9otNP9r1SJhPJF6RiXu-Bw,151
25
- fastlife/templates/H2.jinja,sha256=WGyjUhuh2UL-iGGhHLwCeHTMGMEEewZ1683VZxIhMfY,151
26
- fastlife/templates/Hidden.jinja,sha256=nOGfkWwhXckJE2O4wL5IXNKB5dvU0HbmdWCTECxEFHs,120
27
- fastlife/templates/Input.jinja,sha256=2GQ-7a9woZaz3JRghe-E09p5sP7q1B3wU9ZWsPVee54,612
28
- fastlife/templates/Label.jinja,sha256=QVw9R9Wf5vV51dsdLaJUxv6hV2AiURdkGSRJ2-RgZ9Y,147
29
- fastlife/templates/Option.jinja,sha256=zxY0l5cBgyv37fEo5jldtYvQoQNNIvtzQV1MLu-4reU,65
30
- fastlife/templates/Radio.jinja,sha256=RNqN2RkekqOXIne9b1PhuOV_fZSCWD2Ws8fYeBvBucQ,654
31
- fastlife/templates/Select.jinja,sha256=DVheZlrxrcQGClyE5g44V9JInkPcV13v4cUGHNEbiWs,407
32
- fastlife/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
- fastlife/templates/pydantic_form/Boolean.jinja,sha256=OLgifXvyqDB2T3w8V6oNZCa1DO5G6WgNe-0jB8lQAE0,279
34
- fastlife/templates/pydantic_form/Dropdown.jinja,sha256=DKP_vcC89W4PLQL2QwzUiPiDx94gBre5fYejRn_qtGM,539
35
- fastlife/templates/pydantic_form/Hidden.jinja,sha256=n6CbTSwZr2E_oY8TO2WPbnrLHBaWfe_CXVCYOYmCfts,83
36
- fastlife/templates/pydantic_form/Model.jinja,sha256=tvaK6rnGzonBsRtyi-jxtxRVRpyC3tM0FgIS5ZLjIQo,493
37
- fastlife/templates/pydantic_form/Sequence.jinja,sha256=2HDlTmmsd87WEIn3WSYOrC4MePHAybgS_64j8cbJVrI,1409
38
- fastlife/templates/pydantic_form/Text.jinja,sha256=TrxKR-xLeBC8Bw1KyBj22YaJysLhdF-6N6LeT_oZous,503
39
- fastlife/templates/pydantic_form/Union.jinja,sha256=W5r7KHIL2A_2Vc8Z4xtwTQzeVBEkY1wVqzMMLvmvG60,1003
40
- fastlife/templates/pydantic_form/Widget.jinja,sha256=8raoMjtO4ARBfbz8EG-HKT112KkrWG82BbUfbXpAmZs,287
41
- fastlife/templating/__init__.py,sha256=RKqAsVyy0mS5qiujpmRbbe-FL1exf46Zo71_0QJwmrA,220
42
- fastlife/templating/binding.py,sha256=XlABwvd1UFt6ymbcqLOR_MXz6HlXkwpdlIiKhAgD5F0,1907
43
- fastlife/templating/renderer/__init__.py,sha256=ndygjyodU5F8EVzBLVvXq5fLpnPo5lTyk6yia79RHJY,156
44
- fastlife/templating/renderer/abstract.py,sha256=qVQ3d7gyesKx6Pz25XN0-NuI2088oKvkOUjGHiT1ALk,801
45
- fastlife/templating/renderer/jinjax.py,sha256=dfdaq3KuZXA1Jnmny_cIzcVzLXvr9ESqcgQIrLRlaQ0,2379
46
- fastlife/templating/renderer/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
- fastlife/templating/renderer/widgets/base.py,sha256=EnztAzopgEJMQMNf3xyOlf9P3i4pZzq15uSK5bf9wfg,2446
48
- fastlife/templating/renderer/widgets/boolean.py,sha256=RVp2jLeVR3f6JHl-9yBDZHcr-p2qufw0NVm12D_62aU,454
49
- fastlife/templating/renderer/widgets/dropdown.py,sha256=TuNxww-2yqTM3pdqG_gMYD3r9BWVxbhniFBx2l7Gfg8,891
50
- fastlife/templating/renderer/widgets/factory.py,sha256=oz_qKiMgTmPmE8mQSXY8aLgPc6u4tUArTJtJ2y66vbo,9626
51
- fastlife/templating/renderer/widgets/hidden.py,sha256=PP_mOR2cd9LQURHDrfJ3b85pR3F1SEzsv7n_UhUq-dA,317
52
- fastlife/templating/renderer/widgets/model.py,sha256=t0MatCCA6pUsilcQ3ZTyK9a2LtWTbFzdrBFyQ3sqv6k,943
53
- fastlife/templating/renderer/widgets/sequence.py,sha256=7nHl8je6oWfwn0jbHLau_S_vF-pbB_Alzjtwv5I4ejg,1355
54
- fastlife/templating/renderer/widgets/text.py,sha256=_bmr6FMMFW_oZZdZa_4TH3DfcBpAjOdzm2a5yoonW7o,782
55
- fastlife/templating/renderer/widgets/union.py,sha256=VKfjikIujNATxEslPTVydwzA8EOu_l_sPlOgK_cGcrc,1658
56
- fastlife/testing/__init__.py,sha256=KgTlRI0g8z7HRpL7mD5QgI__LT9Y4QDSzKMlxJG3wNk,67
57
- fastlife/testing/testclient.py,sha256=7aYCHSd0EjsmN4zMboDEh3-xcAZdw7LTvtovaDGZGrg,13780
58
- fastlife/views/__init__.py,sha256=nn4B_8YTbTmhGPvSd20yyKK_9Dh1Pfh_Iq7z6iK8-CE,154
59
- fastlife/views/pydantic_form.py,sha256=5UymAhbhxsNbs_kjSKI5DEk_jeiCAgtbJofYG_ukcSA,1038
60
- fastlifeweb-0.3.0.dist-info/LICENSE,sha256=F75xSseSKMwqzFj8rswYU6NWS3VoWOc_gY3fJYf9_LI,1504
61
- fastlifeweb-0.3.0.dist-info/METADATA,sha256=uPOHHVijDGXd5tDNy8EFo-ztIYMKYtXh7gVPv3ShTmQ,1748
62
- fastlifeweb-0.3.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
63
- fastlifeweb-0.3.0.dist-info/RECORD,,