orionis 0.283.0__py3-none-any.whl → 0.285.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 (44) hide show
  1. orionis/foundation/config/testing/entities/testing.py +25 -0
  2. orionis/metadata/framework.py +1 -1
  3. orionis/services/asynchrony/{async_io.py → coroutines.py} +2 -1
  4. orionis/services/asynchrony/exceptions/__init__.py +0 -0
  5. orionis/services/asynchrony/exceptions/coroutine_exception.py +26 -0
  6. orionis/services/environment/dot_env.py +7 -7
  7. orionis/services/environment/env.py +56 -8
  8. orionis/services/environment/exceptions/__init__.py +0 -0
  9. orionis/services/environment/exceptions/value_exception.py +27 -0
  10. orionis/services/introspection/exceptions/__init__.py +0 -0
  11. orionis/services/introspection/exceptions/types.py +0 -0
  12. orionis/services/introspection/helpers/__init__.py +0 -0
  13. orionis/services/introspection/helpers/functions.py +285 -0
  14. orionis/services/introspection/reflection.py +216 -0
  15. orionis/services/parsers/exceptions/__init__.py +0 -0
  16. orionis/services/parsers/serializer.py +1 -1
  17. orionis/services/paths/exceptions/__init__.py +0 -0
  18. orionis/services/paths/exceptions/not_found_exceptions.py +28 -0
  19. orionis/services/paths/exceptions/path_value_exceptions.py +28 -0
  20. orionis/services/paths/resolver.py +6 -4
  21. orionis/services/standard/exceptions/__init__.py +0 -0
  22. orionis/services/standard/exceptions/path_value_exceptions.py +28 -0
  23. orionis/services/standard/std.py +4 -3
  24. orionis/test/entities/test_result.py +14 -1
  25. orionis/test/exceptions/test_persistence_error.py +34 -0
  26. orionis/test/exceptions/test_runtime_error.py +26 -0
  27. orionis/test/exceptions/test_value_error.py +26 -0
  28. orionis/test/logs/contracts/history.py +29 -56
  29. orionis/test/logs/history.py +309 -188
  30. orionis/test/output/contracts/dumper.py +24 -8
  31. orionis/test/output/dumper.py +52 -21
  32. orionis/test/suites/contracts/test_suite.py +27 -13
  33. orionis/test/suites/contracts/test_unit.py +101 -61
  34. orionis/test/suites/test_suite.py +45 -24
  35. orionis/test/suites/test_unit.py +559 -290
  36. orionis/unittesting.py +8 -0
  37. {orionis-0.283.0.dist-info → orionis-0.285.0.dist-info}/METADATA +1 -1
  38. {orionis-0.283.0.dist-info → orionis-0.285.0.dist-info}/RECORD +44 -26
  39. tests/services/asynchrony/test_async_io.py +3 -2
  40. /orionis/services/parsers/{exception.py → exceptions/exception_parser.py} +0 -0
  41. {orionis-0.283.0.dist-info → orionis-0.285.0.dist-info}/WHEEL +0 -0
  42. {orionis-0.283.0.dist-info → orionis-0.285.0.dist-info}/licenses/LICENCE +0 -0
  43. {orionis-0.283.0.dist-info → orionis-0.285.0.dist-info}/top_level.txt +0 -0
  44. {orionis-0.283.0.dist-info → orionis-0.285.0.dist-info}/zip-safe +0 -0
@@ -136,6 +136,15 @@ class Testing:
136
136
  }
137
137
  )
138
138
 
139
+ persistent_driver: str = field(
140
+ default='sqlite',
141
+ metadata={
142
+ "description": "Specifies the driver to use for persisting test results. Supported values: 'sqlite', 'json'. Default is 'sqlite'.",
143
+ "required": False,
144
+ "default": 'sqlite'
145
+ }
146
+ )
147
+
139
148
  def __post_init__(self):
