orionis 0.404.0__py3-none-any.whl → 0.406.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.
Files changed (165) hide show
  1. orionis/console/base/command.py +57 -50
  2. orionis/console/base/contracts/command.py +68 -0
  3. orionis/console/dynamic/contracts/progress_bar.py +3 -3
  4. orionis/console/dynamic/progress_bar.py +8 -8
  5. orionis/console/output/console.py +8 -2
  6. orionis/console/output/contracts/console.py +1 -1
  7. orionis/container/container.py +2 -2
  8. orionis/container/context/scope.py +4 -1
  9. orionis/container/contracts/service_provider.py +2 -2
  10. orionis/container/entities/binding.py +31 -44
  11. orionis/container/enums/lifetimes.py +22 -1
  12. orionis/container/facades/facade.py +1 -2
  13. orionis/container/providers/service_provider.py +2 -2
  14. orionis/foundation/application.py +542 -248
  15. orionis/foundation/config/app/entities/app.py +107 -90
  16. orionis/foundation/config/auth/entities/auth.py +4 -33
  17. orionis/foundation/config/cache/entities/cache.py +18 -41
  18. orionis/foundation/config/cache/entities/file.py +8 -35
  19. orionis/foundation/config/cache/entities/stores.py +17 -38
  20. orionis/foundation/config/cors/entities/cors.py +41 -54
  21. orionis/foundation/config/database/entities/connections.py +40 -56
  22. orionis/foundation/config/database/entities/database.py +11 -38
  23. orionis/foundation/config/database/entities/mysql.py +48 -76
  24. orionis/foundation/config/database/entities/oracle.py +30 -57
  25. orionis/foundation/config/database/entities/pgsql.py +45 -61
  26. orionis/foundation/config/database/entities/sqlite.py +26 -53
  27. orionis/foundation/config/filesystems/entitites/aws.py +28 -49
  28. orionis/foundation/config/filesystems/entitites/disks.py +27 -47
  29. orionis/foundation/config/filesystems/entitites/filesystems.py +15 -37
  30. orionis/foundation/config/filesystems/entitites/local.py +9 -35
  31. orionis/foundation/config/filesystems/entitites/public.py +14 -41
  32. orionis/foundation/config/logging/entities/channels.py +56 -86
  33. orionis/foundation/config/logging/entities/chunked.py +18 -10
  34. orionis/foundation/config/logging/entities/daily.py +17 -9
  35. orionis/foundation/config/logging/entities/hourly.py +15 -7
  36. orionis/foundation/config/logging/entities/logging.py +12 -18
  37. orionis/foundation/config/logging/entities/monthly.py +16 -8
  38. orionis/foundation/config/logging/entities/stack.py +15 -7
  39. orionis/foundation/config/logging/entities/weekly.py +15 -7
  40. orionis/foundation/config/logging/validators/path.py +6 -0
  41. orionis/foundation/config/mail/entities/file.py +9 -36
  42. orionis/foundation/config/mail/entities/mail.py +22 -40
  43. orionis/foundation/config/mail/entities/mailers.py +29 -44
  44. orionis/foundation/config/mail/entities/smtp.py +47 -48
  45. orionis/foundation/config/queue/entities/brokers.py +19 -41
  46. orionis/foundation/config/queue/entities/database.py +24 -46
  47. orionis/foundation/config/queue/entities/queue.py +28 -40
  48. orionis/foundation/config/roots/paths.py +272 -468
  49. orionis/foundation/config/session/entities/session.py +23 -53
  50. orionis/foundation/config/startup.py +165 -135
  51. orionis/foundation/config/testing/entities/testing.py +137 -122
  52. orionis/foundation/config/testing/enums/__init__.py +6 -2
  53. orionis/foundation/config/testing/enums/drivers.py +16 -0
  54. orionis/foundation/config/testing/enums/verbosity.py +18 -0
  55. orionis/foundation/contracts/application.py +152 -362
  56. orionis/foundation/providers/console_provider.py +24 -2
  57. orionis/foundation/providers/dumper_provider.py +24 -2
  58. orionis/foundation/providers/logger_provider.py +24 -2
  59. orionis/foundation/providers/path_resolver_provider.py +25 -2
  60. orionis/foundation/providers/progress_bar_provider.py +24 -2
  61. orionis/foundation/providers/testing_provider.py +39 -0
  62. orionis/foundation/providers/workers_provider.py +24 -2
  63. orionis/metadata/framework.py +1 -1
  64. orionis/services/environment/helpers/functions.py +1 -2
  65. orionis/services/environment/key/__init__.py +0 -0
  66. orionis/services/environment/key/key_generator.py +37 -0
  67. orionis/services/log/handlers/filename.py +64 -0
  68. orionis/services/log/handlers/size_rotating.py +9 -40
  69. orionis/services/log/handlers/timed_rotating.py +9 -41
  70. orionis/services/log/log_service.py +9 -52
  71. orionis/support/entities/__init__.py +0 -0
  72. orionis/support/entities/base.py +104 -0
  73. orionis/support/facades/testing.py +15 -0
  74. orionis/support/facades/workers.py +1 -1
  75. orionis/test/cases/asynchronous.py +0 -11
  76. orionis/test/cases/synchronous.py +0 -9
  77. orionis/test/contracts/dumper.py +11 -4
  78. orionis/test/contracts/kernel.py +5 -110
  79. orionis/test/contracts/logs.py +27 -65
  80. orionis/test/contracts/printer.py +16 -128
  81. orionis/test/contracts/test_result.py +100 -0
  82. orionis/test/contracts/unit_test.py +87 -150
  83. orionis/test/core/unit_test.py +608 -554
  84. orionis/test/entities/result.py +22 -2
  85. orionis/test/enums/__init__.py +0 -2
  86. orionis/test/enums/status.py +14 -9
  87. orionis/test/exceptions/config.py +9 -1
  88. orionis/test/exceptions/failure.py +34 -11
  89. orionis/test/exceptions/persistence.py +10 -2
  90. orionis/test/exceptions/runtime.py +9 -1
  91. orionis/test/exceptions/value.py +13 -1
  92. orionis/test/kernel.py +87 -289
  93. orionis/test/output/dumper.py +82 -18
  94. orionis/test/output/printer.py +399 -156
  95. orionis/test/records/logs.py +203 -82
  96. orionis/test/validators/__init__.py +33 -0
  97. orionis/test/validators/base_path.py +45 -0
  98. orionis/test/validators/execution_mode.py +45 -0
  99. orionis/test/validators/fail_fast.py +37 -0
  100. orionis/test/validators/folder_path.py +34 -0
  101. orionis/test/validators/module_name.py +31 -0
  102. orionis/test/validators/name_pattern.py +40 -0
  103. orionis/test/validators/pattern.py +36 -0
  104. orionis/test/validators/persistent.py +42 -0
  105. orionis/test/validators/persistent_driver.py +43 -0
  106. orionis/test/validators/print_result.py +37 -0
  107. orionis/test/validators/tags.py +37 -0
  108. orionis/test/validators/throw_exception.py +39 -0
  109. orionis/test/validators/verbosity.py +37 -0
  110. orionis/test/validators/web_report.py +35 -0
  111. orionis/test/validators/workers.py +31 -0
  112. orionis/test/view/render.py +48 -54
  113. {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/METADATA +1 -1
  114. {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/RECORD +160 -108
  115. tests/container/__init__.py +0 -0
  116. tests/container/context/__init__.py +0 -0
  117. tests/container/context/test_manager.py +27 -0
  118. tests/container/context/test_scope.py +23 -0
  119. tests/container/entities/__init__.py +0 -0
  120. tests/container/entities/test_binding.py +133 -0
  121. tests/container/enums/__init__.py +0 -0
  122. tests/container/enums/test_lifetimes.py +63 -0
  123. tests/container/facades/__init__.py +0 -0
  124. tests/container/facades/test_facade.py +61 -0
  125. tests/container/mocks/__init__.py +0 -0
  126. tests/container/mocks/mock_complex_classes.py +482 -0
  127. tests/container/mocks/mock_simple_classes.py +32 -0
  128. tests/container/providers/__init__.py +0 -0
  129. tests/container/providers/test_providers.py +48 -0
  130. tests/container/resolver/__init__.py +0 -0
  131. tests/container/resolver/test_resolver.py +55 -0
  132. tests/container/test_container.py +254 -0
  133. tests/container/test_singleton.py +98 -0
  134. tests/container/test_thread_safety.py +217 -0
  135. tests/container/validators/__init__.py +0 -0
  136. tests/container/validators/test_implements.py +140 -0
  137. tests/container/validators/test_is_abstract_class.py +99 -0
  138. tests/container/validators/test_is_callable.py +73 -0
  139. tests/container/validators/test_is_concrete_class.py +97 -0
  140. tests/container/validators/test_is_instance.py +105 -0
  141. tests/container/validators/test_is_not_subclass.py +117 -0
  142. tests/container/validators/test_is_subclass.py +115 -0
  143. tests/container/validators/test_is_valid_alias.py +113 -0
  144. tests/container/validators/test_lifetime.py +75 -0
  145. tests/foundation/config/logging/test_foundation_config_logging_chunked.py +12 -34
  146. tests/foundation/config/logging/test_foundation_config_logging_daily.py +11 -11
  147. tests/foundation/config/logging/test_foundation_config_logging_hourly.py +7 -8
  148. tests/foundation/config/logging/test_foundation_config_logging_monthly.py +7 -10
  149. tests/foundation/config/logging/test_foundation_config_logging_stack.py +6 -11
  150. tests/foundation/config/logging/test_foundation_config_logging_weekly.py +6 -5
  151. tests/foundation/config/testing/test_foundation_config_testing.py +1 -1
  152. tests/metadata/test_metadata_framework.py +18 -18
  153. tests/testing/test_testing_result.py +117 -117
  154. tests/testing/test_testing_unit.py +209 -209
  155. orionis/foundation/config/base.py +0 -112
  156. orionis/test/arguments/parser.py +0 -187
  157. orionis/test/contracts/parser.py +0 -43
  158. orionis/test/entities/arguments.py +0 -38
  159. orionis/test/enums/execution_mode.py +0 -16
  160. /orionis/{test/arguments → console/base/contracts}/__init__.py +0 -0
  161. /orionis/foundation/config/testing/enums/{test_mode.py → mode.py} +0 -0
  162. {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/WHEEL +0 -0
  163. {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/licenses/LICENCE +0 -0
  164. {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/top_level.txt +0 -0
  165. {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/zip-safe +0 -0
@@ -1,6 +1,8 @@
1
+ import asyncio
1
2
  import time
2
3
  from pathlib import Path
3
4
  from typing import Any, List, Type
5
+ from orionis.console.output.contracts.console import IConsole
4
6
  from orionis.container.container import Container
5
7
  from orionis.container.contracts.service_provider import IServiceProvider
6
8
  from orionis.foundation.config.app.entities.app import App
@@ -12,13 +14,14 @@ from orionis.foundation.config.filesystems.entitites.filesystems import Filesyst
12
14
  from orionis.foundation.config.logging.entities.logging import Logging
13
15
  from orionis.foundation.config.mail.entities.mail import Mail
14
16
  from orionis.foundation.config.queue.entities.queue import Queue
17
+ from orionis.foundation.config.roots.paths import Paths
15
18
  from orionis.foundation.config.session.entities.session import Session
16
19
  from orionis.foundation.config.startup import Configuration
17
20
  from orionis.foundation.config.testing.entities.testing import Testing
18
21
  from orionis.foundation.contracts.application import IApplication
19
- from orionis.foundation.contracts.config import IConfig
20
- from orionis.foundation.exceptions import OrionisTypeError, OrionisRuntimeError
22
+ from orionis.foundation.exceptions import OrionisTypeError, OrionisRuntimeError, OrionisValueError
21
23
  from orionis.foundation.providers.logger_provider import LoggerProvider
24
+ from orionis.services.log.contracts.log_service import ILoggerService
22
25
 
23
26
  class Application(Container, IApplication):
24
27
  """
@@ -48,6 +51,20 @@ class Application(Container, IApplication):
48
51
  """
49
52
  return self.__booted
50
53
 
54
+ @property
55
+ def startAt(
56
+ self
57
+ ) -> int:
58
+ """
59
+ Get the timestamp when the application started.
60
+
61
+ Returns
62
+ -------
63
+ int
64
+ The start time in nanoseconds since epoch
65
+ """
66
+ return self.__startAt
67
+
51
68
  def __init__(
52
69
  self
53
70
  ) -> None:
@@ -57,31 +74,44 @@ class Application(Container, IApplication):
57
74
  Sets up initial state including empty providers list and booted flag.
58
75
  Uses singleton pattern to prevent multiple initializations.
59
76
  """
77
+
60
78
  # Initialize base container with application paths
61
79
  super().__init__()
62
80
 
63
81
  # Singleton pattern - prevent multiple initializations
64
82
  if not hasattr(self, '_Application__initialized'):
83
+
84
+ # Start time in nanoseconds
85
+ self.__startAt = time.time_ns()
86
+
87
+ # Propierty to store service providers.
65
88
  self.__providers: List[IServiceProvider, Any] = []
89
+
90
+ # Property to store configurators and paths
66
91
  self.__configurators : dict = {}
67
- self.__config: dict = {}
92
+
93
+ # Property to indicate if the application has been booted
68
94
  self.__booted: bool = False
69
- self.__startAt = time.time_ns()
95
+
96
+ # Property to store application configuration
97
+ # This will be initialized with default values or from configurators
98
+ self.__config: dict = {}
70
99
 
71
100
  # Flag to prevent re-initialization
72
101
  self.__initialized = True
73
102
 
74
- # << Frameworks Kernel >>
103
+ # === Native Kernels and Providers for Orionis Framework ===
104
+ # Responsible for loading the native kernels and service providers of the Orionis framework.
105
+ # These kernels and providers are essential for the core functionality of the framework.
106
+ # Private methods are used to load these native components, ensuring they cannot be modified externally.
75
107
 
76
108
  def __loadFrameworksKernel(
77
109
  self
78
110
  ) -> None:
79
111
  """
80
112
  Load and register core framework kernels.
81
-
82
- Instantiates and registers kernel components:
83
- - TestKernel: Testing framework kernel
84
113
  """
114
+
85
115
  # Import core framework kernels
86
116
  from orionis.test.kernel import TestKernel, ITestKernel
87
117
 
@@ -94,8 +124,6 @@ class Application(Container, IApplication):
94
124
  for kernel_name, kernel_cls in core_kernels.items():
95
125
  self.instance(kernel_name, kernel_cls(self))
96
126
 
97
- # << Service Providers >>
98
-
99
127
  def __loadFrameworkProviders(
100
128
  self
101
129
  ) -> None:
@@ -110,6 +138,7 @@ class Application(Container, IApplication):
110
138
  from orionis.foundation.providers.path_resolver_provider import PathResolverProvider
111
139
  from orionis.foundation.providers.progress_bar_provider import ProgressBarProvider
112
140
  from orionis.foundation.providers.workers_provider import WorkersProvider
141
+ from orionis.foundation.providers.testing_provider import TestingProvider
113
142
 
114
143
  # Core framework providers
115
144
  core_providers = [
@@ -118,56 +147,18 @@ class Application(Container, IApplication):
118
147
  PathResolverProvider,
119
148
  ProgressBarProvider,
120
149
  WorkersProvider,
121
- LoggerProvider
150
+ LoggerProvider,
151
+ TestingProvider
122
152
  ]
123
153
 
124
154
  # Register each core provider
125
155
  for provider_cls in core_providers:
126
156
  self.addProvider(provider_cls)
127
157
 
128
- def __registerProviders(
129
- self
130
- ) -> None:
131
- """
132
- Register all added service providers.
133
-
134
- Calls the register method on each provider to bind services
135
- into the container.
136
- """
137
-
138
- # Ensure providers list is empty before registration
139
- initialized_providers = []
140
-
141
- # Iterate over each provider and register it
142
- for provider in self.__providers:
143
-
144
- # Initialize the provider
145
- class_provider: IServiceProvider = provider(self)
146
-
147
- # Register the provider in the container
148
- class_provider.register()
149
-
150
- # Add the initialized provider to the list
151
- initialized_providers.append(class_provider)
152
-
153
- # Update the providers list with initialized providers
154
- self.__providers = initialized_providers
155
-
156
- def __bootProviders(
157
- self
158
- ) -> None:
159
- """
160
- Boot all registered service providers.
161
-
162
- Calls the boot method on each provider to initialize services
163
- after all providers have been registered.
164
- """
165
- # Iterate over each provider and boot it
166
- for provider in self.__providers:
167
-
168
- # Ensure provider is initialized before calling boot
169
- if hasattr(provider, 'boot') and callable(getattr(provider, 'boot')):
170
- provider.boot()
158
+ # === Service Provider Registration and Bootstrapping ===
159
+ # These private methods enable developers to register and boot custom service providers.
160
+ # Registration and booting are handled separately, ensuring a clear lifecycle for each provider.
161
+ # Both methods are invoked automatically during application initialization.
171
162
 
172
163
  def withProviders(
173
164
  self,
@@ -232,53 +223,85 @@ class Application(Container, IApplication):
232
223
  # Return self instance.
233
224
  return self
234
225
 
235
- # << Configuration >>
236
-
237
- def __loadConfig(
238
- self,
226
+ def __registerProviders(
227
+ self
239
228
  ) -> None:
240
229
  """
241
- Retrieve a configuration value by key.
230
+ Register all added service providers.
242
231
 
243
- Returns
244
- -------
245
- None
246
- Initializes the application configuration if not already set.
232
+ Calls the register method on each provider to bind services
233
+ into the container.
247
234
  """
248
235
 
249
- # Try to load the configuration
250
- try:
236
+ # Ensure providers list is empty before registration
237
+ initialized_providers = []
251
238
 
252
- # Check if configuration is a dictionary
253
- if not self.__config:
239
+ # Iterate over each provider and register it
240
+ for provider in self.__providers:
254
241
 
255
- # Initialize with default configuration
256
- if not self.__configurators:
257
- self.__config = Configuration().toDict()
242
+ # Initialize the provider
243
+ class_provider: IServiceProvider = provider(self)
244
+
245
+ # Register the provider in the container
246
+ # Check if register is a coroutine function
247
+ if asyncio.iscoroutinefunction(class_provider.register):
248
+ asyncio.run(class_provider.register())
249
+ else:
250
+ class_provider.register()
251
+
252
+ # Add the initialized provider to the list
253
+ initialized_providers.append(class_provider)
254
+
255
+ # Update the providers list with initialized providers
256
+ self.__providers = initialized_providers
257
+
258
+ def __bootProviders(
259
+ self
260
+ ) -> None:
261
+ """
262
+ Boot all registered service providers.
258
263
 
259
- # If configurators are provided, use them to create the configuration
264
+ Calls the boot method on each provider to initialize services
265
+ after all providers have been registered.
266
+ """
267
+
268
+ # Iterate over each provider and boot it
269
+ for provider in self.__providers:
270
+
271
+ # Ensure provider is initialized before calling boot
272
+ if hasattr(provider, 'boot') and callable(getattr(provider, 'boot')):
273
+ # Check if boot is a coroutine function
274
+ if asyncio.iscoroutinefunction(provider.boot):
275
+ asyncio.run(provider.boot())
260
276
  else:
261
- self.__config = Configuration(**self.__configurators).toDict()
277
+ provider.boot()
262
278
 
263
- except Exception as e:
279
+ # Remove the __providers attribute to prevent memory leaks
280
+ if hasattr(self, '_Application__providers'):
281
+ del self.__providers
264
282
 
265
- # Handle any exceptions during configuration loading
266
- raise OrionisRuntimeError(f"Failed to load application configuration: {str(e)}")
283
+ # === Application Skeleton Configuration Methods ===
284
+ # The Orionis framework provides methods to configure each component of the application,
285
+ # enabling the creation of fully customized application skeletons.
286
+ # These configurator loading methods allow developers to tailor the architecture
287
+ # for complex and unique application requirements, supporting advanced customization
288
+ # of every subsystem as needed.
267
289
 
268
290
  def withConfigurators(
269
291
  self,
270
292
  *,
271
- app: App|IConfig = None,
272
- auth: Auth|IConfig = None,
273
- cache : Cache|IConfig = None,
274
- cors : Cors|IConfig = None,
275
- database : Database|IConfig = None,
276
- filesystems : Filesystems|IConfig = None,
277
- logging : Logging|IConfig = None,
278
- mail : Mail|IConfig = None,
279
- queue : Queue|IConfig = None,
280
- session : Session|IConfig = None,
281
- testing : Testing|IConfig = None
293
+ app: App | dict = App(),
294
+ auth: Auth | dict = Auth(),
295
+ cache : Cache | dict = Cache(),
296
+ cors : Cors | dict = Cors(),
297
+ database : Database | dict = Database(),
298
+ filesystems : Filesystems | dict = Filesystems(),
299
+ logging : Logging | dict = Logging(),
300
+ mail : Mail | dict = Mail(),
301
+ path : Paths | dict = Paths(),
302
+ queue : Queue | dict = Queue(),
303
+ session : Session | dict = Session(),
304
+ testing : Testing | dict = Testing()
282
305
  ) -> 'Application':
283
306
  """
284
307
  Configure the application with various service configurators.
@@ -316,64 +339,72 @@ class Application(Container, IApplication):
316
339
  """
317
340
 
318
341
  # Load app configurator
319
- if app is not None and issubclass(app, IConfig):
320
- app = app.config
321
- self.loadConfigApp(app or App())
342
+ if not isinstance(app, (App, dict)):
343
+ raise OrionisTypeError(f"Expected App instance or dict, got {type(app).__name__}")
344
+ self.loadConfigApp(app)
322
345
 
323
346
  # Load auth configurator
324
- if auth is not None and issubclass(auth, IConfig):
325
- auth = auth.config
326
- self.loadConfigAuth(auth or Auth())
347
+ if not isinstance(auth, (Auth, dict)):
348
+ raise OrionisTypeError(f"Expected Auth instance or dict, got {type(auth).__name__}")
349
+ self.loadConfigAuth(auth)
327
350
 
328
351
  # Load cache configurator
329
- if cache is not None and issubclass(cache, IConfig):
330
- cache = cache.config
331
- self.loadConfigCache(cache or Cache())
352
+ if not isinstance(cache, (Cache, dict)):
353
+ raise OrionisTypeError(f"Expected Cache instance or dict, got {type(cache).__name__}")
354
+ self.loadConfigCache(cache)
332
355
 
333
356
  # Load cors configurator
334
- if cors is not None and issubclass(cors, IConfig):
335
- cors = cors.config
336
- self.loadConfigCors(cors or Cors())
357
+ if not isinstance(cors, (Cors, dict)):
358
+ raise OrionisTypeError(f"Expected Cors instance or dict, got {type(cors).__name__}")
359
+ self.loadConfigCors(cors)
337
360
 
338
361
  # Load database configurator
339
- if database is not None and issubclass(database, IConfig):
340
- database = database.config
341
- self.loadConfigDatabase(database or Database())
362
+ if not isinstance(database, (Database, dict)):
363
+ raise OrionisTypeError(f"Expected Database instance or dict, got {type(database).__name__}")
364
+ self.loadConfigDatabase(database)
342
365
 
343
366
  # Load filesystems configurator
344
- if filesystems is not None and issubclass(filesystems, IConfig):
345
- filesystems = filesystems.config
346
- self.loadConfigFilesystems(filesystems or Filesystems())
367
+ if not isinstance(filesystems, (Filesystems, dict)):
368
+ raise OrionisTypeError(f"Expected Filesystems instance or dict, got {type(filesystems).__name__}")
369
+ self.loadConfigFilesystems(filesystems)
347
370
 
348
371
  # Load logging configurator
349
- if logging is not None and issubclass(logging, IConfig):
350
- logging = logging.config
351
- self.loadConfigLogging(logging or Logging())
372
+ if not isinstance(logging, (Logging, dict)):
373
+ raise OrionisTypeError(f"Expected Logging instance or dict, got {type(logging).__name__}")
374
+ self.loadConfigLogging(logging)
352
375
 
353
376
  # Load mail configurator
354
- if mail is not None and issubclass(mail, IConfig):
355
- mail = mail.config
356
- self.loadConfigMail(mail or Mail())
377
+ if not isinstance(mail, (Mail, dict)):
378
+ raise OrionisTypeError(f"Expected Mail instance or dict, got {type(mail).__name__}")
379
+ self.loadConfigMail(mail)
380
+
381
+ # Load paths configurator
382
+ if not isinstance(path, (Paths, dict)):
383
+ raise OrionisTypeError(f"Expected Paths instance or dict, got {type(path).__name__}")
384
+ self.loadPaths(path)
357
385
 
358
386
  # Load queue configurator
359
- if queue is not None and issubclass(queue, IConfig):
360
- queue = queue.config
361
- self.loadConfigQueue(queue or Queue())
387
+ if not isinstance(queue, (Queue, dict)):
388
+ raise OrionisTypeError(f"Expected Queue instance or dict, got {type(queue).__name__}")
389
+ self.loadConfigQueue(queue)
362
390
 
363
391
  # Load session configurator
364
- if session is not None and issubclass(session, IConfig):
365
- session = session.config
366
- self.loadConfigSession(session or Session())
392
+ if not isinstance(session, (Session, dict)):
393
+ raise OrionisTypeError(f"Expected Session instance or dict, got {type(session).__name__}")
394
+ self.loadConfigSession(session)
367
395
 
368
396
  # Load testing configurator
369
- if testing is not None and issubclass(testing, IConfig):
370
- testing = testing.config
371
- self.loadConfigTesting(testing or Testing())
397
+ if not isinstance(testing, (Testing, dict)):
398
+ raise OrionisTypeError(f"Expected Testing instance or dict, got {type(testing).__name__}")
399
+ self.loadConfigTesting(testing)
372
400
 
373
401
  # Return self instance for method chaining
374
402
  return self
375
403
 
376
- def configApp(self, **app_config) -> 'Application':
404
+ def setConfigApp(
405
+ self,
406
+ **app_config
407
+ ) -> 'Application':
377
408
  """
378
409
  Configure the application with various settings.
379
410
 
@@ -400,15 +431,15 @@ class Application(Container, IApplication):
400
431
 
401
432
  def loadConfigApp(
402
433
  self,
403
- app: App
434
+ app: App | dict
404
435
  ) -> 'Application':
405
436
  """
406
437
  Load the application configuration from an App instance.
407
438
 
408
439
  Parameters
409
440
  ----------
410
- config : App
411
- The App instance containing application configuration
441
+ config : App | dict
442
+ The App instance or dictionary containing application configuration.
412
443
 
413
444
  Returns
414
445
  -------
@@ -416,9 +447,13 @@ class Application(Container, IApplication):
416
447
  The application instance for method chaining
417
448
  """
418
449
 
419
- # Validate config type
420
- if not isinstance(app, App):
421
- raise OrionisTypeError(f"Expected App instance, got {type(app).__name__}")
450
+ # Validate app type
451
+ if not isinstance(app, (App, dict)):
452
+ raise OrionisTypeError(f"Expected App instance or dict, got {type(app).__name__}")
453
+
454
+ # If app is a dict, convert it to App instance
455
+ if isinstance(app, dict):
456
+ app = App(**app)
422
457
 
423
458
  # Store the configuration
424
459
  self.__configurators['app'] = app
@@ -426,7 +461,10 @@ class Application(Container, IApplication):
426
461
  # Return the application instance for method chaining
427
462
  return self
428
463
 
429
- def configAuth(self, **auth_config) -> 'Application':
464
+ def setConfigAuth(
465
+ self,
466
+ **auth_config
467
+ ) -> 'Application':
430
468
  """
431
469
  Configure the authentication with various settings.
432
470
 
@@ -453,15 +491,15 @@ class Application(Container, IApplication):
453
491
 
454
492
  def loadConfigAuth(
455
493
  self,
456
- auth: Auth
494
+ auth: Auth | dict
457
495
  ) -> 'Application':
458
496
  """
459
497
  Load the application authentication configuration from an Auth instance.
460
498
 
461
499
  Parameters
462
500
  ----------
463
- auth : Auth
464
- The Auth instance containing authentication configuration
501
+ auth : Auth | dict
502
+ The Auth instance or dictionary containing authentication configuration.
465
503
 
466
504
  Returns
467
505
  -------
@@ -470,8 +508,12 @@ class Application(Container, IApplication):
470
508
  """
471
509
 
472
510
  # Validate auth type
473
- if not isinstance(auth, Auth):
474
- raise OrionisTypeError(f"Expected Auth instance, got {type(auth).__name__}")
511
+ if not isinstance(auth, (Auth, dict)):
512
+ raise OrionisTypeError(f"Expected Auth instance or dict, got {type(auth).__name__}")
513
+
514
+ # If auth is a dict, convert it to Auth instance
515
+ if isinstance(auth, dict):
516
+ auth = Auth(**auth)
475
517
 
476
518
  # Store the configuration
477
519
  self.__configurators['auth'] = auth
@@ -479,7 +521,10 @@ class Application(Container, IApplication):
479
521
  # Return the application instance for method chaining
480
522
  return self
481
523
 
482
- def configCache(self, **cache_config) -> 'Application':
524
+ def setConfigCache(
525
+ self,
526
+ **cache_config
527
+ ) -> 'Application':
483
528
  """
484
529
  Configure the cache with various settings.
485
530
 
@@ -506,15 +551,15 @@ class Application(Container, IApplication):
506
551
 
507
552
  def loadConfigCache(
508
553
  self,
509
- cache: Cache
554
+ cache: Cache | dict
510
555
  ) -> 'Application':
511
556
  """
512
557
  Load the application cache configuration from a Cache instance.
513
558
 
514
559
  Parameters
515
560
  ----------
516
- cache : Cache
517
- The Cache instance containing cache configuration
561
+ cache : Cache | dict
562
+ The Cache instance or dictionary containing cache configuration.
518
563
 
519
564
  Returns
520
565
  -------
@@ -523,8 +568,12 @@ class Application(Container, IApplication):
523
568
  """
524
569
 
525
570
  # Validate cache type
526
- if not isinstance(cache, Cache):
527
- raise OrionisTypeError(f"Expected Cache instance, got {type(cache).__name__}")
571
+ if not isinstance(cache, (Cache, dict)):
572
+ raise OrionisTypeError(f"Expected Cache instance or dict, got {type(cache).__name__}")
573
+
574
+ # If cache is a dict, convert it to Cache instance
575
+ if isinstance(cache, dict):
576
+ cache = Cache(**cache)
528
577
 
529
578
  # Store the configuration
530
579
  self.__configurators['cache'] = cache
@@ -532,7 +581,10 @@ class Application(Container, IApplication):
532
581
  # Return the application instance for method chaining
533
582
  return self
534
583
 
535
- def configCors(self, **cors_config) -> 'Application':
584
+ def setConfigCors(
585
+ self,
586
+ **cors_config
587
+ ) -> 'Application':
536
588
  """
537
589
  Configure the CORS with various settings.
538
590
 
@@ -559,15 +611,15 @@ class Application(Container, IApplication):
559
611
 
560
612
  def loadConfigCors(
561
613
  self,
562
- cors: Cors
614
+ cors: Cors | dict
563
615
  ) -> 'Application':
564
616
  """
565
617
  Load the application CORS configuration from a Cors instance.
566
618
 
567
619
  Parameters
568
620
  ----------
569
- cors : Cors
570
- The Cors instance containing CORS configuration
621
+ cors : Cors | dict
622
+ The Cors instance or dictionary containing CORS configuration.
571
623
 
572
624
  Returns
573
625
  -------
@@ -576,8 +628,12 @@ class Application(Container, IApplication):
576
628
  """
577
629
 
578
630
  # Validate cors type
579
- if not isinstance(cors, Cors):
580
- raise OrionisTypeError(f"Expected Cors instance, got {type(cors).__name__}")
631
+ if not isinstance(cors, (Cors, dict)):
632
+ raise OrionisTypeError(f"Expected Cors instance or dict, got {type(cors).__name__}")
633
+
634
+ # If cors is a dict, convert it to Cors instance
635
+ if isinstance(cors, dict):
636
+ cors = Cors(**cors)
581
637
 
582
638
  # Store the configuration
583
639
  self.__configurators['cors'] = cors
@@ -585,7 +641,7 @@ class Application(Container, IApplication):
585
641
  # Return the application instance for method chaining
586
642
  return self
587
643
 
588
- def configDatabase(
644
+ def setConfigDatabase(
589
645
  self,
590
646
  **database_config
591
647
  ) -> 'Application':
@@ -615,15 +671,15 @@ class Application(Container, IApplication):
615
671
 
616
672
  def loadConfigDatabase(
617
673
  self,
618
- database: Database
674
+ database: Database | dict
619
675
  ) -> 'Application':
620
676
  """
621
677
  Load the application database configuration from a Database instance.
622
678
 
623
679
  Parameters
624
680
  ----------
625
- database : Database
626
- The Database instance containing database configuration
681
+ database : Database | dict
682
+ The Database instance or dictionary containing database configuration.
627
683
 
628
684
  Returns
629
685
  -------
@@ -632,8 +688,12 @@ class Application(Container, IApplication):
632
688
  """
633
689
 
634
690
  # Validate database type
635
- if not isinstance(database, Database):
636
- raise OrionisTypeError(f"Expected Database instance, got {type(database).__name__}")
691
+ if not isinstance(database, (Database, dict)):
692
+ raise OrionisTypeError(f"Expected Database instance or dict, got {type(database).__name__}")
693
+
694
+ # If database is a dict, convert it to Database instance
695
+ if isinstance(database, dict):
696
+ database = Database(**database)
637
697
 
638
698
  # Store the configuration
639
699
  self.__configurators['database'] = database
@@ -641,7 +701,7 @@ class Application(Container, IApplication):
641
701
  # Return the application instance for method chaining
642
702
  return self
643
703
 
644
- def configFilesystems(
704
+ def setConfigFilesystems(
645
705
  self,
646
706
  **filesystems_config
647
707
  ) -> 'Application':
@@ -671,15 +731,15 @@ class Application(Container, IApplication):
671
731
 
672
732
  def loadConfigFilesystems(
673
733
  self,
674
- filesystems: Filesystems
734
+ filesystems: Filesystems | dict
675
735
  ) -> 'Application':
676
736
  """
677
737
  Load the application filesystems configuration from a Filesystems instance.
678
738
 
679
739
  Parameters
680
740
  ----------
681
- filesystems : Filesystems
682
- The Filesystems instance containing filesystems configuration
741
+ filesystems : Filesystems | dict
742
+ The Filesystems instance or dictionary containing filesystems configuration.
683
743
 
684
744
  Returns
685
745
  -------
@@ -688,8 +748,12 @@ class Application(Container, IApplication):
688
748
  """
689
749
 
690
750
  # Validate filesystems type
691
- if not isinstance(filesystems, Filesystems):
692
- raise OrionisTypeError(f"Expected Filesystems instance, got {type(filesystems).__name__}")
751
+ if not isinstance(filesystems, (Filesystems, dict)):
752
+ raise OrionisTypeError(f"Expected Filesystems instance or dict, got {type(filesystems).__name__}")
753
+
754
+ # If filesystems is a dict, convert it to Filesystems instance
755
+ if isinstance(filesystems, dict):
756
+ filesystems = Filesystems(**filesystems)
693
757
 
694
758
  # Store the configuration
695
759
  self.__configurators['filesystems'] = filesystems
@@ -697,7 +761,7 @@ class Application(Container, IApplication):
697
761
  # Return the application instance for method chaining
698
762
  return self
699
763
 
700
- def configLogging(
764
+ def setConfigLogging(
701
765
  self,
702
766
  **logging_config
703
767
  ) -> 'Application':
@@ -727,15 +791,15 @@ class Application(Container, IApplication):
727
791
 
728
792
  def loadConfigLogging(
729
793
  self,
730
- logging: Logging
794
+ logging: Logging | dict
731
795
  ) -> 'Application':
732
796
  """
733
797
  Load the application logging configuration from a Logging instance.
734
798
 
735
799
  Parameters
736
800
  ----------
737
- logging : Logging
738
- The Logging instance containing logging configuration
801
+ logging : Logging | dict
802
+ The Logging instance or dictionary containing logging configuration.
739
803
 
740
804
  Returns
741
805
  -------
@@ -744,8 +808,12 @@ class Application(Container, IApplication):
744
808
  """
745
809
 
746
810
  # Validate logging type
747
- if not isinstance(logging, Logging):
748
- raise OrionisTypeError(f"Expected Logging instance, got {type(logging).__name__}")
811
+ if not isinstance(logging, (Logging, dict)):
812
+ raise OrionisTypeError(f"Expected Logging instance or dict, got {type(logging).__name__}")
813
+
814
+ # If logging is a dict, convert it to Logging instance
815
+ if isinstance(logging, dict):
816
+ logging = Logging(**logging)
749
817
 
750
818
  # Store the configuration
751
819
  self.__configurators['logging'] = logging
@@ -753,7 +821,7 @@ class Application(Container, IApplication):
753
821
  # Return the application instance for method chaining
754
822
  return self
755
823
 
756
- def configMail(
824
+ def setConfigMail(
757
825
  self,
758
826
  **mail_config
759
827
  ) -> 'Application':
@@ -783,15 +851,15 @@ class Application(Container, IApplication):
783
851
 
784
852
  def loadConfigMail(
785
853
  self,
786
- mail: Mail
854
+ mail: Mail | dict
787
855
  ) -> 'Application':
788
856
  """
789
857
  Load the application mail configuration from a Mail instance.
790
858
 
791
859
  Parameters
792
860
  ----------
793
- mail : Mail
794
- The Mail instance containing mail configuration
861
+ mail : Mail | dict
862
+ The Mail instance or dictionary containing mail configuration.
795
863
 
796
864
  Returns
797
865
  -------
@@ -800,8 +868,12 @@ class Application(Container, IApplication):
800
868
  """
801
869
 
802
870
  # Validate mail type
803
- if not isinstance(mail, Mail):
804
- raise OrionisTypeError(f"Expected Mail instance, got {type(mail).__name__}")
871
+ if not isinstance(mail, (Mail, dict)):
872
+ raise OrionisTypeError(f"Expected Mail instance or dict, got {type(mail).__name__}")
873
+
874
+ # If mail is a dict, convert it to Mail instance
875
+ if isinstance(mail, dict):
876
+ mail = Mail(**mail)
805
877
 
806
878
  # Store the configuration
807
879
  self.__configurators['mail'] = mail
@@ -809,7 +881,130 @@ class Application(Container, IApplication):
809
881
  # Return the application instance for method chaining
810
882
  return self
811
883
 
812
- def configQueue(
884
+ def setPaths(
885
+ self,
886
+ *,
887
+ console_scheduler: str | Path = (Path.cwd() / 'app' / 'console' / 'kernel.py').resolve(),
888
+ console_commands: str | Path = (Path.cwd() / 'app' / 'console' / 'commands').resolve(),
889
+ http_controllers: str | Path = (Path.cwd() / 'app' / 'http' / 'controllers').resolve(),
890
+ http_middleware: str | Path = (Path.cwd() / 'app' / 'http' / 'middleware').resolve(),
891
+ http_requests: str | Path = (Path.cwd() / 'app' / 'http' / 'requests').resolve(),
892
+ models: str | Path = (Path.cwd() / 'app' / 'models').resolve(),
893
+ providers: str | Path = (Path.cwd() / 'app' / 'providers').resolve(),
894
+ events: str | Path = (Path.cwd() / 'app' / 'events').resolve(),
895
+ listeners: str | Path = (Path.cwd() / 'app' / 'listeners').resolve(),
896
+ notifications: str | Path = (Path.cwd() / 'app' / 'notifications').resolve(),
897
+ jobs: str | Path = (Path.cwd() / 'app' / 'jobs').resolve(),
898
+ policies: str | Path = (Path.cwd() / 'app' / 'policies').resolve(),
899
+ exceptions: str | Path = (Path.cwd() / 'app' / 'exceptions').resolve(),
900
+ services: str | Path = (Path.cwd() / 'app' / 'services').resolve(),
901
+ views: str | Path = (Path.cwd() / 'resources' / 'views').resolve(),
902
+ lang: str | Path = (Path.cwd() / 'resources' / 'lang').resolve(),
903
+ assets: str | Path = (Path.cwd() / 'resources' / 'assets').resolve(),
904
+ routes_web: str | Path = (Path.cwd() / 'routes' / 'web.py').resolve(),
905
+ routes_api: str | Path = (Path.cwd() / 'routes' / 'api.py').resolve(),
906
+ routes_console: str | Path = (Path.cwd() / 'routes' / 'console.py').resolve(),
907
+ routes_channels: str | Path = (Path.cwd() / 'routes' / 'channels.py').resolve(),
908
+ config: str | Path = (Path.cwd() / 'config').resolve(),
909
+ migrations: str | Path = (Path.cwd() / 'database' / 'migrations').resolve(),
910
+ seeders: str | Path = (Path.cwd() / 'database' / 'seeders').resolve(),
911
+ factories: str | Path = (Path.cwd() / 'database' / 'factories').resolve(),
912
+ storage_logs: str | Path = (Path.cwd() / 'storage' / 'logs').resolve(),
913
+ storage_framework: str | Path = (Path.cwd() / 'storage' / 'framework').resolve(),
914
+ storage_sessions: str | Path = (Path.cwd() / 'storage' / 'framework' / 'sessions').resolve(),
915
+ storage_cache: str | Path = (Path.cwd() / 'storage' / 'framework' / 'cache').resolve(),
916
+ storage_views: str | Path = (Path.cwd() / 'storage' / 'framework' / 'views').resolve(),
917
+ storage_testing: str | Path = (Path.cwd() / 'storage' / 'framework' / 'testing').resolve(),
918
+ ) -> 'Application':
919
+ """
920
+ Set various application paths.
921
+
922
+ Parameters
923
+ ----------
924
+ Each keyword argument sets a specific application path.
925
+
926
+ Returns
927
+ -------
928
+ Application
929
+ The application instance for method chaining
930
+ """
931
+
932
+ # Ensure 'paths' exists in configurators
933
+ self.__configurators['paths'] = {
934
+ 'console_scheduler': str(console_scheduler),
935
+ 'console_commands': str(console_commands),
936
+ 'http_controllers': str(http_controllers),
937
+ 'http_middleware': str(http_middleware),
938
+ 'http_requests': str(http_requests),
939
+ 'models': str(models),
940
+ 'providers': str(providers),
941
+ 'events': str(events),
942
+ 'listeners': str(listeners),
943
+ 'notifications': str(notifications),
944
+ 'jobs': str(jobs),
945
+ 'policies': str(policies),
946
+ 'exceptions': str(exceptions),
947
+ 'services': str(services),
948
+ 'views': str(views),
949
+ 'lang': str(lang),
950
+ 'assets': str(assets),
951
+ 'routes_web': str(routes_web),
952
+ 'routes_api': str(routes_api),
953
+ 'routes_console': str(routes_console),
954
+ 'routes_channels': str(routes_channels),
955
+ 'config': str(config),
956
+ 'migrations': str(migrations),
957
+ 'seeders': str(seeders),
958
+ 'factories': str(factories),
959
+ 'storage_logs': str(storage_logs),
960
+ 'storage_framework': str(storage_framework),
961
+ 'storage_sessions': str(storage_sessions),
962
+ 'storage_cache': str(storage_cache),
963
+ 'storage_views': str(storage_views),
964
+ 'storage_testing': str(storage_testing),
965
+ }
966
+
967
+ # Return self instance for method chaining
968
+ return self
969
+
970
+ def loadPaths(
971
+ self,
972
+ paths: Paths | dict
973
+ ) -> 'Application':
974
+ """
975
+ Load the application paths configuration from a Paths instance or dictionary.
976
+
977
+ Parameters
978
+ ----------
979
+ paths : Paths | dict
980
+ The Paths instance or dictionary containing path configuration.
981
+
982
+ Returns
983
+ -------
984
+ Application
985
+ The application instance for method chaining
986
+
987
+ Raises
988
+ ------
989
+ OrionisTypeError
990
+ If paths is not an instance of Paths or dict.
991
+ """
992
+
993
+ # Validate paths type
994
+ if not isinstance(paths, (Paths, dict)):
995
+ raise OrionisTypeError(f"Expected Paths instance or dict, got {type(paths).__name__}")
996
+
997
+ # If paths is a dict, convert it to Paths instance
998
+ if isinstance(paths, dict):
999
+ paths = Paths(**paths)
1000
+
1001
+ # Store the configuration
1002
+ self.__configurators['paths'] = paths
1003
+
1004
+ # Return the application instance for method chaining
1005
+ return self
1006
+
1007
+ def setConfigQueue(
813
1008
  self,
814
1009
  **queue_config
815
1010
  ) -> 'Application':
@@ -839,7 +1034,7 @@ class Application(Container, IApplication):
839
1034
 
840
1035
  def loadConfigQueue(
841
1036
  self,
842
- queue: Queue
1037
+ queue: Queue | dict
843
1038
  ) -> 'Application':
844
1039
  """
845
1040
  Load the application queue configuration from a Queue instance.
@@ -856,8 +1051,12 @@ class Application(Container, IApplication):
856
1051
  """
857
1052
 
858
1053
  # Validate queue type
859
- if not isinstance(queue, Queue):
860
- raise OrionisTypeError(f"Expected Queue instance, got {type(queue).__name__}")
1054
+ if not isinstance(queue, (Queue, dict)):
1055
+ raise OrionisTypeError(f"Expected Queue instance or dict, got {type(queue).__name__}")
1056
+
1057
+ # If queue is a dict, convert it to Queue instance
1058
+ if isinstance(queue, dict):
1059
+ queue = Queue(**queue)
861
1060
 
862
1061
  # Store the configuration
863
1062
  self.__configurators['queue'] = queue
@@ -865,7 +1064,7 @@ class Application(Container, IApplication):
865
1064
  # Return the application instance for method chaining
866
1065
  return self
867
1066
 
868
- def configSession(
1067
+ def setConfigSession(
869
1068
  self,
870
1069
  **session_config
871
1070
  ) -> 'Application':
@@ -895,15 +1094,15 @@ class Application(Container, IApplication):
895
1094
 
896
1095
  def loadConfigSession(
897
1096
  self,
898
- session: Session
1097
+ session: Session | dict
899
1098
  ) -> 'Application':
900
1099
  """
901
1100
  Load the application session configuration from a Session instance.
902
1101
 
903
1102
  Parameters
904
1103
  ----------
905
- session : Session
906
- The Session instance containing session configuration
1104
+ session : Session | dict
1105
+ The Session instance or dictionary containing session configuration.
907
1106
 
908
1107
  Returns
909
1108
  -------
@@ -912,8 +1111,12 @@ class Application(Container, IApplication):
912
1111
  """
913
1112
 
914
1113
  # Validate session type
915
- if not isinstance(session, Session):
916
- raise OrionisTypeError(f"Expected Session instance, got {type(session).__name__}")
1114
+ if not isinstance(session, (Session, dict)):
1115
+ raise OrionisTypeError(f"Expected Session instance or dict, got {type(session).__name__}")
1116
+
1117
+ # If session is a dict, convert it to Session instance
1118
+ if isinstance(session, dict):
1119
+ session = Session(**session)
917
1120
 
918
1121
  # Store the configuration
919
1122
  self.__configurators['session'] = session
@@ -921,7 +1124,7 @@ class Application(Container, IApplication):
921
1124
  # Return the application instance for method chaining
922
1125
  return self
923
1126
 
924
- def configTesting(
1127
+ def setConfigTesting(
925
1128
  self,
926
1129
  **testing_config
927
1130
  ) -> 'Application':
@@ -951,15 +1154,15 @@ class Application(Container, IApplication):
951
1154
 
952
1155
  def loadConfigTesting(
953
1156
  self,
954
- testing: Testing
1157
+ testing: Testing | dict
955
1158
  ) -> 'Application':
956
1159
  """
957
1160
  Load the application testing configuration from a Testing instance.
958
1161
 
959
1162
  Parameters
960
1163
  ----------
961
- testing : Testing
962
- The Testing instance containing testing configuration
1164
+ testing : Testing | dict
1165
+ The Testing instance or dictionary containing testing configuration.
963
1166
 
964
1167
  Returns
965
1168
  -------
@@ -968,8 +1171,12 @@ class Application(Container, IApplication):
968
1171
  """
969
1172
 
970
1173
  # Validate testing type
971
- if not isinstance(testing, Testing):
972
- raise OrionisTypeError(f"Expected Testing instance, got {type(testing).__name__}")
1174
+ if not isinstance(testing, (Testing, dict)):
1175
+ raise OrionisTypeError(f"Expected Testing instance or dict, got {type(testing).__name__}")
1176
+
1177
+ # If testing is a dict, convert it to Testing instance
1178
+ if isinstance(testing, dict):
1179
+ testing = Testing(**testing)
973
1180
 
974
1181
  # Store the configuration
975
1182
  self.__configurators['testing'] = testing
@@ -977,68 +1184,83 @@ class Application(Container, IApplication):
977
1184
  # Return the application instance for method chaining
978
1185
  return self
979
1186
 
980
- # << Application Lifecycle >>
981
-
982
- def create(
983
- self
984
- ) -> 'Application':
1187
+ def __loadConfig(
1188
+ self,
1189
+ ) -> None:
985
1190
  """
986
- Bootstrap the application by loading providers and kernels.
1191
+ Retrieve a configuration value by key.
987
1192
 
988
1193
  Returns
989
1194
  -------
990
- Application
991
- The application instance for method chaining
1195
+ None
1196
+ Initializes the application configuration if not already set.
992
1197
  """
993
- # Check if already booted
994
- if not self.__booted:
995
1198
 
996
- # Load configuration if not already set
997
- self.__loadConfig()
1199
+ # Try to load the configuration
1200
+ try:
998
1201
 
999
- # Load framework providers and register them
1000
- self.__loadFrameworkProviders()
1001
- self.__registerProviders()
1002
- self.__bootProviders()
1202
+ # Check if configuration is a dictionary
1203
+ if not self.__config:
1003
1204
 
1004
- # Load core framework kernels
1005
- self.__loadFrameworksKernel()
1205
+ # Initialize with default configuration
1206
+ if not self.__configurators:
1207
+ self.__config = Configuration().toDict()
1006
1208
 
1007
- # Mark as booted
1008
- self.__booted = True
1209
+ # Convert configurators to a dictionary
1210
+ else:
1211
+ self.__config = Configuration(**self.__configurators).toDict()
1009
1212
 
1010
- return self
1213
+ # Remove __configurators ofter loading configuration
1214
+ if hasattr(self, '_Application__configurators'):
1215
+ del self.__configurators
1216
+
1217
+ except Exception as e:
1218
+
1219
+ # Handle any exceptions during configuration loading
1220
+ raise OrionisRuntimeError(f"Failed to load application configuration: {str(e)}")
1011
1221
 
1012
- # << Configuration Access >>
1222
+ # === Configuration Access Method ===
1223
+ # The config() method provides access to application configuration settings.
1224
+ # It supports dot notation for retrieving nested configuration values.
1225
+ # You can obtain a specific configuration value by providing a key,
1226
+ # or retrieve the entire configuration dictionary by omitting the key.
1013
1227
 
1014
1228
  def config(
1015
1229
  self,
1016
- key: str,
1230
+ key: str = None,
1017
1231
  default: Any = None
1018
1232
  ) -> Any:
1019
1233
  """
1020
- Retrieve a configuration value by key.
1021
-
1022
- Parameters
1023
- ----------
1024
- key : str
1025
- The configuration key to retrieve using dot notation (e.g. "app.name") (default is None)
1026
- default : Any, optional
1027
- Default value to return if key is not found
1234
+ Retrieves a configuration value from the application settings using dot notation.
1235
+ This method allows you to access nested configuration values by specifying a key in dot notation
1236
+ (e.g., "database.host"). If no key is provided, the entire configuration dictionary is returned.
1237
+ key : str, optional
1238
+ The configuration key to retrieve, using dot notation for nested values (e.g., "app.name").
1239
+ If None, returns the entire configuration dictionary. Default is None.
1240
+ The value to return if the specified key does not exist in the configuration. Default is None.
1241
+ The configuration value associated with the given key, the entire configuration dictionary if
1242
+ key is None, or the default value if the key is not found.
1028
1243
 
1029
- Returns
1030
- -------
1031
- Any
1032
- The configuration value or the entire configuration if key is None
1244
+ Raises
1245
+ ------
1246
+ OrionisRuntimeError
1247
+ If the application has not been booted and configuration is not available.
1248
+ OrionisValueError
1249
+ If the provided key is not a string.
1033
1250
  """
1034
1251
 
1035
1252
  # Ensure the application is booted before accessing configuration
1036
1253
  if not self.__config:
1037
- raise RuntimeError("Application must be booted before accessing configuration. Call create() first.")
1254
+ raise OrionisRuntimeError("Application configuration is not initialized. Please call create() before accessing configuration.")
1038
1255
 
1039
- # If key is None, raise an error to prevent ambiguity
1256
+ # Return the entire configuration if key is None, except for paths
1040
1257
  if key is None:
1041
- raise ValueError("Key cannot be None. Use config() without arguments to get the entire configuration.")
1258
+ del self.__config['paths']
1259
+ return self.__config
1260
+
1261
+ # If key is None, raise an error to prevent ambiguity
1262
+ if not isinstance(key, str):
1263
+ raise OrionisValueError("Key must be a string. Use config() without arguments to retrieve the entire configuration.")
1042
1264
 
1043
1265
  # Split the key by dot notation
1044
1266
  parts = key.split('.')
@@ -1060,46 +1282,118 @@ class Application(Container, IApplication):
1060
1282
  # Return the final configuration value
1061
1283
  return config_value
1062
1284
 
1063
- # << Path Configuration Access >>
1285
+ # === Path Configuration Access Method ===
1286
+ # The path() method provides access to application path configurations.
1287
+ # It allows you to retrieve specific path configurations using dot notation.
1288
+ # If no key is provided, it returns the entire 'paths' configuration dictionary.
1064
1289
 
1065
1290
  def path(
1066
1291
  self,
1067
- key: str,
1068
- default: str = None
1069
- ) -> Path:
1292
+ key: str = None,
1293
+ default: Any = None
1294
+ ) -> Any:
1070
1295
  """
1071
- Retrieve a path configuration value by key.
1072
-
1296
+ Retrieve a path configuration value from the application's configuration.
1073
1297
  Parameters
1074
1298
  ----------
1075
- key : str
1076
- The path key to retrieve using dot notation (e.g. "paths.storage")
1077
- default : str, optional
1078
- Default value to return if key is not found
1079
-
1299
+ key : str, optional
1300
+ Dot-notated key specifying the path configuration to retrieve.
1301
+ If None, returns the entire 'paths' configuration dictionary.
1302
+ default : Any, optional
1303
+ Value to return if the specified key is not found. Defaults to None.
1080
1304
  Returns
1081
1305
  -------
1082
- Path
1083
- The path value as a Path object or None if not found
1306
+ Any
1307
+ The configuration value corresponding to the given key, the entire 'paths'
1308
+ dictionary if key is None, or the default value if the key is not found.
1309
+ Raises
1310
+ ------
1311
+ OrionisRuntimeError
1312
+ If the application configuration is not initialized (not booted).
1313
+ OrionisValueError
1314
+ If the provided key is not a string.
1315
+ Notes
1316
+ -----
1317
+ - The method traverses the 'paths' configuration using dot notation for nested keys.
1318
+ - If any part of the key is not found, the default value is returned.
1084
1319
  """
1085
1320
 
1086
1321
  # Ensure the application is booted before accessing configuration
1087
- if not self.__booted:
1088
- raise RuntimeError("Application must be booted before accessing configuration. Call create() first.")
1322
+ if not self.__config:
1323
+ raise OrionisRuntimeError("Application configuration is not initialized. Please call create() before accessing path configuration.")
1089
1324
 
1090
- # If key is None, raise an error to prevent ambiguity
1325
+ # Return the entire configuration if key is None, except for paths
1091
1326
  if key is None:
1092
- raise ValueError("Key cannot be None. Use path() without arguments to get the entire configuration.")
1327
+ return self.__config['path']
1328
+
1329
+ # If key is None, raise an error to prevent ambiguity
1330
+ if not isinstance(key, str):
1331
+ raise OrionisValueError("Key must be a string. Use path() without arguments to get the entire paths configuration.")
1332
+
1333
+ # Split the key by dot notation
1334
+ parts = key.split('.')
1335
+
1336
+ # Start with the full config
1337
+ config_value = self.__config['path']
1338
+
1339
+ # Traverse the config dictionary based on the key parts
1340
+ for part in parts:
1341
+
1342
+ # If part is not in config_value, return default
1343
+ if isinstance(config_value, dict) and part in config_value:
1344
+ config_value = config_value[part]
1093
1345
 
1094
- # Get the configuration value for the given key
1095
- original_paths = self.config('paths')
1346
+ # If part is not found, return default value
1347
+ else:
1348
+ return default
1349
+
1350
+ # Return the final configuration value
1351
+ return config_value
1352
+
1353
+ # === Application Creation Method ===
1354
+ # The create() method is responsible for bootstrapping the application.
1355
+ # It loads the necessary providers and kernels, ensuring that the application
1356
+ # is ready for use. This method should be called once to initialize the application.
1357
+
1358
+ def create(
1359
+ self
1360
+ ) -> 'Application':
1361
+ """
1362
+ Bootstrap the application by loading providers and kernels.
1363
+
1364
+ Returns
1365
+ -------
1366
+ Application
1367
+ The application instance for method chaining
1368
+ """
1369
+ # Check if already booted
1370
+ if not self.__booted:
1096
1371
 
1097
- # If original_paths is not a dictionary, return the default value as Path
1098
- if not isinstance(original_paths, dict):
1099
- return Path(default) if default is not None else None
1372
+ # Load configuration if not already set
1373
+ self.__loadConfig()
1100
1374
 
1101
- # Get the path value from the dictionary
1102
- path_value = original_paths.get(key, default)
1375
+ # Load framework providers and register them
1376
+ self.__loadFrameworkProviders()
1377
+ self.__registerProviders()
1378
+ self.__bootProviders()
1103
1379
 
1104
- # Return as Path object if value exists, otherwise return None
1105
- return Path(path_value) if path_value is not None else None
1380
+ # Load core framework kernels
1381
+ self.__loadFrameworksKernel()
1382
+
1383
+ # Retrieve logger and console instances from the container
1384
+ logger: ILoggerService = self.make('core.orionis.logger')
1385
+
1386
+ # Calculate elapsed time in milliseconds since application start
1387
+ elapsed_ms = (time.time_ns() - self.startAt) // 1_000_000
1388
+
1389
+ # Compose the boot message
1390
+ boot_message = f"Orionis Framework has been successfully booted. Startup time: {elapsed_ms} ms. Started at: {self.startAt} ns"
1391
+
1392
+ # Log message to the logger
1393
+ logger.info(boot_message)
1394
+
1395
+ # Mark as booted
1396
+ self.__booted = True
1397
+
1398
+ # Return the application instance for method chaining
1399
+ return self