fastlifeweb 0.4.0__py3-none-any.whl → 0.5.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.
@@ -25,8 +25,8 @@ def unflatten_struct(
25
25
  csrf_token_name: Optional[str] = None,
26
26
  ) -> Mapping[str, Any] | Sequence[Any]:
27
27
  # we sort to ensure that list index are ordered
28
- formkeys = sorted(flatten_input.keys())
29
- for key in formkeys:
28
+ # formkeys = sorted(flatten_input.keys())
29
+ for key in flatten_input:
30
30
  if csrf_token_name is not None and key == csrf_token_name:
31
31
  continue
32
32
  lkey, sep, rest = key.partition(".")
@@ -78,8 +78,17 @@ async def unflatten_mapping_form_data(
78
78
  request: Request, reg: Registry
79
79
  ) -> Mapping[str, Any]:
80
80
  form_data = await request.form()
81
+ form_data_decode_list: MutableMapping[str, Any] = {}
82
+ for key, val in form_data.multi_items():
83
+ if key in form_data_decode_list:
84
+ if not isinstance(form_data_decode_list, list):
85
+ form_data_decode_list[key] = [form_data_decode_list[key]]
86
+ form_data_decode_list[key].append(val)
87
+ else:
88
+ form_data_decode_list[key] = val
89
+
81
90
  ret = unflatten_struct(
82
- form_data, {}, csrf_token_name=reg.settings.csrf_token_name
91
+ form_data_decode_list, {}, csrf_token_name=reg.settings.csrf_token_name
83
92
  ) # type: ignore
84
93
  return ret # type: ignore
85
94
 
