orionis 0.286.0__py3-none-any.whl → 0.287.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 (57) hide show
  1. orionis/metadata/framework.py +1 -1
  2. orionis/services/environment/contracts/env.py +45 -50
  3. orionis/services/environment/dot_env.py +205 -181
  4. orionis/services/environment/env.py +68 -85
  5. orionis/services/environment/exceptions/environment_value_error.py +18 -0
  6. orionis/services/environment/exceptions/environment_value_exception.py +23 -0
  7. orionis/services/environment/type_hint.py +559 -0
  8. orionis/test/logs/history.py +3 -5
  9. {orionis-0.286.0.dist-info → orionis-0.287.0.dist-info}/METADATA +1 -1
  10. {orionis-0.286.0.dist-info → orionis-0.287.0.dist-info}/RECORD +56 -54
  11. tests/example/test_example.py +5 -2
  12. tests/foundation/config/app/test_app.py +13 -3
  13. tests/foundation/config/auth/test_auth.py +9 -4
  14. tests/foundation/config/cache/test_cache.py +60 -15
  15. tests/foundation/config/cache/test_cache_file.py +62 -14
  16. tests/foundation/config/cache/test_cache_stores.py +74 -14
  17. tests/foundation/config/cors/test_cors.py +102 -33
  18. tests/foundation/config/database/test_database.py +38 -14
  19. tests/foundation/config/database/test_database_connections.py +79 -5
  20. tests/foundation/config/database/test_database_mysql.py +138 -15
  21. tests/foundation/config/database/test_database_oracle.py +110 -26
  22. tests/foundation/config/database/test_database_pgsql.py +96 -26
  23. tests/foundation/config/database/test_database_sqlite.py +56 -2
  24. tests/foundation/config/exceptions/test_exceptions_integrity.py +44 -10
  25. tests/foundation/config/filesystems/test_filesystems.py +64 -14
  26. tests/foundation/config/filesystems/test_filesystems_aws.py +45 -7
  27. tests/foundation/config/filesystems/test_filesystems_disks.py +78 -8
  28. tests/foundation/config/filesystems/test_filesystems_local.py +66 -18
  29. tests/foundation/config/filesystems/test_filesystems_public.py +37 -0
  30. tests/foundation/config/logging/test_logging.py +75 -11
  31. tests/foundation/config/logging/test_logging_channels.py +79 -2
  32. tests/foundation/config/logging/test_logging_chunked.py +85 -12
  33. tests/foundation/config/logging/test_logging_daily.py +79 -12
  34. tests/foundation/config/logging/test_logging_hourly.py +68 -2
  35. tests/foundation/config/logging/test_logging_monthly.py +48 -2
  36. tests/foundation/config/logging/test_logging_stack.py +49 -14
  37. tests/foundation/config/logging/test_logging_weekly.py +92 -2
  38. tests/foundation/config/mail/test_mail.py +87 -15
  39. tests/foundation/config/mail/test_mail_file.py +40 -4
  40. tests/foundation/config/mail/test_mail_mailers.py +56 -8
  41. tests/foundation/config/mail/test_mail_smtp.py +58 -14
  42. tests/foundation/config/queue/test_queue.py +62 -9
  43. tests/foundation/config/queue/test_queue_brokers.py +27 -10
  44. tests/foundation/config/queue/test_queue_database.py +53 -15
  45. tests/foundation/config/root/test_root_paths.py +69 -2
  46. tests/foundation/config/session/test_session.py +30 -1
  47. tests/foundation/config/startup/test_config_startup.py +77 -7
  48. tests/foundation/config/testing/test_testing.py +68 -0
  49. tests/patterns/singleton/test_singleton.py +10 -1
  50. tests/services/environment/test_env.py +3 -4
  51. tests/testing/test_testing_result.py +56 -19
  52. tests/testing/test_testing_unit.py +93 -24
  53. orionis/services/environment/exceptions/value_exception.py +0 -27
  54. {orionis-0.286.0.dist-info → orionis-0.287.0.dist-info}/WHEEL +0 -0
  55. {orionis-0.286.0.dist-info → orionis-0.287.0.dist-info}/licenses/LICENCE +0 -0
  56. {orionis-0.286.0.dist-info → orionis-0.287.0.dist-info}/top_level.txt +0 -0
  57. {orionis-0.286.0.dist-info → orionis-0.287.0.dist-info}/zip-safe +0 -0
