orionis 0.406.0__py3-none-any.whl → 0.408.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/container/container.py +11 -9
- orionis/container/enums/lifetimes.py +2 -0
- orionis/container/validators/__init__.py +21 -0
- orionis/metadata/framework.py +1 -1
- orionis/services/asynchrony/contracts/coroutines.py +13 -5
- orionis/services/asynchrony/coroutines.py +33 -29
- orionis/services/asynchrony/exceptions/exception.py +9 -1
- orionis/services/environment/core/dot_env.py +46 -34
- orionis/services/environment/enums/__init__.py +0 -0
- orionis/services/environment/enums/cast_type.py +42 -0
- orionis/services/environment/serializer/__init__.py +0 -0
- orionis/services/environment/serializer/values.py +21 -0
- orionis/services/environment/validators/__init__.py +0 -0
- orionis/services/environment/validators/key_name.py +46 -0
- orionis/services/environment/validators/types.py +45 -0
- orionis/services/system/contracts/imports.py +38 -18
- orionis/services/system/contracts/workers.py +29 -12
- orionis/services/system/imports.py +65 -25
- orionis/services/system/runtime/imports.py +18 -9
- orionis/services/system/workers.py +49 -16
- orionis/test/output/dumper.py +1 -0
- {orionis-0.406.0.dist-info → orionis-0.408.0.dist-info}/METADATA +1 -1
- {orionis-0.406.0.dist-info → orionis-0.408.0.dist-info}/RECORD +52 -45
- tests/container/context/test_manager.py +15 -5
- tests/container/context/test_scope.py +12 -4
- tests/container/entities/test_binding.py +130 -21
- tests/container/enums/test_lifetimes.py +52 -18
- tests/container/facades/test_facade.py +29 -12
- tests/container/providers/test_providers.py +17 -10
- tests/container/resolver/test_resolver.py +14 -7
- tests/container/test_container.py +226 -71
- tests/container/test_singleton.py +43 -24
- tests/container/test_thread_safety.py +28 -156
- tests/container/validators/test_implements.py +59 -13
- tests/container/validators/test_is_abstract_class.py +73 -25
- tests/container/validators/test_is_callable.py +55 -26
- tests/container/validators/test_is_concrete_class.py +80 -17
- tests/container/validators/test_is_instance.py +67 -22
- tests/container/validators/test_is_not_subclass.py +28 -95
- tests/container/validators/test_is_subclass.py +84 -21
- tests/container/validators/test_is_valid_alias.py +46 -12
- tests/container/validators/test_lifetime.py +45 -14
- tests/example/test_example.py +2 -2
- tests/metadata/test_metadata_framework.py +71 -6
- tests/metadata/test_metadata_package.py +55 -10
- tests/services/asynchrony/test_services_asynchrony_coroutine.py +52 -7
- tests/services/system/test_services_system_imports.py +119 -16
- tests/services/system/test_services_system_workers.py +71 -30
- {orionis-0.406.0.dist-info → orionis-0.408.0.dist-info}/WHEEL +0 -0
- {orionis-0.406.0.dist-info → orionis-0.408.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.406.0.dist-info → orionis-0.408.0.dist-info}/top_level.txt +0 -0
- {orionis-0.406.0.dist-info → orionis-0.408.0.dist-info}/zip-safe +0 -0
|
@@ -1,35 +1,52 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
|
|
3
3
|
class IWorkers(ABC):
|
|
4
|
-
"""
|
|
5
|
-
Interface for calculating the optimal number of workers a machine can handle based on CPU and memory resources.
|
|
6
|
-
|
|
7
|
-
Notes
|
|
8
|
-
-----
|
|
9
|
-
Implementations should provide logic to determine the recommended number of worker processes
|
|
10
|
-
according to the available CPU and memory resources of the current machine.
|
|
11
|
-
"""
|
|
12
4
|
|
|
13
5
|
@abstractmethod
|
|
14
6
|
def setRamPerWorker(self, ram_per_worker: float) -> None:
|
|
15
7
|
"""
|
|
16
|
-
Set the amount of RAM
|
|
8
|
+
Set the amount of RAM to allocate for each worker process.
|
|
17
9
|
|
|
18
10
|
Parameters
|
|
19
11
|
----------
|
|
20
12
|
ram_per_worker : float
|
|
21
|
-
|
|
13
|
+
The amount of RAM, in gigabytes (GB), to allocate for each worker process.
|
|
14
|
+
|
|
15
|
+
Returns
|
|
16
|
+
-------
|
|
17
|
+
None
|
|
18
|
+
This method does not return any value.
|
|
19
|
+
|
|
20
|
+
Notes
|
|
21
|
+
-----
|
|
22
|
+
This method should be implemented by subclasses to configure the memory usage
|
|
23
|
+
per worker, which may affect the total number of workers that can be spawned
|
|
24
|
+
based on system resources.
|
|
22
25
|
"""
|
|
26
|
+
|
|
27
|
+
# Implementation should assign the specified RAM per worker.
|
|
23
28
|
pass
|
|
24
29
|
|
|
25
30
|
@abstractmethod
|
|
26
31
|
def calculate(self) -> int:
|
|
27
32
|
"""
|
|
28
|
-
Compute the maximum number of
|
|
33
|
+
Compute the recommended maximum number of worker processes for the current machine.
|
|
34
|
+
|
|
35
|
+
This method should consider both CPU and memory constraints to determine the optimal
|
|
36
|
+
number of worker processes that can be safely spawned without overloading system resources.
|
|
29
37
|
|
|
30
38
|
Returns
|
|
31
39
|
-------
|
|
32
40
|
int
|
|
33
|
-
|
|
41
|
+
The maximum number of worker processes that can be supported by the current machine,
|
|
42
|
+
based on available CPU cores and memory limits.
|
|
43
|
+
|
|
44
|
+
Notes
|
|
45
|
+
-----
|
|
46
|
+
Subclasses should implement this method to analyze system resources and return an integer
|
|
47
|
+
representing the recommended number of workers. The calculation should ensure that each
|
|
48
|
+
worker receives sufficient resources as configured (e.g., RAM per worker).
|
|
34
49
|
"""
|
|
50
|
+
|
|
51
|
+
# Implementation should analyze system resources and return the recommended worker count.
|
|
35
52
|
pass
|
|
@@ -2,58 +2,68 @@ from typing import List, Dict, Any
|
|
|
2
2
|
from orionis.services.system.contracts.imports import IImports
|
|
3
3
|
|
|
4
4
|
class Imports(IImports):
|
|
5
|
-
"""
|
|
6
|
-
Utility class to collect and display information about currently loaded Python modules.
|
|
7
|
-
|
|
8
|
-
This class provides methods to gather details about user-defined Python modules
|
|
9
|
-
currently loaded in `sys.modules`, excluding standard library and virtual environment modules.
|
|
10
|
-
It can display the collected information in a formatted table using the Rich library.
|
|
11
|
-
"""
|
|
12
5
|
|
|
13
6
|
def __init__(self):
|
|
14
7
|
"""
|
|
15
|
-
Initialize the Imports
|
|
8
|
+
Initialize the Imports instance.
|
|
9
|
+
|
|
10
|
+
This constructor sets up the Imports object by initializing an empty list
|
|
11
|
+
to store information about user-defined Python modules. The list will
|
|
12
|
+
contain dictionaries, each representing a module with its name, file path,
|
|
13
|
+
and defined symbols.
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
Returns
|
|
16
|
+
-------
|
|
17
|
+
None
|
|
18
|
+
This method does not return any value.
|
|
18
19
|
"""
|
|
20
|
+
|
|
21
|
+
# List to hold information about imported modules
|
|
19
22
|
self.imports: List[Dict[str, Any]] = []
|
|
20
23
|
|
|
21
24
|
def collect(self) -> 'Imports':
|
|
22
25
|
"""
|
|
23
26
|
Collect information about user-defined Python modules currently loaded.
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
28
|
+
Iterates through all modules in `sys.modules` and gathers details for each qualifying module:
|
|
29
|
+
- Module name.
|
|
30
|
+
- Relative file path from the current working directory.
|
|
31
|
+
- List of symbols (functions, classes, or submodules) defined in the module.
|
|
29
32
|
|
|
30
|
-
Excludes:
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
33
|
+
Excludes modules that:
|
|
34
|
+
- Are part of the standard library.
|
|
35
|
+
- Reside in the active virtual environment (if any).
|
|
36
|
+
- Are binary extension modules (e.g., `.pyd`, `.dll`, `.so`).
|
|
37
|
+
- Are special modules such as `"__main__"`, `"__mp_main__"`, or those starting with `"_distutils"`.
|
|
35
38
|
|
|
36
|
-
The collected information is stored in `self.imports` as a list of dictionaries.
|
|
39
|
+
The collected information is stored in `self.imports` as a list of dictionaries, each containing the module's name, file path, and symbols.
|
|
37
40
|
|
|
38
41
|
Returns
|
|
39
42
|
-------
|
|
40
43
|
Imports
|
|
41
|
-
The current instance with
|
|
44
|
+
The current instance of `Imports` with the `imports` attribute updated to include information about the collected modules.
|
|
42
45
|
"""
|
|
43
46
|
|
|
44
47
|
import sys
|
|
45
48
|
import os
|
|
46
49
|
import types
|
|
47
50
|
|
|
51
|
+
# Clear any previously collected imports
|
|
48
52
|
self.imports.clear()
|
|
53
|
+
|
|
54
|
+
# Get standard library paths to exclude standard modules
|
|
49
55
|
stdlib_paths = [os.path.dirname(os.__file__)]
|
|
56
|
+
|
|
57
|
+
# Get virtual environment path if active
|
|
50
58
|
venv_path = os.environ.get("VIRTUAL_ENV")
|
|
51
59
|
if venv_path:
|
|
52
60
|
venv_path = os.path.abspath(venv_path)
|
|
53
61
|
|
|
62
|
+
# Iterate over all loaded modules
|
|
54
63
|
for name, module in list(sys.modules.items()):
|
|
55
|
-
file:str = getattr(module, '__file__', None)
|
|
64
|
+
file: str = getattr(module, '__file__', None)
|
|
56
65
|
|
|
66
|
+
# Filter out unwanted modules based on path, type, and name
|
|
57
67
|
if (
|
|
58
68
|
file
|
|
59
69
|
and not any(file.startswith(stdlib_path) for stdlib_path in stdlib_paths)
|
|
@@ -62,18 +72,25 @@ class Imports(IImports):
|
|
|
62
72
|
and name not in ("__main__", "__mp_main__")
|
|
63
73
|
and not name.startswith("_distutils")
|
|
64
74
|
):
|
|
75
|
+
|
|
76
|
+
# Get relative file path from current working directory
|
|
65
77
|
rel_file = os.path.relpath(file, os.getcwd())
|
|
66
78
|
symbols = []
|
|
67
79
|
|
|
80
|
+
# Collect symbols defined in the module (functions, classes, submodules)
|
|
68
81
|
try:
|
|
69
82
|
for attr in dir(module):
|
|
70
83
|
value = getattr(module, attr)
|
|
71
84
|
if isinstance(value, (types.FunctionType, type, types.ModuleType)):
|
|
85
|
+
|
|
86
|
+
# Ensure symbol is defined in this module
|
|
72
87
|
if getattr(value, '__module__', None) == name:
|
|
73
88
|
symbols.append(attr)
|
|
74
89
|
except Exception:
|
|
90
|
+
# Ignore errors during symbol collection
|
|
75
91
|
pass
|
|
76
92
|
|
|
93
|
+
# Only add modules that are not __init__.py and have symbols
|
|
77
94
|
if not rel_file.endswith('__init__.py') and symbols:
|
|
78
95
|
self.imports.append({
|
|
79
96
|
"name": name,
|
|
@@ -81,32 +98,45 @@ class Imports(IImports):
|
|
|
81
98
|
"symbols": symbols,
|
|
82
99
|
})
|
|
83
100
|
|
|
101
|
+
# Return the current instance with updated imports
|
|
84
102
|
return self
|
|
85
103
|
|
|
86
104
|
def display(self) -> None:
|
|
87
105
|
"""
|
|
88
106
|
Display a formatted table of collected import statements using the Rich library.
|
|
89
107
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
rendered inside a styled panel in the console
|
|
108
|
+
This method presents a visual summary of all collected user-defined Python modules.
|
|
109
|
+
If the imports have not been collected yet, it automatically calls `self.collect()` to gather them.
|
|
110
|
+
The output is rendered as a table inside a styled panel in the console, showing each module's name,
|
|
111
|
+
relative file path, and its defined symbols.
|
|
112
|
+
|
|
113
|
+
Parameters
|
|
114
|
+
----------
|
|
115
|
+
None
|
|
93
116
|
|
|
94
117
|
Returns
|
|
95
118
|
-------
|
|
96
119
|
None
|
|
120
|
+
This method does not return any value. It outputs the formatted table to the console.
|
|
97
121
|
"""
|
|
98
122
|
|
|
123
|
+
# Collect imports if not already done
|
|
99
124
|
if not self.imports:
|
|
100
125
|
self.collect()
|
|
101
126
|
|
|
127
|
+
# Import Rich components for console output
|
|
102
128
|
from rich.console import Console
|
|
103
129
|
from rich.table import Table
|
|
104
130
|
from rich.box import MINIMAL
|
|
105
131
|
from rich.panel import Panel
|
|
106
132
|
|
|
133
|
+
# Create a console instance for output
|
|
107
134
|
console = Console()
|
|
135
|
+
|
|
136
|
+
# Set table width to 75% of console width
|
|
108
137
|
width = int(console.size.width * 0.75)
|
|
109
138
|
|
|
139
|
+
# Create a table with minimal box style and custom formatting
|
|
110
140
|
table = Table(
|
|
111
141
|
box=MINIMAL,
|
|
112
142
|
show_header=True,
|
|
@@ -117,14 +147,17 @@ class Imports(IImports):
|
|
|
117
147
|
collapse_padding=True,
|
|
118
148
|
)
|
|
119
149
|
|
|
150
|
+
# Add columns for module name, file path, and symbols
|
|
120
151
|
table.add_column("Name", style="cyan", no_wrap=True)
|
|
121
152
|
table.add_column("File", style="white")
|
|
122
153
|
table.add_column("Symbols", style="magenta")
|
|
123
154
|
|
|
155
|
+
# Populate the table with sorted import data
|
|
124
156
|
for imp in sorted(self.imports, key=lambda x: x["name"].lower()):
|
|
125
157
|
symbols_str = ", ".join(imp["symbols"])
|
|
126
158
|
table.add_row(imp["name"], imp["file"], symbols_str)
|
|
127
159
|
|
|
160
|
+
# Render the table inside a styled panel in the console
|
|
128
161
|
console.print(Panel(
|
|
129
162
|
table,
|
|
130
163
|
title="[bold blue]🔎 Loaded Python Modules (Orionis Imports Trace)[/bold blue]",
|
|
@@ -134,10 +167,17 @@ class Imports(IImports):
|
|
|
134
167
|
|
|
135
168
|
def clear(self) -> None:
|
|
136
169
|
"""
|
|
137
|
-
|
|
170
|
+
Remove all entries from the collected imports list.
|
|
171
|
+
|
|
172
|
+
This method resets the `imports` attribute by removing all currently stored
|
|
173
|
+
module information. It is useful for discarding previously collected data
|
|
174
|
+
before performing a new collection or when a fresh state is required.
|
|
138
175
|
|
|
139
176
|
Returns
|
|
140
177
|
-------
|
|
141
178
|
None
|
|
179
|
+
This method does not return any value. The `imports` list is emptied in place.
|
|
142
180
|
"""
|
|
181
|
+
|
|
182
|
+
# Remove all items from the imports list to reset its state
|
|
143
183
|
self.imports.clear()
|
|
@@ -25,6 +25,7 @@ Notes
|
|
|
25
25
|
- Thread safety is provided via a threading.Lock.
|
|
26
26
|
|
|
27
27
|
"""
|
|
28
|
+
|
|
28
29
|
import builtins
|
|
29
30
|
from collections import defaultdict
|
|
30
31
|
from threading import Lock
|
|
@@ -40,40 +41,48 @@ _import_lock = Lock()
|
|
|
40
41
|
|
|
41
42
|
def custom_import(name, globals=None, locals=None, fromlist=(), level=0):
|
|
42
43
|
"""
|
|
43
|
-
|
|
44
|
+
Tracks and logs imports of modules whose names start with 'orionis'.
|
|
45
|
+
|
|
46
|
+
This function overrides Python's built-in import mechanism to monitor
|
|
47
|
+
how many times modules from the 'orionis' package are imported. It
|
|
48
|
+
increments an internal counter for each such import and prints a log
|
|
49
|
+
message with the module name, import count, and fromlist. Thread safety
|
|
50
|
+
is ensured using a lock.
|
|
44
51
|
|
|
45
52
|
Parameters
|
|
46
53
|
----------
|
|
47
54
|
name : str
|
|
48
55
|
The name of the module to import.
|
|
49
56
|
globals : dict, optional
|
|
50
|
-
The global namespace.
|
|
57
|
+
The global namespace in which the import is performed.
|
|
51
58
|
locals : dict, optional
|
|
52
|
-
The local namespace.
|
|
59
|
+
The local namespace in which the import is performed.
|
|
53
60
|
fromlist : tuple, optional
|
|
54
61
|
Names to import from the module.
|
|
55
62
|
level : int, optional
|
|
56
|
-
Relative import level.
|
|
63
|
+
Relative import level (0 for absolute, >0 for relative).
|
|
57
64
|
|
|
58
65
|
Returns
|
|
59
66
|
-------
|
|
60
|
-
module
|
|
61
|
-
The imported module.
|
|
67
|
+
module : ModuleType
|
|
68
|
+
The imported module object as returned by the original import function.
|
|
62
69
|
"""
|
|
63
|
-
#
|
|
70
|
+
# Only track imports for modules starting with 'orionis'
|
|
64
71
|
if str(name).startswith("orionis"):
|
|
65
72
|
with _import_lock:
|
|
73
|
+
|
|
74
|
+
# Increment the import count for this module
|
|
66
75
|
_import_count[name] += 1
|
|
67
76
|
count = _import_count[name]
|
|
68
77
|
|
|
69
|
-
# Print
|
|
78
|
+
# Print import details to the console
|
|
70
79
|
print(
|
|
71
80
|
f"\033[1;37mModule\033[0m: \033[90m{name}\033[0m | "
|
|
72
81
|
f"\033[1;37mImported\033[0m: \033[90m{count}\033[0m | "
|
|
73
82
|
f"\033[1;37mFromList\033[0m: \033[90m{fromlist}\033[0m"
|
|
74
83
|
)
|
|
75
84
|
|
|
76
|
-
#
|
|
85
|
+
# Delegate the actual import to the original __import__ function
|
|
77
86
|
return _original_import(name, globals, locals, fromlist, level)
|
|
78
87
|
|
|
79
88
|
# Override the built-in __import__ function
|
|
@@ -4,21 +4,10 @@ import psutil
|
|
|
4
4
|
from orionis.services.system.contracts.workers import IWorkers
|
|
5
5
|
|
|
6
6
|
class Workers(IWorkers):
|
|
7
|
-
"""
|
|
8
|
-
Estimate the optimal number of worker processes based on system CPU and memory resources.
|
|
9
|
-
|
|
10
|
-
This class calculates the recommended number of Uvicorn (or similar) workers by considering:
|
|
11
|
-
the number of available CPU cores, total system memory (RAM), and the estimated memory usage per worker.
|
|
12
|
-
|
|
13
|
-
Parameters
|
|
14
|
-
----------
|
|
15
|
-
ram_per_worker : float, optional
|
|
16
|
-
Estimated amount of RAM in gigabytes (GB) that each worker will consume. Default is 0.5.
|
|
17
|
-
"""
|
|
18
7
|
|
|
19
8
|
def __init__(self, ram_per_worker: float = 0.5):
|
|
20
9
|
"""
|
|
21
|
-
Initialize the
|
|
10
|
+
Initialize the Workers system with resource constraints.
|
|
22
11
|
|
|
23
12
|
Parameters
|
|
24
13
|
----------
|
|
@@ -33,31 +22,75 @@ class Workers(IWorkers):
|
|
|
33
22
|
Total system RAM in gigabytes.
|
|
34
23
|
_ram_per_worker : float
|
|
35
24
|
RAM allocated per worker in gigabytes.
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
None
|
|
29
|
+
This constructor does not return a value.
|
|
36
30
|
"""
|
|
31
|
+
|
|
32
|
+
# Get the number of CPU cores available
|
|
37
33
|
self._cpu_count = multiprocessing.cpu_count()
|
|
34
|
+
|
|
35
|
+
# Get the total system RAM in gigabytes
|
|
38
36
|
self._ram_total_gb = psutil.virtual_memory().total / (1024 ** 3)
|
|
37
|
+
|
|
38
|
+
# Set the RAM allocated per worker
|
|
39
39
|
self._ram_per_worker = ram_per_worker
|
|
40
40
|
|
|
41
41
|
def setRamPerWorker(self, ram_per_worker: float) -> None:
|
|
42
42
|
"""
|
|
43
|
-
|
|
43
|
+
Update the RAM allocation per worker.
|
|
44
44
|
|
|
45
45
|
Parameters
|
|
46
46
|
----------
|
|
47
47
|
ram_per_worker : float
|
|
48
|
-
|
|
48
|
+
The new amount of RAM (in GB) to allocate for each worker.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
None
|
|
53
|
+
This method does not return a value. It updates the internal RAM allocation setting.
|
|
54
|
+
|
|
55
|
+
Notes
|
|
56
|
+
-----
|
|
57
|
+
Changing the RAM allocation per worker may affect the recommended number of workers
|
|
58
|
+
calculated by the system. This method only updates the internal configuration and does
|
|
59
|
+
not trigger any recalculation automatically.
|
|
49
60
|
"""
|
|
61
|
+
|
|
62
|
+
# Update the RAM allocated per worker
|
|
50
63
|
self._ram_per_worker = ram_per_worker
|
|
51
64
|
|
|
52
65
|
def calculate(self) -> int:
|
|
53
66
|
"""
|
|
54
|
-
Compute the maximum number of
|
|
67
|
+
Compute the recommended maximum number of worker processes for the current machine,
|
|
68
|
+
considering both CPU and memory constraints.
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
None
|
|
55
73
|
|
|
56
74
|
Returns
|
|
57
75
|
-------
|
|
58
76
|
int
|
|
59
|
-
The
|
|
77
|
+
The maximum number of worker processes that can be safely run in parallel,
|
|
78
|
+
determined by the lesser of available CPU cores and memory capacity.
|
|
79
|
+
|
|
80
|
+
Notes
|
|
81
|
+
-----
|
|
82
|
+
The calculation is based on:
|
|
83
|
+
- The total number of CPU cores available.
|
|
84
|
+
- The total system RAM divided by the RAM allocated per worker.
|
|
85
|
+
The method ensures that neither CPU nor memory resources are overcommitted.
|
|
86
|
+
|
|
60
87
|
"""
|
|
88
|
+
|
|
89
|
+
# Calculate the maximum workers allowed by CPU core count
|
|
61
90
|
max_workers_by_cpu = self._cpu_count
|
|
91
|
+
|
|
92
|
+
# Calculate the maximum workers allowed by available RAM
|
|
62
93
|
max_workers_by_ram = math.floor(self._ram_total_gb / self._ram_per_worker)
|
|
94
|
+
|
|
95
|
+
# Return the minimum of the two to avoid overcommitting resources
|
|
63
96
|
return min(max_workers_by_cpu, max_workers_by_ram)
|
orionis/test/output/dumper.py
CHANGED