orionis 0.246.0__py3-none-any.whl → 0.247.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 (90) hide show
  1. orionis/framework.py +1 -1
  2. orionis/luminate/config/app/__init__.py +10 -0
  3. orionis/luminate/config/app/entities/app.py +205 -0
  4. orionis/luminate/config/app/enums/ciphers.py +34 -0
  5. orionis/luminate/config/app/enums/environments.py +15 -0
  6. orionis/luminate/config/auth/__init__.py +7 -0
  7. orionis/luminate/config/auth/entities/auth.py +11 -0
  8. orionis/luminate/config/cache/__init__.py +9 -0
  9. orionis/luminate/config/cache/entities/cache.py +58 -0
  10. orionis/luminate/config/cache/entities/file.py +29 -0
  11. orionis/luminate/config/cache/entities/stores.py +35 -0
  12. orionis/luminate/config/cache/enums/drivers.py +12 -0
  13. orionis/luminate/config/entities/testing.py +192 -14
  14. orionis/luminate/config/exceptions/integrity_exception.py +30 -0
  15. orionis/luminate/console/dumper/dump_die.py +418 -0
  16. orionis/luminate/contracts/facades/commands/scheduler_facade.py +1 -1
  17. orionis/luminate/facades/files/path_facade.py +1 -1
  18. orionis/luminate/patterns/__init__.py +4 -0
  19. orionis/luminate/patterns/singleton/__init__.py +10 -0
  20. orionis/luminate/patterns/singleton/meta_class.py +56 -0
  21. orionis/luminate/providers/commands/reactor_commands_service_provider.py +3 -3
  22. orionis/luminate/providers/commands/scheduler_provider.py +1 -1
  23. orionis/luminate/providers/config/config_service_provider.py +1 -1
  24. orionis/luminate/providers/environment/environment__service_provider.py +2 -2
  25. orionis/luminate/providers/files/paths_provider.py +1 -1
  26. orionis/luminate/providers/log/log_service_provider.py +2 -2
  27. orionis/luminate/services/environment/__init__.py +10 -0
  28. orionis/luminate/services/environment/contracts/__init__.py +5 -0
  29. orionis/luminate/services/environment/contracts/env.py +93 -0
  30. orionis/luminate/services/environment/dot_env.py +293 -0
  31. orionis/luminate/services/environment/env.py +77 -0
  32. orionis/luminate/services/paths/__init__.py +9 -0
  33. orionis/luminate/services/paths/contracts/resolver.py +67 -0
  34. orionis/luminate/services/paths/resolver.py +83 -0
  35. orionis/luminate/services/workers/__init__.py +10 -0
  36. orionis/luminate/services/workers/maximum_workers.py +36 -0
  37. orionis/luminate/services_/commands/__init__.py +0 -0
  38. orionis/luminate/services_/config/__init__.py +0 -0
  39. orionis/luminate/services_/log/__init__.py +0 -0
  40. orionis/luminate/test/__init__.py +11 -1
  41. orionis/luminate/test/cases/test_async.py +1 -10
  42. orionis/luminate/test/cases/test_case.py +8 -3
  43. orionis/luminate/test/cases/test_sync.py +1 -0
  44. orionis/luminate/test/core/contracts/test_suite.py +19 -31
  45. orionis/luminate/test/core/contracts/test_unit.py +4 -0
  46. orionis/luminate/test/core/test_suite.py +27 -26
  47. orionis/luminate/test/core/test_unit.py +28 -45
  48. orionis/luminate/test/entities/test_result.py +13 -16
  49. orionis/luminate/test/exceptions/test_exception.py +1 -1
  50. orionis/luminate/test/output/contracts/test_std_out.py +22 -11
  51. orionis/luminate/test/output/test_std_out.py +69 -80
  52. {orionis-0.246.0.dist-info → orionis-0.247.0.dist-info}/METADATA +4 -1
  53. {orionis-0.246.0.dist-info → orionis-0.247.0.dist-info}/RECORD +80 -51
  54. tests/config/__init__.py +0 -0
  55. tests/config/test_app.py +122 -0
  56. tests/config/test_auth.py +21 -0
  57. tests/config/test_cache.py +20 -0
  58. tests/patterns/__init__.py +0 -0
  59. tests/patterns/singleton/__init__.py +0 -0
  60. tests/patterns/singleton/test_singleton.py +18 -0
  61. tests/services/__init__.py +0 -0
  62. tests/services/environment/__init__.py +0 -0
  63. tests/services/environment/test_env.py +33 -0
  64. orionis/luminate/config/entities/app.py +0 -47
  65. orionis/luminate/config/entities/auth.py +0 -15
  66. orionis/luminate/config/entities/cache.py +0 -51
  67. orionis/luminate/support/environment/contracts/env.py +0 -68
  68. orionis/luminate/support/environment/env.py +0 -139
  69. orionis/luminate/support/environment/functions.py +0 -49
  70. orionis/luminate/support/environment/helper.py +0 -26
  71. orionis/luminate/support/patterns/singleton.py +0 -44
  72. tests/support/environment/test_env.py +0 -91
  73. tests/support/patterns/test_singleton.py +0 -18
  74. /orionis/luminate/config/{entities → app/entities}/__init__.py +0 -0
  75. /orionis/luminate/{services/commands → config/app/enums}/__init__.py +0 -0
  76. /orionis/luminate/{services/config → config/auth/entities}/__init__.py +0 -0
  77. /orionis/luminate/{services/log → config/cache/entities}/__init__.py +0 -0
  78. /orionis/luminate/{support/environment → config/cache/enums}/__init__.py +0 -0
  79. /orionis/luminate/{support/environment/contracts → config/exceptions}/__init__.py +0 -0
  80. /orionis/luminate/{support/patterns → console/dumper}/__init__.py +0 -0
  81. {tests/support/environment → orionis/luminate/services/paths/contracts}/__init__.py +0 -0
  82. {tests/support/patterns → orionis/luminate/services_}/__init__.py +0 -0
  83. /orionis/luminate/{services → services_}/commands/reactor_commands_service.py +0 -0
  84. /orionis/luminate/{services → services_}/commands/scheduler_service.py +0 -0
  85. /orionis/luminate/{services → services_}/config/config_service.py +0 -0
  86. /orionis/luminate/{services → services_}/log/log_service.py +0 -0
  87. {orionis-0.246.0.dist-info → orionis-0.247.0.dist-info}/LICENCE +0 -0
  88. {orionis-0.246.0.dist-info → orionis-0.247.0.dist-info}/WHEEL +0 -0
  89. {orionis-0.246.0.dist-info → orionis-0.247.0.dist-info}/entry_points.txt +0 -0
  90. {orionis-0.246.0.dist-info → orionis-0.247.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,93 @@
1
+ from typing import Any, Dict, Optional
2
+ from abc import ABC, abstractmethod
3
+
4
+ class IEnv(ABC):
5
+ """Interface contract for environment management operations."""
6
+
7
+ @staticmethod
8
+ @abstractmethod
9
+ def get(key: str, default: Optional[Any] = None) -> Any:
10
+ """
11
+ Retrieves an environment variable's value.
12
+
13
+ Args:
14
+ key: Environment variable name
15
+ default: Default value if key doesn't exist
16
+
17
+ Returns:
18
+ The parsed value or default if not found
19
+ """
20
+ pass
21
+
22
+ @staticmethod
23
+ @abstractmethod
24
+ def set(key: str, value: str) -> bool:
25
+ """
26
+ Sets an environment variable.
27
+
28
+ Args:
29
+ key: Environment variable name
30
+ value: Value to set
31
+
32
+ Returns:
33
+ True if successful, False otherwise
34
+ """
35
+ pass
36
+
37
+ @staticmethod
38
+ @abstractmethod
39
+ def unset(key: str) -> bool:
40
+ """
41
+ Removes an environment variable.
42
+
43
+ Args:
44
+ key: Environment variable name
45
+
46
+ Returns:
47
+ True if successful, False otherwise
48
+ """
49
+ pass
50
+
51
+ @staticmethod
52
+ @abstractmethod
53
+ def all() -> Dict[str, Any]:
54
+ """
55
+ Retrieves all environment variables.
56
+
57
+ Returns:
58
+ Dictionary of all key-value pairs
59
+ """
60
+ pass
61
+
62
+ @staticmethod
63
+ @abstractmethod
64
+ def destroy() -> bool:
65
+ """
66
+ Clears the entire environment.
67
+
68
+ Returns:
69
+ True if successful, False otherwise
70
+ """
71
+ pass
72
+
73
+ @staticmethod
74
+ @abstractmethod
75
+ def toJson() -> str:
76
+ """
77
+ Serializes environment to JSON.
78
+
79
+ Returns:
80
+ JSON string representation
81
+ """
82
+ pass
83
+
84
+ @staticmethod
85
+ @abstractmethod
86
+ def toBase64() -> str:
87
+ """
88
+ Encodes environment to Base64.
89
+
90
+ Returns:
91
+ Base64 encoded string
92
+ """
93
+ pass
@@ -0,0 +1,293 @@
1
+ import ast
2
+ import os
3
+ from pathlib import Path
4
+ from typing import Any, Optional, Union
5
+ from dotenv import dotenv_values, load_dotenv, set_key, unset_key
6
+ from orionis.luminate.patterns.singleton import Singleton
7
+
8
+ class DotEnv(metaclass=Singleton):
9
+ """
10
+ DotEnv is a singleton class for managing environment variables using a `.env` file.
11
+ This class provides methods to load, get, set, unset, and list environment variables,
12
+ with automatic serialization and deserialization of common Python data types.
13
+ It ensures that changes to the `.env` file are reflected in the current process's
14
+ environment variables and vice versa.
15
+ """
16
+
17
+ def __init__(self, path: str = None) -> None:
18
+ """
19
+ Initializes the environment service by resolving the path to the `.env` file, ensuring its existence,
20
+ and loading environment variables from it.
21
+ Args:
22
+ path (str, optional): The path to the `.env` file. If not provided, defaults to a `.env` file
23
+ in the current working directory.
24
+ Raises:
25
+ OSError: If the `.env` file cannot be created when it does not exist.
26
+ """
27
+
28
+ # Path to the `.env` file - If no path is provided, use the default path
29
+ if path:
30
+ self._resolved_path = Path(path).expanduser().resolve()
31
+ else:
32
+ self._resolved_path = Path(os.getcwd()) / ".env"
33
+
34
+ # Ensure that the `.env` file exists
35
+ if not self._resolved_path.exists():
36
+ self._resolved_path.touch()
37
+
38
+ # Load environment variables from the `.env` file
39
+ load_dotenv(self._resolved_path)
40
+
41
+ def destroy(self) -> bool:
42
+ """
43
+ Deletes the `.env` file at the resolved path.
44
+ Returns:
45
+ bool: True if the `.env` file was successfully deleted, False if the file did not exist.
46
+ """
47
+
48
+ # Deletes the `.env` file and returns True if it was successfully deleted.
49
+ if self._resolved_path.exists():
50
+ os.remove(self._resolved_path)
51
+ return True
52
+ return False
53
+
54
+ def get(self, key: str, default: Optional[Any] = None) -> Any:
55
+ """
56
+ Retrieve the value of an environment variable.
57
+ This method attempts to fetch the value of the specified environment variable `key`
58
+ from a `.env` file. If the variable is not found in the `.env` file, it falls back
59
+ to the system environment variables. If the variable is still not found, the provided
60
+ `default` value is returned.
61
+ Args:
62
+ key (str): The name of the environment variable to retrieve.
63
+ default (Optional[Any], optional): The value to return if the environment variable is not found. Defaults to None.
64
+ Returns:
65
+ Any: The value of the environment variable, parsed if found; otherwise, the `default` value.
66
+ """
67
+
68
+ # Gets the value of an environment variable from the `.env` file or from system environment variables.
69
+ value = dotenv_values(self._resolved_path).get(key)
70
+ if value is None:
71
+ value = os.getenv(key)
72
+
73
+ # If the value is not found, return the default value
74
+ return self.__parseValue(value) if value is not None else default
75
+
76
+ def set(self, key: str, value: Union[str, int, float, bool, list, dict]) -> bool:
77
+ """
78
+ Sets the value of an environment variable in both the `.env` file and the current system environment.
79
+ Args:
80
+ key (str): The name of the environment variable to set.
81
+ value (Union[str, int, float, bool, list, dict]): The value to assign to the environment variable.
82
+ The value will be serialized before being written to the `.env` file.
83
+ Notes:
84
+ - The value is serialized for storage in the `.env` file.
85
+ - The environment variable is also set in the current process's environment, making it immediately available.
86
+ """
87
+
88
+ # Serializes and sets the value of an environment variable in the `.env` file.
89
+ serialized_value = self.__serializeValue(value)
90
+
91
+ # Sets the value in the `.env` file
92
+ set_key(str(self._resolved_path), key, serialized_value)
93
+
94
+ # Also sets the value in the system environment variables
95
+ # so that it is available in the current environment.
96
+ # This is useful if you need to access the environment variable immediately
97
+ os.environ[key] = str(value)
98
+
99
+ # Return True to indicate that the operation was successful
100
+ return True
101
+
102
+ def unset(self, key: str) -> bool:
103
+ """
104
+ Removes an environment variable from both the `.env` file and the current system environment.
105
+ Args:
106
+ key (str): The name of the environment variable to remove.
107
+ This method updates the `.env` file by removing the specified key and also ensures
108
+ that the variable is no longer present in the current process's environment variables.
109
+ """
110
+
111
+ # Removes an environment variable from the `.env` file and from the system environment.
112
+ unset_key(str(self._resolved_path), key)
113
+
114
+ # Also removes the environment variable from the system
115
+ # so that it is not available in the current environment.
116
+ os.environ.pop(key, None)
117
+
118
+ # Return True to indicate that the operation was successful
119
+ return True
120
+
121
+ def all(self) -> dict:
122
+ """
123
+ Returns all environment variables from the `.env` file as a dictionary.
124
+
125
+ Reads the environment variables from the resolved `.env` file path, parses each value
126
+ using the `__parseValue` method, and returns a dictionary mapping variable names to their
127
+ parsed values.
128
+
129
+ Returns:
130
+ dict: A dictionary containing all environment variables and their parsed values.
131
+ """
132
+
133
+ # Returns all environment variables from the `.env` file as a dictionary,
134
+ # parsing the values using __parseValue.
135
+ raw_values = dotenv_values(self._resolved_path)
136
+ return {k: self.__parseValue(v) for k, v in raw_values.items()}
137
+
138
+ def toJson(self) -> str:
139
+ """
140
+ Converts the environment variables from the `.env` file into a JSON string.
141
+
142
+ This method retrieves all environment variables, parses their values using the
143
+ `__parseValue` method, and returns a JSON string representation of the resulting dictionary.
144
+
145
+ Returns:
146
+ str: A JSON string representing all environment variables and their parsed values.
147
+ """
148
+
149
+ # Converts the environment variables to a JSON string.
150
+ import json
151
+ return json.dumps(self.all(), indent=4)
152
+
153
+ def toBase64(self) -> str:
154
+ """
155
+ Converts the environment variables from the `.env` file into a Base64 encoded string.
156
+
157
+ This method retrieves all environment variables, parses their values using the
158
+ `__parseValue` method, and returns a Base64 encoded string representation of the resulting dictionary.
159
+
160
+ Returns:
161
+ str: A Base64 encoded string representing all environment variables and their parsed values.
162
+ """
163
+
164
+ # Converts the environment variables to a Base64 encoded string.
165
+ import base64
166
+ import json
167
+ return base64.b64encode(json.dumps(self.all()).encode()).decode()
168
+
169
+ def __parseValue(self, value: Any) -> Any:
170
+ """
171
+ Parses a given value and attempts to convert it into an appropriate Python data type.
172
+ The function handles the following conversions:
173
+ - Returns None for None, empty strings, or string representations of null values ('none', 'null', 'nan').
174
+ - Returns the value unchanged if it is already a primitive type (bool, int, float).
175
+ - Converts string representations of booleans ('true', 'false') to their respective boolean values.
176
+ - Converts string representations of integers and floats to their respective numeric types.
177
+ - Attempts to evaluate the string as a Python literal (e.g., lists, dicts, tuples).
178
+ - Returns the original string if no conversion is possible.
179
+ Args:
180
+ value (Any): The value to parse.
181
+ Returns:
182
+ Any: The parsed value in its appropriate Python data type, or the original string if no conversion is possible.
183
+ """
184
+
185
+ # Parses a string value into a Python data type.
186
+ if value is None:
187
+ return None
188
+
189
+ # If it is already a primitive type, return it
190
+ if isinstance(value, (bool, int, float)):
191
+ return value
192
+
193
+ value_str = str(value).strip()
194
+
195
+ # Special cases: empty or representations of None
196
+ if not value_str or value_str.lower() in {'none', 'null', 'nan'}:
197
+ return None
198
+
199
+ # Booleans
200
+ if value_str.lower() == 'true':
201
+ return True
202
+ if value_str.lower() == 'false':
203
+ return False
204
+
205
+ # Try to convert to int
206
+ try:
207
+ if value_str.isdigit() or (value_str.startswith('-') and value_str[1:].isdigit()):
208
+ return int(value_str)
209
+ except Exception:
210
+ pass
211
+
212
+ # Try to convert to float
213
+ try:
214
+ float_val = float(value_str)
215
+ # Avoid converting strings like '1e10' to float if it is not really a number
216
+ if '.' in value_str or 'e' in value_str.lower():
217
+ return float_val
218
+ except Exception:
219
+ pass
220
+
221
+ # Try to evaluate as Python literal (lists, dicts, etc.)
222
+ try:
223
+ return ast.literal_eval(value_str)
224
+ except Exception:
225
+ pass
226
+
227
+ # If all else fails, return the original string
228
+ return value_str
229
+
230
+ def __serializeValue(self, value: Any) -> str:
231
+ """
232
+ Serializes a Python value into a string suitable for storing in a `.env` file.
233
+ Supported types:
234
+ - None: serialized as the string "None"
235
+ - str: returned as-is
236
+ - bool: converted to "true" or "false"
237
+ - int, float: converted to their string representation
238
+ - list, dict: converted to their string representation using repr()
239
+ Raises:
240
+ TypeError: If the value is an instance of a custom class or an unsupported type (e.g., set, tuple).
241
+ Args:
242
+ value (Any): The value to serialize.
243
+ Returns:
244
+ str: The serialized string representation of the value.
245
+ """
246
+
247
+ # if it is None, return "None"
248
+ # This is useful to avoid problems when saving None in the .env file
249
+ if value is None:
250
+ return "None"
251
+
252
+ # If it is a string, return it as is
253
+ if isinstance(value, str):
254
+ return value
255
+
256
+ # If it is a boolean, convert it to string
257
+ if isinstance(value, bool):
258
+ return str(value).lower()
259
+
260
+ # If it is a number, convert it to string
261
+ if isinstance(value, int):
262
+ return str(value)
263
+
264
+ # If is a float, convert it to string
265
+ if isinstance(value, float):
266
+ value = str(value)
267
+ if 'e' in value or 'E' in value:
268
+ raise ValueError('scientific notation is not supported, use a string instead')
269
+ return value
270
+
271
+ # If it is a list or dictionary, convert them to string
272
+ if isinstance(value, (list, dict)):
273
+ return repr(value)
274
+
275
+ # If it is an object of a custom class, raise an error
276
+ # This is useful to avoid problems when saving class instances in the .env file
277
+ if hasattr(value, '__dict__'):
278
+ raise TypeError(f"Type {type(value).__name__} is not serializable for .env")
279
+
280
+ # If it is an unsupported data type, raise an error
281
+ # This is useful to avoid problems when saving unsupported data types in the .env file
282
+ # such as sets, tuples, etc.
283
+ if not isinstance(value, (list, dict, bool, int, float, str)):
284
+ raise TypeError(f"Type {type(value).__name__} is not serializable for .env")
285
+
286
+ # Serializes a Python data type into a string for storing in the `.env` file.
287
+ # Only allows simple serializable types and not class instances.
288
+ if isinstance(value, (list, dict, bool, int, float, str)):
289
+ # Prevent serializing instances of custom classes
290
+ if type(value).__module__ != "builtins" and not isinstance(value, str):
291
+ raise TypeError(f"Type {type(value).__name__} is not serializable for .env")
292
+ return repr(value) if not isinstance(value, str) else value
293
+ raise TypeError(f"Type {type(value).__name__} is not serializable for .env")
@@ -0,0 +1,77 @@
1
+ from orionis.luminate.services.environment.contracts.env import IEnv
2
+ from orionis.luminate.services.environment.dot_env import DotEnv
3
+ from typing import Any, Optional, Dict
4
+
5
+ def env(key: str, default: Any = None) -> Any:
6
+ """
7
+ Helper function to retrieve the value of an environment variable by key.
8
+ """
9
+ return DotEnv().get(key, default)
10
+
11
+ class Env(IEnv):
12
+ """
13
+ Env is a utility class that provides static methods for managing environment variables
14
+ using the DotEnv class. It allows getting, setting, unsetting, listing, destroying,
15
+ and serializing environment variables.
16
+ """
17
+
18
+ _dotenv_instance: Optional[DotEnv] = None
19
+
20
+ @classmethod
21
+ def _dotenv(cls) -> DotEnv:
22
+ if cls._dotenv_instance is None:
23
+ cls._dotenv_instance = DotEnv()
24
+ return cls._dotenv_instance
25
+
26
+ @staticmethod
27
+ def get(key: str, default: Any = None) -> Any:
28
+ """
29
+ Retrieve the value of an environment variable by key.
30
+ """
31
+ return Env._dotenv().get(key, default)
32
+
33
+ @staticmethod
34
+ def set(key: str, value: str) -> bool:
35
+ """
36
+ Sets the value of an environment variable.
37
+ """
38
+ return Env._dotenv().set(key, value)
39
+
40
+ @staticmethod
41
+ def unset(key: str) -> bool:
42
+ """
43
+ Removes the specified environment variable from the environment.
44
+ """
45
+ return Env._dotenv().unset(key)
46
+
47
+ @staticmethod
48
+ def all() -> Dict[str, Any]:
49
+ """
50
+ Retrieve all environment variables from the DotEnv instance.
51
+ """
52
+ return Env._dotenv().all()
53
+
54
+ @staticmethod
55
+ def destroy() -> bool:
56
+ """
57
+ Destroys the current environment by resetting the DotEnv instance.
58
+ """
59
+ if Env._dotenv_instance is not None:
60
+ result = Env._dotenv_instance.destroy()
61
+ Env._dotenv_instance = None
62
+ return result
63
+ return False
64
+
65
+ @staticmethod
66
+ def toJson() -> str:
67
+ """
68
+ Serializes the current environment variables managed by the DotEnv instance to a JSON-formatted string.
69
+ """
70
+ return Env._dotenv().toJson()
71
+
72
+ @staticmethod
73
+ def toBase64() -> str:
74
+ """
75
+ Converts the current environment variables to a Base64-encoded string.
76
+ """
77
+ return Env._dotenv().toBase64()
@@ -0,0 +1,9 @@
1
+ from orionis.luminate.services.paths.resolver import Resolver
2
+
3
+ __all__ = [
4
+ "Resolver",
5
+ ]
6
+ __author__ = "Raúl Mauricio Uñate Castro"
7
+ __description__ = (
8
+ "This module provides a Resolver class that is used to resolve paths in the Orionis Framework application. "
9
+ )
@@ -0,0 +1,67 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Optional
3
+
4
+ class IResolver(ABC):
5
+ """
6
+ Interface for a utility class that resolves file and directory paths relative to a base path.
7
+ """
8
+
9
+ @abstractmethod
10
+ def __init__(self, root_path: Optional[str] = None):
11
+ """
12
+ Initializes the resolver with an optional root path.
13
+
14
+ Parameters
15
+ ----------
16
+ root_path : str, optional
17
+ The root directory to resolve relative paths from.
18
+ """
19
+ pass
20
+
21
+ @abstractmethod
22
+ def relativePath(self, relative_path: str):
23
+ """
24
+ Resolves a relative path into an absolute one and validates its existence.
25
+
26
+ Parameters
27
+ ----------
28
+ relative_path : str
29
+ The relative path to resolve.
30
+
31
+ Returns
32
+ -------
33
+ ResolverInterface
34
+ The instance itself for method chaining.
35
+
36
+ Raises
37
+ ------
38
+ FileNotFoundError
39
+ If the resolved path does not exist.
40
+ ValueError
41
+ If the resolved path is neither a file nor a directory.
42
+ """
43
+ pass
44
+
45
+ @abstractmethod
46
+ def toString(self) -> str:
47
+ """
48
+ Returns the resolved path as a string.
49
+
50
+ Returns
51
+ -------
52
+ str
53
+ The resolved path.
54
+ """
55
+ pass
56
+
57
+ @abstractmethod
58
+ def __str__(self) -> str:
59
+ """
60
+ Returns the resolved path as a string (for print or str()).
61
+
62
+ Returns
63
+ -------
64
+ str
65
+ The resolved path.
66
+ """
67
+ pass
@@ -0,0 +1,83 @@
1
+ import os
2
+ from pathlib import Path
3
+ from orionis.luminate.support.paths.contracts.resolver import IResolver
4
+
5
+ class Resolver(IResolver):
6
+ """
7
+ A utility class for resolving file and directory paths relative to the project's root directory.
8
+ """
9
+
10
+ def __init__(self, root_path: str = None):
11
+ """
12
+ Initializes the Resolver instance with the project's root directory.
13
+
14
+ Parameters
15
+ ----------
16
+ root_path : str, optional
17
+ The root directory of the project. If not provided, it defaults to the current working directory.
18
+ """
19
+ self.base_path = Path(root_path).resolve() if root_path else Path(os.getcwd()).resolve()
20
+ self.resolved_path = None
21
+
22
+ def relativePath(self, relative_path: str) -> 'Resolver':
23
+ """
24
+ Resolves a given relative path to an absolute path and validates its existence.
25
+
26
+ This method combines the project's root directory with the provided relative path,
27
+ resolves it to an absolute path, and ensures it exists as either a directory or a file.
28
+
29
+ Parameters
30
+ ----------
31
+ relative_path : str
32
+ The relative path to a directory or file to be resolved.
33
+
34
+ Returns
35
+ -------
36
+ Resolver
37
+ The current instance of the Resolver class with the resolved path.
38
+
39
+ Raises
40
+ ------
41
+ FileNotFoundError
42
+ If the resolved path does not exist.
43
+ ValueError
44
+ If the resolved path is neither a valid directory nor a file.
45
+ """
46
+ # Combine the base path with the relative path and resolve it
47
+ resolved_path = (self.base_path / relative_path).resolve()
48
+
49
+ # Validate that the path exists
50
+ if not resolved_path.exists():
51
+ raise FileNotFoundError(f"The requested path does not exist: {resolved_path}")
52
+
53
+ # Validate that the path is either a directory or a file
54
+ if not (resolved_path.is_dir() or resolved_path.is_file()):
55
+ raise ValueError(f"The requested path is neither a valid directory nor a file: {resolved_path}")
56
+
57
+ # Store the resolved path in the instance variable
58
+ self.resolved_path = resolved_path
59
+
60
+ # Return the current instance
61
+ return self
62
+
63
+ def toString(self) -> str:
64
+ """
65
+ Returns the string representation of the resolved path.
66
+
67
+ Returns
68
+ -------
69
+ str
70
+ The resolved path as a string.
71
+ """
72
+ return str(self.resolved_path)
73
+
74
+ def __str__(self) -> str:
75
+ """
76
+ Returns the string representation of the resolved path.
77
+
78
+ Returns
79
+ -------
80
+ str
81
+ The resolved path as a string.
82
+ """
83
+ return str(self.resolved_path)
@@ -0,0 +1,10 @@
1
+ from orionis.luminate.services.workers.maximum_workers import MaximumWorkers
2
+
3
+ __all__ = [
4
+ "MaximumWorkers",
5
+ ]
6
+ __author__ = "Raúl Mauricio Uñate Castro"
7
+ __description__ = (
8
+ "This module provides a class to calculate the maximum number of workers "
9
+ "that can be run on a machine based on its CPU and memory resources. "
10
+ )
@@ -0,0 +1,36 @@
1
+ import multiprocessing
2
+ import math
3
+ import psutil
4
+
5
+ class MaximumWorkers:
6
+ """
7
+ Calculates the optimal number of workers a machine can handle based on CPU and memory resources.
8
+
9
+ This class estimates the maximum number of Uvicorn (or similar) workers by considering:
10
+ - The number of available CPU cores.
11
+ - The total system memory (RAM).
12
+ - The estimated memory usage per worker (configurable).
13
+
14
+ Parameters
15
+ ----------
16
+ ram_per_worker : float, optional
17
+ Estimated amount of RAM (in GB) that each worker will consume. Default is 0.5 GB.
18
+ """
19
+
20
+ def __init__(self, ram_per_worker: float = 0.5):
21
+ self._cpu_count = multiprocessing.cpu_count()
22
+ self._ram_total_gb = psutil.virtual_memory().total / (1024 ** 3)
23
+ self._ram_per_worker = ram_per_worker
24
+
25
+ def calculate(self) -> int:
26
+ """
27
+ Computes the maximum number of workers supported by the current machine.
28
+
29
+ Returns
30
+ -------
31
+ int
32
+ The recommended number of worker processes based on CPU and memory limits.
33
+ """
34
+ max_workers_by_cpu = self._cpu_count
35
+ max_workers_by_ram = math.floor(self._ram_total_gb / self._ram_per_worker)
36
+ return min(max_workers_by_cpu, max_workers_by_ram)
File without changes
File without changes
File without changes