@@ -0,0 +1,559 @@
1
+ from orionis.services.environment.exceptions.environment_value_error import OrionisEnvironmentValueError
2
+ from orionis.services.environment.exceptions.environment_value_exception import OrionisEnvironmentValueException
3
+
4
+ class EnvTypes:
5
+
6
+ # Type class to handle different types of environment variables
7
+ OPTIONS = {
8
+ 'path',
9
+ 'str',
10
+ 'int',
11
+ 'float',
12
+ 'bool',
13
+ 'list',
14
+ 'dict',
15
+ 'tuple',
16
+ 'set'
17
+ }
18
+
19
+ @staticmethod
20
+ def options() -> set:
21
+ """
22
+ Returns the set of valid type hints that can be used with this Type class.
23
+
24
+ Returns
25
+ -------
26
+ set
27
+ A set containing the valid type hints.
28
+ """
29
+ return EnvTypes.OPTIONS
30
+
31
+ def __init__(self, raw: str = None):
32
+ """
33
+ Parse a raw string input into a type hint and value string.
34
+
35
+ Parameters
36
+ ----------
37
+ raw : str, optional
38
+ String that may contain a type hint and value separated by a colon (e.g., "int: 42").
39
+ If a colon is present, the part before the colon is treated as the type hint and the part after as the value.
40
+ If no colon is present, the entire string is treated as the value with no type hint.
41
+
42
+ Attributes
43
+ ----------
44
+ __type_hint : str or None
45
+ The extracted type hint in lowercase, or None if not provided.
46
+ __value_str : str or None
47
+ The extracted value string, or None if not provided.
48
+ """
49
+ type_hint, value_str = raw.split(':', 1)
50
+ if type_hint.strip().lower() in self.OPTIONS:
51
+ self.__type_hint = type_hint.strip().lower()
52
+ self.__value_str = value_str.strip() if value_str else None
53
+ else:
54
+ self.__type_hint = None
55
+ self.__value_str = raw.strip() if raw else None
56
+
57
+ def to(self, type_hint: str):
58
+ """
59
+ Set the type hint for the Type instance.
60
+
61
+ Parameters
62
+ ----------
63
+ type_hint : str
64
+ The type hint to set, which must be one of the valid options defined in OPTIONS.
65
+
66
+ Raises
67
+ ------
68
+ OrionisEnvironmentValueError
69
+ If the provided type hint is not one of the valid options.
70
+ """
71
+
72
+ # Validate and set the type hint
73
+ type_hint = type_hint.strip().lower()
74
+ if type_hint not in self.OPTIONS:
75
+ raise OrionisEnvironmentValueError(f"Invalid type hint: {type_hint}. Must be one of {self.OPTIONS}.")
76
+ self.__type_hint = type_hint
77
+
78
+ # Parse the value to the specified type hint
79
+ if self.__type_hint == 'path':
80
+ return self.__toPath()
81
+
82
+ if self.__type_hint == 'str':
83
+ return self.__toStr()
84
+
85
+ if self.__type_hint == 'int':
86
+ return self.__toInt()
87
+
88
+ if self.__type_hint == 'float':
89
+ return self.__toFloat()
90
+
91
+ if self.__type_hint == 'bool':
92
+ return self.__toBool()
93
+
94
+ if self.__type_hint == 'list':
95
+ return self.__toList()
96
+
97
+ if self.__type_hint == 'dict':
98
+ return self.__toDict()
99
+
100
+ if self.__type_hint == 'tuple':
101
+ return self.__toTuple()
102
+
103
+ if self.__type_hint == 'set':
104
+ return self.__toSet()
105
+
106
+ def hasValidTypeHint(self) -> bool:
107
+ """
108
+ Check if the type hint is valid.
109
+
110
+ Returns
111
+ -------
112
+ bool
113
+ True if the type hint is valid (exists in the OPTIONS set), False otherwise.
114
+ """
115
+ return self.__type_hint in self.OPTIONS
116
+
117
+ def explode(self) -> tuple:
118
+ """
119
+ Returns a tuple containing the type hint and value string.
120
+
121
+ Returns
122
+ -------
123
+ tuple
124
+ A tuple (type_hint, value_str) where:
125
+ type_hint : str or None
126
+ The extracted type hint in lowercase, or None if not provided.
127
+ value_str : str or None
128
+ The extracted value string, or None if not provided.
129
+ """
130
+ return self.__type_hint, self.__value_str
131
+
132
+ def __parsePath(self):
133
+ """
134
+ Returns the value as a string, assuming the type hint is 'path:'.
135
+
136
+ Parameters
137
+ ----------
138
+ None
139
+
140
+ Returns
141
+ -------
142
+ str
143
+ The value string with backslashes replaced by forward slashes, if the type hint is 'path:'.
144
+
145
+ Raises
146
+ ------
147
+ OrionisEnvironmentValueException
148
+ If the value cannot be processed as a path.
149
+ """
150
+ return self.__value_str.replace('\\', '/').strip()
151
+
152
+ def __toPath(self):
153
+ """
154
+ Converts the internal string value to a formatted path string.
155
+
156
+ Returns
157
+ -------
158
+ str
159
+ A string representing the type hint and the value, with backslashes replaced by forward slashes.
160
+
161
+ Raises
162
+ ------
163
+ OrionisEnvironmentValueError
164
+ If the internal value is not a string.
165
+ """
166
+ if not isinstance(self.__value_str, str):
167
+ raise OrionisEnvironmentValueError(f"Value must be a string to convert to path, got {type(self.__value_str).__name__} instead.")
168
+ value = self.__value_str.replace('\\', '/').strip()
169
+ return f"{self.__type_hint}:{value}"
170
+
171
+ def __parseStr(self):
172
+ """
173
+ Returns the value as a string, assuming the type hint is 'str:'.
174
+
175
+ Returns
176
+ -------
177
+ str
178
+ The value string if the type hint is 'str:', otherwise raises an error.
179
+ """
180
+ return self.__value_str.strip()
181
+
182
+ def __toStr(self):
183
+ """
184
+ Converts the internal value to a string representation.
185
+
186
+ Returns
187
+ -------
188
+ str
189
+ A string representing the type hint and the value.
190
+
191
+ Raises
192
+ ------
193
+ OrionisEnvironmentValueError
194
+ If the internal value is not a string.
195
+ """
196
+ if not isinstance(self.__value_str, str):
197
+ raise OrionisEnvironmentValueError(f"Value must be a string to convert to str, got {type(self.__value_str).__name__} instead.")
198
+ return f"{self.__type_hint}:{self.__value_str}"
199
+
200
+ def __parseInt(self):
201
+ """
202
+ Returns the value as an integer, assuming the type hint is 'int:'.
203
+
204
+ Parameters
205
+ ----------
206
+ None
207
+
208
+ Returns
209
+ -------
210
+ int
211
+ The value converted to an integer if the type hint is 'int:'.
212
+
213
+ Raises
214
+ ------
215
+ OrionisEnvironmentValueException
216
+ If the value cannot be converted to an integer.
217
+ """
218
+ value = self.__value_str.strip()
219
+ try:
220
+ return int(value)
221
+ except ValueError as e:
222
+ raise OrionisEnvironmentValueException(f"Cannot convert '{value}' to int: {str(e)}")
223
+
224
+ def __toInt(self):
225
+ """
226
+ Converts the internal value to an integer representation.
227
+
228
+ Returns
229
+ -------
230
+ str
231
+ A string representing the type hint and the value as an integer.
232
+
233
+ Raises
234
+ ------
235
+ OrionisEnvironmentValueError
236
+ If the internal value is not a string or cannot be converted to an integer.
237
+ """
238
+ if not isinstance(self.__value_str, int):
239
+ raise OrionisEnvironmentValueError(f"Value must be an integer to convert to int, got {type(self.__value_str).__name__} instead.")
240
+ return f"{self.__type_hint}:{str(self.__value_str)}"
241
+
242
+ def __parseFloat(self):
243
+ """
244
+ Returns the value as a float, assuming the type hint is 'float:'.
245
+
246
+ Parameters
247
+ ----------
248
+ None
249
+
250
+ Returns
251
+ -------
252
+ float
253
+ The value converted to a float if the type hint is 'float:'.
254
+
255
+ Raises
256
+ ------
257
+ OrionisEnvironmentValueException
258
+ If the value cannot be converted to a float.
259
+ """
260
+ value = self.__value_str.strip()
261
+ try:
262
+ return float(value)
263
+ except ValueError as e:
264
+ raise OrionisEnvironmentValueException(f"Cannot convert '{value}' to float: {str(e)}")
265
+
266
+ def __toFloat(self):
267
+ """
268
+ Converts the internal value to a float representation.
269
+
270
+ Returns
271
+ -------
272
+ str
273
+ A string representing the type hint and the value as a float.
274
+
275
+ Raises
276
+ ------
277
+ OrionisEnvironmentValueError
278
+ If the internal value is not a string or cannot be converted to a float.
279
+ """
280
+ if not isinstance(self.__value_str, float):
281
+ raise OrionisEnvironmentValueError(f"Value must be a float to convert to float, got {type(self.__value_str).__name__} instead.")
282
+ return f"{self.__type_hint}:{str(self.__value_str)}"
283
+
284
+ def __parseBool(self):
285
+ """
286
+ Returns the value as a boolean, assuming the type hint is 'bool:'.
287
+
288
+ Parameters
289
+ ----------
290
+ None
291
+
292
+ Returns
293
+ -------
294
+ bool
295
+ The value converted to a boolean if the type hint is 'bool:'.
296
+
297
+ Raises
298
+ ------
299
+ OrionisEnvironmentValueException
300
+ If the value cannot be converted to a boolean.
301
+ """
302
+ value = self.__value_str.strip().lower()
303
+ if value in {'true', '1', 'yes', 'on'}:
304
+ return True
305
+ elif value in {'false', '0', 'no', 'off'}:
306
+ return False
307
+ else:
308
+ raise OrionisEnvironmentValueException(f"Cannot convert '{value}' to bool.")
309
+
310
+ def __toBool(self):
311
+ """
312
+ Converts the internal value to a boolean representation.
313
+
314
+ Returns
315
+ -------
316
+ str
317
+ A string representing the type hint and the value as a boolean.
318
+
319
+ Raises
320
+ ------
321
+ OrionisEnvironmentValueError
322
+ If the internal value is not a boolean.
323
+ """
324
+ if not isinstance(self.__value_str, bool):
325
+ raise OrionisEnvironmentValueError(f"Value must be a boolean to convert to bool, got {type(self.__value_str).__name__} instead.")
326
+ return f"{self.__type_hint}:{str(self.__value_str).lower()}"
327
+
328
+ def __parseList(self):
329
+ """
330
+ Returns the value as a list, assuming the type hint is 'list:'.
331
+
332
+ Returns
333
+ -------
334
+ list
335
+ The value converted to a list if the type hint is 'list:'.
336
+
337
+ Raises
338
+ ------
339
+ OrionisEnvironmentValueException
340
+ If the value cannot be converted to a list.
341
+ """
342
+ import ast
343
+
344
+ value = self.__value_str.strip()
345
+ try:
346
+ parsed = ast.literal_eval(value)
347
+ if not isinstance(parsed, list):
348
+ raise ValueError("Value is not a list")
349
+ return parsed
350
+ except (ValueError, SyntaxError) as e:
351
+ raise OrionisEnvironmentValueException(f"Cannot convert '{value}' to list: {str(e)}")
352
+
353
+ def __toList(self):
354
+ """
355
+ Converts the internal value to a list representation.
356
+
357
+ Returns
358
+ -------
359
+ str
360
+ A string representing the type hint and the value as a list.
361
+
362
+ Raises
363
+ ------
364
+ OrionisEnvironmentValueError
365
+ If the internal value is not a list.
366
+ """
367
+ if not isinstance(self.__value_str, list):
368
+ raise OrionisEnvironmentValueError(f"Value must be a list to convert to list, got {type(self.__value_str).__name__} instead.")
369
+ return f"{self.__type_hint}:{repr(self.__value_str)}"
370
+
371
+ def __parseDict(self):
372
+ """
373
+ Returns the value as a dict, assuming the type hint is 'dict:'.
374
+
375
+ Parameters
376
+ ----------
377
+ None
378
+
379
+ Returns
380
+ -------
381
+ dict
382
+ The value converted to a dict if the type hint is 'dict:'.
383
+
384
+ Raises
385
+ ------
386
+ OrionisEnvironmentValueException
387
+ If the value cannot be converted to a dict.
388
+ """
389
+ import ast
390
+
391
+ value = self.__value_str.strip()
392
+ try:
393
+ parsed = ast.literal_eval(value)
394
+ if not isinstance(parsed, dict):
395
+ raise ValueError("Value is not a dict")
396
+ return parsed
397
+ except (ValueError, SyntaxError) as e:
398
+ raise OrionisEnvironmentValueException(f"Cannot convert '{value}' to dict: {str(e)}")
399
+
400
+ def __toDict(self):
401
+ """
402
+ Converts the internal value to a dict representation.
403
+
404
+ Returns
405
+ -------
406
+ str
407
+ A string representing the type hint and the value as a dict.
408
+
409
+ Raises
410
+ ------
411
+ OrionisEnvironmentValueError
412
+ If the internal value is not a dict.
413
+ """
414
+ if not isinstance(self.__value_str, dict):
415
+ raise OrionisEnvironmentValueError(f"Value must be a dict to convert to dict, got {type(self.__value_str).__name__} instead.")
416
+ return f"{self.__type_hint}:{repr(self.__value_str)}"
417
+
418
+ def __parseTuple(self):
419
+ """
420
+ Returns the value as a tuple, assuming the type hint is 'tuple:'.
421
+
422
+ Parameters
423
+ ----------
424
+ None
425
+
426
+ Returns
427
+ -------
428
+ tuple
429
+ The value converted to a tuple if the type hint is 'tuple:'.
430
+
431
+ Raises
432
+ ------
433
+ OrionisEnvironmentValueException
434
+ If the value cannot be converted to a tuple.
435
+ """
436
+ import ast
437
+
438
+ value = self.__value_str.strip()
439
+ try:
440
+ parsed = ast.literal_eval(value)
441
+ if not isinstance(parsed, tuple):
442
+ raise ValueError("Value is not a tuple")
443
+ return parsed
444
+ except (ValueError, SyntaxError) as e:
445
+ raise OrionisEnvironmentValueException(f"Cannot convert '{value}' to tuple: {str(e)}")
446
+
447
+ def __toTuple(self):
448
+ """
449
+ Converts the internal value to a tuple representation.
450
+
451
+ Returns
452
+ -------
453
+ str
454
+ A string representing the type hint and the value as a tuple.
455
+
456
+ Raises
457
+ ------
458
+ OrionisEnvironmentValueError
459
+ If the internal value is not a tuple.
460
+ """
461
+ if not isinstance(self.__value_str, tuple):
462
+ raise OrionisEnvironmentValueError(f"Value must be a tuple to convert to tuple, got {type(self.__value_str).__name__} instead.")
463
+ return f"{self.__type_hint}:{repr(self.__value_str)}"
464
+
465
+ def __parseSet(self):
466
+ """
467
+ Returns the value as a set, assuming the type hint is 'set:'.
468
+
469
+ Parameters
470
+ ----------
471
+ None
472
+
473
+ Returns
474
+ -------
475
+ set
476
+ The value converted to a set if the type hint is 'set:'.
477
+
478
+ Raises
479
+ ------
480
+ OrionisEnvironmentValueException
481
+ If the value cannot be converted to a set.
482
+ """
483
+ import ast
484
+
485
+ value = self.__value_str.strip()
486
+ try:
487
+ parsed = ast.literal_eval(value)
488
+ if not isinstance(parsed, set):
489
+ raise ValueError("Value is not a set")
490
+ return parsed
491
+ except (ValueError, SyntaxError) as e:
492
+ raise OrionisEnvironmentValueException(f"Cannot convert '{value}' to set: {str(e)}")
493
+
494
+ def __toSet(self):
495
+ """
496
+ Converts the internal value to a set representation.
497
+
498
+ Returns
499
+ -------
500
+ str
501
+ A string representing the type hint and the value as a set.
502
+
503
+ Raises
504
+ ------
505
+ OrionisEnvironmentValueError
506
+ If the internal value is not a set.
507
+ """
508
+ if not isinstance(self.__value_str, set):
509
+ raise OrionisEnvironmentValueError(f"Value must be a set to convert to set, got {type(self.__value_str).__name__} instead.")
510
+ return f"{self.__type_hint}:{repr(self.__value_str)}"
511
+
512
+ def get(self):
513
+ """
514
+ Returns the value corresponding to the specified type hint.
515
+
516
+ Checks if the provided type hint is valid and then dispatches the call to the appropriate
517
+ method for handling the type.
518
+
519
+ Supported type hints include: 'path:', 'str:', 'int:', 'float:', 'bool:', 'list:', 'dict:', 'tuple:', and 'set:'.
520
+
521
+ Returns
522
+ -------
523
+ Any
524
+ The value converted or processed according to the specified type hint.
525
+
526
+ Raises
527
+ ------
528
+ OrionisEnvironmentValueError
529
+ If the type hint is not one of the supported options.
530
+ """
531
+ if not self.__type_hint in self.OPTIONS:
532
+ raise OrionisEnvironmentValueError(f"Invalid type hint: {self.__type_hint}. Must be one of {self.OPTIONS}.")
533
+
534
+ if self.__type_hint == 'path':
535
+ return self.__parsePath()
536
+
537
+ if self.__type_hint == 'str':
538
+ return self.__parseStr()
539
+
540
+ if self.__type_hint == 'int':
541
+ return self.__parseInt()
542
+
543
+ if self.__type_hint == 'float':
544
+ return self.__parseFloat()
545
+
546
+ if self.__type_hint == 'bool':
547
+ return self.__parseBool()
548
+
549
+ if self.__type_hint == 'list':
550
+ return self.__parseList()
551
+
552
+ if self.__type_hint == 'dict':
553
+ return self.__parseDict()
554
+
555
+ if self.__type_hint == 'tuple':
556
+ return self.__parseTuple()
557
+
558
+ if self.__type_hint == 'set':
559
+ return self.__parseSet()
@@ -54,7 +54,7 @@ class TestHistory(ITestHistory):
54
54
  if db_path.is_dir():