140
149
  """
141
150
  Post-initialization validation for the testing configuration entity.
@@ -202,6 +211,7 @@ class Testing:
202
211
  raise OrionisIntegrityException(
203
212
  f"Invalid type for 'folder_path': {type(self.folder_path).__name__}. It must be a string or a list of strings representing the folder path pattern."
204
213
  )
214
+
205
215
  if isinstance(self.folder_path, list):
206
216
  for i, folder in enumerate(self.folder_path):
207
217
  if not isinstance(folder, str):
@@ -230,6 +240,21 @@ class Testing:
230
240
  f"Invalid type for tag at index {i} in 'tags': {type(tag).__name__}. Each tag must be a string."
231
241
  )
232
242
 
243
+ if not isinstance(self.persistent, bool):
244
+ raise OrionisIntegrityException(
245
+ f"Invalid type for 'persistent': {type(self.persistent).__name__}. It must be a boolean (True or False)."
246
+ )
247
+
248
+ if not isinstance(self.persistent_driver, str):
249
+ raise OrionisIntegrityException(
250
+ f"Invalid type for 'persistent_driver': {type(self.persistent_driver).__name__}. It must be a string."
251
+ )
252
+
253
+ if self.persistent_driver not in ['sqlite', 'json']:
254
+ raise OrionisIntegrityException(
255
+ f"Invalid value for 'persistent_driver': {self.persistent_driver}. It must be one of: ['sqlite', 'json']."
256
+ )
257
+
233
258
  def toDict(self) -> dict:
234
259
  """
235
260
  Convert the object to a dictionary representation.
@@ -5,7 +5,7 @@
5
5
  NAME = "orionis"
6
6
 
7
7
  # Current version of the framework
8
- VERSION = "0.283.0"
8
+ VERSION = "0.285.0"
9
9
 
10
10
  # Full name of the author or maintainer of the project
11
11
  AUTHOR = "Raul Mauricio Uñate Castro"
@@ -1,5 +1,6 @@
1
1
  import asyncio
2
2
  from typing import Any, Coroutine, TypeVar, Union
3
+ from orionis.services.asynchrony.exceptions.coroutine_exception import OrionisCoroutineException
3
4
 
4
5
  T = TypeVar("T")
5
6
 
@@ -18,7 +19,7 @@ def run_coroutine(coro: Coroutine[Any, Any, T]) -> Union[T, asyncio.Future]:
18
19
  from inspect import iscoroutine
19
20
 
20
21
  if not iscoroutine(coro):
21
- raise TypeError("Expected a coroutine object.")
22
+ raise OrionisCoroutineException("Expected a coroutine object.")
22
23
 
23
24
  try:
24
25
  loop = asyncio.get_running_loop()
File without changes
@@ -0,0 +1,26 @@
1
+ class OrionisCoroutineException(Exception):
2
+ """
3
+ Exception raised for errors related to coroutine operations in the Orionis framework.
4
+ This exception is intended to signal issues encountered during asynchronous
5
+ operations, providing a clear and descriptive error message to facilitate debugging.
6
+ msg (str): A detailed message describing the cause of the exception.
7
+ Example:
8
+ raise OrionisCoroutineException("Coroutine execution failed due to timeout.")
9
+ """
10
+
11
+ def __init__(self, msg: str):
12
+ """
13
+ Initialize the exception with a custom error message.
14
+ Args:
15
+ msg (str): The error message describing the exception.
16
+ """
17
+ super().__init__(msg)
18
+
19
+ def __str__(self) -> str:
20
+ """
21
+ Return a string representation of the exception, including the class name and the first argument.
22
+
23
+ Returns:
24
+ str: A formatted string with the exception class name and its first argument.
25
+ """
26
+ return f"{self.__class__.__name__}: {self.args[0]}"
@@ -5,6 +5,7 @@ from pathlib import Path
5
5
  from typing import Any, Optional, Union
6
6
  from dotenv import dotenv_values, load_dotenv, set_key, unset_key
7
7
  from orionis.patterns.singleton.meta_class import Singleton
8
+ from orionis.services.environment.exceptions.value_exception import OrionisEnvironmentValueException
8
9
 
9
10
  class DotEnv(metaclass=Singleton):
10
11
  """
@@ -206,8 +207,7 @@ class DotEnv(metaclass=Singleton):
206
207
  str: The serialized string representation of the value.
207
208
 
208
209
  Raises:
209
- ValueError: If a float value is in scientific notation.
210
- TypeError: If the value's type is not serializable for .env files.
210
+ OrionisEnvironmentValueException: If a float value is in scientific notation or If the value's type is not serializable for .env files.
211
211
  """
