orionis 0.578.0__py3-none-any.whl → 0.580.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 (78) hide show
  1. orionis/console/core/reactor.py +5 -5
  2. orionis/console/kernel.py +2 -2
  3. orionis/console/tasks/schedule.py +2 -2
  4. orionis/container/container.py +6 -11
  5. orionis/container/exceptions/__init__.py +4 -4
  6. orionis/failure/base/handler.py +5 -5
  7. orionis/foundation/application.py +116 -91
  8. orionis/foundation/config/database/entities/sqlite.py +1 -1
  9. orionis/foundation/config/roots/paths.py +8 -0
  10. orionis/foundation/config/startup.py +1 -1
  11. orionis/foundation/contracts/application.py +4 -4
  12. orionis/foundation/exceptions/__init__.py +6 -4
  13. orionis/foundation/exceptions/application.py +11 -0
  14. orionis/foundation/providers/catch_provider.py +1 -1
  15. orionis/foundation/providers/cli_request_provider.py +1 -1
  16. orionis/foundation/providers/console_provider.py +1 -1
  17. orionis/foundation/providers/directory_provider.py +1 -1
  18. orionis/foundation/providers/dumper_provider.py +1 -1
  19. orionis/foundation/providers/executor_provider.py +1 -1
  20. orionis/foundation/providers/inspirational_provider.py +1 -1
  21. orionis/foundation/providers/logger_provider.py +1 -1
  22. orionis/foundation/providers/performance_counter_provider.py +1 -1
  23. orionis/foundation/providers/progress_bar_provider.py +1 -1
  24. orionis/foundation/providers/reactor_provider.py +1 -2
  25. orionis/foundation/providers/scheduler_provider.py +1 -1
  26. orionis/foundation/providers/testing_provider.py +1 -1
  27. orionis/foundation/providers/workers_provider.py +1 -1
  28. orionis/metadata/framework.py +1 -1
  29. orionis/services/asynchrony/exceptions/__init__.py +1 -1
  30. orionis/services/asynchrony/exceptions/asynchrony.py +11 -0
  31. orionis/services/environment/core/dot_env.py +3 -2
  32. orionis/services/environment/dynamic/caster.py +8 -8
  33. orionis/services/environment/exceptions/__init__.py +11 -3
  34. orionis/services/environment/exceptions/environment.py +125 -0
  35. orionis/services/environment/key/key_generator.py +25 -21
  36. orionis/services/environment/validators/key_name.py +1 -1
  37. orionis/services/environment/validators/types.py +1 -1
  38. orionis/services/file/contracts/directory.py +12 -0
  39. orionis/services/file/directory.py +12 -1
  40. orionis/services/introspection/dataclass/{attributes.py → extractor.py} +2 -2
  41. orionis/services/introspection/exceptions/__init__.py +5 -3
  42. orionis/services/introspection/exceptions/introspection.py +76 -0
  43. orionis/services/log/exceptions/__init__.py +1 -1
  44. orionis/support/facades/application.py +3 -2
  45. orionis/support/facades/console.py +3 -2
  46. orionis/support/facades/directory.py +3 -2
  47. orionis/support/facades/dumper.py +3 -2
  48. orionis/support/facades/executor.py +3 -2
  49. orionis/support/facades/inspire.py +3 -2
  50. orionis/support/facades/logger.py +3 -2
  51. orionis/support/facades/performance_counter.py +3 -2
  52. orionis/support/facades/progress_bar.py +3 -2
  53. orionis/support/facades/reactor.py +3 -2
  54. orionis/support/facades/testing.py +3 -2
  55. orionis/support/facades/workers.py +5 -4
  56. orionis/support/standard/exceptions/__init__.py +1 -1
  57. orionis/support/standard/exceptions/standard.py +23 -0
  58. orionis/test/core/unit_test.py +10 -1
  59. orionis/test/kernel.py +21 -19
  60. orionis/test/output/printer.py +37 -9
  61. {orionis-0.578.0.dist-info → orionis-0.580.0.dist-info}/METADATA +1 -1
  62. {orionis-0.578.0.dist-info → orionis-0.580.0.dist-info}/RECORD +67 -73
  63. orionis/foundation/exceptions/integrity.py +0 -19
  64. orionis/foundation/exceptions/runtime.py +0 -19
  65. orionis/foundation/exceptions/type.py +0 -19
  66. orionis/foundation/exceptions/value.py +0 -19
  67. orionis/services/asynchrony/exceptions/exception.py +0 -27
  68. orionis/services/environment/exceptions/exception.py +0 -23
  69. orionis/services/environment/exceptions/value.py +0 -23
  70. orionis/services/introspection/exceptions/attribute.py +0 -20
  71. orionis/services/introspection/exceptions/type.py +0 -19
  72. orionis/services/introspection/exceptions/value.py +0 -19
  73. orionis/support/standard/exceptions/value.py +0 -19
  74. /orionis/container/exceptions/{container_exceptions.py → container.py} +0 -0
  75. /orionis/services/log/exceptions/{runtime.py → log.py} +0 -0
  76. {orionis-0.578.0.dist-info → orionis-0.580.0.dist-info}/WHEEL +0 -0
  77. {orionis-0.578.0.dist-info → orionis-0.580.0.dist-info}/licenses/LICENCE +0 -0
  78. {orionis-0.578.0.dist-info → orionis-0.580.0.dist-info}/top_level.txt +0 -0
