orionis 0.282.0__py3-none-any.whl → 0.284.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 (48) hide show
  1. orionis/application.py +2 -7
  2. orionis/foundation/config/testing/entities/testing.py +25 -0
  3. orionis/metadata/framework.py +1 -1
  4. orionis/services/asynchrony/{async_io.py → coroutines.py} +2 -1
  5. orionis/services/asynchrony/exceptions/__init__.py +0 -0
  6. orionis/services/asynchrony/exceptions/coroutine_exception.py +26 -0
  7. orionis/services/environment/dot_env.py +44 -14
  8. orionis/services/environment/env.py +60 -12
  9. orionis/services/environment/exceptions/__init__.py +0 -0
  10. orionis/services/environment/exceptions/value_exception.py +27 -0
  11. orionis/services/introspection/exceptions/__init__.py +0 -0
  12. orionis/services/introspection/exceptions/types.py +0 -0
  13. orionis/services/introspection/helpers/__init__.py +0 -0
  14. orionis/services/introspection/helpers/functions.py +285 -0
  15. orionis/services/introspection/reflection.py +216 -0
  16. orionis/services/parsers/exceptions/__init__.py +0 -0
  17. orionis/services/parsers/serializer.py +1 -1
  18. orionis/services/paths/exceptions/__init__.py +0 -0
  19. orionis/services/paths/exceptions/not_found_exceptions.py +28 -0
  20. orionis/services/paths/exceptions/path_value_exceptions.py +28 -0
  21. orionis/services/paths/resolver.py +6 -4
  22. orionis/services/standard/exceptions/__init__.py +0 -0
  23. orionis/services/standard/exceptions/path_value_exceptions.py +28 -0
  24. orionis/services/standard/std.py +4 -3
  25. orionis/test/entities/test_result.py +14 -1
  26. orionis/test/exceptions/test_persistence_error.py +34 -0
  27. orionis/test/exceptions/test_runtime_error.py +26 -0
  28. orionis/test/exceptions/test_value_error.py +26 -0
  29. orionis/test/logs/contracts/__init__.py +0 -0
  30. orionis/test/logs/contracts/history.py +54 -0
  31. orionis/test/logs/history.py +372 -0
  32. orionis/test/output/contracts/dumper.py +24 -8
  33. orionis/test/output/dumper.py +52 -21
  34. orionis/test/suites/contracts/test_suite.py +27 -13
  35. orionis/test/suites/contracts/test_unit.py +101 -61
  36. orionis/test/suites/test_suite.py +57 -25
  37. orionis/test/suites/test_unit.py +559 -290
  38. orionis/unittesting.py +13 -1
  39. {orionis-0.282.0.dist-info → orionis-0.284.0.dist-info}/METADATA +1 -1
  40. {orionis-0.282.0.dist-info → orionis-0.284.0.dist-info}/RECORD +47 -27
  41. tests/services/asynchrony/test_async_io.py +3 -2
  42. tests/services/environment/test_env.py +3 -3
  43. orionis/test/logs/log_test.py +0 -211
  44. /orionis/services/parsers/{exception.py → exceptions/exception_parser.py} +0 -0
  45. {orionis-0.282.0.dist-info → orionis-0.284.0.dist-info}/WHEEL +0 -0
  46. {orionis-0.282.0.dist-info → orionis-0.284.0.dist-info}/licenses/LICENCE +0 -0
  47. {orionis-0.282.0.dist-info → orionis-0.284.0.dist-info}/top_level.txt +0 -0
  48. {orionis-0.282.0.dist-info → orionis-0.284.0.dist-info}/zip-safe +0 -0
orionis/application.py CHANGED
@@ -1,15 +1,10 @@
1
1
  from orionis.foundation.config.startup import Configuration
2
+ from orionis.patterns.singleton.meta_class import Singleton
2
3
 
3
- class Orionis:
4
+ class Orionis(metaclass=Singleton):
4
5
 
5
6
  def __init__(
6
7
  self,
7
8
  config: Configuration = None
8
9
  ):
9
- """
10
- Initializes the Orionis instance with optional configuration objects.
11
-
12
- Args:
13
- config (Configuration, optional): Custom application configuration.
14
- """
15
10
  self.__config = config or Configuration()
@@ -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.282.0"
8
+ VERSION = "0.284.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
  """
@@ -39,7 +40,7 @@ class DotEnv(metaclass=Singleton):
39
40
 
40
41
  load_dotenv(self._resolved_path)
41
42
 
42
- def get(self, key: str, default: Optional[Any] = None) -> Any:
43
+ def get(self, key: str, default: Optional[Any] = None, is_path:bool=False) -> Any:
43
44
  """
44
45
  Retrieve the value of an environment variable by key.
45
46
 
@@ -54,6 +55,8 @@ class DotEnv(metaclass=Singleton):
54
55
  key (str): The name of the environment variable to retrieve.
55
56
  default (Optional[Any], optional): The value to return if the key is not found.
56
57
  Defaults to None.
58
+ is_path (bool, optional): If True, the value is treated as a file path and backslashes are replaced with forward slashes.
59
+ This is useful for Windows paths. Defaults to False.
57
60
 
58
61
  Returns:
59
62
  Any: The parsed value of the environment variable, or the default if not found.
@@ -62,9 +65,9 @@ class DotEnv(metaclass=Singleton):
62
65
  value = dotenv_values(self._resolved_path).get(key)
63
66
  if value is None:
64
67
  value = os.getenv(key)
65
- return self.__parseValue(value) if value is not None else default
68
+ return self.__parseValue(value, is_path) if value is not None else default
66
69
 
