orionis 0.405.0__py3-none-any.whl → 0.407.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 (175) 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 +9 -9
  34. orionis/foundation/config/logging/entities/daily.py +8 -8
  35. orionis/foundation/config/logging/entities/hourly.py +6 -6
  36. orionis/foundation/config/logging/entities/logging.py +12 -18
  37. orionis/foundation/config/logging/entities/monthly.py +7 -7
  38. orionis/foundation/config/logging/entities/stack.py +5 -5
  39. orionis/foundation/config/logging/entities/weekly.py +6 -6
  40. orionis/foundation/config/mail/entities/file.py +9 -36
  41. orionis/foundation/config/mail/entities/mail.py +22 -40
  42. orionis/foundation/config/mail/entities/mailers.py +29 -44
  43. orionis/foundation/config/mail/entities/smtp.py +47 -48
  44. orionis/foundation/config/queue/entities/brokers.py +19 -41
  45. orionis/foundation/config/queue/entities/database.py +24 -46
  46. orionis/foundation/config/queue/entities/queue.py +28 -40
  47. orionis/foundation/config/roots/paths.py +272 -468
  48. orionis/foundation/config/session/entities/session.py +23 -53
  49. orionis/foundation/config/startup.py +165 -135
  50. orionis/foundation/config/testing/entities/testing.py +137 -122
  51. orionis/foundation/config/testing/enums/__init__.py +6 -2
  52. orionis/foundation/config/testing/enums/drivers.py +16 -0
  53. orionis/foundation/config/testing/enums/verbosity.py +18 -0
  54. orionis/foundation/contracts/application.py +152 -362
  55. orionis/foundation/providers/console_provider.py +24 -2
  56. orionis/foundation/providers/dumper_provider.py +24 -2
  57. orionis/foundation/providers/logger_provider.py +24 -2
  58. orionis/foundation/providers/path_resolver_provider.py +25 -2
  59. orionis/foundation/providers/progress_bar_provider.py +24 -2
  60. orionis/foundation/providers/testing_provider.py +39 -0
  61. orionis/foundation/providers/workers_provider.py +24 -2
  62. orionis/metadata/framework.py +1 -1
  63. orionis/services/asynchrony/contracts/coroutines.py +13 -5
  64. orionis/services/asynchrony/coroutines.py +33 -29
  65. orionis/services/asynchrony/exceptions/exception.py +9 -1
  66. orionis/services/environment/core/dot_env.py +46 -34
  67. orionis/services/environment/enums/__init__.py +0 -0
  68. orionis/services/environment/enums/cast_type.py +42 -0
  69. orionis/services/environment/helpers/functions.py +1 -2
  70. orionis/services/environment/key/__init__.py +0 -0
  71. orionis/services/environment/key/key_generator.py +37 -0
  72. orionis/services/environment/serializer/__init__.py +0 -0
  73. orionis/services/environment/serializer/values.py +21 -0
  74. orionis/services/environment/validators/__init__.py +0 -0
  75. orionis/services/environment/validators/key_name.py +46 -0
  76. orionis/services/environment/validators/types.py +45 -0
  77. orionis/services/system/contracts/imports.py +38 -18
  78. orionis/services/system/contracts/workers.py +29 -12
  79. orionis/services/system/imports.py +65 -25
  80. orionis/services/system/runtime/imports.py +18 -9
  81. orionis/services/system/workers.py +49 -16
  82. orionis/support/entities/__init__.py +0 -0
  83. orionis/support/entities/base.py +104 -0
  84. orionis/support/facades/testing.py +15 -0
  85. orionis/support/facades/workers.py +1 -1
  86. orionis/test/cases/asynchronous.py +0 -11
  87. orionis/test/cases/synchronous.py +0 -9
  88. orionis/test/contracts/dumper.py +11 -4
  89. orionis/test/contracts/kernel.py +5 -110
  90. orionis/test/contracts/logs.py +27 -65
  91. orionis/test/contracts/printer.py +16 -128
  92. orionis/test/contracts/test_result.py +100 -0
  93. orionis/test/contracts/unit_test.py +87 -150
  94. orionis/test/core/unit_test.py +608 -554
  95. orionis/test/entities/result.py +22 -2
  96. orionis/test/enums/__init__.py +0 -2
  97. orionis/test/enums/status.py +14 -9
  98. orionis/test/exceptions/config.py +9 -1
  99. orionis/test/exceptions/failure.py +34 -11
  100. orionis/test/exceptions/persistence.py +10 -2
  101. orionis/test/exceptions/runtime.py +9 -1
  102. orionis/test/exceptions/value.py +13 -1
  103. orionis/test/kernel.py +87 -289
  104. orionis/test/output/dumper.py +83 -18
  105. orionis/test/output/printer.py +399 -156
  106. orionis/test/records/logs.py +203 -82
  107. orionis/test/validators/__init__.py +33 -0
  108. orionis/test/validators/base_path.py +45 -0
  109. orionis/test/validators/execution_mode.py +45 -0
  110. orionis/test/validators/fail_fast.py +37 -0
  111. orionis/test/validators/folder_path.py +34 -0
  112. orionis/test/validators/module_name.py +31 -0
  113. orionis/test/validators/name_pattern.py +40 -0
  114. orionis/test/validators/pattern.py +36 -0
  115. orionis/test/validators/persistent.py +42 -0
  116. orionis/test/validators/persistent_driver.py +43 -0
  117. orionis/test/validators/print_result.py +37 -0
  118. orionis/test/validators/tags.py +37 -0
  119. orionis/test/validators/throw_exception.py +39 -0
  120. orionis/test/validators/verbosity.py +37 -0
  121. orionis/test/validators/web_report.py +35 -0
  122. orionis/test/validators/workers.py +31 -0
  123. orionis/test/view/render.py +48 -54
  124. {orionis-0.405.0.dist-info → orionis-0.407.0.dist-info}/METADATA +1 -1
  125. {orionis-0.405.0.dist-info → orionis-0.407.0.dist-info}/RECORD +170 -112
  126. tests/container/__init__.py +0 -0
  127. tests/container/context/__init__.py +0 -0
  128. tests/container/context/test_manager.py +27 -0
  129. tests/container/context/test_scope.py +23 -0
  130. tests/container/entities/__init__.py +0 -0
  131. tests/container/entities/test_binding.py +133 -0
  132. tests/container/enums/__init__.py +0 -0
  133. tests/container/enums/test_lifetimes.py +63 -0
  134. tests/container/facades/__init__.py +0 -0
  135. tests/container/facades/test_facade.py +61 -0
  136. tests/container/mocks/__init__.py +0 -0
  137. tests/container/mocks/mock_complex_classes.py +482 -0
  138. tests/container/mocks/mock_simple_classes.py +32 -0
  139. tests/container/providers/__init__.py +0 -0
  140. tests/container/providers/test_providers.py +48 -0
  141. tests/container/resolver/__init__.py +0 -0
  142. tests/container/resolver/test_resolver.py +55 -0
  143. tests/container/test_container.py +254 -0
  144. tests/container/test_singleton.py +98 -0
  145. tests/container/test_thread_safety.py +217 -0
  146. tests/container/validators/__init__.py +0 -0
  147. tests/container/validators/test_implements.py +140 -0
  148. tests/container/validators/test_is_abstract_class.py +99 -0
  149. tests/container/validators/test_is_callable.py +73 -0
  150. tests/container/validators/test_is_concrete_class.py +97 -0
  151. tests/container/validators/test_is_instance.py +105 -0
  152. tests/container/validators/test_is_not_subclass.py +117 -0
  153. tests/container/validators/test_is_subclass.py +115 -0
  154. tests/container/validators/test_is_valid_alias.py +113 -0
  155. tests/container/validators/test_lifetime.py +75 -0
  156. tests/example/test_example.py +2 -2
  157. tests/foundation/config/testing/test_foundation_config_testing.py +1 -1
  158. tests/metadata/test_metadata_framework.py +89 -24
  159. tests/metadata/test_metadata_package.py +55 -10
  160. tests/services/asynchrony/test_services_asynchrony_coroutine.py +52 -7
  161. tests/services/system/test_services_system_imports.py +119 -16
  162. tests/services/system/test_services_system_workers.py +71 -30
  163. tests/testing/test_testing_result.py +117 -117
  164. tests/testing/test_testing_unit.py +209 -209
  165. orionis/foundation/config/base.py +0 -112
  166. orionis/test/arguments/parser.py +0 -187
  167. orionis/test/contracts/parser.py +0 -43
  168. orionis/test/entities/arguments.py +0 -38
  169. orionis/test/enums/execution_mode.py +0 -16
  170. /orionis/{test/arguments → console/base/contracts}/__init__.py +0 -0
  171. /orionis/foundation/config/testing/enums/{test_mode.py → mode.py} +0 -0
  172. {orionis-0.405.0.dist-info → orionis-0.407.0.dist-info}/WHEEL +0 -0
  173. {orionis-0.405.0.dist-info → orionis-0.407.0.dist-info}/licenses/LICENCE +0 -0
  174. {orionis-0.405.0.dist-info → orionis-0.407.0.dist-info}/top_level.txt +0 -0
  175. {orionis-0.405.0.dist-info → orionis-0.407.0.dist-info}/zip-safe +0 -0
