fastapi-startkit 0.1.2__tar.gz → 0.1.3__tar.gz
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.
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/PKG-INFO +1 -1
- fastapi_startkit-0.1.3/fastapi_startkit/application.py +132 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/configuration/Configuration.py +1 -6
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/container/container.py +27 -13
- fastapi_startkit-0.1.3/fastapi_startkit/facades/Facade.py +4 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/loader/Loader.py +1 -1
- fastapi_startkit-0.1.3/fastapi_startkit/logging/configs/logging.py +20 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/providers/LoggingProvider.py +11 -0
- fastapi_startkit-0.1.3/fastapi_startkit/providers/Provider.py +21 -0
- fastapi_startkit-0.1.3/fastapi_startkit/tests/configurations/test_config_merge.py +54 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/structures.py +8 -9
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/pyproject.toml +1 -1
- fastapi_startkit-0.1.2/fastapi_startkit/application.py +0 -63
- fastapi_startkit-0.1.2/fastapi_startkit/facades/Facade.py +0 -11
- fastapi_startkit-0.1.2/fastapi_startkit/logging/configs/logging.py +0 -46
- fastapi_startkit-0.1.2/fastapi_startkit/providers/Provider.py +0 -14
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/__init__.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/configuration/__init__.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/configuration/helpers.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/configuration/providers/ConfigurationProvider.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/configuration/providers/__init__.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/container/__init__.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/environment/environment.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/DD.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/ExceptionHandler.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/__init__.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/exceptionite/__init__.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/exceptionite/blocks.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/exceptionite/controllers.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/exceptionite/solutions.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/exceptionite/tabs.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/exceptions.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/handlers/DumpExceptionHandler.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/handlers/HttpExceptionHandler.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/handlers/ModelNotFoundHandler.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Auth.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Auth.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Broadcast.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Cache.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Config.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Config.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Dump.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Dump.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Gate.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Gate.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Hash.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Hash.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Loader.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Loader.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Mail.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Mail.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Notification.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Notification.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Queue.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Queue.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/RateLimiter.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/RateLimiter.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Request.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Request.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Response.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Response.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Session.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Session.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Storage.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Storage.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Url.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Url.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/View.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/View.pyi +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/__init__.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/loader/__init__.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/ChannelFactory.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/Logger.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/__init__.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/BaseChannel.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/DailyChannel.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/MultiBaseChannel.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/SingleChannel.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/SlackChannel.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/StackChannel.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/SyslogChannel.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/TerminalChannel.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/__init__.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/drivers/BaseDriver.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/drivers/LogSingleDriver.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/drivers/LogSlackDriver.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/drivers/LogSyslogDriver.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/drivers/LogTerminalDriver.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/drivers/__init__.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/factory.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/listeners.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/managers/LoggingManager.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/managers/__init__.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/providers/__init__.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/providers/__init__.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/__init__.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/collections.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/console.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/data/mime.types +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/filesystem.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/http.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/location.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/str.py +0 -0
- {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/time.py +0 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Type, Callable, Any
|
|
3
|
+
|
|
4
|
+
from fastapi import FastAPI, APIRouter
|
|
5
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
6
|
+
|
|
7
|
+
from .container import Container
|
|
8
|
+
from .environment.environment import LoadEnvironment
|
|
9
|
+
from .configuration.providers import ConfigurationProvider
|
|
10
|
+
|
|
11
|
+
def app() -> 'Container':
|
|
12
|
+
return Container.instance()
|
|
13
|
+
|
|
14
|
+
class Application(Container):
|
|
15
|
+
DEFAULT_PROVIDERS = [
|
|
16
|
+
ConfigurationProvider,
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
def __init__(self, base_path: str = None, providers=None):
|
|
20
|
+
super().__init__()
|
|
21
|
+
|
|
22
|
+
self.base_path: str = base_path
|
|
23
|
+
self.providers = self.DEFAULT_PROVIDERS + (providers or [])
|
|
24
|
+
self.published_resources = {}
|
|
25
|
+
|
|
26
|
+
# Set global singleton
|
|
27
|
+
Container.set_instance(self)
|
|
28
|
+
|
|
29
|
+
# Boot application
|
|
30
|
+
self.load_environment()
|
|
31
|
+
self.configure_paths()
|
|
32
|
+
self.register_providers()
|
|
33
|
+
|
|
34
|
+
self._fastapi = FastAPI()
|
|
35
|
+
self.load_providers()
|
|
36
|
+
|
|
37
|
+
def register_providers(self):
|
|
38
|
+
providers = []
|
|
39
|
+
for provider_data in self.providers:
|
|
40
|
+
config = {}
|
|
41
|
+
if isinstance(provider_data, tuple):
|
|
42
|
+
provider_class, config = provider_data
|
|
43
|
+
else:
|
|
44
|
+
provider_class = provider_data
|
|
45
|
+
|
|
46
|
+
provider = provider_class(self, config=config)
|
|
47
|
+
provider.register()
|
|
48
|
+
providers.append(provider)
|
|
49
|
+
|
|
50
|
+
self.providers = providers
|
|
51
|
+
return self
|
|
52
|
+
|
|
53
|
+
def load_providers(self):
|
|
54
|
+
for provider in self.providers:
|
|
55
|
+
self.resolve(provider.boot)
|
|
56
|
+
return self
|
|
57
|
+
|
|
58
|
+
def use_fastapi(self, fastapi: FastAPI):
|
|
59
|
+
self._fastapi = fastapi
|
|
60
|
+
return self
|
|
61
|
+
|
|
62
|
+
def get(self, path: str, **kwargs) -> Callable:
|
|
63
|
+
return self._fastapi.get(path, **kwargs)
|
|
64
|
+
|
|
65
|
+
def post(self, path: str, **kwargs) -> Callable:
|
|
66
|
+
return self._fastapi.post(path, **kwargs)
|
|
67
|
+
|
|
68
|
+
def put(self, path: str, **kwargs) -> Callable:
|
|
69
|
+
return self._fastapi.put(path, **kwargs)
|
|
70
|
+
|
|
71
|
+
def delete(self, path: str, **kwargs) -> Callable:
|
|
72
|
+
return self._fastapi.delete(path, **kwargs)
|
|
73
|
+
|
|
74
|
+
def patch(self, path: str, **kwargs) -> Callable:
|
|
75
|
+
return self._fastapi.patch(path, **kwargs)
|
|
76
|
+
|
|
77
|
+
def options(self, path: str, **kwargs) -> Callable:
|
|
78
|
+
return self._fastapi.options(path, **kwargs)
|
|
79
|
+
|
|
80
|
+
def head(self, path: str, **kwargs) -> Callable:
|
|
81
|
+
return self._fastapi.head(path, **kwargs)
|
|
82
|
+
|
|
83
|
+
def trace(self, path: str, **kwargs) -> Callable:
|
|
84
|
+
return self._fastapi.trace(path, **kwargs)
|
|
85
|
+
|
|
86
|
+
# Include routers
|
|
87
|
+
def include_router(self, router: APIRouter, **kwargs):
|
|
88
|
+
self._fastapi.include_router(router, **kwargs)
|
|
89
|
+
return self
|
|
90
|
+
|
|
91
|
+
# Add middleware
|
|
92
|
+
def add_middleware(self, middleware_class: Type[BaseHTTPMiddleware], **options):
|
|
93
|
+
self._fastapi.add_middleware(middleware_class, **options)
|
|
94
|
+
return self
|
|
95
|
+
|
|
96
|
+
# Add event handlers (startup/shutdown)
|
|
97
|
+
def add_event_handler(self, event_type: str, func: Callable[..., Any]):
|
|
98
|
+
self._fastapi.add_event_handler(event_type, func)
|
|
99
|
+
return self
|
|
100
|
+
|
|
101
|
+
# Mount sub-apps
|
|
102
|
+
def mount(self, path: str, app_instance: FastAPI, **kwargs):
|
|
103
|
+
self._fastapi.mount(path, app_instance, **kwargs)
|
|
104
|
+
return self
|
|
105
|
+
|
|
106
|
+
# Add custom exception handlers
|
|
107
|
+
def add_exception_handler(self, exc_class_or_status_code: Any, handler: Callable[..., Any]):
|
|
108
|
+
self._fastapi.add_exception_handler(exc_class_or_status_code, handler)
|
|
109
|
+
return self
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def fastapi(self) -> FastAPI:
|
|
113
|
+
return self._fastapi
|
|
114
|
+
|
|
115
|
+
def __call__(self, *args, **kwargs):
|
|
116
|
+
return self._fastapi
|
|
117
|
+
|
|
118
|
+
def load_environment(self):
|
|
119
|
+
LoadEnvironment(base_path=self.base_path)
|
|
120
|
+
|
|
121
|
+
def configure_paths(self):
|
|
122
|
+
self.bind('config.location', os.path.join(self.base_path, "config"))
|
|
123
|
+
|
|
124
|
+
def use_config_path(self, path: str = None):
|
|
125
|
+
self.bind('config.location', path)
|
|
126
|
+
|
|
127
|
+
return self
|
|
128
|
+
|
|
129
|
+
def use_storage_path(self, path: str = None):
|
|
130
|
+
self.bind('storage.location', path)
|
|
131
|
+
|
|
132
|
+
return self
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/configuration/Configuration.py
RENAMED
|
@@ -33,11 +33,6 @@ class Configuration:
|
|
|
33
33
|
for name, value in params.items():
|
|
34
34
|
self._config[f"{module_name}.{name.lower()}"] = value
|
|
35
35
|
|
|
36
|
-
# check loaded configuration
|
|
37
|
-
if not self._config.get("application"):
|
|
38
|
-
raise InvalidConfigurationLocation(
|
|
39
|
-
f"Config directory {config_root} does not contain required configuration files."
|
|
40
|
-
)
|
|
41
36
|
|
|
42
37
|
def merge_with(self, path, external_config):
|
|
43
38
|
"""Merge external config at key with project config at same key. It's especially
|
|
@@ -53,7 +48,7 @@ class Configuration:
|
|
|
53
48
|
)
|
|
54
49
|
if isinstance(external_config, str):
|
|
55
50
|
# config is a path and should be loaded
|
|
56
|
-
params = Loader.get_parameters(external_config)
|
|
51
|
+
params = Loader().get_parameters(external_config)
|
|
57
52
|
else:
|
|
58
53
|
params = external_config
|
|
59
54
|
base_config = {name.lower(): value for name, value in params.items()}
|
|
@@ -15,19 +15,33 @@ class Container:
|
|
|
15
15
|
Performs bindings and resolving of objects to and from the container.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
18
|
+
_instance = None
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def set_instance(cls, instance):
|
|
22
|
+
cls._instance = instance
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def instance(cls):
|
|
26
|
+
if cls._instance is None:
|
|
27
|
+
raise RuntimeError("Container not initialized")
|
|
28
|
+
return cls._instance
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def __init__(self):
|
|
32
|
+
if not hasattr(self, 'objects'):
|
|
33
|
+
self.objects = {}
|
|
34
|
+
self.strict = False
|
|
35
|
+
self.override = True
|
|
36
|
+
self.resolve_parameters = {}
|
|
37
|
+
self.remember = False
|
|
38
|
+
self._hooks = {
|
|
39
|
+
"make": {},
|
|
40
|
+
"bind": {},
|
|
41
|
+
"resolve": {},
|
|
42
|
+
}
|
|
43
|
+
self.swaps = {}
|
|
44
|
+
self._remembered = {}
|
|
31
45
|
|
|
32
46
|
def bind(self, name, class_obj):
|
|
33
47
|
"""Bind classes into the container with a key value pair.
|
|
@@ -25,7 +25,7 @@ class Loader:
|
|
|
25
25
|
module_paths = list(map(as_filepath, files_or_directories))
|
|
26
26
|
for module_loader, name, _ in pkgutil.iter_modules(module_paths):
|
|
27
27
|
module = load(
|
|
28
|
-
f"{
|
|
28
|
+
f"{module_loader.path}/{name}.py",
|
|
29
29
|
raise_exception=raise_exception,
|
|
30
30
|
)
|
|
31
31
|
_modules.update({name: module})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from fastapi_startkit.environment.environment import env
|
|
2
|
+
|
|
3
|
+
DEFAULT = env('LOG_CHANNEL', 'single')
|
|
4
|
+
|
|
5
|
+
CHANNELS = {
|
|
6
|
+
'timezone': env('LOG_TIMEZONE', 'UTC'),
|
|
7
|
+
'single': {
|
|
8
|
+
'driver': 'single',
|
|
9
|
+
'level': 'debug',
|
|
10
|
+
'path': 'storage/logs/single.log'
|
|
11
|
+
},
|
|
12
|
+
'stack': {
|
|
13
|
+
'driver': 'stack',
|
|
14
|
+
'channels': ['single', 'terminal']
|
|
15
|
+
},
|
|
16
|
+
'terminal': {
|
|
17
|
+
'driver': 'terminal',
|
|
18
|
+
'level': 'info'
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from fastapi_startkit.providers import Provider
|
|
2
3
|
|
|
3
4
|
from ..ChannelFactory import ChannelFactory
|
|
@@ -9,12 +10,20 @@ from ..managers import LoggingManager
|
|
|
9
10
|
|
|
10
11
|
class LoggingProvider(Provider):
|
|
11
12
|
def register(self):
|
|
13
|
+
self.merge_config_from(self.config, 'logging')
|
|
14
|
+
source = os.path.abspath(os.path.join(os.path.dirname(__file__), "../configs/logging.py"))
|
|
15
|
+
self.merge_config_from(source, 'logging')
|
|
16
|
+
|
|
12
17
|
self.application.bind('LogChannelFactory', ChannelFactory)
|
|
13
18
|
self.application.bind('LogDriverFactory', DriverFactory)
|
|
14
19
|
self.application.bind('LoggingManager', LoggingManager(ChannelFactory, DriverFactory))
|
|
15
20
|
self.application.simple(LoggerExceptionListener)
|
|
16
21
|
|
|
17
22
|
def boot(self):
|
|
23
|
+
source = os.path.abspath(os.path.join(os.path.dirname(__file__), "../configs/logging.py"))
|
|
24
|
+
self.publishes({
|
|
25
|
+
source: 'config/logging.py'
|
|
26
|
+
})
|
|
18
27
|
config = self.application.make('config')
|
|
19
28
|
if not config.get('logging.default'):
|
|
20
29
|
return
|
|
@@ -23,3 +32,5 @@ class LoggingProvider(Provider):
|
|
|
23
32
|
|
|
24
33
|
self.application.bind('logger', channel)
|
|
25
34
|
self.application.swap(Logger, channel)
|
|
35
|
+
|
|
36
|
+
self.application.make('logger').info(self.config)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from ..container import Container
|
|
5
|
+
|
|
6
|
+
class Provider:
|
|
7
|
+
def __init__(self, application, config: dict = None) -> None:
|
|
8
|
+
self.application = application
|
|
9
|
+
self.config = config or {}
|
|
10
|
+
|
|
11
|
+
def register(self) -> None:
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
def boot(self) -> None:
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
def merge_config_from(self, source: str | dict, key: str) -> None:
|
|
18
|
+
self.application.make('config').merge_with(key, source)
|
|
19
|
+
|
|
20
|
+
def publishes(self, resources: dict, tag: str = None) -> None:
|
|
21
|
+
self.application.published_resources.update(resources)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from unittest.mock import MagicMock, patch
|
|
2
|
+
from fastapi_startkit.configuration import Configuration
|
|
3
|
+
from fastapi_startkit.loader import Loader
|
|
4
|
+
|
|
5
|
+
class TestConfiguration:
|
|
6
|
+
def test_merge_with_dict(self):
|
|
7
|
+
"""Test merge_with accepts a dictionary and merges it as defaults."""
|
|
8
|
+
app = MagicMock()
|
|
9
|
+
config = Configuration(app)
|
|
10
|
+
|
|
11
|
+
# Set up the existing config
|
|
12
|
+
config.set('testkey', {'existing_key': 'existing_value'})
|
|
13
|
+
|
|
14
|
+
# Dictionary to merge (defaults)
|
|
15
|
+
defaults = {'new_key': 'new_value', 'existing_key': 'default_value'}
|
|
16
|
+
|
|
17
|
+
# Act
|
|
18
|
+
config.merge_with('testkey', defaults)
|
|
19
|
+
|
|
20
|
+
# Assert
|
|
21
|
+
merged = config.get('testkey')
|
|
22
|
+
# New keys should be added
|
|
23
|
+
assert merged['new_key'] == 'new_value'
|
|
24
|
+
# Existing keys should be PRESERVED (Low Priority behavior of merge_with)
|
|
25
|
+
# {**base_config, **self.get(path, {})} -> Existing overwrites base
|
|
26
|
+
assert merged['existing_key'] == 'existing_value'
|
|
27
|
+
|
|
28
|
+
def test_merge_with_file_path(self):
|
|
29
|
+
"""Test merge_with accepts a file path, loads it, and merges as defaults."""
|
|
30
|
+
app = MagicMock()
|
|
31
|
+
config = Configuration(app)
|
|
32
|
+
|
|
33
|
+
# Setup
|
|
34
|
+
config.set('testkey', {'existing': 'orig'})
|
|
35
|
+
|
|
36
|
+
# Mock Loader to return params from file
|
|
37
|
+
with patch('fastapi_startkit.configuration.Configuration.Loader') as MockLoaderClass:
|
|
38
|
+
mock_loader = MockLoaderClass.return_value
|
|
39
|
+
mock_loader.get_parameters.return_value = {
|
|
40
|
+
'New': 'from_file',
|
|
41
|
+
'Existing': 'default_from_file'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Act
|
|
45
|
+
config.merge_with('testkey', '/path/to/config.py')
|
|
46
|
+
|
|
47
|
+
# Assert
|
|
48
|
+
mock_loader.get_parameters.assert_called_once_with('/path/to/config.py')
|
|
49
|
+
|
|
50
|
+
merged = config.get('testkey')
|
|
51
|
+
# Keys are lowercased by merge_with logic: {name.lower(): value ...}
|
|
52
|
+
assert merged['new'] == 'from_file'
|
|
53
|
+
# Existing check
|
|
54
|
+
assert merged['existing'] == 'orig'
|
|
@@ -20,17 +20,16 @@ def load(path, object_name=None, default=None, raise_exception=False):
|
|
|
20
20
|
Returns:
|
|
21
21
|
{object} -- The value (or default) read in the module or the module if no object name
|
|
22
22
|
"""
|
|
23
|
-
# modularize path if needed
|
|
24
|
-
module_path = modularize(path)
|
|
25
|
-
# module = pydoc.locate(dotted_path)
|
|
26
23
|
try:
|
|
27
|
-
|
|
24
|
+
name = path.split('/')[-1].replace('.py', '') if '/' in path else path.replace('.py', '')
|
|
25
|
+
spec = importlib.util.spec_from_file_location(name, path)
|
|
26
|
+
module = importlib.util.module_from_spec(spec)
|
|
27
|
+
spec.loader.exec_module(module)
|
|
28
28
|
except Exception as e:
|
|
29
29
|
error_message = (
|
|
30
|
-
f"'{
|
|
30
|
+
f"'{path}' error when loading from file: {str(e)}"
|
|
31
31
|
)
|
|
32
32
|
print("Warning: " + error_message)
|
|
33
|
-
|
|
34
33
|
if raise_exception:
|
|
35
34
|
raise LoaderNotFound(error_message)
|
|
36
35
|
return None
|
|
@@ -42,12 +41,12 @@ def load(path, object_name=None, default=None, raise_exception=False):
|
|
|
42
41
|
return getattr(module, object_name)
|
|
43
42
|
except KeyError:
|
|
44
43
|
if raise_exception:
|
|
45
|
-
raise LoaderNotFound(f"{object_name} not found in {
|
|
44
|
+
raise LoaderNotFound(f"{object_name} not found in {path}")
|
|
46
45
|
else:
|
|
47
46
|
return default
|
|
48
47
|
|
|
49
48
|
|
|
50
|
-
def data(dictionary=
|
|
49
|
+
def data(dictionary=None):
|
|
51
50
|
"""Transform the given dictionary to be read/written with dot notation.
|
|
52
51
|
|
|
53
52
|
Arguments:
|
|
@@ -56,7 +55,7 @@ def data(dictionary={}):
|
|
|
56
55
|
Returns:
|
|
57
56
|
{dict} -- A dot dictionary
|
|
58
57
|
"""
|
|
59
|
-
return dotty(dictionary)
|
|
58
|
+
return dotty(dictionary or {})
|
|
60
59
|
|
|
61
60
|
|
|
62
61
|
def data_get(dictionary, key, default=None):
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
from fastapi import FastAPI
|
|
4
|
-
|
|
5
|
-
from .container import Container
|
|
6
|
-
from .environment.environment import LoadEnvironment
|
|
7
|
-
from .facades import Facade
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class Application(Container):
|
|
11
|
-
def __init__(self, base_path: str = None, providers=None):
|
|
12
|
-
self.base_path: str = base_path
|
|
13
|
-
self.providers = providers if providers else []
|
|
14
|
-
|
|
15
|
-
Facade.application = self
|
|
16
|
-
|
|
17
|
-
self.load_environment()
|
|
18
|
-
self.configure_paths()
|
|
19
|
-
self.register_providers()
|
|
20
|
-
|
|
21
|
-
self.fastapi = FastAPI()
|
|
22
|
-
self.load_providers()
|
|
23
|
-
|
|
24
|
-
def register_providers(self):
|
|
25
|
-
providers = []
|
|
26
|
-
for provider_class in self.providers:
|
|
27
|
-
provider = provider_class(self)
|
|
28
|
-
provider.register()
|
|
29
|
-
providers.append(provider)
|
|
30
|
-
|
|
31
|
-
self.providers = providers
|
|
32
|
-
return self
|
|
33
|
-
|
|
34
|
-
def load_providers(self):
|
|
35
|
-
for provider in self.providers:
|
|
36
|
-
self.resolve(provider.boot)
|
|
37
|
-
return self
|
|
38
|
-
|
|
39
|
-
def use_fastapi(self, fastapi: FastAPI):
|
|
40
|
-
self.fastapi = fastapi
|
|
41
|
-
return self
|
|
42
|
-
|
|
43
|
-
def get(self, path: str):
|
|
44
|
-
return self.fastapi.get(path)
|
|
45
|
-
|
|
46
|
-
def __call__(self):
|
|
47
|
-
return self.fastapi
|
|
48
|
-
|
|
49
|
-
def load_environment(self):
|
|
50
|
-
LoadEnvironment(base_path=self.base_path)
|
|
51
|
-
|
|
52
|
-
def configure_paths(self):
|
|
53
|
-
self.bind('config.location', os.path.join(self.base_path, "config"))
|
|
54
|
-
|
|
55
|
-
def use_config_path(self, path: str = None):
|
|
56
|
-
self.bind('config.location', path)
|
|
57
|
-
|
|
58
|
-
return self
|
|
59
|
-
|
|
60
|
-
def use_storage_path(self, path: str = None):
|
|
61
|
-
self.bind('storage.location', path)
|
|
62
|
-
|
|
63
|
-
return self
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
class Facade(type):
|
|
2
|
-
|
|
3
|
-
application = None
|
|
4
|
-
|
|
5
|
-
def __getattr__(self, attribute, *args, **kwargs):
|
|
6
|
-
if self.application:
|
|
7
|
-
return getattr(self.application.make(self.key), attribute)
|
|
8
|
-
|
|
9
|
-
from wsgi import application
|
|
10
|
-
|
|
11
|
-
return getattr(application.make(self.key), attribute)
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
""" Logging Settings """
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
from fastapi_startkit.environment.environment import env
|
|
5
|
-
|
|
6
|
-
"""Default Channel
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
DEFAULT=env('LOG_CHANNEL', 'single')
|
|
10
|
-
|
|
11
|
-
"""Channels
|
|
12
|
-
"""
|
|
13
|
-
CHANNELS = {
|
|
14
|
-
'timezone': env('LOG_TIMEZONE', 'UTC'),
|
|
15
|
-
'single': {
|
|
16
|
-
'driver': 'single',
|
|
17
|
-
'level': 'debug',
|
|
18
|
-
'path': 'storage/logs/single.log'
|
|
19
|
-
},
|
|
20
|
-
'stack': {
|
|
21
|
-
'driver': 'stack',
|
|
22
|
-
'channels': ['single', 'daily', 'slack', 'terminal']
|
|
23
|
-
},
|
|
24
|
-
'daily': {
|
|
25
|
-
'driver': 'daily',
|
|
26
|
-
'level': 'debug',
|
|
27
|
-
'path': 'storage/logs'
|
|
28
|
-
},
|
|
29
|
-
'terminal': {
|
|
30
|
-
'driver': 'terminal',
|
|
31
|
-
'level': 'info',
|
|
32
|
-
},
|
|
33
|
-
'slack': {
|
|
34
|
-
'driver': 'slack',
|
|
35
|
-
'channel': '#bot',
|
|
36
|
-
'emoji': ':warning:',
|
|
37
|
-
'username': 'Logging Bot',
|
|
38
|
-
'token': env('SLACK_TOKEN', None),
|
|
39
|
-
'level': 'debug'
|
|
40
|
-
},
|
|
41
|
-
'syslog': {
|
|
42
|
-
'driver': 'syslog',
|
|
43
|
-
'path': '/var/run/syslog',
|
|
44
|
-
'level': 'debug'
|
|
45
|
-
}
|
|
46
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING
|
|
2
|
-
|
|
3
|
-
if TYPE_CHECKING:
|
|
4
|
-
from ..container import Container
|
|
5
|
-
|
|
6
|
-
class Provider:
|
|
7
|
-
def __init__(self, application) -> None:
|
|
8
|
-
self.application = application
|
|
9
|
-
|
|
10
|
-
def register(self) -> None:
|
|
11
|
-
pass
|
|
12
|
-
|
|
13
|
-
def boot(self) -> None:
|
|
14
|
-
pass
|
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/configuration/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/environment/environment.py
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/ExceptionHandler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/exceptionite/blocks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/exceptionite/tabs.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/ChannelFactory.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/BaseChannel.py
RENAMED
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/DailyChannel.py
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/SingleChannel.py
RENAMED
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/SlackChannel.py
RENAMED
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/StackChannel.py
RENAMED
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/SyslogChannel.py
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/__init__.py
RENAMED
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/drivers/BaseDriver.py
RENAMED
|
File without changes
|
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/drivers/LogSlackDriver.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/drivers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/managers/__init__.py
RENAMED
|
File without changes
|
{fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/providers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|