orionis 0.200.0__py3-none-any.whl → 0.202.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 (28) hide show
  1. orionis/framework.py +1 -1
  2. orionis/luminate/container/container_integrity.py +56 -1
  3. orionis/luminate/facades/tests/tests_facade.py +1 -1
  4. orionis/luminate/support/inspection/container_integrity.py +292 -0
  5. orionis/luminate/support/inspection/functions.py +235 -0
  6. orionis/luminate/support/inspection/reflection.py +649 -0
  7. orionis/luminate/support/inspection/reflexion_abstract.py +23 -0
  8. orionis/luminate/support/inspection/reflexion_concrete.py +24 -0
  9. orionis/luminate/support/inspection/reflexion_concrete_with_abstract.py +31 -0
  10. orionis/luminate/support/inspection/reflexion_instance.py +364 -0
  11. orionis/luminate/support/inspection/reflexion_instance_with_abstract.py +309 -0
  12. orionis/luminate/support/inspection/reflexion_module.py +19 -0
  13. orionis/luminate/support/inspection/reflexion_module_with_classname.py +22 -0
  14. orionis/luminate/test/{exception.py → test_exception.py} +1 -2
  15. orionis/luminate/test/test_result.py +30 -0
  16. orionis/luminate/test/test_status.py +22 -0
  17. orionis/luminate/test/tests.py +67 -0
  18. orionis/luminate/test/unit_test.py +276 -56
  19. {orionis-0.200.0.dist-info → orionis-0.202.0.dist-info}/METADATA +1 -1
  20. {orionis-0.200.0.dist-info → orionis-0.202.0.dist-info}/RECORD +26 -14
  21. tests/main.py +0 -0
  22. tests/tools/class_example.py +0 -50
  23. tests/tools/test_reflection.py +0 -128
  24. {tests/tools → orionis/luminate/support/inspection}/__init__.py +0 -0
  25. {orionis-0.200.0.dist-info → orionis-0.202.0.dist-info}/LICENCE +0 -0
  26. {orionis-0.200.0.dist-info → orionis-0.202.0.dist-info}/WHEEL +0 -0
  27. {orionis-0.200.0.dist-info → orionis-0.202.0.dist-info}/entry_points.txt +0 -0
  28. {orionis-0.200.0.dist-info → orionis-0.202.0.dist-info}/top_level.txt +0 -0
orionis/framework.py CHANGED
@@ -5,7 +5,7 @@
5
5
  NAME = "orionis"
6
6
 
7
7
  # Current version of the framework
8
- VERSION = "0.200.0"
8
+ VERSION = "0.202.0"
9
9
 
10
10
  # Full name of the author or maintainer of the project
11
11
  AUTHOR = "Raul Mauricio Uñate Castro"
@@ -1,13 +1,68 @@
1
1
  import re
2
2
  import inspect
3
3
  from abc import ABC
4
- from typing import Any, Callable, Type
4
+ from typing import Any, Callable, Set, Type
5
5
  from orionis.luminate.container.exception import OrionisContainerValueError, OrionisContainerTypeError
6
6
  from orionis.luminate.contracts.container.container_integrity import IContainerIntegrity
7
7
 
8
8
  class ContainerIntegrity(IContainerIntegrity):
9
9
 
10
10
  @staticmethod