67
- def set(self, key: str, value: Union[str, int, float, bool, list, dict]) -> bool:
70
+ def set(self, key: str, value: Union[str, int, float, bool, list, dict], is_path:bool=False) -> bool:
68
71
  """
69
72
  Sets an environment variable with the specified key and value.
70
73
 
@@ -73,13 +76,16 @@ class DotEnv(metaclass=Singleton):
73
76
  Args:
74
77
  key (str): The name of the environment variable to set.
75
78
  value (Union[str, int, float, bool, list, dict]): The value to assign to the environment variable. Supported types include string, integer, float, boolean, list, and dictionary.
79
+ is_path (bool, optional): If True, the value is treated as a file path and backslashes are replaced with forward slashes.
80
+ This is useful for Windows paths. Defaults to False.
76
81
 
77
82
  Returns:
78
83
  bool: True if the environment variable was successfully set.
79
84
  """
85
+
80
86
  with self._lock:
81
- serialized_value = self.__serializeValue(value)
82
- set_key(str(self._resolved_path), key, f'"{serialized_value}"', quote_mode="never")
87
+ serialized_value = self.__serializeValue(value, is_path)
88
+ set_key(str(self._resolved_path), key, serialized_value)
83
89
  os.environ[key] = str(value)
84
90
  return True
85
91
 
@@ -133,7 +139,7 @@ class DotEnv(metaclass=Singleton):
133
139
  with self._lock:
134
140
  return base64.b64encode(json.dumps(self.all()).encode()).decode()
135
141
 
136
- def __parseValue(self, value: Any) -> Any:
142
+ def __parseValue(self, value: Any, is_path:bool=False) -> Any:
137
143
  """
138
144
  Parses and converts the input value to an appropriate Python data type.
139
145
 
@@ -151,67 +157,91 @@ class DotEnv(metaclass=Singleton):
151
157
  """
152
158
  if value is None:
153
159
  return None
160
+
154
161
  if isinstance(value, (bool, int, float)):
155
162
  return value
163
+
156
164
  value_str = str(value).strip()
157
165
  if not value_str or value_str.lower() in {'none', 'null', 'nan'}:
158
166
  return None
167
+
159
168
  if value_str.lower() == 'true':
160
169
  return True
170
+
161
171
  if value_str.lower() == 'false':
162
172
  return False
173
+
174
+ if is_path:
175
+ return value_str.replace("\\", "/")
176
+
163
177
  try:
164
178
  if value_str.isdigit() or (value_str.startswith('-') and value_str[1:].isdigit()):
165
179
  return int(value_str)
166
180
  except Exception:
167
181
  pass
182
+
168
183
  try:
169
184
  float_val = float(value_str)
170
185
  if '.' in value_str or 'e' in value_str.lower():
171
186
  return float_val
172
187
  except Exception:
173
188
  pass
189
+
174
190
  try:
175
191
  return ast.literal_eval(value_str)
176
192
  except Exception:
177
193
  pass
194
+
178
195
  return value_str
179
196
 
180
- def __serializeValue(self, value: Any) -> str:
197
+ def __serializeValue(self, value: Any, is_path:bool=False) -> str:
181
198
  """
182
199
  Serializes a given value to a string suitable for storage in a .env file.
183
200
 
184
201
  Parameters:
185
202
  value (Any): The value to serialize. Supported types are None, str, bool, int, float, list, and dict.
203
+ is_path (bool): If True, the value is treated as a file path and backslashes are replaced with forward slashes.
204
+ This is useful for Windows paths.
186
205
 
187
206
  Returns:
188
207
  str: The serialized string representation of the value.
189
208
 
190
209
  Raises:
191
- ValueError: If a float value is in scientific notation.
192
- 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.
193
211
  """
212
+ if is_path:
213
+ return str(value).replace("\\", "/")
214
+
194
215
  if value is None:
195
216
  return "None"
217
+
196
218
  if isinstance(value, str):
197
219
  return value
220
+
198
221
  if isinstance(value, bool):
199
222
  return str(value).lower()
223
+
200
224
  if isinstance(value, int):
201
225
  return str(value)
226
+
202
227
  if isinstance(value, float):
203
228
  value = str(value)
204
229
  if 'e' in value or 'E' in value:
205
- raise ValueError('scientific notation is not supported, use a string instead')
230
+ raise OrionisEnvironmentValueException('scientific notation is not supported, use a string instead')
206
231
  return value
232
+
207
233
  if isinstance(value, (list, dict)):
208
234
  return repr(value)
235
+
209
236
  if hasattr(value, '__dict__'):
210
- 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
+
211
239
  if not isinstance(value, (list, dict, bool, int, float, str)):
212
- 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
+
213
242
  if isinstance(value, (list, dict, bool, int, float, str)):
214
243
  if type(value).__module__ != "builtins" and not isinstance(value, str):
215
- 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")
216
245
  return repr(value) if not isinstance(value, str) else value
217
- raise TypeError(f"Type {type(value).__name__} is not serializable for .env")
246
+
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,48 +27,88 @@ 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
25
42
 
26
43
  @staticmethod
27
- def get(key: str, default: Any = None) -> Any:
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
- return Env._dotenv().get(key, default)
56
+ return Env._dotenv().get(key, default, is_path)
32
57
 
33
58
  @staticmethod
34
- def set(key: str, value: str) -> bool:
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
- return Env._dotenv().set(key, value)
71
+ return Env._dotenv().set(key, value, is_path)
39
72
 
40
73
  @staticmethod
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