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.
Files changed (104) hide show
  1. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/PKG-INFO +1 -1
  2. fastapi_startkit-0.1.3/fastapi_startkit/application.py +132 -0
  3. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/configuration/Configuration.py +1 -6
  4. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/container/container.py +27 -13
  5. fastapi_startkit-0.1.3/fastapi_startkit/facades/Facade.py +4 -0
  6. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/loader/Loader.py +1 -1
  7. fastapi_startkit-0.1.3/fastapi_startkit/logging/configs/logging.py +20 -0
  8. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/providers/LoggingProvider.py +11 -0
  9. fastapi_startkit-0.1.3/fastapi_startkit/providers/Provider.py +21 -0
  10. fastapi_startkit-0.1.3/fastapi_startkit/tests/configurations/test_config_merge.py +54 -0
  11. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/structures.py +8 -9
  12. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/pyproject.toml +1 -1
  13. fastapi_startkit-0.1.2/fastapi_startkit/application.py +0 -63
  14. fastapi_startkit-0.1.2/fastapi_startkit/facades/Facade.py +0 -11
  15. fastapi_startkit-0.1.2/fastapi_startkit/logging/configs/logging.py +0 -46
  16. fastapi_startkit-0.1.2/fastapi_startkit/providers/Provider.py +0 -14
  17. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/__init__.py +0 -0
  18. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/configuration/__init__.py +0 -0
  19. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/configuration/helpers.py +0 -0
  20. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/configuration/providers/ConfigurationProvider.py +0 -0
  21. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/configuration/providers/__init__.py +0 -0
  22. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/container/__init__.py +0 -0
  23. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/environment/environment.py +0 -0
  24. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/DD.py +0 -0
  25. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/ExceptionHandler.py +0 -0
  26. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/__init__.py +0 -0
  27. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/exceptionite/__init__.py +0 -0
  28. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/exceptionite/blocks.py +0 -0
  29. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/exceptionite/controllers.py +0 -0
  30. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/exceptionite/solutions.py +0 -0
  31. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/exceptionite/tabs.py +0 -0
  32. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/exceptions.py +0 -0
  33. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/handlers/DumpExceptionHandler.py +0 -0
  34. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/handlers/HttpExceptionHandler.py +0 -0
  35. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/exceptions/handlers/ModelNotFoundHandler.py +0 -0
  36. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Auth.py +0 -0
  37. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Auth.pyi +0 -0
  38. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Broadcast.py +0 -0
  39. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Cache.py +0 -0
  40. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Config.py +0 -0
  41. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Config.pyi +0 -0
  42. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Dump.py +0 -0
  43. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Dump.pyi +0 -0
  44. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Gate.py +0 -0
  45. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Gate.pyi +0 -0
  46. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Hash.py +0 -0
  47. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Hash.pyi +0 -0
  48. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Loader.py +0 -0
  49. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Loader.pyi +0 -0
  50. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Mail.py +0 -0
  51. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Mail.pyi +0 -0
  52. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Notification.py +0 -0
  53. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Notification.pyi +0 -0
  54. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Queue.py +0 -0
  55. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Queue.pyi +0 -0
  56. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/RateLimiter.py +0 -0
  57. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/RateLimiter.pyi +0 -0
  58. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Request.py +0 -0
  59. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Request.pyi +0 -0
  60. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Response.py +0 -0
  61. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Response.pyi +0 -0
  62. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Session.py +0 -0
  63. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Session.pyi +0 -0
  64. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Storage.py +0 -0
  65. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Storage.pyi +0 -0
  66. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Url.py +0 -0
  67. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/Url.pyi +0 -0
  68. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/View.py +0 -0
  69. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/View.pyi +0 -0
  70. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/facades/__init__.py +0 -0
  71. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/loader/__init__.py +0 -0
  72. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/ChannelFactory.py +0 -0
  73. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/Logger.py +0 -0
  74. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/__init__.py +0 -0
  75. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/BaseChannel.py +0 -0
  76. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/DailyChannel.py +0 -0
  77. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/MultiBaseChannel.py +0 -0
  78. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/SingleChannel.py +0 -0
  79. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/SlackChannel.py +0 -0
  80. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/StackChannel.py +0 -0
  81. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/SyslogChannel.py +0 -0
  82. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/TerminalChannel.py +0 -0
  83. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/channels/__init__.py +0 -0
  84. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/drivers/BaseDriver.py +0 -0
  85. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/drivers/LogSingleDriver.py +0 -0
  86. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/drivers/LogSlackDriver.py +0 -0
  87. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/drivers/LogSyslogDriver.py +0 -0
  88. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/drivers/LogTerminalDriver.py +0 -0
  89. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/drivers/__init__.py +0 -0
  90. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/factory.py +0 -0
  91. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/listeners.py +0 -0
  92. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/managers/LoggingManager.py +0 -0
  93. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/managers/__init__.py +0 -0
  94. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/logging/providers/__init__.py +0 -0
  95. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/providers/__init__.py +0 -0
  96. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/__init__.py +0 -0
  97. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/collections.py +0 -0
  98. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/console.py +0 -0
  99. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/data/mime.types +0 -0
  100. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/filesystem.py +0 -0
  101. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/http.py +0 -0
  102. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/location.py +0 -0
  103. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/str.py +0 -0
  104. {fastapi_startkit-0.1.2 → fastapi_startkit-0.1.3}/fastapi_startkit/utils/time.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-startkit
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Fastapi_Startkit components
5
5
  Author: Bedram Tamang
6
6
  Author-email: tmgbedu@gmail.com
@@ -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
@@ -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
- objects = {}
19
- strict = False
20
- override = True
21
- resolve_parameters = {}
22
- remember = False
23
- _hooks = {
24
- "make": {},
25
- "bind": {},
26
- "resolve": {},
27
- }
28
-
29
- swaps = {}
30
- _remembered = {}
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.
@@ -0,0 +1,4 @@
1
+ class Facade(type):
2
+ def __getattr__(self, attribute, *args, **kwargs):
3
+ from ..application import app
4
+ return getattr(app().make(self.key), attribute)
@@ -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"{os.path.relpath(module_loader.path)}.{name}",
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
- module = importlib.import_module(module_path)
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"'{module_path}' not found OR error when importing this module: {str(e)}"
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 {module_path}")
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,6 +1,6 @@
1
1
  [project]
2
2
  name = "fastapi-startkit"
3
- version = "0.1.2"
3
+ version = "0.1.3"
4
4
  description = "Fastapi_Startkit components"
5
5
  authors = [
6
6
  {name = "Bedram Tamang", email = "tmgbedu@gmail.com"}
@@ -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