@@ -1,7 +1,8 @@
1
1
  {# def
2
2
  name,
3
- value,
4
3
  id,
4
+ value=None,
5
+ checked=False,
5
6
  class_="""
6
7
  bg-neutral-100
7
8
  border-neutral-300
@@ -17,4 +18,5 @@ focus:ring-2
17
18
  focus:ring-primary-500
18
19
  """
19
20
  #}
20
- <input name="{{name}}" {% if value%}checked{% endif %} type="checkbox" id="{{id}}" class="{{attrs.class or class_}}" />
21
+ <input name="{{name}}" {% if value is not none %}value="{{value}}" {% endif %} {% if checked %}checked{% endif %}
22
+ type="checkbox" id="{{id}}" class="{{attrs.class or class_}}" />
@@ -4,7 +4,7 @@ action=None,
4
4
  hx_post=None,
5
5
  class_="space-y-4 md:space-y-6"
6
6
  #}
7
- <form class="{{attrs.class or class_}}" {% if hx_post is not none %}hx-post="{{hx_post}}" {% endif %}{% if action is not
7
+ <form class="{{attrs.class or class_}}" {% if hx_post is not none %}hx-post="{% if hx_post is not true %}{{hx_post}}{% endif%}" {% endif %}{% if action is not
8
8
  none %}action="{{action}}" {% endif %}{% if method %}method="{{method}}" {% endif %}>
9
9
  <CsrfToken />
10
10
  {{ content }}
@@ -1,6 +1,7 @@
1
1
  {# def
2
2
  name,
3
3
  id,
4
+ multiple=False,
4
5
  class_="""
5
6
  bg-neutral-50
6
7
  block
@@ -22,6 +23,6 @@ dark:text-white
22
23
  """
23
24
  #}
24
25
 
25
- <select name="{{name}}" id="{{id}}" class="{{attrs.class or class_}}">
26
+ <select name="{{name}}" id="{{id}}" class="{{attrs.class or class_}}" {% if multiple %}multiple{% endif %}>
26
27
  {{- content -}}
27
28
  </select>
@@ -2,6 +2,6 @@
2
2
  <pydantic_form.Widget widget={widget} removable={widget.removable}>
3
3
  <div class="pt-4">
4
4
  <Label for={widget.id}>{{widget.title}}</Label>
5
- <Checkbox name={widget.name} type="checkbox" id={widget.id} {% if widget.value %}checked{% endif %} />
5
+ <Checkbox name={widget.name} type="checkbox" id={widget.id} checked={widget.value} />
6
6
  </div>
7
7
  </pydantic_form.Widget>
@@ -0,0 +1,20 @@
1
+ {# def
2
+ widget,
3
+ #}
4
+
5
+ <pydantic_form.Widget widget={widget} removable={widget.removable}>
6
+ <div class="pt-4">
7
+ <details open>
8
+ <summary class="justify-between items-center font-medium cursor-pointer">
9
+ <H3>{{widget.title}}</H3>
10
+ </summary>
11
+ <div>
12
+ {% for value in widget.value %}
13
+ <div class="flex items-center mb-4">
14
+ <Checkbox name={widget.name} type="checkbox" id={value.id} value={value.value} checked={value.checked} />
15
+ <Label for={value.id} class="ms-2 text-base text-neutral-900 dark:text-white">{{value.label}}</Label>
16
+ </div>
17
+ {% endfor %}
18
+ </div>
19
+ </div>
20
+ </pydantic_form.Widget>
@@ -1,5 +1,5 @@
1
1
  import abc
2
- from typing import Any, Mapping, Type
2
+ from typing import Any, Mapping, Optional, Type
3
3
 
4
4
  from fastapi import Request
5
5
  from markupsafe import Markup
@@ -11,8 +11,21 @@ class AbstractTemplateRenderer(abc.ABC):
11
11
  """Used to buid pydantic form"""
12
12
 
13
13
  @abc.abstractmethod
14
- def render_template(self, template: str, **params: Any) -> str:
15
- ...
14
+ def render_template(
15
+ self,
16
+ template: str,
17
+ *,
18
+ globals: Optional[Mapping[str, Any]] = None,
19
+ **params: Any,
20
+ ) -> str:
21
+ """
22
+ Render the given template with the given params.
23
+
24
+ While rendering templates, the globals parameter is keps by the instantiated
25
+ renderer and sent to every rendering made by the request.
26
+ This is used by the pydantic form method that will render other templates
27
+ for the request.
28
+ """
16
29
 
17
30
  @abc.abstractmethod
18
31
  def pydantic_form(
@@ -1,4 +1,4 @@
1
- from typing import TYPE_CHECKING, Any, Mapping, Optional, Sequence, Type
1
+ from typing import TYPE_CHECKING, Any, Mapping, MutableMapping, Optional, Sequence, Type
2
2
 
3
3
  from fastapi import Request
4
4
  from jinjax.catalog import Catalog
@@ -41,6 +41,7 @@ class JinjaxRenderer(AbstractTemplateRenderer):
41
41
  self.request = request
42
42
  self.csrf_token_name = csrf_token_name
43
43
  self.form_data_model_prefix = form_data_model_prefix
44
+ self.globals: MutableMapping[str, Any] = {}
44
45
 
45
46
  def build_globals(self) -> Mapping[str, Any]:
46
47
  return {
@@ -50,9 +51,18 @@ class JinjaxRenderer(AbstractTemplateRenderer):
50
51
  "value": self.request.scope.get(self.csrf_token_name, ""),
51
52
  },
52
53
  "pydantic_form": self.pydantic_form,
54
+ **self.globals,
53
55
  }
54
56
 
55
- def render_template(self, template: str, **params: Any) -> str:
57
+ def render_template(
58
+ self,
59
+ template: str,
60
+ *,
61
+ globals: Optional[Mapping[str, Any]] = None,
62
+ **params: Any,
63
+ ) -> str:
64
+ if globals:
65
+ self.globals.update(globals)
56
66
  return self.catalog.render( # type: ignore
57
67
  template, __globals=self.build_globals(), **params
58
68
  )
@@ -1,5 +1,3 @@
1
- from typing import Optional
2
-
3
1
  from .base import Widget
4
2
 
5
3
 
@@ -8,7 +6,7 @@ class BooleanWidget(Widget[bool]):
8
6
  self,
9
7
  name: str,
10
8
  *,
11
- title: Optional[str],
9
+ title: str | None,
12
10
  value: bool = False,
13
11
  removable: bool = False,
14
12
  token: str,
@@ -0,0 +1,36 @@
1
+ from typing import Sequence
2
+
3
+ from pydantic import BaseModel
4
+
5
+ from .base import Widget
6
+
7
+
8
+ class Checkable(BaseModel):
9
+ label: str
10
+ name: str
11
+ value: str
12
+ token: str
13
+ checked: bool
14
+
15
+ @property
16
+ def id(self) -> str:
17
+ id = f"{self.name}-{self.value}-{self.token}"
18
+ return id.replace(".", "-").replace("_", "-")
19
+
20
+
21
+ class ChecklistWidget(Widget[Sequence[Checkable]]):
22
+ def __init__(
23
+ self,
24
+ name: str,
25
+ *,
26
+ title: str | None,
27
+ value: Sequence[Checkable],
28
+ token: str,
29
+ removable: bool,
30
+ ) -> None:
31
+ super().__init__(
32
+ name, value=value, token=token, title=title, removable=removable
33
+ )
34
+
35
+ def get_template(self) -> str:
36
+ return "pydantic_form.Checklist"
@@ -1,6 +1,7 @@
1
1
  import secrets
2
2
  from collections.abc import MutableSequence, Sequence
3
3
  from decimal import Decimal
4
+ from enum import Enum
4
5
  from types import NoneType
5
6
  from typing import Any, Literal, Mapping, Optional, Type, cast, get_origin
6
7
  from uuid import UUID
@@ -12,6 +13,7 @@ from pydantic.fields import FieldInfo
12
13
  from fastlife.shared_utils.infer import is_complex_type, is_union
13
14
  from fastlife.templating.renderer.abstract import AbstractTemplateRenderer
14
15
  from fastlife.templating.renderer.widgets.boolean import BooleanWidget
16
+ from fastlife.templating.renderer.widgets.checklist import Checkable, ChecklistWidget
15
17
  from fastlife.templating.renderer.widgets.dropdown import DropDownWidget
16
18
  from fastlife.templating.renderer.widgets.hidden import HiddenWidget
17
19
  from fastlife.templating.renderer.widgets.sequence import SequenceWidget
@@ -96,6 +98,12 @@ class WidgetFactory:
96
98
  if type_origin is Literal:
97
99
  return self.build_literal(name, typ, field, value, removable)
98
100
 
101
+ if type_origin is set:
102
+ return self.build_set(name, typ, field, value, removable)
103
+
104
+ if issubclass(typ, Enum): # if it raises here, the type_origin is unknown
105
+ return self.build_enum(name, typ, field, value, removable)
106
+
99
107
  if issubclass(typ, BaseModel): # if it raises here, the type_origin is unknown
100
108
  return self.build_model(name, typ, field, value or {}, removable)
101
109
 
@@ -228,6 +236,55 @@ class WidgetFactory:
228
236
  removable=removable,
229
237
  )
230
238
 
239
+ def build_set(
240
+ self,
241
+ field_name: str,
242
+ field_type: Type[Any],
243
+ field: Optional[FieldInfo],
244
+ value: Optional[Sequence[Any]],
245
+ removable: bool,
246
+ ) -> Widget[Any]:
247
+ choice_wrapper = field_type.__args__[0]
248
+ choices = []
249
+ choice_wrapper_origin = get_origin(choice_wrapper)
250
+ if choice_wrapper_origin:
251
+ if choice_wrapper_origin is Literal:
252
+ litchoice: list[str] = choice_wrapper.__args__ # type: ignore
253
+ choices = [
254
+ Checkable(
255
+ label=c,
256
+ value=c,
257
+ checked=c in value if value else False, # type: ignore
258
+ name=field_name,
259
+ token=self.token,
260
+ )
261
+ for c in litchoice
262
+ ]
263
+
264
+ else:
265
+ raise NotImplementedError
266
+ elif issubclass(choice_wrapper, Enum):
267
+ choices = [
268
+ Checkable(
269
+ label=e.value,
270
+ value=e.name,
271
+ checked=e.name in value if value else False, # type: ignore
272
+ name=field_name,
273
+ token=self.token,
274
+ )
275
+ for e in choice_wrapper
276
+ ]
277
+ else:
278
+ raise NotImplementedError
279
+
280
+ return ChecklistWidget(
281
+ field_name,
282
+ title=field.title if field else "",
283
+ token=self.token,
284
+ value=choices,
285
+ removable=removable,
286
+ )
287
+
231
288
  def build_boolean(
232
289
  self,
233
290
  field_name: str,
@@ -306,6 +363,24 @@ class WidgetFactory:
306
363
  value=str(value),
307
364
  )
308
365
 
366
+ def build_enum(
367
+ self,
368
+ field_name: str,
369
+ field_type: Type[Any], # an enum subclass
370
+ field: FieldInfo | None,
371
+ value: str | int | float,
372
+ removable: bool,
373
+ ) -> Widget[Any]:
374
+ options = [(item.name, item.value) for item in field_type] # type: ignore
375
+ return DropDownWidget(
376
+ field_name,
377
+ options=options, # type: ignore
378
+ removable=removable,
379
+ title=field.title if field else "",
380
+ token=self.token,
381
+ value=str(value),
382
+ )
383
+
309
384
  def build_simpletype(
310
385
  self,
311
386
  field_name: str,
@@ -31,4 +31,6 @@ class ModelWidget(Widget[Sequence[Widget[Any]]]):
31
31
  "widget": self,
32
32
  "children_widget": children_widget,
33
33
  }
34
- return Markup(renderer.render_template(self.get_template(), **kwargs))
34
+ return Markup(
35
+ renderer.render_template(self.get_template(), globals=None, **kwargs)
36
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastlifeweb
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: High-level web framework
5
5
  Home-page: https://github.com/mardiros/fastlife
6
6
  License: BSD-derived
@@ -6,7 +6,7 @@ fastlife/configurator/registry.py,sha256=1sOicKvwIvLbrzRk9z8yb65YUXxxagJK9AK-2gG
6
6
  fastlife/configurator/settings.py,sha256=ftv5MkNXeyBrvcqxnt2WKtLuzo7ge2_BNx1gX4CcSOE,1489
7
7
  fastlife/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  fastlife/request/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- fastlife/request/form_data.py,sha256=gKXo92ZwsMX6UpMkf4BlNYDiOfwxyzDzItc_lpplAzY,3642
9
+ fastlife/request/form_data.py,sha256=A1mowwOrsQ0yPvfMwh_m9Mw6yoYTgqayibiXNyyOGLM,4049
10
10
  fastlife/security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  fastlife/security/csrf.py,sha256=47epJVJtr5X6j-Hog54WCGOoiRLQQHvgBU71iqR1N0A,1025
12
12
  fastlife/security/policy.py,sha256=5jV5nypy5O8XPFFRJ_bzG8Ltk5xcPWclkz23qiG1_I8,509
@@ -18,9 +18,9 @@ fastlife/shared_utils/infer.py,sha256=_hmGzu84VlZAkdw_owkW8eHknULqH3MLDBlXj7LkEs
18
18
  fastlife/shared_utils/resolver.py,sha256=wXQQTB4jf86m4qENhMOkHkWpLJj_T4-_eND_ItTLnTE,1410
19
19
  fastlife/templates/A.jinja,sha256=q71nu4Rq874LG6SykCKv8W-EZeX13NMF0AsLc9FFAP0,677
20
20
  fastlife/templates/Button.jinja,sha256=535UIK4Prunj4f0YZXBmCI0rfOiTr5GJTQkM0XDmtNA,1137
21
- fastlife/templates/Checkbox.jinja,sha256=EbKJ9KmfexegcocxeMtoG1kt-x4bP8Tq3odh8prbJoQ,366
21
+ fastlife/templates/Checkbox.jinja,sha256=XdgtI7GTVjPkxTje9KMILzHqjMFRmTpubtKM8nL7K1o,447
22
22
  fastlife/templates/CsrfToken.jinja,sha256=X2-H0VmukyUV448blh9M8rhr_gN82ar51TnRAwfV3OY,59
23
- fastlife/templates/Form.jinja,sha256=cfXWtFWCcnxdIOayR3_XWoY1PpQATiyQDqkY0Vg91g0,329
23
+ fastlife/templates/Form.jinja,sha256=h8Y2yrlrTRuLnwqCTDv8eeFgaej2eQiXDvFOx5eEcGg,367
24
24
  fastlife/templates/H1.jinja,sha256=fetjIhZvgw5gm6xJhl_-cnHvrPwuLYE4S1rVYgbqNl8,196
25
25
  fastlife/templates/H2.jinja,sha256=z022lvv3PO_g-ZxWuAiKmBOaFZ3u8Kbzd0jkuWNsOS0,196
26
26
  fastlife/templates/H3.jinja,sha256=BmSznvHOCAT8569hf-lOh0qlK3yHAPdO3KCOLD7BaPE,195
@@ -33,9 +33,10 @@ fastlife/templates/Label.jinja,sha256=m71vGDUi2HzzA5d9L3TQhdEXYKHcp6LXP6PE0-bx44
33
33
  fastlife/templates/Option.jinja,sha256=YlgBayJj94kSXK0mlGkMxTmHjTBr8T-UzvQmTcANK0Q,121
34
34
  fastlife/templates/P.jinja,sha256=xEcHIv9dJRpELu_SdqQcftvKrU8z1i_BHTEVO5Mu5dU,164
35
35
  fastlife/templates/Radio.jinja,sha256=51I5n1yjWQ_uLZfzuUUf3C8ngo-KT4dPw29-pjP9uSU,723
36
- fastlife/templates/Select.jinja,sha256=6Rw9A51JbSTprgNLz8A8E_UKfsLFf26eqdhwLnaMms4,427
36
+ fastlife/templates/Select.jinja,sha256=GoK2oh4Jl5dAfL2wN718RCwtHaQIslzDCN_nwy6a9oY,480
37
37
  fastlife/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
- fastlife/templates/pydantic_form/Boolean.jinja,sha256=XE3NM4vhwQwRUIbjv10t9dI70MzWhicyQ_l6FAOs8-k,298
38
+ fastlife/templates/pydantic_form/Boolean.jinja,sha256=tzEZpgv3t9vEuQr2_NzLvhqyusGm2w7kEkehCRJyG2c,281
39
+ fastlife/templates/pydantic_form/Checklist.jinja,sha256=wv224DG6X-XTpiU0hQl5AjuAsb-MMS7TaMdVaYhEkq4,665
39
40
  fastlife/templates/pydantic_form/Dropdown.jinja,sha256=JBkQp7LYtz6vvW6MyAXyW-6UDZ_xY0Fgd4mpDCQA-wM,500
40
41
  fastlife/templates/pydantic_form/Hidden.jinja,sha256=n6CbTSwZr2E_oY8TO2WPbnrLHBaWfe_CXVCYOYmCfts,83
41
42
  fastlife/templates/pydantic_form/Hint.jinja,sha256=O0ZsAQnATcG0a_qLQfrwM6VZHmAw3k1W33WYlEBUas8,123
@@ -47,15 +48,16 @@ fastlife/templates/pydantic_form/Widget.jinja,sha256=8raoMjtO4ARBfbz8EG-HKT112Kk
47
48
  fastlife/templating/__init__.py,sha256=QdrTmxt-K7UcJ_o9LY1n-QZAvk5foct5MNFt02zXr2w,234
48
49
  fastlife/templating/binding.py,sha256=n-MDg98Bne49BmWWWCopZsI6ef7h6PIdcszM_pRutYA,1378
49
50
  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
51
+ fastlife/templating/renderer/abstract.py,sha256=ksWrz25bmsN4MQJK08OYzoZfiYvAg39Z1DtjxyDDe84,1251
52
+ fastlife/templating/renderer/jinjax.py,sha256=bgHgu6VLA4S8fS7Evu68iPaODdev6v0s6Z79TX8c6Mo,3458
52
53
  fastlife/templating/renderer/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
54
  fastlife/templating/renderer/widgets/base.py,sha256=_MMtrhYvtQgX1EQNku0NSBYwhCDvQ5y8TjnCC2b4CAw,2769
54
- fastlife/templating/renderer/widgets/boolean.py,sha256=bk9bExUDp73is6tvbpC28ploIVjyMecFsxPN8NxnOd8,468
55
+ fastlife/templating/renderer/widgets/boolean.py,sha256=UPQOxIpydP2NzDVUQuNau1DewS2FkRqx5WkWSwEu1kk,436
56
+ fastlife/templating/renderer/widgets/checklist.py,sha256=lR6A1nui6iYnx9E4cNEaxsgNPsZWtEEofh2l3C9qWos,770
55
57
  fastlife/templating/renderer/widgets/dropdown.py,sha256=o3BfD9gDiRPsCPB93xCZSYRo50LRAb_tZOmK4x7zkzY,901
56
- fastlife/templating/renderer/widgets/factory.py,sha256=uEYaxHYygLQ-5eWWjsOlvPM6GmhHW7Nu_w-1l2HRihA,10335
58
+ fastlife/templating/renderer/widgets/factory.py,sha256=59BfKpNIB8T71vZ0RjjcjNjdP0TfSm-WHgXmelyIziY,12850
57
59
  fastlife/templating/renderer/widgets/hidden.py,sha256=2fsbTQKsACV0JVYpCjXaQAV7VnQTIBPCi4lJPdWCRHc,308
58
- fastlife/templating/renderer/widgets/model.py,sha256=obZNgUiTWsMf24ge-KPSay7S3rkFn3kdQlEBbR-r-14,950
60
+ fastlife/templating/renderer/widgets/model.py,sha256=L7tdmRj0cetM_UusaD2zk5YJtIGBYizCMBKQzFyN_Ko,986
59
61
  fastlife/templating/renderer/widgets/sequence.py,sha256=x6o1L_hOh3WRlW16ZQaeO6H_qtnIitALuK-w7ck0RAw,1347
60
62
  fastlife/templating/renderer/widgets/text.py,sha256=mlW8LrRmj-aEMp8QzZOURhns6mfbV7jsOMQrsYZcU3Q,819
61
63
  fastlife/templating/renderer/widgets/union.py,sha256=GntHssMjcgpD6S5h0JVr9_aWy3pSbMhYZ9QU-ugBH-Y,1689
@@ -63,7 +65,7 @@ fastlife/testing/__init__.py,sha256=KgTlRI0g8z7HRpL7mD5QgI__LT9Y4QDSzKMlxJG3wNk,
63
65
  fastlife/testing/testclient.py,sha256=JhWgruKDeIn0QFmEAXKhUXXsZpTpPi_JWIHhWbWIXg0,16404
64
66
  fastlife/views/__init__.py,sha256=nn4B_8YTbTmhGPvSd20yyKK_9Dh1Pfh_Iq7z6iK8-CE,154
65
67
  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,,
68
+ fastlifeweb-0.5.0.dist-info/LICENSE,sha256=F75xSseSKMwqzFj8rswYU6NWS3VoWOc_gY3fJYf9_LI,1504
69
+ fastlifeweb-0.5.0.dist-info/METADATA,sha256=TETmIX7HOLds7N5Ue7tMcGozk1EqIxZ52cKazmT3__M,1833
70
+ fastlifeweb-0.5.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
71
+ fastlifeweb-0.5.0.dist-info/RECORD,,