@@ -3,15 +3,37 @@ from orionis.console.output.contracts.console import IConsole
3
3
  from orionis.container.providers.service_provider import ServiceProvider
4
4
 
5
5
  class ConsoleProvider(ServiceProvider):
6
+ """
7
+ ConsoleProvider
8
+ ===============
9
+
10
+ Registers the console output service in the application container.
11
+ Provides access to various console output features, including information, warnings, errors, debug messages, tables, confirmations, and password prompts.
12
+
13
+ Methods
14
+ -------
15
+ register()
16
+ Registers the console service in the application container.
17
+ boot()
18
+ Performs post-registration initialization if needed.
19
+ """
6
20
 
7
21
  def register(self) -> None:
8
22
  """
9
- Register services into the application container.
23
+ Registers the console service in the application container.
24
+
25
+ Returns
26
+ -------
27
+ None
10
28
  """
11
29
  self.app.transient(IConsole, Console, alias="core.orionis.console")
12
30
 
13
31
  def boot(self) -> None:
14
32
  """
15
- Perform any post-registration bootstrapping or initialization.
33
+ Performs post-registration initialization if needed.
34
+
35
+ Returns
36
+ -------
37
+ None
16
38
  """
17
39
  pass
@@ -3,15 +3,37 @@ from orionis.console.dumper.contracts.dump import IDebug
3
3
  from orionis.container.providers.service_provider import ServiceProvider