11
+ def ensureImplementation(abstract: Type, concrete: Type, raise_exception: bool = True) -> bool:
12
+ """
13
+ Strictly verify that 'concrete' implements all abstract methods of 'abstract'.
14
+
15
+ Args:
16
+ abstract: Abstract class or interface to verify against
17
+ concrete: Concrete class that should implement the abstract class
18
+
19
+ Raises:
20
+ OrionisContainerTypeError: If concrete doesn't properly implement abstract
21
+ """
22
+
23
+ # Check if abstract is a class
24
+ if not inspect.isclass(abstract):
25
+ if raise_exception:
26
+ raise OrionisContainerTypeError(
27
+ f"Abstract must be a class, got {type(abstract).__name__}"
28
+ )
29
+ return False
30
+
31
+ # Check if concrete is a class
32
+ if not inspect.isclass(concrete):
33
+ if raise_exception:
34
+ raise OrionisContainerTypeError(
35
+ f"Concrete must be a class, got {type(concrete).__name__}"
36
+ )
37
+ return False
38
+
39
+ # Check if concrete inherits from abstract
40
+ if not issubclass(concrete, abstract):
41
+ if raise_exception:
42
+ raise OrionisContainerTypeError(
43
+ f"{concrete.__name__} must inherit from {abstract.__name__}"
44
+ )
45
+ return False
46
+
47
+ abstract_methods: Set[str] = set()
48
+ for base in abstract.__mro__:
49
+ if hasattr(base, '__abstractmethods__'):
50
+ abstract_methods.update(base.__abstractmethods__)
51
+
52
+ if not abstract_methods:
53
+ return # No abstract methods to implement
54
+
55
+ class_methods = {
56
+ name for name, member in inspect.getmembers(concrete)
57
+ if not name.startswith("_") and inspect.isfunction(member)
58
+ }
59
+
60
+ missing_methods = abstract_methods - class_methods
61
+ if missing_methods:
62
+ raise OrionisContainerTypeError(
63
+ f"{concrete.__name__} must implement: {sorted(missing_methods)}"
64
+ )
65
+
11
66
  def ensureImplementation(abstract: Type, concrete: Type) -> None:
