fastlifeweb 0.13.0__py3-none-any.whl → 0.14.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.
- fastlife/config/configurator.py +65 -39
- fastlife/config/registry.py +1 -5
- fastlife/services/translations.py +4 -4
- {fastlifeweb-0.13.0.dist-info → fastlifeweb-0.14.0.dist-info}/METADATA +1 -1
- {fastlifeweb-0.13.0.dist-info → fastlifeweb-0.14.0.dist-info}/RECORD +7 -7
- {fastlifeweb-0.13.0.dist-info → fastlifeweb-0.14.0.dist-info}/LICENSE +0 -0
- {fastlifeweb-0.13.0.dist-info → fastlifeweb-0.14.0.dist-info}/WHEEL +0 -0
fastlife/config/configurator.py
CHANGED
@@ -14,11 +14,12 @@ phase.
|
|
14
14
|
import importlib
|
15
15
|
import inspect
|
16
16
|
import logging
|
17
|
-
from collections
|
17
|
+
from collections import defaultdict
|
18
|
+
from collections.abc import Mapping, Sequence
|
18
19
|
from enum import Enum
|
19
20
|
from pathlib import Path
|
20
21
|
from types import ModuleType
|
21
|
-
from typing import TYPE_CHECKING, Annotated, Any, Callable, Self, Tuple, Type
|
22
|
+
from typing import TYPE_CHECKING, Annotated, Any, Callable, Self, Tuple, Type
|
22
23
|
|
23
24
|
import venusian
|
24
25
|
from fastapi import Depends, FastAPI
|
@@ -48,6 +49,8 @@ if TYPE_CHECKING:
|
|
48
49
|
log = logging.getLogger(__name__)
|
49
50
|
VENUSIAN_CATEGORY = "fastlife"
|
50
51
|
|
52
|
+
venusian_ignored_item = str | Callable[[str], bool]
|
53
|
+
|
51
54
|
|
52
55
|
class ConfigurationError(Exception):
|
53
56
|
"""
|
@@ -102,18 +105,34 @@ class Configurator:
|
|
102
105
|
self.api_description: str = ""
|
103
106
|
self.api_summary: str | None = None
|
104
107
|
|
105
|
-
self.
|
108
|
+
self._route_prefix: str = ""
|
109
|
+
self._routers: dict[str, Router] = defaultdict(Router)
|
110
|
+
|
106
111
|
self.scanner = venusian.Scanner(fastlife=self)
|
107
112
|
self.include("fastlife.views")
|
108
113
|
self.include("fastlife.middlewares")
|
109
114
|
|
115
|
+
@property
|
116
|
+
def _current_router(self) -> Router:
|
117
|
+
return self._routers[self._route_prefix]
|
118
|
+
|
110
119
|
def build_asgi_app(self) -> FastAPI:
|
111
120
|
"""
|
112
121
|
Build the app after configuration in order to start after beeing configured.
|
113
122
|
|
114
123
|
:return: FastAPI application.
|
115
124
|
"""
|
116
|
-
|
125
|
+
|
126
|
+
# register our main template renderer at then end, to ensure that
|
127
|
+
# if settings have been manipulated, everythins is taken into account.
|
128
|
+
self.add_renderer(
|
129
|
+
self.registry.settings.jinjax_file_ext,
|
130
|
+
resolve("fastlife.adapters.jinjax.renderer:JinjaxTemplateRenderer")(
|
131
|
+
self.registry.settings
|
132
|
+
),
|
133
|
+
)
|
134
|
+
|
135
|
+
app = FastAPI(
|
117
136
|
title=self.api_title,
|
118
137
|
version=self.api_version,
|
119
138
|
description=self.api_description,
|
@@ -125,48 +144,34 @@ class Configurator:
|
|
125
144
|
if self.tags
|
126
145
|
else None,
|
127
146
|
)
|
128
|
-
|
129
|
-
for
|
130
|
-
|
131
|
-
_app.router.add_api_route(
|
132
|
-
path=route.path,
|
133
|
-
endpoint=route.endpoint,
|
134
|
-
response_model=route.response_model,
|
135
|
-
status_code=route.status_code,
|
136
|
-
tags=route.tags,
|
137
|
-
dependencies=route.dependencies,
|
138
|
-
summary=route.summary,
|
139
|
-
description=route.description,
|
140
|
-
response_description=route.response_description,
|
141
|
-
deprecated=route.deprecated,
|
142
|
-
methods=route.methods,
|
143
|
-
operation_id=route.operation_id,
|
144
|
-
response_model_include=route.response_model_include,
|
145
|
-
response_model_exclude=route.response_model_exclude,
|
146
|
-
response_model_by_alias=route.response_model_by_alias,
|
147
|
-
response_model_exclude_unset=route.response_model_exclude_unset,
|
148
|
-
response_model_exclude_defaults=route.response_model_exclude_defaults,
|
149
|
-
response_model_exclude_none=route.response_model_exclude_none,
|
150
|
-
include_in_schema=route.include_in_schema,
|
151
|
-
name=route.name,
|
152
|
-
openapi_extra=route.openapi_extra,
|
153
|
-
)
|
147
|
+
app.router.route_class = Route
|
148
|
+
for prefix, router in self._routers.items():
|
149
|
+
app.include_router(router, prefix=prefix)
|
154
150
|
|
155
151
|
for middleware_class, options in self.middlewares:
|
156
|
-
|
152
|
+
app.add_middleware(middleware_class, **options) # type: ignore
|
157
153
|
|
158
154
|
for status_code_or_exc, exception_handler in self.exception_handlers:
|
159
|
-
|
155
|
+
app.add_exception_handler(status_code_or_exc, exception_handler)
|
160
156
|
|
161
157
|
for route_path, directory, name in self.mounts:
|
162
|
-
|
163
|
-
return
|
158
|
+
app.mount(route_path, StaticFiles(directory=directory), name=name)
|
159
|
+
return app
|
164
160
|
|
165
|
-
def include(
|
161
|
+
def include(
|
162
|
+
self,
|
163
|
+
module: str | ModuleType,
|
164
|
+
route_prefix: str = "",
|
165
|
+
ignore: venusian_ignored_item | Sequence[venusian_ignored_item] | None = None,
|
166
|
+
) -> Self:
|
166
167
|
"""
|
167
168
|
Include a module in order to load its configuration.
|
168
169
|
|
169
|
-
It will
|
170
|
+
It will scan and load all the submodule until you add an ignore rule.
|
171
|
+
|
172
|
+
The `ignore` argument allows you to ignore certain modules.
|
173
|
+
If it is a scrint, it can be an absolute module name or a relative
|
174
|
+
one, if starts with a dot.
|
170
175
|
|
171
176
|
Here is an example.
|
172
177
|
|
@@ -182,6 +187,8 @@ class Configurator:
|
|
182
187
|
```
|
183
188
|
|
184
189
|
:param module: a module to include.
|
190
|
+
:param route_prefix: prepend all included route with a prefix
|
191
|
+
:param ignore: ignore submodules
|
185
192
|
"""
|
186
193
|
if isinstance(module, str):
|
187
194
|
package = None
|
@@ -190,7 +197,15 @@ class Configurator:
|
|
190
197
|
package = caller_module.__name__ if caller_module else "__main__"
|
191
198
|
|
192
199
|
module = importlib.import_module(module, package)
|
193
|
-
self.
|
200
|
+
old, self._route_prefix = self._route_prefix, route_prefix
|
201
|
+
try:
|
202
|
+
self.scanner.scan( # type: ignore
|
203
|
+
module,
|
204
|
+
categories=[VENUSIAN_CATEGORY],
|
205
|
+
ignore=ignore,
|
206
|
+
)
|
207
|
+
finally:
|
208
|
+
self._route_prefix = old
|
194
209
|
return self
|
195
210
|
|
196
211
|
def set_locale_negociator(self, locale_negociator: "LocaleNegociator") -> Self:
|
@@ -326,7 +341,7 @@ class Configurator:
|
|
326
341
|
if permission:
|
327
342
|
dependencies.append(Depends(self.registry.check_permission(permission)))
|
328
343
|
|
329
|
-
self.
|
344
|
+
self._current_router.add_api_route(
|
330
345
|
path,
|
331
346
|
endpoint,
|
332
347
|
# response_model=response_model,
|
@@ -404,7 +419,7 @@ class Configurator:
|
|
404
419
|
|
405
420
|
endpoint = render
|
406
421
|
|
407
|
-
self.
|
422
|
+
self._current_router.add_api_route(
|
408
423
|
path,
|
409
424
|
endpoint,
|
410
425
|
status_code=status_code,
|
@@ -479,6 +494,17 @@ class Configurator:
|
|
479
494
|
self.registry.renderers[f".{file_ext.lstrip('.')}"] = renderer # type: ignore
|
480
495
|
return self
|
481
496
|
|
497
|
+
def add_template_search_path(self, path: str) -> Self:
|
498
|
+
"""
|
499
|
+
Add a template search path directly from the code.
|
500
|
+
|
501
|
+
:param path: template path.
|
502
|
+
"""
|
503
|
+
self.registry.settings.template_search_path = (
|
504
|
+
f"{self.registry.settings.template_search_path},{path}"
|
505
|
+
)
|
506
|
+
return self
|
507
|
+
|
482
508
|
|
483
509
|
def configure(
|
484
510
|
wrapped: Callable[[Configurator], None],
|
fastlife/config/registry.py
CHANGED
@@ -42,11 +42,7 @@ class AppRegistry:
|
|
42
42
|
self.settings = settings
|
43
43
|
self.check_permission = resolve(settings.check_permission)
|
44
44
|
self.locale_negociator = _default_negociator(self.settings)
|
45
|
-
self.renderers = {
|
46
|
-
f".{settings.jinjax_file_ext}": resolve(
|
47
|
-
"fastlife.adapters.jinjax.renderer:JinjaxTemplateRenderer"
|
48
|
-
)(settings),
|
49
|
-
}
|
45
|
+
self.renderers = {}
|
50
46
|
self.localizer = LocalizerFactory()
|
51
47
|
|
52
48
|
def get_renderer(self, template: str) -> "AbstractTemplateRendererFactory":
|
@@ -114,10 +114,10 @@ class LocalizerFactory:
|
|
114
114
|
for locale_name, domain, file_ in find_mo_files(root_path):
|
115
115
|
with file_.open("rb") as f:
|
116
116
|
t = Translations(f, domain)
|
117
|
-
if locale_name in self._translations:
|
118
|
-
self._translations[locale_name]
|
119
|
-
|
120
|
-
|
117
|
+
if locale_name not in self._translations:
|
118
|
+
self._translations[locale_name] = Translations()
|
119
|
+
self._translations[locale_name].add(t)
|
120
|
+
self._translations[locale_name].merge(t)
|
121
121
|
|
122
122
|
def __call__(self, request: "Request") -> Localizer:
|
123
123
|
"""Create the translation context for the given request."""
|
@@ -1666,9 +1666,9 @@ 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=O_Mw2XOxo55SArHdGKRhlTrroRN8ymwfzYKlHG0eV_s,418
|
1669
|
-
fastlife/config/configurator.py,sha256=
|
1669
|
+
fastlife/config/configurator.py,sha256=LNLFLyuqhoz6y6ORX6qeU0ZycPBSKu0KVZYcARAge7k,18540
|
1670
1670
|
fastlife/config/exceptions.py,sha256=2MS2MFgb3mDbtdHEwnC-QYubob3Rl0v8O8y615LY0ds,1180
|
1671
|
-
fastlife/config/registry.py,sha256=
|
1671
|
+
fastlife/config/registry.py,sha256=rwCS_vyu1Hli8-4Cs6Z-Jxn7N7ZXWSyZqnXIIV0EJ5I,1958
|
1672
1672
|
fastlife/config/resources.py,sha256=Db183g_CC0Voa6IblaNSzcv7XBH1S3s2nTAFNXtz9Cg,8732
|
1673
1673
|
fastlife/config/settings.py,sha256=ZMjfx_XQhu6mPE5q3eI9dG5disnbGUcALUqNYDCknhk,4199
|
1674
1674
|
fastlife/config/views.py,sha256=Dxi6lO4gFs6GriAW7Rh5GDvebwbrpS2HzYhf30pXJiE,2058
|
@@ -1693,7 +1693,7 @@ fastlife/security/csrf.py,sha256=PvC9Fqdb6c0IzzsnaMx2quQdjjKrb-nOPoAHfcwoAe8,214
|
|
1693
1693
|
fastlife/security/policy.py,sha256=MYNPQlvnTBe1XsYNJoIwiGW2DFb8vciU3XyUi9ZlLt0,945
|
1694
1694
|
fastlife/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1695
1695
|
fastlife/services/templates.py,sha256=7gPJxGWD-XqputbZpy_Icsz3WHKJaWg2JgkVOeKrjfA,3840
|
1696
|
-
fastlife/services/translations.py,sha256=
|
1696
|
+
fastlife/services/translations.py,sha256=Bo5CIjdbQ3g_ihbv4Bz60hzd8VOtqEEPOyhJEbGcvP4,4363
|
1697
1697
|
fastlife/shared_utils/__init__.py,sha256=i66ytuf-Ezo7jSiNQHIsBMVIcB-tDX0tg28-pUOlhzE,26
|
1698
1698
|
fastlife/shared_utils/infer.py,sha256=CJjsL_F5OQRG7-0_89MQiZhyd7IcMGyirlQhjtcaIT4,728
|
1699
1699
|
fastlife/shared_utils/resolver.py,sha256=BRU88Ej4oA1iDIyG4Z6T7Q9WFvPHiMm6zuSh623312A,1725
|
@@ -1704,7 +1704,7 @@ fastlife/testing/__init__.py,sha256=vuqwoNUd3BuIp3fm7nkvmYkIGjIimf5zUGhDkeWrg2s,
|
|
1704
1704
|
fastlife/testing/testclient.py,sha256=BC7lLQ_jc59UmknAKzgRtW9a3cpX_V_QLp9Mg2ScLA8,20546
|
1705
1705
|
fastlife/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1706
1706
|
fastlife/views/pydantic_form.py,sha256=fvjk_5-JAugpBPxwD5GkxxsQiz9eAxzWeCSU9kiyc6s,1438
|
1707
|
-
fastlifeweb-0.
|
1708
|
-
fastlifeweb-0.
|
1709
|
-
fastlifeweb-0.
|
1710
|
-
fastlifeweb-0.
|
1707
|
+
fastlifeweb-0.14.0.dist-info/LICENSE,sha256=F75xSseSKMwqzFj8rswYU6NWS3VoWOc_gY3fJYf9_LI,1504
|
1708
|
+
fastlifeweb-0.14.0.dist-info/METADATA,sha256=fvjfxD3v-keA5bZ1agcr4wjeJ3VxhBwwtR0Gs7yHamk,3278
|
1709
|
+
fastlifeweb-0.14.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1710
|
+
fastlifeweb-0.14.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|