4
4
 
5
5
  class DumperProvider(ServiceProvider):
6
+ """
7
+ DumperProvider
8
+ ==============
9
+
10
+ Registers the debug message service in the application container.
11
+ Provides access to debug message printing, error reporting, and other console diagnostics.
12
+
13
+ Methods
14
+ -------
15
+ register()
16
+ Registers the debug service in the application container.
17
+ boot()
18
+ Performs post-registration initialization if needed.
19
+ """
6
20
 
7
21
  def register(self) -> None:
8
22
  """
9
- Register services into the application container.
23
+ Registers the debug service in the application container.
24
+
25
+ Returns
26
+ -------
27
+ None
10
28
  """
11
29
  self.app.transient(IDebug, Debug, alias="core.orionis.dumper")
12
30
 
13
31
  def boot(self) -> None:
14
32
  """
15
- Perform any post-registration bootstrapping or initialization.
33
+ Performs post-registration initialization if needed.
34
+
35
+ Returns
36
+ -------
37
+ None
16
38
  """
17
39
  pass
@@ -3,15 +3,37 @@ from orionis.services.log.contracts.log_service import ILoggerService
3
3
  from orionis.services.log.log_service import LoggerService
4
4
 
5
5
  class LoggerProvider(ServiceProvider):
6
+ """
7
+ LoggerProvider
8
+ ==============
9
+
10
+ Registers the logging service in the application container.
11
+ Provides a `LoggerService` instance for application-wide logging.
12
+
13
+ Methods
14
+ -------
15
+ register()
16
+ Registers the logging service in the application container.
17
+ boot()
18
+ Performs post-registration initialization if needed.
19
+ """
6
20
 
7
21
  def register(self) -> None:
8
22
  """
9
- Register services into the application container.
23
+ Registers the logging service in the application container.
24
+
25
+ Returns
26
+ -------
27
+ None
10
28
  """
11
29
  self.app.instance(ILoggerService, LoggerService(self.app.config('logging')), alias="core.orionis.logger")
12
30
 
13
31
  def boot(self) -> None:
14
32
  """
15
- Perform any post-registration bootstrapping or initialization.
33
+ Performs post-registration initialization if needed.
34
+
35
+ Returns
36
+ -------
37
+ None
16
38
  """
17
39
  pass
@@ -3,15 +3,38 @@ from orionis.services.paths.contracts.resolver import IResolver
3
3
  from orionis.services.paths.resolver import Resolver
4
4
 
5
5
  class PathResolverProvider(ServiceProvider):
6
+ """
7
+ PathResolverProvider
8
+ ===================
9
+
10
+ Registers the path resolution service in the application container.
11
+ Provides compatibility with the file system for resolving paths.
12
+
13
+ Methods
14
+ -------
15
+ register()
16
+ Registers the path resolver service in the application container.
17
+ boot()
18
+ Performs post-registration initialization if needed.
19
+ """
20
+
6
21
 
7
22
  def register(self) -> None:
8
23
  """
9
- Register services into the application container.
24
+ Registers the path resolver service in the application container.
25
+
26
+ Returns
27
+ -------
28
+ None
10
29
  """
11
30
  self.app.transient(IResolver, Resolver, alias="core.orionis.path_resolver")
12
31
 
13
32
  def boot(self) -> None:
14
33
  """
15
- Perform any post-registration bootstrapping or initialization.
34
+ Performs post-registration initialization if needed.
35
+
36
+ Returns
37
+ -------
38
+ None
16
39
  """
17
40
  pass
@@ -3,15 +3,37 @@ from orionis.console.dynamic.progress_bar import ProgressBar
3
3
  from orionis.container.providers.service_provider import ServiceProvider
4
4
 
5
5
  class ProgressBarProvider(ServiceProvider):
6
+ """
7
+ ProgressBarProvider
8
+ ===================
9
+
10
+ Registers the dynamic progress bar service in the application container.
11
+ Provides a console progress bar for visual feedback during operations.
12
+
13
+ Methods
14
+ -------
15
+ register()
16
+ Registers the progress bar service in the application container.
17
+ boot()
18
+ Performs post-registration initialization if needed.
19
+ """
6
20
 
7
21
  def register(self) -> None:
8
22
  """
9
- Register services into the application container.
23
+ Registers the progress bar service in the application container.
24
+
25
+ Returns
26
+ -------
27
+ None
10
28
  """
11
29
  self.app.transient(IProgressBar, ProgressBar, alias="core.orionis.progress_bar")
12
30
 
13
31
  def boot(self) -> None:
14
32
  """
15
- Perform any post-registration bootstrapping or initialization.
33
+ Performs post-registration initialization if needed.
34
+
35
+ Returns
36
+ -------
37
+ None
16
38
  """
17
39
  pass
@@ -0,0 +1,39 @@
1
+ from orionis.container.providers.service_provider import ServiceProvider
2
+ from orionis.test.contracts.unit_test import IUnitTest
3
+ from orionis.test.core.unit_test import UnitTest
4
+
5
+ class TestingProvider(ServiceProvider):
6
+ """
7
+ TestingProvider
8
+ ===============
9
+
10
+ Registers the unit testing environment service in the application container.
11
+ Provides a native unit testing framework for Orionis with features beyond common frameworks.
12
+
13
+ Methods
14
+ -------
15
+ register()
16
+ Registers the unit testing service in the application container.
17
+ boot()
18
+ Performs post-registration initialization if needed.
19
+ """
20
+
21
+ def register(self) -> None:
22
+ """
23
+ Registers the unit testing service in the application container.
24
+
25
+ Returns
26
+ -------
27
+ None
28
+ """
29
+ self.app.singleton(IUnitTest, UnitTest, alias="core.orionis.testing")
30
+
31
+ def boot(self) -> None:
32
+ """
33
+ Performs post-registration initialization if needed.
34
+
35
+ Returns
36
+ -------
37
+ None
38
+ """
39
+ pass
@@ -3,15 +3,37 @@ from orionis.services.system.contracts.workers import IWorkers
3
3
  from orionis.services.system.workers import Workers