@@ -64,13 +64,13 @@ class Reactor(IReactor):
64
64
  self.__commands: dict[str, Command] = {}
65
65
 
66
66
  # Initialize the executor for command output management
67
- self.__executer: IExecutor = self.__app.make('x-orionis.console.output.executor')
67
+ self.__executer: IExecutor = self.__app.make(IExecutor)
68
68
 
69
69
  # Initialize the logger service for logging command execution details
70
- self.__logger: ILogger = self.__app.make('x-orionis.services.log.log_service')
70
+ self.__logger: ILogger = self.__app.make(ILogger)
71
71
 
72
72
  # Initialize the performance counter for measuring command execution time
73
- self.__performance_counter: IPerformanceCounter = self.__app.make('x-orionis.support.performance.counter')
73
+ self.__performance_counter: IPerformanceCounter = self.__app.make(IPerformanceCounter)
74
74
 
75
75
  # List to hold fluent command definitions
76
76
  self.__fluent_commands: List[ICommand] = []
@@ -179,7 +179,7 @@ class Reactor(IReactor):
179
179
  return
180
180
 
181
181
  # Get the project root directory for module path resolution
182
- root_path = self.__app.getBasePath()
182
+ root_path = str(self.__app.getBasePath())
183
183
 
184
184
  # List all .py files in the routes directory and subdirectories
185
185
  for current_directory, _, files in os.walk(routes_path):
@@ -377,7 +377,7 @@ class Reactor(IReactor):
377
377
 
378
378
  # Ensure the provided commands_path is a valid directory
379
379
  commands_path = (Path(self.__app.path('console')) / 'commands').resolve()
380
- root_path = self.__app.getBasePath()
380
+ root_path = str(self.__app.getBasePath())
381
381
 
382
382
  # Iterate through the command path and load command modules
383
383
  for current_directory, _, files in os.walk(commands_path):
orionis/console/kernel.py CHANGED
@@ -39,10 +39,10 @@ class KernelCLI(IKernelCLI):
39
39
 
40
40
  # Retrieve and initialize the reactor instance from the application container.
41
41
  # The reactor is responsible for dispatching CLI commands.
42
- self.__reactor: IReactor = app.make('x-orionis.console.core.reactor')
42
+ self.__reactor: IReactor = app.make(IReactor)
43
43
 
44
44
  # Retrieve and initialize the catch instance from the application container.
45
- self.__catch: ICatch = app.make('x-orionis.failure.catch')
45
+ self.__catch: ICatch = app.make(ICatch)
46
46
 
47
47
  def handle(self, args: List[str] = []) -> None:
