orionis 0.304.0__py3-none-any.whl → 0.306.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/metadata/framework.py +1 -1
- orionis/services/introspection/modules/{reflection_instance.py → reflection_module.py} +1 -1
- orionis/services/introspection/reflection.py +90 -0
- orionis/{services → support}/standard/std.py +2 -2
- orionis/test/{facade/test_suite.py → test_suite.py} +1 -2
- orionis/unittesting.py +1 -1
- {orionis-0.304.0.dist-info → orionis-0.306.0.dist-info}/METADATA +1 -1
- {orionis-0.304.0.dist-info → orionis-0.306.0.dist-info}/RECORD +28 -65
- tests/{services → support}/standard/test_services_std.py +3 -3
- tests/{services → support}/wrapper/test_services_wrapper_docdict.py +2 -2
- orionis/support/abstracts/entities/abstract_class_attributes.py +0 -37
- orionis/support/abstracts/reflect_abstract.py +0 -556
- orionis/support/helpers/functions.py +0 -285
- orionis/support/introspection/container_integrity.py +0 -292
- orionis/support/introspection/contracts/__init__.py +0 -0
- orionis/support/introspection/contracts/reflection.py +0 -187
- orionis/support/introspection/contracts/reflexion_abstract.py +0 -264
- orionis/support/introspection/helpers/__init__.py +0 -0
- orionis/support/introspection/helpers/functions.py +0 -281
- orionis/support/introspection/instances/__init__.py +0 -0
- orionis/support/introspection/instances/contracts/__init__.py +0 -0
- orionis/support/introspection/instances/contracts/reflection_instance.py +0 -649
- orionis/support/introspection/instances/entities/__init__.py +0 -0
- orionis/support/introspection/instances/reflection_instance.py +0 -758
- orionis/support/introspection/reflect_decorators.py +0 -335
- orionis/support/introspection/reflection.py +0 -216
- orionis/support/introspection/reflexion_concrete.py +0 -276
- orionis/support/introspection/reflexion_concrete_with_abstract.py +0 -185
- orionis/support/introspection/reflexion_instance_with_abstract.py +0 -230
- orionis/support/introspection/reflexion_module.py +0 -19
- orionis/support/introspection/reflexion_module_with_classname.py +0 -22
- orionis/support/reflection.py +0 -216
- orionis/test/facade/__init__.py +0 -0
- orionis/test/facade/contracts/__init__.py +0 -0
- orionis/test/facade/contracts/test_suite.py +0 -25
- tests/services/standard/__init__.py +0 -0
- tests/services/wrapper/__init__.py +0 -0
- tests/support/inspection/__init__.py +0 -0
- tests/support/inspection/fakes/__init__.py +0 -0
- tests/support/inspection/fakes/fake_reflect_abstract.py +0 -276
- tests/support/inspection/fakes/fake_reflection_concrete.py +0 -44
- tests/support/inspection/fakes/fake_reflection_concrete_with_abstract.py +0 -78
- tests/support/inspection/fakes/fake_reflection_instance_with_abstract.py +0 -45
- tests/support/inspection/test_reflect_abstract.py +0 -334
- tests/support/inspection/test_reflect_instance.py +0 -288
- tests/support/inspection/test_reflection_concrete.py +0 -142
- tests/support/inspection/test_reflection_concrete_with_abstract.py +0 -87
- tests/support/inspection/test_reflection_instance_with_abstract.py +0 -79
- /orionis/services/introspection/modules/contracts/{reflection_instance.py → reflection_module.py} +0 -0
- /orionis/{services → support}/standard/__init__.py +0 -0
- /orionis/{services → support}/standard/contracts/__init__.py +0 -0
- /orionis/{services → support}/standard/contracts/std.py +0 -0
- /orionis/{services → support}/standard/exceptions/__init__.py +0 -0
- /orionis/{services → support}/standard/exceptions/std_value_exception.py +0 -0
- /orionis/{services → support}/wrapper/__init__.py +0 -0
- /orionis/{services → support}/wrapper/dicts/__init__.py +0 -0
- /orionis/{services → support}/wrapper/dicts/dot_dict.py +0 -0
- {orionis-0.304.0.dist-info → orionis-0.306.0.dist-info}/WHEEL +0 -0
- {orionis-0.304.0.dist-info → orionis-0.306.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.304.0.dist-info → orionis-0.306.0.dist-info}/top_level.txt +0 -0
- {orionis-0.304.0.dist-info → orionis-0.306.0.dist-info}/zip-safe +0 -0
- {orionis/support/abstracts → tests/services/inspection/reflection}/__init__.py +0 -0
- {orionis/support/abstracts/entities → tests/services/inspection/reflection/mock}/__init__.py +0 -0
- /tests/{support/inspection/fakes → services/inspection/reflection/mock}/fake_reflect_instance.py +0 -0
- {orionis/support/helpers → tests/support/standard}/__init__.py +0 -0
- {orionis/support/introspection → tests/support/wrapper}/__init__.py +0 -0
@@ -1,556 +0,0 @@
|
|
1
|
-
import abc
|
2
|
-
import ast
|
3
|
-
import inspect
|
4
|
-
import types
|
5
|
-
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, TypeVar
|
6
|
-
from orionis.services.introspection.abstracts.entities.abstract_class_attributes import AbstractClassAttributes
|
7
|
-
|
8
|
-
ABC = TypeVar('ABC', bound=abc.ABC)
|
9
|
-
|
10
|
-
class ReflexionAbstract:
|
11
|
-
"""A reflection object encapsulating an abstract class.
|
12
|
-
|
13
|
-
Parameters
|
14
|
-
----------
|
15
|
-
abstract : Type[ABC]
|
16
|
-
The abstract class being reflected upon
|
17
|
-
|
18
|
-
Attributes
|
19
|
-
----------
|
20
|
-
_abstract : Type[ABC]
|
21
|
-
The encapsulated abstract class
|
22
|
-
"""
|
23
|
-
|
24
|
-
def __init__(self, abstract: Type[ABC]) -> None:
|
25
|
-
"""Initialize with the abstract class."""
|
26
|
-
self._abstract = abstract
|
27
|
-
|
28
|
-
def parse(self) -> None:
|
29
|
-
pass
|
30
|
-
|
31
|
-
def getClassName(self) -> str:
|
32
|
-
"""
|
33
|
-
Get the name of the abstract class.
|
34
|
-
|
35
|
-
Returns
|
36
|
-
-------
|
37
|
-
str
|
38
|
-
The name of the abstract class
|
39
|
-
"""
|
40
|
-
return self._abstract.__name__
|
41
|
-
|
42
|
-
def getClass(self) -> RuntimeError:
|
43
|
-
"""
|
44
|
-
Retrieve the class of the abstract base class.
|
45
|
-
This method is intended to be overridden in subclasses to provide
|
46
|
-
the actual abstract class. By default, it raises a RuntimeError
|
47
|
-
since abstract classes cannot be instantiated directly.
|
48
|
-
The abstract base class itself.
|
49
|
-
Raises
|
50
|
-
------
|
51
|
-
RuntimeError
|
52
|
-
If called directly on the abstract class.
|
53
|
-
"""
|
54
|
-
raise RuntimeError("Cannot instantiate an abstract class.")
|
55
|
-
|
56
|
-
def getModuleName(self) -> str:
|
57
|
-
"""
|
58
|
-
Get the name of the module where the abstract class is defined.
|
59
|
-
|
60
|
-
Returns
|
61
|
-
-------
|
62
|
-
str
|
63
|
-
The module name
|
64
|
-
"""
|
65
|
-
return self._abstract.__module__
|
66
|
-
|
67
|
-
def getAllAttributes(self) -> AbstractClassAttributes:
|
68
|
-
"""
|
69
|
-
Get all attributes of the abstract class.
|
70
|
-
|
71
|
-
Returns
|
72
|
-
-------
|
73
|
-
Dict[str, Any]
|
74
|
-
Dictionary of attribute names and their values
|
75
|
-
"""
|
76
|
-
attributes = {
|
77
|
-
name: value for name, value in vars(self._abstract).items()
|
78
|
-
if not callable(value) and not isinstance(value, (staticmethod, classmethod, property))
|
79
|
-
and not isinstance(value, types.MemberDescriptorType)
|
80
|
-
}
|
81
|
-
class_name = self.getClassName()
|
82
|
-
public = {}
|
83
|
-
private = {}
|
84
|
-
protected = {}
|
85
|
-
|
86
|
-
for attr, value in attributes.items():
|
87
|
-
if (str(attr).startswith("__") and str(attr).endswith("__")) or str(attr).startswith("_abc_"):
|
88
|
-
continue
|
89
|
-
if str(attr).startswith("_") and not str(attr).startswith("__") and not str(attr).startswith(f"_{class_name}"):
|
90
|
-
protected[attr] = value
|
91
|
-
elif str(attr).startswith(f"_{class_name}"):
|
92
|
-
private[str(attr).replace(f"_{class_name}", "")] = value
|
93
|
-
else:
|
94
|
-
public[attr] = value
|
95
|
-
|
96
|
-
return AbstractClassAttributes(
|
97
|
-
public=public,
|
98
|
-
private=private,
|
99
|
-
protected=protected
|
100
|
-
)
|
101
|
-
|
102
|
-
def getAttributes(self) -> Dict[str, Any]:
|
103
|
-
"""
|
104
|
-
Get all attributes of the instance.
|
105
|
-
|
106
|
-
Returns
|
107
|
-
-------
|
108
|
-
Dict[str, Any]
|
109
|
-
Dictionary of attribute names and their values
|
110
|
-
"""
|
111
|
-
attr = self.getAllAttributes()
|
112
|
-
return {**attr.public, **attr.private, **attr.protected}
|
113
|
-
|
114
|
-
def getPublicAttributes(self) -> Dict[str, Any]:
|
115
|
-
"""
|
116
|
-
Get all public attributes of the instance.
|
117
|
-
|
118
|
-
Returns
|
119
|
-
-------
|
120
|
-
Dict[str, Any]
|
121
|
-
Dictionary of public attribute names and their values
|
122
|
-
"""
|
123
|
-
attr = self.getAllAttributes()
|
124
|
-
return attr.public
|
125
|
-
|
126
|
-
def getPrivateAttributes(self) -> Dict[str, Any]:
|
127
|
-
"""
|
128
|
-
Get all private attributes of the instance.
|
129
|
-
|
130
|
-
Returns
|
131
|
-
-------
|
132
|
-
Dict[str, Any]
|
133
|
-
Dictionary of private attribute names and their values
|
134
|
-
"""
|
135
|
-
attr = self.getAllAttributes()
|
136
|
-
return attr.private
|
137
|
-
|
138
|
-
def getProtectedAttributes(self) -> Dict[str, Any]:
|
139
|
-
"""
|
140
|
-
Get all Protected attributes of the instance.
|
141
|
-
|
142
|
-
Returns
|
143
|
-
-------
|
144
|
-
Dict[str, Any]
|
145
|
-
Dictionary of Protected attribute names and their values
|
146
|
-
"""
|
147
|
-
attr = self.getAllAttributes()
|
148
|
-
return attr.protected
|
149
|
-
|
150
|
-
def getAllMethods(self) -> Dict[str, List[str]]:
|
151
|
-
"""
|
152
|
-
Categorize all methods and relevant members of the abstract class into public, private, protected,
|
153
|
-
static, asynchronous, synchronous, class methods, magic methods, abstract methods, and properties.
|
154
|
-
|
155
|
-
Returns
|
156
|
-
-------
|
157
|
-
Dict[str, List[str]]
|
158
|
-
A dictionary categorizing methods and attributes into various types.
|
159
|
-
"""
|
160
|
-
class_name = self.getClassName()
|
161
|
-
private_prefix = f"_{class_name}"
|
162
|
-
attributes = set(self.getAttributes().keys()) | {attr for attr in dir(self._abstract) if attr.startswith('_abc_')}
|
163
|
-
|
164
|
-
result = {
|
165
|
-
"public": [],
|
166
|
-
"private": [],
|
167
|
-
"protected": [],
|
168
|
-
"static": [],
|
169
|
-
"asynchronous": [],
|
170
|
-
"synchronous": [],
|
171
|
-
"class_methods": [],
|
172
|
-
"asynchronous_static": [],
|
173
|
-
"synchronous_static": [],
|
174
|
-
"magic": [],
|
175
|
-
"abstract": [],
|
176
|
-
"abstract_class_methods": [],
|
177
|
-
"abstract_static_methods": []
|
178
|
-
}
|
179
|
-
|
180
|
-
# Precompute all members once
|
181
|
-
members = inspect.getmembers(self._abstract)
|
182
|
-
static_attrs = {}
|
183
|
-
|
184
|
-
# First pass to collect all relevant information
|
185
|
-
for name, attr in members:
|
186
|
-
if name in attributes:
|
187
|
-
continue
|
188
|
-
|
189
|
-
# Get static attribute once
|
190
|
-
static_attr = inspect.getattr_static(self._abstract, name)
|
191
|
-
static_attrs[name] = static_attr
|
192
|
-
|
193
|
-
# Magic methods
|
194
|
-
if name.startswith("__") and name.endswith("__"):
|
195
|
-
result["magic"].append(name)
|
196
|
-
continue
|
197
|
-
|
198
|
-
# Static and class methods
|
199
|
-
if isinstance(static_attr, staticmethod):
|
200
|
-
result["static"].append(name)
|
201
|
-
elif isinstance(static_attr, classmethod):
|
202
|
-
result["class_methods"].append(name)
|
203
|
-
|
204
|
-
# Private, protected, public
|
205
|
-
if name.startswith(private_prefix):
|
206
|
-
clean_name = name.replace(private_prefix, "")
|
207
|
-
result["private"].append(clean_name)
|
208
|
-
elif name.startswith("_"):
|
209
|
-
result["protected"].append(name)
|
210
|
-
else:
|
211
|
-
result["public"].append(name)
|
212
|
-
|
213
|
-
# Async methods
|
214
|
-
if inspect.iscoroutinefunction(attr):
|
215
|
-
clean_name = name.replace(private_prefix, "")
|
216
|
-
if name in result["static"]:
|
217
|
-
result["asynchronous_static"].append(clean_name)
|
218
|
-
else:
|
219
|
-
result["asynchronous"].append(clean_name)
|
220
|
-
|
221
|
-
# Second pass for synchronous methods (needs info from first pass)
|
222
|
-
for name, attr in members:
|
223
|
-
if name in attributes or name in result["magic"] or name in result["class_methods"] or name in result["static"]:
|
224
|
-
continue
|
225
|
-
|
226
|
-
if inspect.isfunction(attr):
|
227
|
-
clean_name = name.replace(private_prefix, "")
|
228
|
-
if clean_name not in result["asynchronous"]:
|
229
|
-
result["synchronous"].append(clean_name)
|
230
|
-
|
231
|
-
# Synchronous static methods
|
232
|
-
for name in result["static"]:
|
233
|
-
if name not in attributes and name not in result["asynchronous_static"] and name not in result["class_methods"]:
|
234
|
-
result["synchronous_static"].append(name)
|
235
|
-
|
236
|
-
# Abstract methods
|
237
|
-
abstract_methods = getattr(self._abstract, "__abstractmethods__", set())
|
238
|
-
for name in abstract_methods:
|
239
|
-
if name in attributes:
|
240
|
-
continue
|
241
|
-
|
242
|
-
static_attr = static_attrs.get(name, inspect.getattr_static(self._abstract, name))
|
243
|
-
if isinstance(static_attr, staticmethod):
|
244
|
-
result["abstract_static_methods"].append(name)
|
245
|
-
elif isinstance(static_attr, classmethod):
|
246
|
-
result["abstract_class_methods"].append(name)
|
247
|
-
elif not isinstance(static_attr, property):
|
248
|
-
result["abstract"].append(name)
|
249
|
-
|
250
|
-
return result
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
def getAbstractProperties(self) -> Set[str]:
|
265
|
-
"""Get all abstract property names required by the class.
|
266
|
-
|
267
|
-
Returns
|
268
|
-
-------
|
269
|
-
Set[str]
|
270
|
-
Set of abstract property names
|
271
|
-
"""
|
272
|
-
properties = []
|
273
|
-
for name in getattr(self._abstract, '__abstractmethods__', set()):
|
274
|
-
attr = getattr(self._abstract, name, None)
|
275
|
-
if isinstance(attr, property):
|
276
|
-
properties.append(name)
|
277
|
-
return set(properties)
|
278
|
-
|
279
|
-
def getConcreteMethods(self) -> Dict[str, Callable]:
|
280
|
-
"""Get all concrete methods implemented in the abstract class.
|
281
|
-
|
282
|
-
Returns
|
283
|
-
-------
|
284
|
-
Dict[str, Callable]
|
285
|
-
Dictionary of method names and their implementations
|
286
|
-
"""
|
287
|
-
return {
|
288
|
-
name: member for name, member in inspect.getmembers(
|
289
|
-
self._abstract,
|
290
|
-
predicate=inspect.isfunction
|
291
|
-
) if not name.startswith('_') and name not in self.getAbstractMethods()
|
292
|
-
}
|
293
|
-
|
294
|
-
def getStaticMethods(self) -> List[str]:
|
295
|
-
"""Get all static method names of the abstract class.
|
296
|
-
|
297
|
-
Returns
|
298
|
-
-------
|
299
|
-
List[str]
|
300
|
-
List of static method names
|
301
|
-
"""
|
302
|
-
return [
|
303
|
-
name for name in dir( self._abstract)
|
304
|
-
if not name.startswith('_') and
|
305
|
-
isinstance(inspect.getattr_static( self._abstract, name), staticmethod)
|
306
|
-
]
|
307
|
-
|
308
|
-
def getClassMethods(self) -> List[str]:
|
309
|
-
"""Get all class method names of the abstract class.
|
310
|
-
|
311
|
-
Returns
|
312
|
-
-------
|
313
|
-
List[str]
|
314
|
-
List of class method names, excluding private/protected methods (starting with '_')
|
315
|
-
|
316
|
-
Notes
|
317
|
-
-----
|
318
|
-
- Uses inspect.getattr_static to avoid method binding
|
319
|
-
- Properly handles both @classmethod decorator and classmethod instances
|
320
|
-
- Filters out private/protected methods (starting with '_')
|
321
|
-
|
322
|
-
Examples
|
323
|
-
--------
|
324
|
-
>>> class MyAbstract(ABC):
|
325
|
-
... @classmethod
|
326
|
-
... def factory(cls): pass
|
327
|
-
... @classmethod
|
328
|
-
... def _protected_factory(cls): pass
|
329
|
-
>>> reflex = ReflexionAbstract(MyAbstract)
|
330
|
-
>>> reflex.getClassMethods()
|
331
|
-
['factory']
|
332
|
-
"""
|
333
|
-
return [
|
334
|
-
name for name in dir(self._abstract)
|
335
|
-
if not name.startswith('_') and
|
336
|
-
isinstance(
|
337
|
-
inspect.getattr_static(self._abstract, name),
|
338
|
-
(classmethod, types.MethodType)
|
339
|
-
)
|
340
|
-
]
|
341
|
-
|
342
|
-
def getProperties(self) -> List[str]:
|
343
|
-
"""Get all property names of the abstract class.
|
344
|
-
|
345
|
-
Returns
|
346
|
-
-------
|
347
|
-
List[str]
|
348
|
-
List of property names
|
349
|
-
"""
|
350
|
-
return [
|
351
|
-
name for name, member in inspect.getmembers(
|
352
|
-
self._abstract,
|
353
|
-
predicate=lambda x: isinstance(x, property))
|
354
|
-
if not name.startswith('_')
|
355
|
-
]
|
356
|
-
|
357
|
-
def getMethodSignature(self, methodName: str) -> inspect.Signature:
|
358
|
-
"""Get the signature of a method.
|
359
|
-
|
360
|
-
Parameters
|
361
|
-
----------
|
362
|
-
methodName : str
|
363
|
-
Name of the method
|
364
|
-
|
365
|
-
Returns
|
366
|
-
-------
|
367
|
-
inspect.Signature
|
368
|
-
The method signature
|
369
|
-
|
370
|
-
Raises
|
371
|
-
------
|
372
|
-
AttributeError
|
373
|
-
If the method doesn't exist
|
374
|
-
"""
|
375
|
-
method = getattr(self._abstract, methodName)
|
376
|
-
if callable(method):
|
377
|
-
return inspect.signature(method)
|
378
|
-
|
379
|
-
def getPropertySignature(self, propertyName: str) -> inspect.Signature:
|
380
|
-
"""Get the signature of an abstract property's getter.
|
381
|
-
|
382
|
-
Parameters
|
383
|
-
----------
|
384
|
-
propertyName : str
|
385
|
-
Name of the abstract property
|
386
|
-
|
387
|
-
Returns
|
388
|
-
-------
|
389
|
-
inspect.Signature
|
390
|
-
The getter signature of the abstract property
|
391
|
-
|
392
|
-
Raises
|
393
|
-
------
|
394
|
-
AttributeError
|
395
|
-
If the property doesn't exist or is not an abstract property
|
396
|
-
"""
|
397
|
-
attr = getattr(self._abstract, propertyName, None)
|
398
|
-
if isinstance(attr, property) and attr.fget is not None:
|
399
|
-
return inspect.signature(attr.fget)
|
400
|
-
raise AttributeError(f"{propertyName} is not an abstract property or doesn't have a getter.")
|
401
|
-
|
402
|
-
def getDocstring(self) -> Optional[str]:
|
403
|
-
"""Get the docstring of the abstract class.
|
404
|
-
|
405
|
-
Returns
|
406
|
-
-------
|
407
|
-
Optional[str]
|
408
|
-
The class docstring
|
409
|
-
"""
|
410
|
-
return self._abstract.__doc__
|
411
|
-
|
412
|
-
def getBaseAbstractClasses(self) -> Tuple[Type[ABC], ...]:
|
413
|
-
"""Get the abstract base classes.
|
414
|
-
|
415
|
-
Returns
|
416
|
-
-------
|
417
|
-
Tuple[Type[ABC], ...]
|
418
|
-
Tuple of abstract base classes
|
419
|
-
"""
|
420
|
-
return tuple(
|
421
|
-
base for base in self._abstract.__bases__
|
422
|
-
if inspect.isabstract(base) or issubclass(base, abc.ABC) or isinstance(base, abc.ABCMeta)
|
423
|
-
)
|
424
|
-
|
425
|
-
def getInterfaceMethods(self) -> Dict[str, inspect.Signature]:
|
426
|
-
"""Get all abstract methods with their signatures.
|
427
|
-
|
428
|
-
Returns
|
429
|
-
-------
|
430
|
-
Dict[str, inspect.Signature]
|
431
|
-
Dictionary of method names and their signatures
|
432
|
-
"""
|
433
|
-
return {
|
434
|
-
name: inspect.signature(getattr(self._abstract, name))
|
435
|
-
for name in self.getAbstractMethods()
|
436
|
-
}
|
437
|
-
|
438
|
-
def isSubclassOf(self, abstract_class: Type[ABC]) -> bool:
|
439
|
-
"""Check if the abstract class inherits from another abstract class.
|
440
|
-
|
441
|
-
Parameters
|
442
|
-
----------
|
443
|
-
abstract_class : Type[ABC]
|
444
|
-
The abstract class to check against
|
445
|
-
|
446
|
-
Returns
|
447
|
-
-------
|
448
|
-
bool
|
449
|
-
True if this is a subclass
|
450
|
-
"""
|
451
|
-
return issubclass(self._abstract, abstract_class)
|
452
|
-
|
453
|
-
def getSourceCode(self) -> Optional[str]:
|
454
|
-
"""Get the source code of the abstract class.
|
455
|
-
|
456
|
-
Returns
|
457
|
-
-------
|
458
|
-
Optional[str]
|
459
|
-
The source code if available
|
460
|
-
"""
|
461
|
-
try:
|
462
|
-
return inspect.getsource(self._abstract)
|
463
|
-
except (TypeError, OSError):
|
464
|
-
return None
|
465
|
-
|
466
|
-
def getFileLocation(self) -> Optional[str]:
|
467
|
-
"""Get the file location where the abstract class is defined.
|
468
|
-
|
469
|
-
Returns
|
470
|
-
-------
|
471
|
-
Optional[str]
|
472
|
-
The file path if available
|
473
|
-
"""
|
474
|
-
try:
|
475
|
-
return inspect.getfile(self._abstract)
|
476
|
-
except (TypeError, OSError):
|
477
|
-
return None
|
478
|
-
|
479
|
-
def getAnnotations(self) -> Dict[str, Any]:
|
480
|
-
"""Get type annotations of the abstract class.
|
481
|
-
|
482
|
-
Returns
|
483
|
-
-------
|
484
|
-
Dict[str, Any]
|
485
|
-
Dictionary of attribute names and their type annotations
|
486
|
-
"""
|
487
|
-
return self._abstract.__annotations__
|
488
|
-
|
489
|
-
def getDecorators(self, method_name: str) -> List[str]:
|
490
|
-
"""
|
491
|
-
Get decorators applied to a method.
|
492
|
-
|
493
|
-
Parameters
|
494
|
-
----------
|
495
|
-
method_name : str
|
496
|
-
Name of the method to inspect
|
497
|
-
"""
|
498
|
-
method = getattr(self._abstract, method_name, None)
|
499
|
-
if method is None:
|
500
|
-
return []
|
501
|
-
|
502
|
-
try:
|
503
|
-
source = inspect.getsource(self._abstract)
|
504
|
-
except (OSError, TypeError):
|
505
|
-
return []
|
506
|
-
|
507
|
-
tree = ast.parse(source)
|
508
|
-
|
509
|
-
class DecoratorVisitor(ast.NodeVisitor):
|
510
|
-
def __init__(self):
|
511
|
-
self.decorators = []
|
512
|
-
|
513
|
-
def visit_FunctionDef(self, node):
|
514
|
-
if node.name == method_name:
|
515
|
-
for deco in node.decorator_list:
|
516
|
-
if isinstance(deco, ast.Name):
|
517
|
-
self.decorators.append(deco.id)
|
518
|
-
elif isinstance(deco, ast.Call):
|
519
|
-
# handles decorators with arguments like @deco(arg)
|
520
|
-
if isinstance(deco.func, ast.Name):
|
521
|
-
self.decorators.append(deco.func.id)
|
522
|
-
elif isinstance(deco, ast.Attribute):
|
523
|
-
self.decorators.append(deco.attr)
|
524
|
-
# No need to visit deeper
|
525
|
-
return
|
526
|
-
|
527
|
-
visitor = DecoratorVisitor()
|
528
|
-
visitor.visit(tree)
|
529
|
-
|
530
|
-
return visitor.decorators
|
531
|
-
|
532
|
-
def isProtocol(self) -> bool:
|
533
|
-
"""Check if the abstract class is a Protocol.
|
534
|
-
|
535
|
-
Returns
|
536
|
-
-------
|
537
|
-
bool
|
538
|
-
True if this is a Protocol class
|
539
|
-
"""
|
540
|
-
return hasattr(self._abstract, '_is_protocol') and self._abstract._is_protocol
|
541
|
-
|
542
|
-
def getRequiredAttributes(self) -> Set[str]:
|
543
|
-
"""For Protocol classes, get required attributes.
|
544
|
-
|
545
|
-
Returns
|
546
|
-
-------
|
547
|
-
Set[str]
|
548
|
-
Set of required attribute names
|
549
|
-
"""
|
550
|
-
if not self.isProtocol():
|
551
|
-
return set()
|
552
|
-
|
553
|
-
return {
|
554
|
-
name for name in dir(self._abstract)
|
555
|
-
if not name.startswith('_') and not inspect.isfunction(getattr(self._abstract, name))
|
556
|
-
}
|