212
212
  if is_path:
213
213
  return str(value).replace("\\", "/")
@@ -227,21 +227,21 @@ class DotEnv(metaclass=Singleton):
227
227
  if isinstance(value, float):
228
228
  value = str(value)
229
229
  if 'e' in value or 'E' in value:
230
- raise ValueError('scientific notation is not supported, use a string instead')
230
+ raise OrionisEnvironmentValueException('scientific notation is not supported, use a string instead')
231
231
  return value
232
232
 
233
233
  if isinstance(value, (list, dict)):
234
234
  return repr(value)
235
235
 
236
236
  if hasattr(value, '__dict__'):
237
- raise TypeError(f"Type {type(value).__name__} is not serializable for .env")
237
+ raise OrionisEnvironmentValueException(f"Type {type(value).__name__} is not serializable for .env")
238
238
 
239
239
  if not isinstance(value, (list, dict, bool, int, float, str)):
240
- raise TypeError(f"Type {type(value).__name__} is not serializable for .env")
240
+ raise OrionisEnvironmentValueException(f"Type {type(value).__name__} is not serializable for .env")
241
241
 
242
242
  if isinstance(value, (list, dict, bool, int, float, str)):
243
243
  if type(value).__module__ != "builtins" and not isinstance(value, str):
244
- raise TypeError(f"Type {type(value).__name__} is not serializable for .env")
244
+ raise OrionisEnvironmentValueException(f"Type {type(value).__name__} is not serializable for .env")
245
245
  return repr(value) if not isinstance(value, str) else value
246
246
 
247
- raise TypeError(f"Type {type(value).__name__} is not serializable for .env")
247
+ raise OrionisEnvironmentValueException(f"Type {type(value).__name__} is not serializable for .env")
@@ -2,11 +2,19 @@ from orionis.services.environment.contracts.env import IEnv
2
2
  from orionis.services.environment.dot_env import DotEnv
3
3
  from typing import Any, Optional, Dict
4
4
 
5
- def env(key: str, default: Any = None) -> Any:
5
+ def env(key: str, default: Any = None, is_path: bool = False) -> Any:
6
6
  """
7
- Helper function to retrieve the value of an environment variable by key.
7
+ Retrieve the value of an environment variable by key.
8
+
9
+ Args:
10
+ key (str): The name of the environment variable to retrieve.
11
+ default (Any, optional): The value to return if the key is not found. Defaults to None.
12
+ is_path (bool, optional): If True, the value will be treated as a file path. Defaults to False.
13
+
14
+ Returns:
15
+ Any: The value of the environment variable if found, otherwise the default value.
8
16
  """
9
- return DotEnv().get(key, default)
17
+ return DotEnv().get(key, default, is_path)
10
18
 
11
19
  class Env(IEnv):
12
20
  """
@@ -19,6 +27,15 @@ class Env(IEnv):
19
27
 
20
28
  @classmethod
21
29
  def _dotenv(cls) -> DotEnv:
30
+ """
31
+ Returns a singleton instance of the DotEnv class.
32
+
33
+ If the instance does not exist, it creates a new one and stores it in the class attribute.
34
+ Subsequent calls will return the same instance.
35
+
36
+ Returns:
37
+ DotEnv: The singleton instance of the DotEnv class.
38
+ """
22
39
  if cls._dotenv_instance is None:
23
40
  cls._dotenv_instance = DotEnv()
24
41
  return cls._dotenv_instance
@@ -26,14 +43,30 @@ class Env(IEnv):
26
43
  @staticmethod
27
44
  def get(key: str, default: Any = None, is_path: bool = False) -> Any:
28
45
  """
29
- Retrieve the value of an environment variable by key.
46
+ Retrieve the value of an environment variable.
47
+
48
+ Args:
49
+ key (str): The name of the environment variable to retrieve.
50
+ default (Any, optional): The value to return if the environment variable is not found. Defaults to None.
51
+ is_path (bool, optional): If True, treat the value as a filesystem path. Defaults to False.
52
+
53
+ Returns:
54
+ Any: The value of the environment variable if found, otherwise the default value.
30
55
  """
31
56
  return Env._dotenv().get(key, default, is_path)
32
57
 
33
58
  @staticmethod
34
59
  def set(key: str, value: str, is_path: bool = False) -> bool:
35
60
  """