48
48
  """
@@ -87,7 +87,7 @@ class Schedule(ISchedule):
87
87
  logger.disabled = True
88
88
 
89
89
  # Initialize the logger from the application instance.
90
- self.__logger: ILogger = self.__app.make('x-orionis.services.log.log_service')
90
+ self.__logger: ILogger = self.__app.make(ILogger)
91
91
 
92
92
  # Store the reactor instance for command management.
93
93
  self.__reactor: IReactor = reactor
@@ -111,7 +111,7 @@ class Schedule(ISchedule):
111
111
  self._stopEvent: Optional[asyncio.Event] = None
112
112
 
113
113
  # Retrieve and initialize the catch instance from the application container.
114
- self.__catch: ICatch = app.make('x-orionis.failure.catch')
114
+ self.__catch: ICatch = app.make(ICatch)
115
115
 
116
116
  def __getCurrentTime(
117
117
  self
@@ -361,10 +361,9 @@ class Container(IContainer):
361
361
  if alias:
362
362
  IsValidAlias(alias)
363
363
 
364
- # Extract the module and class name for the alias
364
+ # Cretate a default alias if none provided
365
365
  else:
366
- rf_asbtract = ReflectionAbstract(abstract)
367
- alias = rf_asbtract.getModuleWithClassName()
366
+ alias = f"{abstract.__module__}.{abstract.__name__}"
368
367
 
369
368
  # If the service is already registered, drop it
370
369
  self.drop(abstract, alias)
@@ -446,8 +445,7 @@ class Container(IContainer):
446
445
  if alias:
447
446
  IsValidAlias(alias)
448
447
  else:
449
- rf_asbtract = ReflectionAbstract(abstract)
450
- alias = rf_asbtract.getModuleWithClassName()
448
+ alias = f"{abstract.__module__}.{abstract.__name__}"
451
449
 
452
450
  # If the service is already registered, drop it
453
451
  self.drop(abstract, alias)
@@ -529,8 +527,7 @@ class Container(IContainer):
529
527
  if alias:
530
528
  IsValidAlias(alias)
531
529
  else:
532
- rf_asbtract = ReflectionAbstract(abstract)
533
- alias = rf_asbtract.getModuleWithClassName()
530
+ alias = f"{abstract.__module__}.{abstract.__name__}"
534
531
 
535
532
  # If the service is already registered, drop it
536
533
  self.drop(abstract, alias)
@@ -617,8 +614,7 @@ class Container(IContainer):
617
614
  if alias:
618
615
  IsValidAlias(alias)
619
616
  else:
620
- rf_asbtract = ReflectionAbstract(abstract)
621
- alias = rf_asbtract.getModuleWithClassName()
617
+ alias = f"{abstract.__module__}.{abstract.__name__}"
622
618
 
623
619
  # If the service is already registered in container bindings, drop it
624
620
  self.drop(abstract, alias)
@@ -708,8 +704,7 @@ class Container(IContainer):
708
704
  if alias:
709
705
  IsValidAlias(alias)
710
706
  else:
711
- rf_asbtract = ReflectionAbstract(abstract)
712
- alias = rf_asbtract.getModuleWithClassName()
707
+ alias = f"{abstract.__module__}.{abstract.__name__}"
713
708
 
714
709
  # If the service is already registered, drop it
715
710
  self.drop(abstract, alias)
@@ -1,7 +1,7 @@
1
- from .container_exceptions import OrionisContainerAttributeError
2
- from .container_exceptions import OrionisContainerException
3
- from .container_exceptions import OrionisContainerTypeError
4
- from .container_exceptions import OrionisContainerValueError
1
+ from .container import OrionisContainerAttributeError
2
+ from .container import OrionisContainerException
3
+ from .container import OrionisContainerTypeError
4
+ from .container import OrionisContainerValueError
5
5
 
6
6
  __all__ = [
7
7
  "OrionisContainerAttributeError",
@@ -1,3 +1,4 @@
1
+ import traceback
1
2
  from typing import Any, List
2
3
  from orionis.console.contracts.cli_request import ICLIRequest
3
4
  from orionis.console.contracts.console import IConsole
@@ -33,12 +34,11 @@ class BaseExceptionHandler(IBaseExceptionHandler):
33
34
  and wraps them in a `Throwable` object for consistent error handling and reporting.
34
35
  """
35
36
 
36
- # Create and return a Throwable object with detailed exception information
37
37
  return Throwable(
38
- classtype=type(exception), # The class/type of the exception
39
- message=str(exception), # The exception message as a string
40
- args=exception.args, # The arguments passed to the exception
41
- traceback=getattr(exception, '__traceback__', None) # The traceback object, if available
38
+ classtype=type(exception), # The class/type of the exception
39
+ message=exception.args[0] if exception.args else str(exception), # The exception message as a string
40
+ args=exception.args, # The arguments passed to the exception
41
+ traceback=exception.__traceback__ or traceback.format_exc() # The traceback object, if available
42
42
  )
43
43
 
44
44
  async def shouldIgnoreException(self, exception: BaseException) -> bool:
@@ -1,7 +1,7 @@
1
1
  import asyncio
2
2
  import time
3
3
  from pathlib import Path
4
- from typing import Any, List, Type
4
+ from typing import Any, List, Type, Dict
5
5
  from orionis.console.contracts.base_scheduler import IBaseScheduler
6
6
  from orionis.console.base.scheduler import BaseScheduler
7
7
  from orionis.container.container import Container
@@ -23,7 +23,6 @@ from orionis.foundation.config.startup import Configuration
23
23
  from orionis.foundation.config.testing.entities.testing import Testing
24
24
  from orionis.foundation.contracts.application import IApplication
25
25
  from orionis.foundation.exceptions import OrionisTypeError, OrionisRuntimeError, OrionisValueError
