orionis 0.246.0__py3-none-any.whl → 0.248.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.
- orionis/framework.py +1 -1
- orionis/luminate/config/app/__init__.py +10 -0
- orionis/luminate/config/app/entities/app.py +205 -0
- orionis/luminate/config/app/enums/ciphers.py +34 -0
- orionis/luminate/config/app/enums/environments.py +15 -0
- orionis/luminate/config/auth/__init__.py +7 -0
- orionis/luminate/config/auth/entities/auth.py +11 -0
- orionis/luminate/config/cache/__init__.py +9 -0
- orionis/luminate/config/cache/entities/cache.py +58 -0
- orionis/luminate/config/cache/entities/file.py +29 -0
- orionis/luminate/config/cache/entities/stores.py +35 -0
- orionis/luminate/config/cache/enums/drivers.py +12 -0
- orionis/luminate/config/entities/testing.py +192 -14
- orionis/luminate/config/exceptions/integrity_exception.py +30 -0
- orionis/luminate/console/dumper/dump_die.py +418 -0
- orionis/luminate/contracts/facades/commands/scheduler_facade.py +1 -1
- orionis/luminate/facades/files/path_facade.py +1 -1
- orionis/luminate/patterns/__init__.py +4 -0
- orionis/luminate/patterns/singleton/__init__.py +10 -0
- orionis/luminate/patterns/singleton/meta_class.py +56 -0
- orionis/luminate/providers/commands/reactor_commands_service_provider.py +3 -3
- orionis/luminate/providers/commands/scheduler_provider.py +1 -1
- orionis/luminate/providers/config/config_service_provider.py +1 -1
- orionis/luminate/providers/environment/environment__service_provider.py +2 -2
- orionis/luminate/providers/files/paths_provider.py +1 -1
- orionis/luminate/providers/log/log_service_provider.py +2 -2
- orionis/luminate/services/environment/__init__.py +10 -0
- orionis/luminate/services/environment/contracts/__init__.py +5 -0
- orionis/luminate/services/environment/contracts/env.py +93 -0
- orionis/luminate/services/environment/dot_env.py +293 -0
- orionis/luminate/services/environment/env.py +77 -0
- orionis/luminate/services/paths/__init__.py +9 -0
- orionis/luminate/services/paths/contracts/resolver.py +67 -0
- orionis/luminate/services/paths/resolver.py +83 -0
- orionis/luminate/services/workers/__init__.py +10 -0
- orionis/luminate/services/workers/maximum_workers.py +36 -0
- orionis/luminate/services_/commands/__init__.py +0 -0
- orionis/luminate/services_/config/__init__.py +0 -0
- orionis/luminate/services_/log/__init__.py +0 -0
- orionis/luminate/test/__init__.py +11 -1
- orionis/luminate/test/cases/test_async.py +1 -10
- orionis/luminate/test/cases/test_case.py +8 -3
- orionis/luminate/test/cases/test_sync.py +1 -0
- orionis/luminate/test/core/contracts/test_suite.py +19 -31
- orionis/luminate/test/core/contracts/test_unit.py +4 -0
- orionis/luminate/test/core/test_suite.py +27 -26
- orionis/luminate/test/core/test_unit.py +28 -45
- orionis/luminate/test/entities/test_result.py +13 -16
- orionis/luminate/test/exceptions/test_exception.py +1 -1
- orionis/luminate/test/output/contracts/test_std_out.py +22 -11
- orionis/luminate/test/output/test_std_out.py +69 -80
- orionis/static/__init__.py +0 -0
- {orionis-0.246.0.dist-info → orionis-0.248.0.dist-info}/METADATA +4 -1
- {orionis-0.246.0.dist-info → orionis-0.248.0.dist-info}/RECORD +81 -51
- tests/config/__init__.py +0 -0
- tests/config/test_app.py +122 -0
- tests/config/test_auth.py +21 -0
- tests/config/test_cache.py +20 -0
- tests/patterns/__init__.py +0 -0
- tests/patterns/singleton/__init__.py +0 -0
- tests/patterns/singleton/test_singleton.py +18 -0
- tests/services/__init__.py +0 -0
- tests/services/environment/__init__.py +0 -0
- tests/services/environment/test_env.py +33 -0
- orionis/luminate/config/entities/app.py +0 -47
- orionis/luminate/config/entities/auth.py +0 -15
- orionis/luminate/config/entities/cache.py +0 -51
- orionis/luminate/support/environment/contracts/env.py +0 -68
- orionis/luminate/support/environment/env.py +0 -139
- orionis/luminate/support/environment/functions.py +0 -49
- orionis/luminate/support/environment/helper.py +0 -26
- orionis/luminate/support/patterns/singleton.py +0 -44
- tests/support/environment/test_env.py +0 -91
- tests/support/patterns/test_singleton.py +0 -18
- /orionis/luminate/config/{entities → app/entities}/__init__.py +0 -0
- /orionis/luminate/{services/commands → config/app/enums}/__init__.py +0 -0
- /orionis/luminate/{services/config → config/auth/entities}/__init__.py +0 -0
- /orionis/luminate/{services/log → config/cache/entities}/__init__.py +0 -0
- /orionis/luminate/{support/environment → config/cache/enums}/__init__.py +0 -0
- /orionis/luminate/{support/environment/contracts → config/exceptions}/__init__.py +0 -0
- /orionis/luminate/{support/patterns → console/dumper}/__init__.py +0 -0
- {tests/support/environment → orionis/luminate/services/paths/contracts}/__init__.py +0 -0
- {tests/support/patterns → orionis/luminate/services_}/__init__.py +0 -0
- /orionis/luminate/{services → services_}/commands/reactor_commands_service.py +0 -0
- /orionis/luminate/{services → services_}/commands/scheduler_service.py +0 -0
- /orionis/luminate/{services → services_}/config/config_service.py +0 -0
- /orionis/luminate/{services → services_}/log/log_service.py +0 -0
- {orionis-0.246.0.dist-info → orionis-0.248.0.dist-info}/LICENCE +0 -0
- {orionis-0.246.0.dist-info → orionis-0.248.0.dist-info}/WHEEL +0 -0
- {orionis-0.246.0.dist-info → orionis-0.248.0.dist-info}/entry_points.txt +0 -0
- {orionis-0.246.0.dist-info → orionis-0.248.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
|