12
67
  """
13
68
  Verify at runtime if `concrete` implements all methods of `abstract`.
@@ -42,7 +42,7 @@ class UnitTests(IUnitTests):
42
42
  # Recursively walk through the 'tests' directory
43
43
  for root, dirs, files in os.walk(tests_path):
44
44
  for dir in dirs:
45
- test_suite.addFolderTests(folder_path=dir, pattern=pattern)
45
+ test_suite.addFolder(folder_path=dir, pattern=pattern)
46
46
 
47
47
  # Execute the tests and return the results
48
48
  return test_suite.run()
@@ -0,0 +1,292 @@
1
+ import re
2
+ import inspect
3
+ from abc import ABC
4
+ from typing import Any, Callable, Set, Type
5
+ from orionis.luminate.container.exception import OrionisContainerValueError, OrionisContainerTypeError
6
+ from orionis.luminate.contracts.container.container_integrity import IContainerIntegrity
7
+
8
+ class ContainerIntegrity(IContainerIntegrity):
9
+
10
+ @staticmethod
11
+ def ensureImplementation(abstract: Type, concrete: Type, raise_exception: bool = True) -> bool:
12
+ """
13
+ Strictly verify that 'concrete' implements all abstract methods of 'abstract'.
14
+
15
+ Args:
16
+ abstract: Abstract class or interface to verify against
17
+ concrete: Concrete class that should implement the abstract class
18
+
19
+ Raises:
20
+ OrionisContainerTypeError: If concrete doesn't properly implement abstract
21
+ """
22
+
23
+ # Check if abstract is a class
24
+ if not inspect.isclass(abstract):
25
+ if raise_exception:
26
+ raise OrionisContainerTypeError(
27
+ f"Abstract must be a class, got {type(abstract).__name__}"
28
+ )
29
+ return False
30
+
31
+ # Check if concrete is a class
32
+ if not inspect.isclass(concrete):
33
+ if raise_exception:
34
+ raise OrionisContainerTypeError(
35
+ f"Concrete must be a class, got {type(concrete).__name__}"
36
+ )
37
+ return False
38
+
39
+ # Check if concrete inherits from abstract
40
+ if not issubclass(concrete, abstract):
41
+ if raise_exception:
42
+ raise OrionisContainerTypeError(
43
+ f"{concrete.__name__} must inherit from {abstract.__name__}"
44
+ )
45
+ return False
46
+
47
+ abstract_methods: Set[str] = set()
48
+ for base in abstract.__mro__:
49
+ if hasattr(base, '__abstractmethods__'):
50
+ abstract_methods.update(base.__abstractmethods__)
51
+
52
+ if not abstract_methods:
53
+ return # No abstract methods to implement
54
+
55
+ class_methods = {
56
+ name for name, member in inspect.getmembers(concrete)
57
+ if not name.startswith("_") and inspect.isfunction(member)
58
+ }
59
+
60
+ missing_methods = abstract_methods - class_methods
61
+ if missing_methods:
62
+ raise OrionisContainerTypeError(
63
+ f"{concrete.__name__} must implement: {sorted(missing_methods)}"
64
+ )
65
+
66
+ def ensureImplementation(abstract: Type, concrete: Type) -> None:
67
+ """
68
+ Verify at runtime if `concrete` implements all methods of `abstract`.
69
+
70
+ :param abstract: Abstract class or interface.
71
+ :param concrete: Concrete class that should implement the abstract class.
72
+ :raises TypeError: If `concrete` does not implement all methods of `abstract`.
73
+ """
74
+ # Get public methods of the interface (excluding magic methods)
75
+ interface_methods = {
76
+ name for name, func in inspect.getmembers(abstract, predicate=inspect.isfunction)
77
+ if not name.startswith("_")
78
+ }
79
+
80
+ # Get public methods of the concrete class
81
+ class_methods = {
82
+ name for name, func in inspect.getmembers(concrete, predicate=inspect.isfunction)
83
+ if not name.startswith("_")
84
+ }
85
+
86
+ # Verify that all interface methods are in the concrete class
87
+ if not interface_methods.issubset(class_methods):
88
+ missing_methods = interface_methods - class_methods
89
+ raise OrionisContainerTypeError(f"{concrete.__name__} does not implement the required methods of {abstract.__name__}: {missing_methods}")
90
+
91
+
92
+ @staticmethod
93
+ def ensureIsAbstract(abstract: Callable[..., Any]) -> None:
94
+ """
95
+ Ensure that the given abstract is a valid abstract class.
96
+
97
+ :param abstract: Class to check
98
+ :raises OrionisContainerValueError: If the class is not a valid abstract interface
99
+ """
100
+ if not isinstance(abstract, type) or not issubclass(abstract, ABC) or abstract is ABC:
101
+ raise OrionisContainerValueError("The provided class must inherit from ABC and not be ABC itself.")
102
+
103
+ if not any(getattr(attr, "__isabstractmethod__", False) for attr in abstract.__dict__.values()):
104
+ raise OrionisContainerValueError("The provided class must define at least one abstract method.")
105
+
106
+ @staticmethod
107
+ def ensureIsCallable(concrete: Callable[..., Any]) -> None:
108
+ """
109
+ Ensure that the given implementation is callable or instantiable.
110
+
111
+ Parameters
112
+ ----------
113
+ concrete : Callable[..., Any]
114
+ The implementation to check.
115
+
116
+ Raises
117
+ ------
118
+ OrionisContainerTypeError
119
+ If the implementation is not callable.
120
+ """
121
+ if not callable(concrete):
122
+ raise OrionisContainerTypeError(f"The implementation '{str(concrete)}' must be callable or an instantiable class.")
123
+
124
+ @staticmethod
125
+ def ensureIsInstance(instance: Any) -> None:
126
+ """
127
+ Ensure that the given instance is a valid object.
128
+
129
+ Parameters
130
+ ----------
131
+ instance : Any
132
+ The instance to check.
133
+
134
+ Raises
135
+ ------
136
+ OrionisContainerValueError
137
+ If the instance is not a valid object.
138
+ """
139
+ if not isinstance(instance, object):
140
+ raise OrionisContainerValueError(f"The instance '{str(instance)}' must be a valid object.")
141
+
142
+ module = type(instance).__module__
143
+ if module in ['builtins', 'abc']:
144
+ raise OrionisContainerValueError(f"The instance '{str(instance)}' is not a valid user-defined object. It belongs to the '{module}' module.")
145
+
146
+ @staticmethod
147
+ def ensureNotMain(concrete: Callable[..., Any]) -> str:
148
+ """
149
+ Ensure that a class is not defined in the main script.
150
+
151
+ Parameters
152
+ ----------
153
+ concrete : Callable[..., Any]
154
+ The class or function to check.
155
+
156
+ Returns
157
+ -------
158
+ str
159
+ The fully qualified name of the class.
160
+
161
+ Raises
162
+ ------
163
+ OrionisContainerValueError
164
+ If the class is defined in the main module.
165
+ """
166
+ if concrete.__module__ == "__main__":
167
+ raise OrionisContainerValueError("Cannot register a class from the (__main__) module in the container.")
168
+
169
+ @staticmethod
170
+ def ensureIsAlias(name: str) -> bool:
171
+ """
172
+ Ensure that the given alias name is a valid string, with no special characters or spaces,
173
+ and it is not a primitive type.
174
+
175
+ Parameters
176
+ ----------
177
+ name : str
178
+ The alias name to check.
179
+
180
+ Raises
181
+ ------
182
+ OrionisContainerValueError
183
+ If the alias is invalid.
184
+ """
185
+ if not isinstance(name, str):
186
+ raise OrionisContainerValueError(f"The alias '{name}' must be a string.")
187
+
188
+ if not re.match(r'^[a-zA-Z0-9_:]+$', name):
189
+ raise OrionisContainerValueError(
190
+ f"The alias '{name}' can only contain letters, numbers, underscores, and colons, without spaces or other special characters."
191
+ )
192
+
193
+ if name in {
194
+ int, "int",
195
+ float, "float",
196
+ str, "str",
197
+ bool, "bool",
198
+ bytes, "bytes",
199
+ type(None), "None",
200
+ complex, "complex",
201
+ list, "list",
202
+ tuple, "tuple",
203
+ dict, "dict",
204
+ set, "set",
205
+ frozenset, "frozenset"
206
+ }:
207
+ raise OrionisContainerValueError(f"The alias '{name}' cannot be a primitive type.")
208
+
209
+ @staticmethod
210
+ def isAlias(name: str) -> bool:
211
+ """
212
+ Check if the given alias name is a valid string, with no special characters or spaces,
213
+ and it is not a primitive type.
214
+
215
+ Parameters
216
+ ----------
217
+ name : str
218
+ The alias name to check.
219
+
220
+ Returns
221
+ -------
222
+ bool
223
+ True if the alias is valid, False otherwise.
224
+ """
225
+ try:
226
+ ContainerIntegrity.ensureIsAlias(name)
227
+ return True
228
+ except OrionisContainerValueError:
229
+ return False
230
+
231
+ @staticmethod
232
+ def isCallable(concrete: Callable[..., Any]) -> bool:
233
+ """
234
+ Check if the given implementation is callable or instantiable.
235
+
236
+ Parameters
237
+ ----------
238
+ concrete : Callable[..., Any]
239
+ The implementation to check.
240
+
241
+ Returns
242
+ -------
243
+ bool
244
+ True if the implementation is callable, False otherwise.
245
+ """
246
+ try:
247
+ ContainerIntegrity.ensureIsCallable(concrete)
248
+ return True
249
+ except OrionisContainerTypeError:
250
+ return False
251
+
252
+ @staticmethod
253
+ def isInstance(instance: Any) -> bool:
254
+ """
255
+ Check if the given instance is a valid object.
256
+
257
+ Parameters
258
+ ----------
259
+ instance : Any
260
+ The instance to check.
261
+
262
+ Returns
263
+ -------
264
+ bool
265
+ True if the instance is valid, False otherwise.
266
+ """
267
+ try:
268
+ ContainerIntegrity.ensureIsInstance(instance)
269
+ return True
270
+ except OrionisContainerValueError:
271
+ return False
272
+
273
+ @staticmethod
274
+ def isAbstract(abstract: Callable[..., Any]) -> bool:
275
+ """
276
+ Check if the given abstract is a valid abstract class.
277
+
278
+ Parameters
279
+ ----------
280
+ abstract : Callable[..., Any]
281
+ The class to check.
282
+
283
+ Returns
284
+ -------
285
+ bool
286
+ True if the class is a valid abstract interface, False otherwise.
287
+ """
288
+ try:
289
+ ContainerIntegrity.ensureIsAbstract(abstract)
290
+ return True
291
+ except OrionisContainerValueError:
292
+ return False
@@ -0,0 +1,235 @@
1
+ from typing import Any, Type
2
+ import inspect
3
+ import importlib
4
+
5
+ def is_valid_module(module_name: str) -> bool:
6
+ """Check if a module name is valid and can be imported.
7
+
8
+ Parameters
9
+ ----------
10
+ module_name : str
11
+ The name of the module to check
12
+
13
+ Returns
14
+ -------
15
+ bool
16
+ True if the module is valid and can be imported, False otherwise
17
+ """
18
+ try:
19
+ importlib.import_module(module_name)
20
+ return True
21
+ except ImportError:
22
+ return False
23
+
24
+ def ensure_valid_module(module_name: str) -> None:
25
+ """Ensure a module name is valid and can be imported.
26
+
27
+ Parameters
28
+ ----------
29
+ module_name : str
30
+ The name of the module to check
31
+
32
+ Raises
33
+ ------
34
+ ValueError
35
+ If the module cannot be imported or is invalid
36
+ """
37
+ if not isinstance(module_name, str):
38
+ raise TypeError(f"Module name must be a string, got {type(module_name)}")
39
+ if not is_valid_module(module_name):
40
+ raise ValueError(f"Invalid or non-importable module: {module_name}")
41
+
42
+ def is_instantiable_class(cls: Type) -> bool:
43
+ """Check if a class is concrete and can be instantiated.
44
+
45
+ Parameters
46
+ ----------
47
+ cls : Type
48
+ The class to check
49
+
50
+ Returns
51
+ --
52
+ bool
53
+ True if the class is concrete and can be instantiated, False otherwise
54
+ """
55
+ if not isinstance(cls, type):
56
+ return False
57
+ if is_abstract_class(cls):
58
+ return False
59
+ try:
60
+ # Try to create an instance to verify it's truly concrete
61
+ cls()
62
+ return True
63
+ except TypeError:
64
+ return False
65
+
66
+ def ensure_instantiable_class(cls: Type) -> None:
67
+ """Ensure a class is concrete and can be instantiated.
68
+
69
+ Parameters
70
+ ----------
71
+ cls : Type
72
+ The class to check
73
+
74
+ Raises
75
+ ------
76
+ TypeError
77
+ If the input is not a class
78
+ ValueError
79
+ If the class is abstract or cannot be instantiated
80
+ """
81
+ if not isinstance(cls, type):
82
+ raise TypeError(f"Expected a class, got {type(cls)}")
83
+ if is_abstract_class(cls):
84
+ raise ValueError(f"Class '{cls.__name__}' is abstract")
85
+ try:
86
+ cls()
87
+ except TypeError as e:
88
+ raise ValueError(f"Class '{cls.__name__}' cannot be instantiated: {str(e)}")
89
+
90
+ def is_valid_class_name(module_name: str, class_name: str) -> bool:
91
+ """Check if a class exists in a given module.
92
+
93
+ Parameters
94
+ ----------
95
+ module_name : str
96
+ The name of the module to check
97
+ class_name : str
98
+ The name of the class to look for
99
+
100
+ Returns
101
+ -------
102
+ bool
103
+ True if the class exists in the module, False otherwise
104
+ """
105
+ try:
106
+ module = importlib.import_module(module_name)
107
+ return hasattr(module, class_name) and inspect.isclass(getattr(module, class_name))
108
+ except ImportError:
109
+ return False
110
+
111
+ def ensure_valid_class_name(module_name: str, class_name: str) -> None:
112
+ """Ensure a class exists in a given module.
113
+
114
+ Parameters
115
+ ----------
116
+ module_name : str
117
+ The name of the module to check
118
+ class_name : str
119
+ The name of the class to look for
120
+
121
+ Raises
122
+ ------
123
+ ValueError
124
+ If the class doesn't exist in the module
125
+ """
126
+ if not is_valid_class_name(module_name, class_name):
127
+ raise ValueError(f"Class '{class_name}' not found in module '{module_name}'")
128
+
129
+ def is_user_defined_class_instance(instance: Any) -> bool:
130
+ """Check if an object is an instance of a user-defined class.
131
+
132
+ Parameters
133
+ ----------
134
+ instance : Any
135
+ The object to check
136
+
137
+ Returns
138
+ -------
139
+ bool
140
+ True if the object is an instance of a user-defined class, False otherwise
141
+ """
142
+ return isinstance(instance, object) and type(instance).__module__ not in {'builtins', 'abc', '__main__'}
143
+
144
+ def ensure_user_defined_class_instance(instance: Any) -> None:
145
+ """Ensure an object is an instance of a user-defined class.
146
+
147
+ Parameters
148
+ ----------
149
+ instance : Any
150
+ The object to check
151
+
152
+ Raises
153
+ ------
154
+ TypeError
155
+ If the input is not an object instance
156
+ ValueError
157
+ If the instance is from builtins, abc, or __main__
158
+ """
159
+ if not isinstance(instance, object):
160
+ raise TypeError(f"Invalid object: {instance!r}")
161
+ module = type(instance).__module__
162
+ if module in {'builtins', 'abc'}:
163
+ raise ValueError(f"'{instance!r}' is not a user-defined class instance, belongs to '{module}'.")
164
+ if module == '__main__':
165
+ raise ValueError("Instance originates from '__main__', origin indeterminate.")
166
+
167
+ def is_abstract_class(cls: Type) -> bool:
168
+ """Check if a class is abstract.
169
+
170
+ Parameters
171
+ ----------
172
+ cls : Type
173
+ The class to check
174
+
175
+ Returns
176
+ -------
177
+ bool
178
+ True if the class is abstract, False otherwise
179
+ """
180
+ return isinstance(cls, type) and bool(getattr(cls, '__abstractmethods__', False))
181
+
182
+ def ensure_abstract_class(cls: Type) -> None:
183
+ """Ensure a class is abstract.
184
+
185
+ Parameters
186
+ ----------
187
+ cls : Type
188
+ The class to check
189
+
190
+ Raises
191
+ ------
192
+ TypeError
193
+ If the input is not a class
194
+ ValueError
195
+ If the class is not abstract
196
+ """
197
+ if not isinstance(cls, type):
198
+ raise TypeError(f"Invalid class: {cls!r}")
199
+ if not is_abstract_class(cls):
200
+ raise ValueError(f"Class '{cls.__name__}' is not abstract.")
201
+
202
+ def is_concrete_class(cls: Type) -> bool:
203
+ """Check if a class is concrete.
204
+
205
+ Parameters
206
+ ----------
207
+ cls : Type
208
+ The class to check
209
+
210
+ Returns
211
+ -------
212
+ bool
213
+ True if the class is concrete, False otherwise
214
+ """
215
+ return isinstance(cls, type) and not is_abstract_class(cls)
216
+
217
+ def ensure_concrete_class(cls: Type) -> None:
218
+ """Ensure a class is concrete.
219
+
220
+ Parameters
221
+ ----------
222
+ cls : Type
223
+ The class to check
224
+
225
+ Raises
226
+ ------
227
+ TypeError
228
+ If the input is not a class
229
+ ValueError
230
+ If the class is not concrete
231
+ """
232
+ if not isinstance(cls, type):
233
+ raise TypeError(f"Invalid class: {cls!r}")
234
+ if not is_concrete_class(cls):
235
+ raise ValueError(f"Class '{cls.__name__}' is not concrete.")