26
- from orionis.foundation.providers.logger_provider import LoggerProvider
27
26
  from orionis.services.asynchrony.coroutines import Coroutine
28
27
  from orionis.services.log.contracts.log_service import ILogger
29
28
 
@@ -186,34 +185,37 @@ class Application(Container, IApplication):
186
185
  providers are registered.
187
186
  """
188
187
  # Import core framework providers
188
+ from orionis.foundation.providers.catch_provider import CathcProvider
189
+ from orionis.foundation.providers.cli_request_provider import CLRequestProvider
189
190
  from orionis.foundation.providers.console_provider import ConsoleProvider
191
+ from orionis.foundation.providers.directory_provider import DirectoryProvider
190
192
  from orionis.foundation.providers.dumper_provider import DumperProvider
191
- from orionis.foundation.providers.progress_bar_provider import ProgressBarProvider
192
- from orionis.foundation.providers.workers_provider import WorkersProvider
193
- from orionis.foundation.providers.testing_provider import TestingProvider
194
- from orionis.foundation.providers.inspirational_provider import InspirationalProvider
195
193
  from orionis.foundation.providers.executor_provider import ConsoleExecuteProvider
196
- from orionis.foundation.providers.reactor_provider import ReactorProvider
194
+ from orionis.foundation.providers.inspirational_provider import InspirationalProvider
195
+ from orionis.foundation.providers.logger_provider import LoggerProvider
197
196
  from orionis.foundation.providers.performance_counter_provider import PerformanceCounterProvider
197
+ from orionis.foundation.providers.progress_bar_provider import ProgressBarProvider
198
+ from orionis.foundation.providers.reactor_provider import ReactorProvider
198
199
  from orionis.foundation.providers.scheduler_provider import ScheduleProvider
199
- from orionis.foundation.providers.catch_provider import CathcProvider
200
- from orionis.foundation.providers.directory_provider import DirectoryProvider
200
+ from orionis.foundation.providers.testing_provider import TestingProvider
201
+ from orionis.foundation.providers.workers_provider import WorkersProvider
201
202
 
202
203
  # Core framework providers
203
204
  core_providers = [
205
+ CathcProvider,
206
+ CLRequestProvider,
204
207
  ConsoleProvider,
208
+ DirectoryProvider,
205
209
  DumperProvider,
206
- ProgressBarProvider,
207
- WorkersProvider,
208
- LoggerProvider,
209
- TestingProvider,
210
- InspirationalProvider,
211
210
  ConsoleExecuteProvider,
212
- ReactorProvider,
211
+ InspirationalProvider,
212
+ LoggerProvider,
213
213
  PerformanceCounterProvider,
214
+ ProgressBarProvider,
215
+ ReactorProvider,
214
216
  ScheduleProvider,
215
- CathcProvider,
216
- DirectoryProvider
217
+ TestingProvider,
218
+ WorkersProvider
217
219
  ]
218
220
 
219
221
  # Register each core provider
@@ -257,6 +259,8 @@ class Application(Container, IApplication):
257
259
 
258
260
  # Add each provider class
259
261
  for provider_cls in providers:
262
+
263
+ # Register the provider
260
264
  self.addProvider(provider_cls)
261
265
 
262
266
  # Return self instance for method chaining
@@ -628,88 +632,88 @@ class Application(Container, IApplication):
628
632
  """
629
633
 
630
634
  # Convert dataclass instances to dictionaries
631
- from orionis.services.introspection.dataclass.attributes import attributes
635
+ from orionis.services.introspection.dataclass.extractor import extractor
632
636
 
633
637
  # Load app configurator
634
638
  if (isinstance(app, type) and issubclass(app, App)):
635
- app = attributes(app)
639
+ app = extractor(app)
636
640
  if not isinstance(app, (App, dict)):
637
641
  raise OrionisTypeError(f"Expected App instance or dict, got {type(app).__name__}")
638
642
  self.loadConfigApp(app)
639
643
 
640
644
  # Load auth configurator
641
645
  if (isinstance(auth, type) and issubclass(auth, Auth)):
642
- auth = attributes(auth)
646
+ auth = extractor(auth)
643
647
  if not isinstance(auth, (Auth, dict)):
644
648
  raise OrionisTypeError(f"Expected Auth instance or dict, got {type(auth).__name__}")
645
649
  self.loadConfigAuth(auth)
646
650
 
647
651
  # Load cache configurator
648
652
  if (isinstance(cache, type) and issubclass(cache, Cache)):
649
- cache = attributes(cache)
653
+ cache = extractor(cache)
650
654
  if not isinstance(cache, (Cache, dict)):