4
4
 
5
5
  class WorkersProvider(ServiceProvider):
6
+ """
7
+ WorkersProvider
8
+ ===============
9
+
10
+ Registers the worker management service in the application container.
11
+ Determines the optimal number of workers to start based on system analysis.
12
+
13
+ Methods
14
+ -------
15
+ register()
16
+ Registers the worker service in the application container.
17
+ boot()
18
+ Performs post-registration initialization if needed.
19
+ """
6
20
 
7
21
  def register(self) -> None:
8
22
  """
9
- Register services into the application container.
23
+ Registers the worker service in the application container.
24
+
25
+ Returns
26
+ -------
27
+ None
10
28
  """
11
29
  self.app.transient(IWorkers, Workers, alias="core.orionis.workers")
12
30
 
13
31
  def boot(self) -> None:
14
32
  """
15
- Perform any post-registration bootstrapping or initialization.
33
+ Performs post-registration initialization if needed.
34
+
35
+ Returns
36
+ -------
37
+ None
16
38
  """
17
39
  pass
@@ -5,7 +5,7 @@
5
5
  NAME = "orionis"
6
6
 
7
7
  # Current version of the framework
8
- VERSION = "0.405.0"
8
+ VERSION = "0.407.0"
9
9
 
10
10
  # Full name of the author or maintainer of the project
11
11
  AUTHOR = "Raul Mauricio Uñate Castro"
@@ -9,16 +9,24 @@ class ICoroutine(ABC):
9
9
  @abstractmethod
10
10
  def run(self) -> Union[T, asyncio.Future]:
11
11
  """
12
- Execute the wrapped coroutine.
12
+ Executes the wrapped coroutine, either synchronously or asynchronously depending on the context.
13
+
14
+ Parameters
15
+ ----------
16
+ None
13
17
 
14
18
  Returns
15
19
  -------
16
- result : T or asyncio.Future
17
- The result of the coroutine if run synchronously, or a Future if run in an event loop.
20
+ T or asyncio.Future
21
+ If called outside an event loop, returns the result of the coroutine execution (type T).
22
+ If called within an event loop, returns an asyncio.Future representing the scheduled coroutine.
18
23
 
19
24
  Notes
20
25
  -----
21
- - If called from outside an event loop, this method will run the coroutine synchronously.
22
- - If called from within an event loop, it will schedule the coroutine and return a Future.
26
+ - When invoked outside of an event loop, the coroutine is executed synchronously and its result is returned.
27
+ - When invoked inside an event loop, the coroutine is scheduled for asynchronous execution and a Future is returned.
28
+ - The caller is responsible for awaiting the Future if asynchronous execution is used.
23
29
  """
30
+
31
+ # This method should be implemented by subclasses to handle coroutine execution.
24
32
  pass
@@ -7,20 +7,6 @@ from orionis.services.introspection.objects.types import Type
7
7
  T = TypeVar("T")
8
8
 
9
9
  class Coroutine(ICoroutine):
10
- """
11
- Wrapper class for coroutine objects to facilitate execution in both synchronous
12
- and asynchronous contexts.
13
-
14
- Parameters
15
- ----------
16
- func : Coroutine
17
- The coroutine object to be wrapped.
18
-
19
- Raises
20
- ------
21
- OrionisCoroutineException
22
- If the provided object is not a coroutine.
23
- """
24
10
 
25
11
  def __init__(self, func: TypingCoroutine[Any, Any, T]) -> None:
26
12
  """
@@ -29,51 +15,69 @@ class Coroutine(ICoroutine):
29
15
  Parameters
30
16
  ----------
31
17
  func : Coroutine
32
- The coroutine object to be wrapped.
18
+ The coroutine object to be wrapped. Must be an awaitable coroutine.
33
19
 
34
20
  Raises
35
21
  ------
36
22
  OrionisCoroutineException
37
23
  If the provided object is not a coroutine.
24
+
25
+ Returns
26
+ -------
27
+ None
28
+ This method does not return a value.
29
+
30
+ Notes
31
+ -----
32
+ This constructor validates that the provided object is a coroutine using the framework's type introspection.
33
+ If the validation fails, an exception is raised to prevent improper usage.
38
34
  """