36
- Sets the value of an environment variable.
61
+ Sets an environment variable with the specified key and value.
62
+
63
+ Args:
64
+ key (str): The name of the environment variable to set.
65
+ value (str): The value to assign to the environment variable.
66
+ is_path (bool, optional): If True, treats the value as a file system path. Defaults to False.
67
+
68
+ Returns:
69
+ bool: True if the environment variable was set successfully, False otherwise.
37
70
  """
38
71
  return Env._dotenv().set(key, value, is_path)
39
72
 
@@ -41,26 +74,41 @@ class Env(IEnv):
41
74
  def unset(key: str) -> bool:
42
75
  """
43
76
  Removes the specified environment variable from the environment.
77
+
78
+ Args:
79
+ key (str): The name of the environment variable to remove.
80
+
81
+ Returns:
82
+ bool: True if the variable was successfully removed, False otherwise.
44
83
  """
45
84
  return Env._dotenv().unset(key)
46
85
 
47
86
  @staticmethod
48
87
  def all() -> Dict[str, Any]:
49
88
  """
50
- Retrieve all environment variables from the DotEnv instance.
89
+ Retrieve all environment variables as a dictionary.
90
+
91
+ Returns:
92
+ Dict[str, Any]: A dictionary containing all environment variables loaded by the dotenv configuration.
51
93
  """
52
94
  return Env._dotenv().all()
53
95
 
54
96
  @staticmethod
55
97
  def toJson() -> str:
56
98
  """
57
- Serializes the current environment variables managed by the DotEnv instance to a JSON-formatted string.
99
+ Serializes the environment variables managed by the Env class into a JSON-formatted string.
100
+
101
+ Returns:
102
+ str: A JSON string representation of the environment variables.
58
103
  """
59
104
  return Env._dotenv().toJson()
60
105
 
61
106
  @staticmethod
62
107
  def toBase64() -> str:
63
108
  """