651
655
  raise OrionisTypeError(f"Expected Cache instance or dict, got {type(cache).__name__}")
652
656
  self.loadConfigCache(cache)
653
657
 
654
658
  # Load cors configurator
655
659
  if (isinstance(cors, type) and issubclass(cors, Cors)):
656
- cors = attributes(cors)
660
+ cors = extractor(cors)
657
661
  if not isinstance(cors, (Cors, dict)):
658
662
  raise OrionisTypeError(f"Expected Cors instance or dict, got {type(cors).__name__}")
659
663
  self.loadConfigCors(cors)
660
664
 
661
665
  # Load database configurator
662
666
  if (isinstance(database, type) and issubclass(database, Database)):
663
- database = attributes(database)
667
+ database = extractor(database)
664
668
  if not isinstance(database, (Database, dict)):
665
669
  raise OrionisTypeError(f"Expected Database instance or dict, got {type(database).__name__}")
666
670
  self.loadConfigDatabase(database)
667
671
 
668
672
  # Load filesystems configurator
669
673
  if (isinstance(filesystems, type) and issubclass(filesystems, Filesystems)):
670
- filesystems = attributes(filesystems)
674
+ filesystems = extractor(filesystems)
671
675
  if not isinstance(filesystems, (Filesystems, dict)):
672
676
  raise OrionisTypeError(f"Expected Filesystems instance or dict, got {type(filesystems).__name__}")
673
677
  self.loadConfigFilesystems(filesystems)
674
678
 
675
679
  # Load logging configurator
676
680
  if (isinstance(logging, type) and issubclass(logging, Logging)):
677
- logging = attributes(logging)
681
+ logging = extractor(logging)
678
682
  if not isinstance(logging, (Logging, dict)):
679
683
  raise OrionisTypeError(f"Expected Logging instance or dict, got {type(logging).__name__}")
680
684
  self.loadConfigLogging(logging)
681
685
 
682
686
  # Load mail configurator
683
687
  if (isinstance(mail, type) and issubclass(mail, Mail)):
684
- mail = attributes(mail)
688
+ mail = extractor(mail)
685
689
  if not isinstance(mail, (Mail, dict)):
686
690
  raise OrionisTypeError(f"Expected Mail instance or dict, got {type(mail).__name__}")
687
691
  self.loadConfigMail(mail)
688
692
 
689
693
  # Load paths configurator
690
694
  if (isinstance(path, type) and issubclass(path, Paths)):
691
- path = attributes(path)
695
+ path = extractor(path)
692
696
  if not isinstance(path, (Paths, dict)):
693
697
  raise OrionisTypeError(f"Expected Paths instance or dict, got {type(path).__name__}")
694
698
  self.loadPaths(path)
695
699
 
696
700
  # Load queue configurator
697
701
  if (isinstance(queue, type) and issubclass(queue, Queue)):
698
- queue = attributes(queue)
702
+ queue = extractor(queue)
699
703
  if not isinstance(queue, (Queue, dict)):
700
704
  raise OrionisTypeError(f"Expected Queue instance or dict, got {type(queue).__name__}")
701
705
  self.loadConfigQueue(queue)
702
706
 
703
707
  # Load session configurator
704
708
  if (isinstance(session, type) and issubclass(session, Session)):
705
- session = attributes(session)
709
+ session = extractor(session)
706
710
  if not isinstance(session, (Session, dict)):
707
711
  raise OrionisTypeError(f"Expected Session instance or dict, got {type(session).__name__}")
708
712
  self.loadConfigSession(session)
709
713
 
710
714
  # Load testing configurator
711
715
  if (isinstance(testing, type) and issubclass(testing, Testing)):
712
- testing = attributes(testing)
716
+ testing = extractor(testing)
713
717
  if not isinstance(testing, (Testing, dict)):
714
718
  raise OrionisTypeError(f"Expected Testing instance or dict, got {type(testing).__name__}")
715
719
  self.loadConfigTesting(testing)
@@ -1449,7 +1453,8 @@ class Application(Container, IApplication):
1449
1453
  sessions: str | Path = (Path.cwd() / 'storage' / 'framework' / 'sessions').resolve(),
1450
1454
  cache: str | Path = (Path.cwd() / 'storage' / 'framework' / 'cache').resolve(),
1451
1455
  testing: str | Path = (Path.cwd() / 'storage' / 'framework' / 'testing').resolve(),
1452
- storage: str | Path = (Path.cwd() / 'storage').resolve()
1456
+ storage: str | Path = (Path.cwd() / 'storage').resolve(),
1457
+ tests: str | Path = (Path.cwd() / 'tests').resolve()
1453
1458
  ) -> 'Application':