35
+ # Validate that the provided object is a coroutine
39
36
  if not Type(func).isCoroutine():
40
37
  raise OrionisCoroutineException(
41
38
  f"Expected a coroutine object, but got {type(func).__name__}."
42
39
  )
43
40
 
44
- # Store the coroutine function
41
+ # Store the coroutine object for later execution
45
42
  self.__func = func
46
43
 
47
44
  def run(self) -> Union[T, asyncio.Future]:
48
45
  """
49
- Execute the wrapped coroutine.
46
+ Executes the wrapped coroutine, either synchronously or asynchronously depending on the context.
47
+
48
+ Parameters
49
+ ----------
50
+ None
50
51
 
51
52
  Returns
52
53
  -------
53
- result : T or asyncio.Future
54
- The result of the coroutine if run synchronously, or a Future if run in an event loop.
54
+ T or asyncio.Future
55
+ If called outside an event loop, returns the result of the coroutine after synchronous execution.
56
+ If called within an event loop, returns an asyncio.Future representing the scheduled coroutine.
57
+
58
+ Raises
59
+ ------
60
+ RuntimeError
61
+ If the coroutine cannot be executed due to event loop issues.
55
62
 
56
63
  Notes
57
64
  -----
58
- - If called from outside an event loop, this method will run the coroutine synchronously.
59
- - If called from within an event loop, it will schedule the coroutine and return a Future.
65
+ - When invoked outside an active event loop, the coroutine is executed synchronously and its result is returned.
66
+ - When invoked inside an active event loop, the coroutine is scheduled for asynchronous execution and a Future is returned.
67
+ - This method automatically detects the execution context and chooses the appropriate execution strategy.
60
68
  """
69
+ # Attempt to get the currently running event loop
61
70
  try:
62
-
63
- # Get the current event loop
64
71
  loop = asyncio.get_running_loop()
65
72
 
73
+ # No running event loop; execute the coroutine synchronously and return its result
66
74
  except RuntimeError:
67
-
68
- # No running event loop, run synchronously
69
75
  return asyncio.run(self.__func)
70
76
 
77
+ # If inside an active event loop, schedule the coroutine and return a Future
71
78
  if loop.is_running():
72
-
73
- # Inside an event loop, schedule as a Future
74
79
  return asyncio.ensure_future(self.__func)
75
80
 
81
+ # If no event loop is running, execute the coroutine synchronously using the loop
76
82
  else:
77
-
78
- # No running loop, run synchronously
79
83
  return loop.run_until_complete(self.__func)
@@ -2,18 +2,26 @@ class OrionisCoroutineException(Exception):
2
2
 
3
3
  def __init__(self, msg: str):
4
4
  """
5
+ Initializes an OrionisCoroutineException with a descriptive error message.
6
+
5
7
  Parameters
6
8
  ----------
7
9
  msg : str
8
10
  Descriptive error message explaining the cause of the exception.
9
11
  """
12
+
13
+ # Call the base Exception constructor with the provided message
10
14
  super().__init__(msg)
11
15
 
12
16
  def __str__(self) -> str:
13
17
  """
18
+ Returns a formatted string representation of the exception message.
19
+
14
20
  Returns
15
21
  -------
16
22
  str
17
- Formatted string describing the exception.
23
+ The error message provided during exception initialization.
18
24
  """
25
+
26
+ # Return the first argument passed to the Exception, which is the error message
19
27
  return str(self.args[0])
@@ -5,6 +5,9 @@ import threading
5
5
  from pathlib import Path
6
6
  from typing import Any, Optional, Union
7
7
  from dotenv import dotenv_values, load_dotenv, set_key, unset_key
8
+ from orionis.services.environment.enums.cast_type import EnvCastType
9
+ from orionis.services.environment.validators.key_name import ValidateKeyName
10
+ from orionis.services.environment.validators.types import ValidateTypes
8
11
  from orionis.support.patterns.singleton import Singleton
9
12
  from orionis.services.environment.exceptions import OrionisEnvironmentValueException, OrionisEnvironmentValueError