64
- Converts the current environment variables to a Base64-encoded string.
109
+ Converts the environment variables loaded by the dotenv instance to a Base64-encoded string.
110
+
111
+ Returns:
112
+ str: The Base64-encoded representation of the environment variables.
65
113
  """
66
114
  return Env._dotenv().toBase64()
File without changes
@@ -0,0 +1,27 @@
1
+ class OrionisEnvironmentValueException(Exception):
2
+ """
3
+ Exception raised for invalid or unexpected environment values within the Orionis framework.
4
+ This exception is intended to signal issues encountered when an environment variable,
5
+ configuration value, or similar parameter does not meet the expected criteria or format.
6
+ It provides a clear and descriptive error message to facilitate debugging and error handling.
7
+ Attributes:
8
+ raise OrionisEnvironmentValueException("Invalid value for ORIONIS_MODE: expected 'production' or 'development'.")
9
+ msg (str): The error message describing the specific value-related exception.
10
+ """
11
+
12
+ def __init__(self, msg: str):
13
+ """
14
+ Initializes the exception with a custom error message.
15
+ Args:
16
+ msg (str): The error message describing the exception.
17
+ """
18
+ super().__init__(msg)
19
+
20
+ def __str__(self) -> str:
21
+ """
22
+ Return a string representation of the exception, including the class name and the first argument.
23
+
24
+ Returns:
25
+ str: A formatted string with the exception class name and its first argument.
26
+ """
27
+ return f"{self.__class__.__name__}: {self.args[0]}"
File without changes
File without changes
File without changes
@@ -0,0 +1,285 @@
1
+ import importlib
2
+ import inspect
3
+ from typing import Any, Type
4
+
5
+ class HelpersReflection:
6
+ """
7
+ A collection of helper functions for reflection and inspection.
8
+ """
9
+
10
+ @staticmethod
11
+ def isValidModule(module_name: str) -> bool:
12
+ """Check if a module name is valid and can be imported.
13
+
14
+ Parameters
15
+ ----------
16
+ module_name : str
17
+ The name of the module to check
18
+
19
+ Returns
20
+ -------
21
+ bool
22
+ True if the module is valid and can be imported, False otherwise
23
+ """
24
+ try:
25
+ importlib.import_module(module_name)
26
+ return True
27
+ except ImportError:
28
+ return False
29
+
30
+ @staticmethod
31
+ def ensureValidModule(module_name: str) -> None:
32
+ """Ensure a module name is valid and can be imported.
33
+
34
+ Parameters
35
+ ----------
36
+ module_name : str
37
+ The name of the module to check
38
+
39
+ Raises
40
+ ------
41
+ ValueError
42
+ If the module cannot be imported or is invalid
43
+ """
44
+ if not isinstance(module_name, str):
45
+ raise TypeError(f"Module name must be a string, got {type(module_name)}")
46
+ if not HelpersReflection.isValidModule(module_name):
47
+ raise ValueError(f"Invalid or non-importable module: {module_name}")
48
+
49
+ @staticmethod
50
+ def isInstantiableClass(cls: Type) -> bool:
51
+ """Check if a class is concrete and can be instantiated.
52
+
53
+ Parameters
54
+ ----------
55
+ cls : Type
56
+ The class to check
57
+
58
+ Returns
59
+ --
60
+ bool
61
+ True if the class is concrete and can be instantiated, False otherwise
62
+ """
63
+ if not isinstance(cls, type):
64
+ return False
65
+ if HelpersReflection.isAbstractClass(cls):
66
+ return False
67
+
68
+ # Try to create an instance to verify it's truly concrete
69
+ try:
70
+ cls()
71
+ return True
72
+ except TypeError:
73
+ return False
74
+
75
+ @staticmethod
76
+ def ensureNotBuiltinType(cls: Type) -> None:
77
+ """Ensure a class is not a built-in or primitive type.
78
+
79
+ Parameters
80
+ ----------
81
+ cls : Type
82
+ The class to check
83
+
84
+ Raises
85
+ ------
86
+ TypeError
87
+ If the input is not a class
88
+ ValueError
89
+ If the class is a built-in or primitive type
90
+ """
91
+ if not isinstance(cls, type):
92
+ raise TypeError(f"Expected a class, got {type(cls)}")
93
+
94
+ builtin_types = {
95
+ int, float, str, bool, bytes, type(None), complex,
96
+ list, tuple, dict, set, frozenset
97
+ }
98
+
99
+ if cls in builtin_types:
100
+ raise ValueError(f"Class '{cls.__name__}' is a built-in or primitive type and cannot be used.")
101
+
102
+ @staticmethod
103
+ def ensureInstantiableClass(cls: Type) -> None:
104
+ """Ensure a class is concrete and can be instantiated.
105
+
106
+ Parameters
107
+ ----------
108
+ cls : Type
109
+ The class to check
110
+
111
+ Raises
112
+ ------
113
+ TypeError
114
+ If the input is not a class
115
+ ValueError
116
+ If the class is abstract or cannot be instantiated
117
+ """
118
+ if HelpersReflection.ensureNotBuiltinType(cls):
119
+ raise TypeError(f"Invalid class: {cls!r}")
120
+
121
+ if not isinstance(cls, type):
122
+ raise TypeError(f"Expected a class, got {type(cls)}")
123
+
124
+ if HelpersReflection.isAbstractClass(cls):
125
+ raise ValueError(f"Class '{cls.__name__}' is abstract")
126
+
127
+ try:
128
+ cls()
129
+ except TypeError as e:
130
+ raise ValueError(f"Class '{cls.__name__}' cannot be instantiated: {str(e)}")
131
+
132
+ @staticmethod
133
+ def isValidClassName(module_name: str, class_name: str) -> bool:
134
+ """Check if a class exists in a given module.
135
+
136
+ Parameters
137
+ ----------
138
+ module_name : str
139
+ The name of the module to check
140
+ class_name : str
141
+ The name of the class to look for
142
+
143
+ Returns
144
+ -------
145
+ bool
146
+ True if the class exists in the module, False otherwise
147
+ """
148
+ try:
149
+ module = importlib.import_module(module_name)
150
+ return hasattr(module, class_name) and inspect.isclass(getattr(module, class_name))
151
+ except ImportError:
152
+ return False
153
+
154
+ @staticmethod
155
+ def ensureValidClassName(module_name: str, class_name: str) -> None:
156
+ """Ensure a class exists in a given module.
157
+
158
+ Parameters
159
+ ----------
160
+ module_name : str
161
+ The name of the module to check
162
+ class_name : str
163
+ The name of the class to look for
164
+
165
+ Raises
166
+ ------
167
+ ValueError
168
+ If the class doesn't exist in the module
169
+ """
170
+ if not HelpersReflection.isValidClassName(module_name, class_name):
171
+ raise ValueError(f"Class '{class_name}' not found in module '{module_name}'")
172
+
173
+ @staticmethod
174
+ def isUserDefinedClassInstance(instance: Any) -> bool:
175
+ """Check if an object is an instance of a user-defined class.
176
+
177
+ Parameters
178
+ ----------
179
+ instance : Any
180
+ The object to check
181
+
182
+ Returns
183
+ -------
184
+ bool
185
+ True if the object is an instance of a user-defined class, False otherwise
186
+ """
187
+ return isinstance(instance, object) and type(instance).__module__ not in {'builtins', 'abc', '__main__'}
188
+
189
+ @staticmethod
190
+ def ensureUserDefinedClassInstance(instance: Any) -> None:
191
+ """Ensure an object is an instance of a user-defined class.
192
+
193
+ Parameters
194
+ ----------
195
+ instance : Any
196
+ The object to check
197
+
198
+ Raises
199
+ ------
200
+ TypeError
201
+ If the input is not an object instance
202
+ ValueError
203
+ If the instance is from builtins, abc, or __main__
204
+ """
205
+ if not isinstance(instance, object):
206
+ raise TypeError(f"Invalid object: {instance!r}")
207
+ module = type(instance).__module__
208
+ if module in {'builtins', 'abc'}:
209
+ raise ValueError(f"'{instance!r}' is not a user-defined class instance, belongs to '{module}'.")
210
+ if module == '__main__':
211
+ raise ValueError("Instance originates from '__main__', origin indeterminate.")
212
+
213
+ @staticmethod
214
+ def isAbstractClass(cls: Type) -> bool:
215
+ """Check if a class is abstract.
216
+
217
+ Parameters
218
+ ----------
219
+ cls : Type
220
+ The class to check
221
+
222
+ Returns
223
+ -------
224
+ bool
225
+ True if the class is abstract, False otherwise
226
+ """
227
+ return isinstance(cls, type) and bool(getattr(cls, '__abstractmethods__', False))
228
+
229
+ @staticmethod
230
+ def ensureAbstractClass(cls: Type) -> None:
231
+ """Ensure a class is abstract.
232
+
233
+ Parameters
234
+ ----------
235
+ cls : Type
236
+ The class to check
237
+
238
+ Raises
239
+ ------
240
+ TypeError
241
+ If the input is not a class
242
+ ValueError
243
+ If the class is not abstract
244
+ """
245
+ if not isinstance(cls, type):
246
+ raise TypeError(f"Invalid class: {cls!r}")
247
+ if not HelpersReflection.isAbstractClass(cls):
248
+ raise ValueError(f"Class '{cls.__name__}' is not abstract.")
249
+
250
+ @staticmethod
251
+ def isConcreteClass(cls: Type) -> bool:
252
+ """Check if a class is concrete.
253
+
254
+ Parameters
255
+ ----------
256
+ cls : Type
257
+ The class to check
258
+
259
+ Returns
260
+ -------
261
+ bool
262
+ True if the class is concrete, False otherwise
263
+ """
264
+ return isinstance(cls, type) and not HelpersReflection.isAbstractClass(cls)
265
+
266
+ @staticmethod
267
+ def ensureConcreteClass(cls: Type) -> None:
268
+ """Ensure a class is concrete.
269
+
270
+ Parameters
271
+ ----------
272
+ cls : Type
273
+ The class to check
274
+
275
+ Raises
276
+ ------
277
+ TypeError
278
+ If the input is not a class
279
+ ValueError
280
+ If the class is not concrete
281
+ """
282
+ if not isinstance(cls, type):
283
+ raise TypeError(f"Invalid class: {cls!r}")
284
+ if not HelpersReflection.isConcreteClass(cls):
285
+ raise ValueError(f"Class '{cls.__name__}' is not concrete.")