55
55
  db_path = db_path / self.__db_name
56
56
  else:
57
- env_path = Env.get(key="TEST_DB_PATH", default=None, is_path=True)
57
+ env_path = Env.get("TEST_DB_PATH", None)
58
58
  if env_path:
59
59
  db_path = Path(env_path).expanduser().resolve()
60
60
  if db_path.is_dir():
@@ -66,7 +66,7 @@ class TestHistory(ITestHistory):
66
66
  db_path.parent.mkdir(parents=True, exist_ok=True)
67
67
 
68
68
  # Store path in environment
69
- Env.set(key="TEST_DB_PATH", value=str(db_path), is_path=True)
69
+ Env.set("TEST_DB_PATH", str(db_path), 'path')
70
70
  self.__db_path = db_path
71
71
 
72
72
  # Create a connection to the database, initially set to None
@@ -89,8 +89,6 @@ class TestHistory(ITestHistory):
89
89
  self._conn = sqlite3.connect(str(self.__db_path))
90
90
  except (sqlite3.Error, Exception) as e:
91
91
  raise OrionisTestPersistenceError(f"Database connection error: {e}")
92
- finally:
93
- self._conn = None
94
92
 
95
93
  def __createTableIfNotExists(self) -> bool:
96
94
  """
@@ -177,7 +175,7 @@ class TestHistory(ITestHistory):
177
175
  # Validate report structure
178
176
  missing = []
179
177
  for key in fields:
180
- if key not in report:
178
+ if key not in report and key != "json":
181
179
  missing.append(key)
182
180
  if missing:
183
181
  raise OrionisTestValueError(f"Missing report fields: {missing}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orionis
3
- Version: 0.286.0
3
+ Version: 0.287.0
4
4
  Summary: Orionis Framework – Elegant, Fast, and Powerful.
5
5
  Home-page: https://github.com/orionis-framework/framework
6
6
  Author: Raul Mauricio Uñate Castro