10
13
  from orionis.services.environment.dynamic.types import EnvTypes
@@ -16,33 +19,56 @@ class DotEnv(metaclass=Singleton):
16
19
 
17
20
  def __init__(self, path: str = None) -> None:
18
21
  """
19
- Initialize the environment service by resolving the path to the `.env` file, ensuring its existence,
20
- and loading environment variables from it.
22
+ Initialize the DotEnv service by resolving and preparing the `.env` file.
23
+
24
+ This method determines the path to the `.env` file, ensures its existence,
25
+ and loads environment variables from it into the current process environment.
21
26
 
22
27
  Parameters
23
28
  ----------
24
29
  path : str, optional
25
- Path to the `.env` file. If not provided, defaults to a `.env` file
30
+ The path to the `.env` file. If not provided, defaults to a `.env` file
26
31
  in the current working directory.
27
32
 
33
+ Returns
34
+ -------
35
+ None
36
+ This method does not return any value.
37
+
28
38
  Raises
29
39
  ------
30
40
  OSError
31
- If the `.env` file cannot be created when it does not exist.
41
+ If the `.env` file cannot be created or accessed.
42
+
43
+ Notes
44
+ -----
45
+ - If the specified `.env` file does not exist, it will be created.
46
+ - Environment variables from the `.env` file are loaded into the process environment.
32
47
  """
48
+
33
49
  try:
50
+
51
+ # Use a thread-safe lock to ensure only one thread can execute this block at a time
34
52
  with self._lock:
53
+
54
+ # Defualt Environment file path
55
+ self.__resolved_path = Path(os.getcwd()) / ".env"
56
+
57
+ # If a path is provided, resolve it
35
58
  if path:
36
- self._resolved_path = Path(path).expanduser().resolve()
37
- else:
38
- self._resolved_path = Path(os.getcwd()) / ".env"
59
+ self.__resolved_path = Path(path).expanduser().resolve()
60
+
61
+ # Create the .env file if it does not exist
62
+ if not self.__resolved_path.exists():
63
+ self.__resolved_path.touch()
39
64
 
40
- if not self._resolved_path.exists():
41
- self._resolved_path.touch()
65
+ # Load environment variables from the .env file into the process environment
66
+ load_dotenv(self.__resolved_path)
42
67
 
43
- load_dotenv(self._resolved_path)
44
68
  except OSError as e:
45
- raise OSError(f"Failed to create or access the .env file at {self._resolved_path}: {e}")
69
+
70
+ # Raise an error if the .env file cannot be created or accessed
71
+ raise OSError(f"Failed to create or access the .env file at {self.__resolved_path}: {e}")
46
72
 
47
73
  def __parseValue(self, value: Any) -> Any:
48
74
  """
@@ -172,7 +198,7 @@ class DotEnv(metaclass=Singleton):
172
198
  )
173
199
 
174
200
  # Get the value from the .env file or the current environment.
175
- value = dotenv_values(self._resolved_path).get(key)
201
+ value = dotenv_values(self.__resolved_path).get(key)
176
202
 
177
203
  # If the value is not found in the .env file, check the current environment variables.
178
204
  if value is None:
@@ -181,7 +207,7 @@ class DotEnv(metaclass=Singleton):
181
207
  # Parse the value using the internal __parseValue method and return it
182
208
  return self.__parseValue(value) if value is not None else default
183
209
 
184
- def set(self, key: str, value: Union[str, int, float, bool, list, dict, tuple, set], type_hint: str = None) -> bool:
210
+ def set(self, key: str, value: Union[str, int, float, bool, list, dict, tuple, set], type_hint: str | EnvCastType = None) -> bool:
185
211
  """
186
212
  Set an environment variable with the specified key and value.
187
213
 
