fastlifeweb 0.23.0__py3-none-any.whl → 0.23.1__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,7 @@
1
+ ## 0.23.1 - Released on 2025-01-14
2
+ * Fix typing issue
3
+ * Update docs
4
+
1
5
  ## 0.23.0 - Released on 2024-12-04
2
6
  * Update Request type.
3
7
  * Breaking changes: Request[TUser, TRegistry] -> Request[TRegistry, TIdentity, TClaimedIdentity].
fastlife/__init__.py CHANGED
@@ -48,6 +48,7 @@ from .service.security_policy import (
48
48
  AbstractSecurityPolicy,
49
49
  InsecurePolicy,
50
50
  )
51
+ from .service.translations import TranslatableStringFactory
51
52
  from .settings import Settings
52
53
 
53
54
  __all__ = [
@@ -97,4 +98,5 @@ __all__ = [
97
98
  "JinjaXTemplate",
98
99
  # i18n
99
100
  "Localizer",
101
+ "TranslatableStringFactory",
100
102
  ]
@@ -8,7 +8,7 @@ from fastlife.service.translations import Localizer as RequestLocalizer
8
8
 
9
9
  def get_localizer(request: Request) -> RequestLocalizer:
10
10
  """Return the localizer for the given request."""
11
- return request.registry.localizer(request)
11
+ return request.registry.localizer(request.locale_name)
12
12
 
13
13
 
14
14
  Localizer = Annotated[RequestLocalizer, Depends(get_localizer)]
@@ -473,7 +473,7 @@ class GenericConfigurator(Generic[TRegistry]):
473
473
  * `gettext`, `ngettext`, `dgettext`, `dngettext`, `pgettext`, `dpgettext`,
474
474
  `npgettext`, `dnpgettext` methods are installed for i18n purpose.
475
475
  """
476
- lczr = request.registry.localizer(request)
476
+ lczr = request.registry.localizer(request.locale_name)
477
477
  custom_globals = {}
478
478
  for key, (val, evaluate) in self._renderer_globals.items():
479
479
  if evaluate and callable(val):
@@ -22,7 +22,7 @@ class GenericRegistry(Generic[TSettings]):
22
22
  It is initialized by the configurator and accessed by the `fastlife.Registry`.
23
23
  """
24
24
 
25
- settings: Settings
25
+ settings: TSettings
26
26
  """Application settings."""
27
27
  renderers: Mapping[str, "AbstractTemplateRendererFactory"]
28
28
  """Registered template engine."""
@@ -31,7 +31,7 @@ class GenericRegistry(Generic[TSettings]):
31
31
  localizer: "LocalizerFactory"
32
32
  """Used to localized message."""
33
33
 
34
- def __init__(self, settings: Settings) -> None:
34
+ def __init__(self, settings: TSettings) -> None:
35
35
  from fastlife.service.locale_negociator import default_negociator
36
36
  from fastlife.service.translations import LocalizerFactory
37
37
 
@@ -0,0 +1 @@
1
+
@@ -3,18 +3,52 @@ from collections import defaultdict
3
3
  from collections.abc import Callable, Iterator
4
4
  from gettext import GNUTranslations
5
5
  from io import BufferedReader
6
- from typing import TYPE_CHECKING
7
6
 
8
7
  from fastlife.shared_utils.resolver import resolve_path
9
8
 
10
- if TYPE_CHECKING:
11
- from fastlife import Request # coverage: ignore
12
-
13
9
  LocaleName = str
14
10
  Domain = str
15
11
  CONTEXT_ENCODING = "%s\x04%s"
16
12
 
17
13
 
14
+ class TranslatableString(str):
15
+ """
16
+ Create a string made for translation associated to a domain.
17
+ This class is instanciated by the
18
+ :class:`fastlife.service.translations.TranslatableStringFactory` class.
19
+ """
20
+
21
+ __slots__ = ("domain",)
22
+
23
+ def __new__(cls, msgid: str, domain: str) -> "TranslatableString":
24
+ self = str.__new__(cls, msgid)
25
+ self.domain = domain # type: ignore
26
+ return self
27
+
28
+
29
+ class TranslatableStringFactory:
30
+ """Create a catalog of string associated to a domain."""
31
+
32
+ def __init__(self, domain: str):
33
+ self.domain = domain
34
+
35
+ def __call__(self, msgid: str) -> str:
36
+ """
37
+ Use to generate the translatable string.
38
+
39
+ usually:
40
+
41
+ ```python
42
+ _ = TranslatableStringFactory("mydomain")
43
+ mymessage = _("translatable")
44
+ ```
45
+
46
+ Note that the string is associated to mydomain, so the babel extraction has
47
+ to be initialized with that particular domain.
48
+ """
49
+ return TranslatableString(msgid, self.domain)
50
+
51
+
18
52
  def find_mo_files(root_path: str) -> Iterator[tuple[LocaleName, Domain, pathlib.Path]]:
19
53
  """
20
54
  Find .mo files in a locales directory.
@@ -68,7 +102,10 @@ class Localizer:
68
102
  return self.gettext(message, mapping)
69
103
 
70
104
  def gettext(self, message: str, mapping: dict[str, str] | None = None) -> str:
71
- ret = self.global_translations.gettext(message)
105
+ if isinstance(message, TranslatableString):
106
+ ret = self.translations[message.domain].gettext(message) # type: ignore
107
+ else:
108
+ ret = self.global_translations.gettext(message)
72
109
  if mapping:
73
110
  ret = ret.format(**mapping)
74
111
  return ret
@@ -177,8 +214,8 @@ class LocalizerFactory:
177
214
  root_path = resolve_path(path)
178
215
  self._translations.load(root_path)
179
216
 
180
- def __call__(self, request: "Request") -> Localizer:
217
+ def __call__(self, locale_name: LocaleName) -> Localizer:
181
218
  """Create the translation context for the given request."""
182
- if request.locale_name not in self._translations:
219
+ if locale_name not in self._translations:
183
220
  return self.null_localizer
184
- return self._translations.get(request.locale_name)
221
+ return self._translations.get(locale_name)
@@ -126,7 +126,7 @@ class WebTestClient:
126
126
 
127
127
  @property
128
128
  def session(self) -> MutableMapping[str, Any]:
129
- """Session shared between the server and the client."""
129
+ """Server session stored in a cookies."""
130
130
  return Session(self)
131
131
 
132
132
  def request(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastlifeweb
3
- Version: 0.23.0
3
+ Version: 0.23.1
4
4
  Summary: High-level web framework
5
5
  Author-Email: Guillaume Gauvrit <guillaume@gauvr.it>
6
6
  License: MIT License
@@ -1,10 +1,10 @@
1
- CHANGELOG.md,sha256=Ap14kfVx07rFhITGDmtQfwEsJowg5osxKETE0w478lU,7801
2
- fastlife/__init__.py,sha256=cx3BScbBelH-Tm63VPdAp5siW1fBZ0uOMwNW_SPs2xQ,2126
1
+ CHANGELOG.md,sha256=gvl1gBNgVNhdz4fSMSIsPwWpArqetbDebCW2PAB6yoE,7871
2
+ fastlife/__init__.py,sha256=nXWE4AbhkhG_yBjPJU-XnKDMTsU9ebv7Vj4eIciWQI0,2219
3
3
  fastlife/adapters/__init__.py,sha256=imPD1hImpgrYkvUJRhHA5kVyGAua7VbP2WGkhSWKJT8,93
4
4
  fastlife/adapters/fastapi/__init__.py,sha256=1goV1FGFP04TGyskJBLKZam4Gvt1yoAvLMNs4ekWSSQ,243
5
5
  fastlife/adapters/fastapi/form.py,sha256=csxsDI6RK-g41pMwFhaVQCLDhF7dAZzgUp-VcrC3NFY,823
6
6
  fastlife/adapters/fastapi/form_data.py,sha256=2DQ0o-RvY6iROUKQjS-UJdNYEVSsNPd-AjpergI3w54,4473
7
- fastlife/adapters/fastapi/localizer.py,sha256=XD1kCJuAlkGevivmvAJEcGMCBWMef9rAfTOGmt3PVWU,436
7
+ fastlife/adapters/fastapi/localizer.py,sha256=Efn6rrf-SnSfM4TqqE_5chacrxaPpupxbvIqXipXEEw,448
8
8
  fastlife/adapters/fastapi/request.py,sha256=COOoSMZAm4VhyJgM7dlqJ7YdGjeGI7qs93PtBsriEPc,1115
9
9
  fastlife/adapters/fastapi/routing/__init__.py,sha256=8EMnQE5n8oA4J9_c3nxzwKDVt3tefZ6fGH0d2owE8mo,195
10
10
  fastlife/adapters/fastapi/routing/route.py,sha256=XnDPvd5V0Zl7Ke6bBErEtUCjmNQPcV2U_w1dWpx6qM4,1476
@@ -1686,7 +1686,7 @@ fastlife/components/pydantic_form/FatalError.jinja,sha256=lFVlNrXzBR6ExMahq77h0t
1686
1686
  fastlife/components/pydantic_form/Hint.jinja,sha256=8leBpfMGDmalc_KAjr2paTojr_rwq-luS6m_1BGj7Tw,202
1687
1687
  fastlife/components/pydantic_form/Widget.jinja,sha256=PgguUpvhG6CY9AW6H8qQMjKqjlybjDCAaFFAOHzrzVQ,418
1688
1688
  fastlife/config/__init__.py,sha256=5qpuaVYqi-AS0GgsfggM6rFsSwXgrqrLBo9jH6dVroc,407
1689
- fastlife/config/configurator.py,sha256=SURXmBrdTghHoG2f9R2BUF6TKZXg1lNmwP3ZbuApJ3M,24722
1689
+ fastlife/config/configurator.py,sha256=gxa3aTfBXvGqRLLAaZ0CoNAjwHlQIhx4yr1DfY_z2IQ,24734
1690
1690
  fastlife/config/exceptions.py,sha256=9MdBnbfy-Aw-KaIFzju0Kh8Snk41-v9LqK2w48Tdy1s,1169
1691
1691
  fastlife/config/openapiextra.py,sha256=rYoerrn9sni2XwnO3gIWqaz7M0aDZPhVLjzqhDxue0o,514
1692
1692
  fastlife/config/resources.py,sha256=u6OgnbHfGkC5idH-YPNkIPf8GJnZpJoGVZ-Ym022BCo,8533
@@ -1712,10 +1712,11 @@ fastlife/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
1712
1712
  fastlife/service/check_permission.py,sha256=-TsI58YZJtWIw5bsm0fVpfuaCMUx4cmoLTKGXeyQPDk,1809
1713
1713
  fastlife/service/csrf.py,sha256=wC1PaKOmZ3il0FF_kevxnlg9PxDqruRdLrNnOA3ZHrU,1886
1714
1714
  fastlife/service/locale_negociator.py,sha256=JUqzTukxDqTJVOR-CNI7Vqo6kvdvwxYvZQe8P3V9S2U,796
1715
- fastlife/service/registry.py,sha256=B6n5b_b0RgxJj0qFOpnrJFmG7_MPtvShwV6yH9V6vi0,2098
1715
+ fastlife/service/registry.py,sha256=sdLJ-1K7R1_RwVNpQUcyktPOORhDm_pyKw8jehhR5VM,2100
1716
1716
  fastlife/service/security_policy.py,sha256=qYXs4mhfz_u4x59NhUkirqKYKQbFv9YrzyRuXj7mxE0,4688
1717
1717
  fastlife/service/templates.py,sha256=QPAIUbbZiekazz_jV3q4JCwQd6Q4KA6a4RDek2RWuhE,2548
1718
- fastlife/service/translations.py,sha256=D-1D3pVNytEcps1u-0K7FmgQ8Wo6Yu4XVHvZrPhBmAI,5795
1718
+ fastlife/service/translatablestring.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
1719
+ fastlife/service/translations.py,sha256=s6qFZSXR-1vYxSr7RRH-mS-VjNaa8OTxR7-k6Ib7h0E,6878
1719
1720
  fastlife/settings.py,sha256=q-rz4CEF2RQGow5-m-yZJOvdh3PPb2c1Q_ZLJGnu4VQ,3647
1720
1721
  fastlife/shared_utils/__init__.py,sha256=i66ytuf-Ezo7jSiNQHIsBMVIcB-tDX0tg28-pUOlhzE,26
1721
1722
  fastlife/shared_utils/infer.py,sha256=0GflLkaWJ-4LZ1Ig3moR-_o55wwJ_p_vJ4xo-yi3lyA,1406
@@ -1725,12 +1726,12 @@ fastlife/testing/__init__.py,sha256=VpxkS3Zp3t_hH8dBiLaGFGhsvt511dhBS_8fMoFXdmU,
1725
1726
  fastlife/testing/dom.py,sha256=dVzDoZokn-ii681UaEwAr-khM5KE-CHgXSSLSo24oH0,4489
1726
1727
  fastlife/testing/form.py,sha256=ST0xNCoUqz_oD92cWHzQ6CbJ5hFopvu_NNKpOfiuYWY,7874
1727
1728
  fastlife/testing/session.py,sha256=LEFFbiR67_x_g-ioudkY0C7PycHdbDfaIaoo_G7GXQ8,2226
1728
- fastlife/testing/testclient.py,sha256=JTIgeMKooA8L4gEodeC3gy4Lo27y3WNswSEIKLlVVPs,6745
1729
+ fastlife/testing/testclient.py,sha256=TWGbxagxwFEm7_SooItjLchhF5ptHwhyKUh7uhWN_CA,6731
1729
1730
  fastlife/views/__init__.py,sha256=zG8gveL8e2zBdYx6_9jtZfpQ6qJT-MFnBY3xXkLwHZI,22
1730
1731
  fastlife/views/pydantic_form.py,sha256=o7EUItciAGL1OSaGNHo-3BTrYAk34GuWE7zGikjiAGY,1486
1731
- fastlifeweb-0.23.0.dist-info/METADATA,sha256=Eww8hBxH7oR5_EeqtALpdmUmJr2f1vs1E0EijXaxM30,3663
1732
- fastlifeweb-0.23.0.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
1733
- fastlifeweb-0.23.0.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
1734
- fastlifeweb-0.23.0.dist-info/licenses/LICENSE,sha256=NlRX9Z-dcv8X1VFW9odlIQBbgNN9pcO94XzvKp2R16o,1075
1732
+ fastlifeweb-0.23.1.dist-info/METADATA,sha256=O3yeVKCL9s-zEyG6Gsl6Z8UmrX9T9cMwkkOgz0bkw58,3663
1733
+ fastlifeweb-0.23.1.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
1734
+ fastlifeweb-0.23.1.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
1735
+ fastlifeweb-0.23.1.dist-info/licenses/LICENSE,sha256=NlRX9Z-dcv8X1VFW9odlIQBbgNN9pcO94XzvKp2R16o,1075
1735
1736
  tailwind.config.js,sha256=EN3EahBDmQBbmJvkw3SdGWNOkfkzw0cg-QvBikOhkrw,1348
1736
- fastlifeweb-0.23.0.dist-info/RECORD,,
1737
+ fastlifeweb-0.23.1.dist-info/RECORD,,