1454
1459
  """
1455
1460
  Set and resolve application directory paths using keyword arguments.
@@ -1553,7 +1558,8 @@ class Application(Container, IApplication):
1553
1558
  'sessions' : str(sessions),
1554
1559
  'cache' : str(cache),
1555
1560
  'testing' : str(testing),
1556
- 'storage' : str(storage)
1561
+ 'storage' : str(storage),
1562
+ 'tests' : str(tests)
1557
1563
  }
1558
1564
 
1559
1565
  # Return self instance for method chaining
@@ -1597,17 +1603,16 @@ class Application(Container, IApplication):
1597
1603
  if not isinstance(paths, (Paths, dict)):
1598
1604
  raise OrionisTypeError(f"Expected Paths instance or dict, got {type(paths).__name__}")
1599
1605
 
1606
+ # Always ensure 'root' path is set
1607
+ base_path = {'root': self.__bootstrap_base_path or str(Path.cwd().resolve())}
1608
+
1600
1609
  # If paths is a dict, convert it to Paths instance
1601
1610
  if isinstance(paths, dict):
1602
- paths.update({
1603
- 'root': self.__bootstrap_base_path or str(Path.cwd().resolve())
1604
- })
1611
+ paths.update(base_path)
1605
1612
  paths = Paths(**paths).toDict()
1606
1613
  elif isinstance(paths, Paths):
1607
1614
  paths = paths.toDict()
1608
- paths.update({
1609
- 'root': self.__bootstrap_base_path or str(Path.cwd().resolve())
1610
- })
1615
+ paths.update(base_path)
1611
1616
 
1612
1617
  # Store the configuration
1613
1618
  self.__configurators['path'] = paths
@@ -1617,49 +1622,63 @@ class Application(Container, IApplication):
1617
1622
 
1618
1623
  def setBasePath(
1619
1624
  self,
1620
- basePath: str | Path
1625
+ basePath: Path
1621
1626
  ) -> 'Application':
1622
1627
  """
1623
1628
  Set the base path for the application.
1624
1629
 
1625
1630
  This method allows setting the base path of the application, which is
1626
1631
  used as the root directory for all relative paths in the application.
1627
- The provided basePath is resolved to an absolute path.
1632
+ The provided basePath must be a Path object.
1628
1633
 
1629
1634
  Parameters
1630
1635
  ----------
1631
- basePath : str or Path
1632
- The base path to set for the application. It can be a string or a Path object.
1636
+ basePath : Path
1637
+ The base path to set for the application. It must be a Path object.
1633
1638
 
1634
1639
  Returns
1635
1640
  -------
1636
1641
  Application
1637
1642
  The current application instance to enable method chaining.
1643
+
1644
+ Raises
1645
+ ------
1646
+ OrionisTypeError
1647
+ If basePath is not a Path instance.
1638
1648
  """
1639
1649
 
1650
+ # If basePath is a string, convert to Path
1651
+ if isinstance(basePath, str):
1652
+ basePath = Path(basePath)
1653
+
1654
+ if not isinstance(basePath, Path):
1655
+ raise OrionisTypeError("basePath must be a Path object or a string convertible to Path.")
1656
+
1640
1657
  # Resolve and store the base path as a string
1641
- self.__bootstrap_base_path = str(Path(basePath).resolve())
1658
+ self.__bootstrap_base_path = str(basePath.resolve())
1642
1659
 
1643
1660
  # Return self instance for method chaining
1644
1661
  return self
1645
1662
 
1646
1663
  def getBasePath(
1647
1664
  self
1648
- ) -> str | Path:
1665
+ ) -> Path:
1649
1666
  """
1650
1667
  Get the base path of the application.
1651
1668
 
1652
1669
  This method returns the base path that was previously set using setBasePath().
1653
- If no base path has been set, it returns None.
1670
+ If no base path has been set, it returns the current working directory as a Path object.
1654
1671
 
1655
1672
  Returns
1656
1673
  -------
1657
- str or Path
1658
- The base path of the application as a string or Path object, or None if not set.
1674
+ Path
1675
+ The base path of the application as a Path object.
1659
1676
  """
1660
1677
 
1661
- # Return the base path if set, otherwise None
1662
- return self.__bootstrap_base_path if self.__bootstrap_base_path else Path.cwd().resolve()
1678
+ # Always return a Path object
1679
+ if self.__bootstrap_base_path:
1680
+ return Path(self.__bootstrap_base_path)
1681
+ return Path.cwd().resolve()
1663
1682
 
1664
1683
  def setConfigQueue(
1665
1684
  self,
@@ -2024,14 +2043,20 @@ class Application(Container, IApplication):
2024
2043
  path() method instead.
2025
2044
  """