@@ -203,32 +229,18 @@ class DotEnv(metaclass=Singleton):
203
229
  """
204
230
  with self._lock:
205
231
 
206
- # Ensure the key is a string.
207
- if not isinstance(key, str) or not re.match(r'^[A-Z][A-Z0-9_]*$', key):
208
- raise OrionisEnvironmentValueError(
209
- f"The environment variable name '{key}' is not valid. It must be an uppercase string, may contain numbers and underscores, and must always start with a letter. Example of a valid name: 'MY_ENV_VAR'."
210
- )
211
-
212
- # Ensure the value is a valid type.
213
- if not isinstance(value, (str, int, float, bool, list, dict, tuple, set)):
214
- raise OrionisEnvironmentValueError(
215
- f"Unsupported value type: {type(value).__name__}. Allowed types are str, int, float, bool, list, dict, tuple, set."
216
- )
232
+ # Ensure name key is valid.
233
+ key = ValidateKeyName(key)
234
+ type = ValidateTypes(value, type_hint)
217
235
 
218
- # Dinamically determine the type hint if not provided.
219
- if isinstance(value, (int, float, bool, list, dict, tuple, set)) and not type_hint:
220
- type_hint = type(value).__name__.lower()
221
236
 
222
- # Validate the type hint if provided.
223
- options = EnvTypes.options()
224
- if type_hint and type_hint not in options:
225
- raise OrionisEnvironmentValueException(f"Invalid type hint: {type_hint}. Allowed types are {str(options)}.")
226
237
 
238
+
227
239
  # Serialize the value based on its type.
228
240
  serialized_value = self.__serializeValue(value, type_hint)
229
241
 
230
242
  # Set the environment variable in the .env file and the current process environment.
231
- set_key(str(self._resolved_path), key, serialized_value)
243
+ set_key(str(self.__resolved_path), key, serialized_value)
232
244
  os.environ[key] = str(value)
233
245
 
234
246
  # Return True to indicate success.
@@ -249,7 +261,7 @@ class DotEnv(metaclass=Singleton):
249
261
  True if the operation was successful.
250
262
  """
251
263
  with self._lock:
252
- unset_key(str(self._resolved_path), key)
264
+ unset_key(str(self.__resolved_path), key)
253
265
  os.environ.pop(key, None)
254
266
  return True
255
267
 
@@ -264,5 +276,5 @@ class DotEnv(metaclass=Singleton):
264
276
  with values parsed using the internal __parseValue method.
265
277
  """
266
278
  with self._lock:
267
- raw_values = dotenv_values(self._resolved_path)
279
+ raw_values = dotenv_values(self.__resolved_path)
268
280
  return {k: self.__parseValue(v) for k, v in raw_values.items()}
File without changes
@@ -0,0 +1,42 @@
1
+ from enum import Enum
2
+
3
+ class EnvCastType(Enum):
4
+ """
5
+ Enum representing supported environment variable cast types.
6
+
7
+ Attributes
8
+ ----------
9
+ PATH : EnvCastType
10
+ Cast to a file system path.
11
+ STR : EnvCastType
12
+ Cast to a string.
13
+ INT : EnvCastType
14
+ Cast to an integer.
15
+ FLOAT : EnvCastType
16
+ Cast to a floating-point number.
17
+ BOOL : EnvCastType
18
+ Cast to a boolean value.
19
+ LIST : EnvCastType
20
+ Cast to a list.
21
+ DICT : EnvCastType
22
+ Cast to a dictionary.
23
+ TUPLE : EnvCastType
24
+ Cast to a tuple.
25
+ SET : EnvCastType
26
+ Cast to a set.
27
+
28
+ Returns
29
+ -------
30
+ EnvCastType
31
+ An enumeration member representing the desired cast type.
32
+ """
33
+
34
+ PATH = 'path' # Represents a file system path
35
+ STR = 'str' # Represents a string type
36
+ INT = 'int' # Represents an integer type
37
+ FLOAT = 'float' # Represents a floating-point type
38
+ BOOL = 'bool' # Represents a boolean type
39
+ LIST = 'list' # Represents a list type
40
+ DICT = 'dict' # Represents a dictionary type
41
+ TUPLE = 'tuple' # Represents a tuple type
42
+ SET = 'set' # Represents a set type
@@ -17,5 +17,4 @@ def env(key: str, default: Any = None) -> Any:
17
17
  Any
18
18
  The value of the environment variable if it exists, otherwise the default value.
19
19
  """
20
- dotenv = DotEnv()
21
- return dotenv.get(key, default)
20
+ return DotEnv().get(key, default)
File without changes