2026
2045
 
2046
+ # Create a local copy of the configuration to avoid mutation
2047
+ local_config = self.__config.copy()
2048
+
2049
+ # Remove 'path' from local copy to prevent mutation
2050
+ if 'path' in local_config:
2051
+ del local_config['path']
2052
+
2027
2053
  # Ensure the application is booted before accessing configuration
2028
- if not self.__config:
2054
+ if not local_config:
2029
2055
  raise OrionisRuntimeError("Application configuration is not initialized. Please call create() before accessing configuration.")
2030
2056
 
2031
2057
  # Return the entire configuration if key is None, except for paths
2032
2058
  if key is None:
2033
- del self.__config['path']
2034
- return self.__config
2059
+ return local_config
2035
2060
 
2036
2061
  # If key is None, raise an error to prevent ambiguity
2037
2062
  if not isinstance(key, str):
@@ -2041,7 +2066,7 @@ class Application(Container, IApplication):
2041
2066
  parts = key.split('.')
2042
2067
 
2043
2068
  # Start with the full config
2044
- config_value = self.__config
2069
+ config_value = local_config
2045
2070
 
2046
2071
  # Traverse the config dictionary based on the key parts
2047
2072
  for part in parts:
@@ -2066,38 +2091,40 @@ class Application(Container, IApplication):
2066
2091
  self,
2067
2092
  key: str = None,
2068
2093
  default: Any = None
2069
- ) -> str:
2094
+ ) -> Path | dict:
2070
2095
  """
2071
2096
  Retrieve application path configuration values using dot notation.
2072
2097
 
2073
- This method provides access to the application's path configuration settings
2074
- with support for nested value retrieval using dot notation. It can return
2075
- either a specific path value or the entire paths configuration dictionary.
2098
+ This method provides access to the application's path configuration settings,
2099
+ supporting retrieval of either a specific path value or the entire paths
2100
+ configuration dictionary. If a key is provided, it returns the corresponding
2101
+ path as a `Path` object. If no key is provided, it returns a dictionary
2102
+ mapping all path configuration keys to their resolved `Path` objects.
2076
2103
 
2077
2104
  Parameters
2078
2105
  ----------
2079
2106
  key : str, optional
2080
2107
  Dot-notated key specifying the path configuration to retrieve (e.g.,
2081
- "console_commands", "storage.logs"). If None, returns the entire
2082
- paths configuration dictionary. Default is None.
2108
+ "console", "storage.logs"). If None, returns the entire paths
2109
+ configuration dictionary. Default is None.
2083
2110
  default : Any, optional
2084
2111
  Value to return if the specified key is not found in the path
2085
2112
  configuration. Default is None.
2086
2113
 
2087
2114
  Returns
2088
2115
  -------
2089
- str
2090
- The path configuration value corresponding to the given key, the entire
2091
- paths dictionary if key is None, or the default value if the key is
2092
- not found.
2116
+ Path or dict
2117
+ If `key` is provided and found, returns the resolved `Path` object for that key.
2118
+ If `key` is None, returns a dictionary mapping all path keys to their `Path` objects.
2119
+ If `key` is not found, returns `default` if specified, otherwise `None`.
2093
2120
 
2094
2121
  Raises
2095
2122
  ------
2096
2123
  OrionisRuntimeError
2097
2124
  If the application configuration has not been initialized. This occurs
2098
- when path() is called before create().
2125
+ when `path()` is called before `create()`.
2099
2126
  OrionisValueError
2100
- If the provided key parameter is not a string type.
2127
+ If the provided `key` parameter is not a string type.
2101
2128
 
2102
2129
  Notes
2103
2130
  -----
@@ -2107,37 +2134,35 @@ class Application(Container, IApplication):
2107
2134
  application configuration.
2108
2135
  """
2109
2136
 
2137
+ # Create a local copy of the path configuration to avoid mutation
2138
+ local_path_config = self.__config.get('path', {}).copy() if self.__config else {}
2139
+
2110
2140
  # Ensure the application is booted before accessing configuration
2111
- if not self.__config:
2112
- raise OrionisRuntimeError("Application configuration is not initialized. Please call create() before accessing path configuration.")
2141
+ if not local_path_config:
2142
+ raise OrionisRuntimeError(
2143
+ "Application configuration is not initialized. Please call create() before accessing path configuration."
2144
+ )
2113
2145
 
2114
- # Return the entire configuration if key is None, except for paths
2146
+ # If no key is provided, return all paths as a dictionary of Path objects
2115
2147
  if key is None:
2116
- return self.__config['path']
2148
+ path_resolved: Dict[str, Path] = {}
2149
+ for k, v in local_path_config.items():
2150
+ # Convert each path string to a Path object
2151
+ path_resolved[k] = Path(v)
2152
+ return path_resolved
2117
2153
 
2118
- # If key is None, raise an error to prevent ambiguity
2154
+ # Ensure the key is a string
2119
2155
  if not isinstance(key, str):
2120
- raise OrionisValueError("Key must be a string. Use path() without arguments to get the entire paths configuration.")
2156
+ raise OrionisValueError(
2157
+ "Key must be a string. Use path() without arguments to get the entire paths configuration."
2158
+ )
2121
2159
 
2122
- # Split the key by dot notation
2123
- parts = key.split('.')
2160
+ # Direct key match: return the resolved Path object if the key exists
2161
+ if key in local_path_config:
2162
+ return Path(local_path_config[key])
2124
2163
 
2125
- # Start with the full config
2126
- config_value = self.__config['path']
2127
-
2128
- # Traverse the config dictionary based on the key parts
2129
- for part in parts:
2130
-
2131
- # If part is not in config_value, return default
2132
- if isinstance(config_value, dict) and part in config_value:
2133
- config_value = config_value[part]
2134
-
2135
- # If part is not found, return default value
2136
- else:
2137
- return default
2138
-
2139
- # Return the final configuration value
2140
- return config_value
2164
+ # If the key is not found, return the default value (if provided), else None
2165
+ return default if default is not None else None
2141
2166
 
2142
2167
  # === Application Creation Method ===
2143
2168
  # The create() method is responsible for bootstrapping the application.
@@ -2178,7 +2203,7 @@ class Application(Container, IApplication):
2178
2203
  if not self.__booted:
2179
2204
 
2180
2205
  # Register the application instance in the container
2181
- self.instance(IApplication, self, alias="x-orionis.foundation.application", enforce_decoupling='X-ORIONIS')
2206
+ self.instance(IApplication, self, alias=f"x-{IApplication.__module__}.{IApplication.__name__}", enforce_decoupling='x-orionis')
2182
2207
 
2183
2208
  # Load configuration if not already set
2184
2209
  self.__loadConfig()
@@ -2192,7 +2217,7 @@ class Application(Container, IApplication):
2192
2217
  self.__loadFrameworksKernel()
2193
2218
 
2194
2219
  # Retrieve logger and console instances from the container
2195
- logger: ILogger = self.make('x-orionis.services.log.log_service')
2220
+ logger: ILogger = self.make(ILogger)
2196
2221
 
2197
2222
  # Calculate elapsed time in milliseconds since application start
2198
2223
  elapsed_ms = (time.time_ns() - self.startAt) // 1_000_000
@@ -4,7 +4,7 @@ from orionis.foundation.config.database.enums import (
4
4
  SQLiteJournalMode,
5
5
  SQLiteSynchronous
6
6
  )
7
- from orionis.foundation.exceptions.integrity import OrionisIntegrityException
7
+ from orionis.foundation.exceptions import OrionisIntegrityException
8
8
  from orionis.services.environment.env import Env
9
9
  from orionis.support.entities.base import BaseEntity
10
10
 
@@ -238,6 +238,14 @@ class Paths(BaseEntity):
238
238
  }
239
239
  )
240
240
 
241
+ tests : str = field(
242
+ default_factory = lambda: str((Path.cwd() / 'tests').resolve()),
243
+ metadata = {
244
+ 'description': 'Directory containing test files.',
245
+ 'default': lambda: str((Path.cwd() / 'tests').resolve())
246
+ }
247
+ )
248
+
241
249
  def __post_init__(self) -> None:
242
250
  """
243
251
  Post-initialization hook to validate path attributes.
@@ -5,7 +5,7 @@ from orionis.foundation.config.cache.entities.cache import Cache
5
5
  from orionis.foundation.config.cors.entities.cors import Cors
6
6
  from orionis.foundation.config.database.entities.database import Database
7
7
  from orionis.foundation.config.roots.paths import Paths
8
- from orionis.foundation.exceptions.integrity import OrionisIntegrityException
8
+ from orionis.foundation.exceptions import OrionisIntegrityException
9
9
  from orionis.foundation.config.filesystems.entitites.filesystems import Filesystems
10
10
  from orionis.foundation.config.logging.entities.logging import Logging
11
11
  from orionis.foundation.config.mail.entities.mail import Mail