orionis 0.296.0__py3-none-any.whl → 0.298.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 (26) hide show
  1. orionis/metadata/framework.py +1 -1
  2. orionis/services/introspection/exceptions/reflection_attribute_error.py +26 -0
  3. orionis/services/introspection/instances/entities/class_property.py +24 -0
  4. orionis/services/introspection/instances/reflection_instance.py +1436 -0
  5. orionis/support/abstracts/entities/__init__.py +0 -0
  6. orionis/support/helpers/__init__.py +0 -0
  7. orionis/support/introspection/instances/contracts/reflection_instance.py +1 -1
  8. orionis/support/introspection/instances/reflection_instance.py +1 -1
  9. {orionis-0.296.0.dist-info → orionis-0.298.0.dist-info}/METADATA +1 -1
  10. {orionis-0.296.0.dist-info → orionis-0.298.0.dist-info}/RECORD +22 -21
  11. tests/support/inspection/fakes/fake_reflect_instance.py +56 -2
  12. orionis/support/introspection/instances/entities/class_attributes.py +0 -11
  13. orionis/support/introspection/instances/entities/class_method.py +0 -18
  14. orionis/support/introspection/instances/entities/class_parsed.py +0 -18
  15. orionis/support/introspection/instances/entities/class_property.py +0 -13
  16. /orionis/services/introspection/{abstracts → instances}/__init__.py +0 -0
  17. /orionis/services/introspection/{abstracts → instances}/entities/__init__.py +0 -0
  18. /orionis/{services/introspection/helpers → support/abstracts}/__init__.py +0 -0
  19. /orionis/{services/introspection → support}/abstracts/entities/abstract_class_attributes.py +0 -0
  20. /orionis/{services/introspection → support}/abstracts/reflect_abstract.py +0 -0
  21. /orionis/{services/introspection → support}/helpers/functions.py +0 -0
  22. /orionis/{services/introspection → support}/reflection.py +0 -0
  23. {orionis-0.296.0.dist-info → orionis-0.298.0.dist-info}/WHEEL +0 -0
  24. {orionis-0.296.0.dist-info → orionis-0.298.0.dist-info}/licenses/LICENCE +0 -0
  25. {orionis-0.296.0.dist-info → orionis-0.298.0.dist-info}/top_level.txt +0 -0
  26. {orionis-0.296.0.dist-info → orionis-0.298.0.dist-info}/zip-safe +0 -0
@@ -0,0 +1,1436 @@
1
+ import inspect
2
+ import keyword
3
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Type
4
+ from orionis.services.asynchrony.coroutines import Coroutine
5
+ from orionis.services.introspection.dependencies.entities.class_dependencies import ClassDependency
6
+ from orionis.services.introspection.dependencies.entities.method_dependencies import MethodDependency
7
+ from orionis.services.introspection.dependencies.reflect_dependencies import ReflectDependencies
8
+ from orionis.services.introspection.exceptions.reflection_attribute_error import ReflectionAttributeError
9
+ from orionis.services.introspection.exceptions.reflection_type_error import ReflectionTypeError
10
+ from orionis.services.introspection.exceptions.reflection_value_error import ReflectionValueError
11
+ from orionis.services.introspection.instances.entities.class_property import ClassProperty
12
+
13
+ class ReflectionInstance:
14
+
15
+ def __init__(self, instance: Any) -> None:
16
+ """
17
+ Initialize the ReflectionInstance with a given object instance.
18
+
19
+ Parameters
20
+ ----------
21
+ instance : Any
22
+ The object instance to be reflected upon.
23
+
24
+ Raises
25
+ ------
26
+ ReflectionTypeError
27
+ If the provided instance is not a valid object instance.
28
+ ReflectionValueError
29
+ If the instance belongs to a built-in, abstract base class, or '__main__' module.
30
+ """
31
+ if not (isinstance(instance, object) and not isinstance(instance, type)):
32
+ raise ReflectionTypeError(
33
+ f"Expected an object instance, got {type(instance).__name__!r}: {instance!r}"
34
+ )
35
+ module = type(instance).__module__
36
+ if module in {'builtins', 'abc'}:
37
+ raise ReflectionValueError(
38
+ f"Instance of type '{type(instance).__name__}' belongs to disallowed module '{module}'."
39
+ )
40
+ if module == '__main__':
41
+ raise ReflectionValueError(
42
+ "Instance originates from '__main__'; please provide an instance from an importable module."
43
+ )
44
+ self._instance = instance
45
+
46
+ def getClass(self) -> Type:
47
+ """
48
+ Get the class of the instance.
49
+
50
+ Returns
51
+ -------
52
+ Type
53
+ The class object of the instance
54
+ """
55
+ return self._instance.__class__
56
+
57
+ def getClassName(self) -> str:
58
+ """
59
+ Get the name of the instance's class.
60
+
61
+ Returns
62
+ -------
63
+ str
64
+ The name of the class
65
+ """
66
+ return self._instance.__class__.__name__
67
+
68
+ def getModuleName(self) -> str:
69
+ """
70
+ Get the name of the module where the class is defined.
71
+
72
+ Returns
73
+ -------
74
+ str
75
+ The module name
76
+ """
77
+ return self._instance.__class__.__module__
78
+
79
+ def getModuleWithClassName(self) -> str:
80
+ """
81
+ Get the name of the module where the class is defined.
82
+
83
+ Returns
84
+ -------
85
+ str
86
+ The module name
87
+ """
88
+ return f"{self.getModuleName()}.{self.getClassName()}"
89
+
90
+ def getDocstring(self) -> Optional[str]:
91
+ """
92
+ Get the docstring of the instance's class.
93
+
94
+ Returns
95
+ -------
96
+ Optional[str]
97
+ The class docstring, or None if not available
98
+ """
99
+ return self._instance.__class__.__doc__
100
+
101
+ def getBaseClasses(self) -> Tuple[Type, ...]:
102
+ """
103
+ Get the base classes of the instance's class.
104
+
105
+ Returns
106
+ -------
107
+ Tuple[Type, ...]
108
+ Tuple of base classes
109
+ """
110
+ return self._instance.__class__.__bases__
111
+
112
+ def getSourceCode(self) -> Optional[str]:
113
+ """
114
+ Get the source code of the instance's class.
115
+
116
+ Returns
117
+ -------
118
+ Optional[str]
119
+ The source code if available, None otherwise
120
+ """
121
+ try:
122
+ return inspect.getsource(self._instance.__class__)
123
+ except (TypeError, OSError):
124
+ return None
125
+
126
+ def getFile(self) -> Optional[str]:
127
+ """
128
+ Get the file location where the class is defined.
129
+
130
+ Returns
131
+ -------
132
+ Optional[str]
133
+ The file path if available, None otherwise
134
+ """
135
+ try:
136
+ return inspect.getfile(self._instance.__class__)
137
+ except (TypeError, OSError):
138
+ return None
139
+
140
+ def getAnnotations(self) -> Dict[str, Any]:
141
+ """
142
+ Get type annotations of the class.
143
+
144
+ Returns
145
+ -------
146
+ Dict[str, Any]
147
+ Dictionary of attribute names and their type annotations
148
+ """
149
+ return self._instance.__class__.__annotations__
150
+
151
+ def hasAttribute(self, name: str) -> bool:
152
+ """
153
+ Check if the instance has a specific attribute.
154
+
155
+ Parameters
156
+ ----------
157
+ name : str
158
+ The attribute name to check
159
+
160
+ Returns
161
+ -------
162
+ bool
163
+ True if the attribute exists
164
+ """
165
+ return name in self.getAttributes()
166
+
167
+ def getAttribute(self, name: str) -> Any:
168
+ """
169
+ Get an attribute value by name.
170
+
171
+ Parameters
172
+ ----------
173
+ name : str
174
+ The attribute name
175
+
176
+ Returns
177
+ -------
178
+ Any
179
+ The attribute value
180
+
181
+ Raises
182
+ ------
183
+ AttributeError
184
+ If the attribute doesn't exist
185
+ """
186
+ attrs = self.getAttributes()
187
+ return attrs.get(name, None)
188
+
189
+ def setAttribute(self, name: str, value: Any) -> bool:
190
+ """
191
+ Set an attribute value.
192
+
193
+ Parameters
194
+ ----------
195
+ name : str
196
+ The attribute name
197
+ value : Any
198
+ The value to set
199
+
200
+ Raises
201
+ ------
202
+ ReflectionAttributeError
203
+ If the attribute is read-only
204
+ """
205
+ # Ensure the name is a valid attr name with regular expression
206
+ if not isinstance(name, str) or not name.isidentifier() or keyword.iskeyword(name):
207
+ raise ReflectionAttributeError(f"Invalid method name '{name}'. Must be a valid Python identifier and not a keyword.")
208
+
209
+ # Ensure the value is not callable
210
+ if callable(value):
211
+ raise ReflectionAttributeError(f"Cannot set attribute '{name}' to a callable. Use setMacro instead.")
212
+
213
+ # Handle private attribute name mangling
214
+ if name.startswith("__") and not name.endswith("__"):
215
+ class_name = self.getClassName()
216
+ name = f"_{class_name}{name}"
217
+
218
+ # Check if the attribute already exists
219
+ setattr(self._instance, name, value)
220
+
221
+ # Return True
222
+ return True
223
+
224
+ def removeAttribute(self, name: str) -> bool:
225
+ """
226
+ Remove an attribute from the instance.
227
+
228
+ Parameters
229
+ ----------
230
+ name : str
231
+ The attribute name to remove
232
+
233
+ Raises
234
+ ------
235
+ ReflectionAttributeError
236
+ If the attribute doesn't exist or is read-only
237
+ """
238
+ if self.getAttribute(name) is None:
239
+ raise ReflectionAttributeError(f"'{self.getClassName()}' object has no attribute '{name}'.")
240
+ if name.startswith("__") and not name.endswith("__"):
241
+ class_name = self.getClassName()
242
+ name = f"_{class_name}{name}"
243
+ delattr(self._instance, name)
244
+ return True
245
+
246
+ def getAttributes(self) -> Dict[str, Any]:
247
+ """
248
+ Get all attributes of the instance, including public, private, protected, and dunder attributes.
249
+
250
+ Returns
251
+ -------
252
+ Dict[str, Any]
253
+ Dictionary of all attribute names and their values
254
+ """
255
+ return {
256
+ **self.getPublicAttributes(),
257
+ **self.getProtectedAttributes(),
258
+ **self.getPrivateAttributes(),
259
+ **self.getDunderAttributes()
260
+ }
261
+
262
+ def getPublicAttributes(self) -> Dict[str, Any]:
263
+ """
264
+ Get all public attributes of the instance.
265
+
266
+ Returns
267
+ -------
268
+ Dict[str, Any]
269
+ Dictionary of public attribute names and their values
270
+ """
271
+ class_name = self.getClassName()
272
+ attributes = vars(self._instance)
273
+ public = {}
274
+
275
+ # Exclude dunder, protected, and private attributes
276
+ for attr, value in attributes.items():
277
+ if attr.startswith("__") and attr.endswith("__"):
278
+ continue
279
+ if attr.startswith(f"_{class_name}"):
280
+ continue
281
+ if attr.startswith("_"):
282
+ continue
283
+ public[attr] = value
284
+
285
+ return public
286
+
287
+ def getProtectedAttributes(self) -> Dict[str, Any]:
288
+ """
289
+ Get all Protected attributes of the instance.
290
+
291
+ Returns
292
+ -------
293
+ Dict[str, Any]
294
+ Dictionary of Protected attribute names and their values
295
+ """
296
+ class_name = self.getClassName()
297
+ attributes = vars(self._instance)
298
+ protected = {}
299
+
300
+ # Select protected attributes that start with a single underscore
301
+ for attr, value in attributes.items():
302
+ if attr.startswith("_") and not attr.startswith("__") and not attr.startswith(f"_{class_name}"):
303
+ protected[attr] = value
304
+
305
+ return protected
306
+
307
+ def getPrivateAttributes(self) -> Dict[str, Any]:
308
+ """
309
+ Get all private attributes of the instance.
310
+
311
+ Returns
312
+ -------
313
+ Dict[str, Any]
314
+ Dictionary of private attribute names and their values
315
+ """
316
+ class_name = self.getClassName()
317
+ attributes = vars(self._instance)
318
+ private = {}
319
+
320
+ # Select private attributes that start with the class name
321
+ for attr, value in attributes.items():
322
+ if attr.startswith(f"_{class_name}"):
323
+ private[str(attr).replace(f"_{class_name}", "")] = value
324
+
325
+ return private
326
+
327
+ def getDunderAttributes(self) -> Dict[str, Any]:
328
+ """
329
+ Get all dunder (double underscore) attributes of the instance.
330
+
331
+ Returns
332
+ -------
333
+ Dict[str, Any]
334
+ Dictionary of dunder attribute names and their values
335
+ """
336
+ attributes = vars(self._instance)
337
+ dunder = {}
338
+
339
+ # Select dunder attributes that start and end with double underscores
340
+ for attr, value in attributes.items():
341
+ if attr.startswith("__") and attr.endswith("__"):
342
+ dunder[attr] = value
343
+
344
+ return dunder
345
+
346
+ def getMagicAttributes(self) -> Dict[str, Any]:
347
+ """
348
+ Get all magic attributes of the instance.
349
+
350
+ Returns
351
+ -------
352
+ Dict[str, Any]
353
+ Dictionary of magic attribute names and their values
354
+ """
355
+ return self.getDunderAttributes()
356
+
357
+ def hasMethod(self, name: str) -> bool:
358
+ """
359
+ Check if the instance has a specific method.
360
+
361
+ Parameters
362
+ ----------
363
+ name : str
364
+ The method name to check
365
+
366
+ Returns
367
+ -------
368
+ bool
369
+ True if the method exists, False otherwise
370
+ """
371
+ # Handle private method name mangling
372
+ if name.startswith("__") and not name.endswith("__"):
373
+ class_name = self.getClassName()
374
+ name = f"_{class_name}{name}"
375
+
376
+ # Check if the method is callable
377
+ if hasattr(self._instance, name):
378
+ return callable(getattr(self._instance, name, None))
379
+
380
+ # Check if the method is a class method or static method
381
+ cls = self._instance.__class__
382
+ if hasattr(cls, name):
383
+ attr = inspect.getattr_static(cls, name)
384
+ return isinstance(attr, (classmethod, staticmethod)) and callable(attr.__func__)
385
+
386
+ # If not found, return False
387
+ return False
388
+
389
+ def callMethod(self, name: str, *args: Any, **kwargs: Any) -> Any:
390
+ """
391
+ Call a method on the instance.
392
+
393
+ Parameters
394
+ ----------
395
+ name : str
396
+ Name of the method to call
397
+ *args : Any
398
+ Positional arguments for the method
399
+ **kwargs : Any
400
+ Keyword arguments for the method
401
+
402
+ Returns
403
+ -------
404
+ Any
405
+ The result of the method call
406
+
407
+ Raises
408
+ ------
409
+ AttributeError
410
+ If the method does not exist on the instance
411
+ TypeError
412
+ If the method is not callable
413
+ """
414
+
415
+ # Hanlde private method name mangling
416
+ if name.startswith("__") and not name.endswith("__"):
417
+ class_name = self.getClassName()
418
+ name = f"_{class_name}{name}"
419
+
420
+ # Try to get the method from the instance
421
+ method = getattr(self._instance, name, None)
422
+
423
+ # If not found, try to get it from the class
424
+ if method is None:
425
+ cls = self._instance.__class__
426
+ method = getattr(cls, name, None)
427
+
428
+ # If still not found, raise an error
429
+ if method is None:
430
+ raise ReflectionAttributeError(f"Method '{name}' does not exist on '{self.getClassName()}'.")
431
+
432
+ # Check if the method is callable
433
+ if not callable(method):
434
+ raise ReflectionTypeError(f"'{name}' is not callable on '{self.getClassName()}'.")
435
+
436
+ # Check if method is coroutine function
437
+ if inspect.iscoroutinefunction(method):
438
+ return Coroutine(method(*args, **kwargs)).run()
439
+
440
+ # Call the method with provided arguments
441
+ return method(*args, **kwargs)
442
+
443
+ def setMethod(self, name: str, value: Callable) -> bool:
444
+ """
445
+ Set a callable attribute value.
446
+
447
+ Parameters
448
+ ----------
449
+ name : str
450
+ The attribute name
451
+ value : Callable
452
+ The callable to set
453
+
454
+ Raises
455
+ ------
456
+ ReflectionAttributeError
457
+ If the attribute is not callable or already exists as a method
458
+ """
459
+ # Ensure the name is a valid method name with regular expression
460
+ if not isinstance(name, str) or not name.isidentifier() or keyword.iskeyword(name):
461
+ raise ReflectionAttributeError(f"Invalid method name '{name}'. Must be a valid Python identifier and not a keyword.")
462
+
463
+ # Ensure the value is callable
464
+ if not callable(value):
465
+ raise ReflectionAttributeError(f"Cannot set attribute '{name}' to a non-callable value.")
466
+
467
+ # Handle private method name mangling
468
+ if name.startswith("__") and not name.endswith("__"):
469
+ class_name = self.getClassName()
470
+ name = f"_{class_name}{name}"
471
+
472
+ # Check if the method already exists
473
+ if hasattr(self._instance, name):
474
+ raise ReflectionAttributeError(f"Attribute '{name}' already exists on '{self.getClassName()}'.")
475
+
476
+ # Set the method on the instance
477
+ setattr(self._instance, name, value)
478
+
479
+ # Return True
480
+ return True
481
+
482
+ def removeMethod(self, name: str) -> None:
483
+ """
484
+ Remove a method from the instance.
485
+
486
+ Parameters
487
+ ----------
488
+ name : str
489
+ The method name to remove
490
+
491
+ Raises
492
+ ------
493
+ ReflectionAttributeError
494
+ If the method does not exist or is not callable
495
+ """
496
+ if not self.hasMethod(name):
497
+ raise ReflectionAttributeError(f"Method '{name}' does not exist on '{self.getClassName()}'.")
498
+ delattr(self._instance, name)
499
+
500
+ def getMethodSignature(self, name: str) -> inspect.Signature:
501
+ """
502
+ Get the signature of a method.
503
+
504
+ Parameters
505
+ ----------
506
+ name : str
507
+ Name of the method
508
+
509
+ Returns
510
+ -------
511
+ inspect.Signature
512
+ The method signature
513
+ """
514
+
515
+ # Handle private method name mangling
516
+ if name.startswith("__") and not name.endswith("__"):
517
+ name = f"_{self.getClassName()}{name}"
518
+
519
+ # Check if the method exists and is callable
520
+ method = getattr(self._instance, name)
521
+ if callable(method):
522
+ return inspect.signature(method)
523
+
524
+ # If the method is not callable, raise an error
525
+ raise ReflectionAttributeError(f"Method '{name}' is not callable on '{self.getClassName()}'.")
526
+
527
+ def getPublicMethods(self) -> List[str]:
528
+ """
529
+ Get all public method names of the instance.
530
+
531
+ Returns
532
+ -------
533
+ List[str]
534
+ List of public method names
535
+ """
536
+ class_name = self.getClassName()
537
+ cls = self._instance.__class__
538
+ public_methods = []
539
+
540
+ # Gather all class methods to exclude them
541
+ class_methods = set()
542
+ for name in dir(cls):
543
+ attr = inspect.getattr_static(cls, name)
544
+ if isinstance(attr, classmethod):
545
+ class_methods.add(name)
546
+
547
+ # Collect public instance methods (not static, not class, not private/protected/magic)
548
+ for name, method in inspect.getmembers(self._instance, predicate=inspect.ismethod):
549
+ if (
550
+ name not in class_methods and
551
+ not (name.startswith("__") and name.endswith("__")) and
552
+ not name.startswith(f"_{class_name}") and
553
+ not (name.startswith("_") and not name.startswith(f"_{class_name}"))
554
+ ):
555
+ public_methods.append(name)
556
+
557
+ return public_methods
558
+
559
+ def getPublicSyncMethods(self) -> List[str]:
560
+ """
561
+ Get all public synchronous method names of the instance.
562
+
563
+ Returns
564
+ -------
565
+ List[str]
566
+ List of public synchronous method names
567
+ """
568
+ methods = self.getPublicMethods()
569
+ return [method for method in methods if not inspect.iscoroutinefunction(getattr(self._instance, method))]
570
+
571
+ def getPublicAsyncMethods(self) -> List[str]:
572
+ """
573
+ Get all public asynchronous method names of the instance.
574
+
575
+ Returns
576
+ -------
577
+ List[str]
578
+ List of public asynchronous method names
579
+ """
580
+ methods = self.getPublicMethods()
581
+ return [method for method in methods if inspect.iscoroutinefunction(getattr(self._instance, method))]
582
+
583
+ def getProtectedMethods(self) -> List[str]:
584
+ """
585
+ Get all protected method names of the instance.
586
+
587
+ Returns
588
+ -------
589
+ List[str]
590
+ List of protected method names
591
+ """
592
+ protected_methods = []
593
+
594
+ # Collect protected instance methods (starting with a single underscore)
595
+ for name, method in inspect.getmembers(self._instance, predicate=inspect.ismethod):
596
+ if name.startswith("_") and not name.startswith("__") and not name.startswith(f"_{self.getClassName()}"):
597
+ protected_methods.append(name)
598
+
599
+ return protected_methods
600
+
601
+ def getProtectedSyncMethods(self) -> List[str]:
602
+ """
603
+ Get all protected synchronous method names of the instance.
604
+
605
+ Returns
606
+ -------
607
+ List[str]
608
+ List of protected synchronous method names
609
+ """
610
+ methods = self.getProtectedMethods()
611
+ return [method for method in methods if not inspect.iscoroutinefunction(getattr(self._instance, method))]
612
+
613
+ def getProtectedAsyncMethods(self) -> List[str]:
614
+ """
615
+ Get all protected asynchronous method names of the instance.
616
+
617
+ Returns
618
+ -------
619
+ List[str]
620
+ List of protected asynchronous method names
621
+ """
622
+ methods = self.getProtectedMethods()
623
+ return [method for method in methods if inspect.iscoroutinefunction(getattr(self._instance, method))]
624
+
625
+ def getPrivateMethods(self) -> List[str]:
626
+ """
627
+ Get all private method names of the instance.
628
+
629
+ Returns
630
+ -------
631
+ List[str]
632
+ List of private method names
633
+ """
634
+ class_name = self.getClassName()
635
+ private_methods = []
636
+
637
+ # Collect private instance methods (starting with class name)
638
+ for name, method in inspect.getmembers(self._instance, predicate=inspect.ismethod):
639
+ if name.startswith(f"_{class_name}") and not name.startswith("__"):
640
+ private_methods.append(name.replace(f"_{class_name}", ""))
641
+
642
+ # Return private methods without the class name prefix
643
+ return private_methods
644
+
645
+ def getPrivateSyncMethods(self) -> List[str]:
646
+ """
647
+ Get all private synchronous method names of the instance.
648
+
649
+ Returns
650
+ -------
651
+ List[str]
652
+ List of private synchronous method names
653
+ """
654
+ class_name = self.getClassName()
655
+ private_methods = []
656
+ for name, method in inspect.getmembers(self._instance, predicate=inspect.ismethod):
657
+ if name.startswith(f"_{class_name}") and not name.startswith("__"):
658
+ # Remove the class name prefix for the returned name
659
+ short_name = name.replace(f"_{class_name}", "")
660
+ if not inspect.iscoroutinefunction(method):
661
+ private_methods.append(short_name)
662
+ return private_methods
663
+
664
+ def getPrivateAsyncMethods(self) -> List[str]:
665
+ """
666
+ Get all private asynchronous method names of the instance.
667
+
668
+ Returns
669
+ -------
670
+ List[str]
671
+ List of private asynchronous method names
672
+ """
673
+ class_name = self.getClassName()
674
+ private_methods = []
675
+ for name, method in inspect.getmembers(self._instance, predicate=inspect.ismethod):
676
+ if name.startswith(f"_{class_name}") and not name.startswith("__"):
677
+ # Remove the class name prefix for the returned name
678
+ short_name = name.replace(f"_{class_name}", "")
679
+ if inspect.iscoroutinefunction(method):
680
+ private_methods.append(short_name)
681
+ return private_methods
682
+
683
+
684
+
685
+
686
+
687
+
688
+
689
+
690
+
691
+
692
+
693
+
694
+
695
+
696
+
697
+ def getPublicClassMethods(self) -> List[str]:
698
+ """
699
+ Get all class method names of the instance.
700
+
701
+ Returns
702
+ -------
703
+ List[str]
704
+ List of class method names
705
+ """
706
+ cls = self._instance.__class__
707
+ class_methods = []
708
+
709
+ # Iterate over all attributes of the class
710
+ for name in dir(cls):
711
+
712
+ # Get the attribute using getattr_static to avoid triggering property getters
713
+ attr = inspect.getattr_static(cls, name)
714
+
715
+ # Check if the attribute is a class method
716
+ if isinstance(attr, classmethod):
717
+
718
+ # Check not private or protected methods
719
+ if not name.startswith(f"_"):
720
+ class_methods.append(name)
721
+
722
+ # Return the list of public class method
723
+ return class_methods
724
+
725
+ def getPublicClassSyncMethods(self) -> List[str]:
726
+ """
727
+ Get all public synchronous class method names of the instance.
728
+
729
+ Returns
730
+ -------
731
+ List[str]
732
+ List of public synchronous class method names
733
+ """
734
+ class_name = self.getClassName()
735
+ cls = self._instance.__class__
736
+ public_class_sync_methods = []
737
+
738
+ # Iterate over all attributes of the class
739
+ for name in dir(cls):
740
+
741
+ # Get the attribute using getattr_static to avoid triggering property getters
742
+ attr = inspect.getattr_static(cls, name)
743
+
744
+ # Check if the attribute is a class method
745
+ if isinstance(attr, classmethod):
746
+
747
+ # Get the underlying function
748
+ func = attr.__func__
749
+
750
+ # Check if it's NOT a coroutine function (i.e., synchronous)
751
+ if not inspect.iscoroutinefunction(func) and not name.startswith(f"_"):
752
+ public_class_sync_methods.append(str(name).replace(f"_{class_name}", ""))
753
+
754
+ # Return the list of public synchronous class method names
755
+ return public_class_sync_methods
756
+
757
+ def getPublicClassAsyncMethods(self) -> List[str]:
758
+ """
759
+ Get all public asynchronous class method names of the instance.
760
+
761
+ Returns
762
+ -------
763
+ List[str]
764
+ List of public asynchronous class method names
765
+ """
766
+ class_name = self.getClassName()
767
+ cls = self._instance.__class__
768
+ public_class_async_methods = []
769
+
770
+ # Iterate over all attributes of the class
771
+ for name in dir(cls):
772
+
773
+ # Get the attribute using getattr_static to avoid triggering property getters
774
+ attr = inspect.getattr_static(cls, name)
775
+
776
+ # Check if the attribute is a class method
777
+ if isinstance(attr, classmethod):
778
+
779
+ # Get the underlying function
780
+ func = attr.__func__
781
+
782
+ # Check if it's a coroutine function (i.e., asynchronous)
783
+ if inspect.iscoroutinefunction(func) and not name.startswith(f"_"):
784
+ public_class_async_methods.append(str(name).replace(f"_{class_name}", ""))
785
+
786
+ # Return the list of public asynchronous class method names
787
+ return public_class_async_methods
788
+
789
+
790
+
791
+
792
+
793
+
794
+
795
+
796
+
797
+
798
+
799
+
800
+
801
+
802
+
803
+
804
+
805
+
806
+
807
+
808
+
809
+
810
+
811
+
812
+
813
+
814
+
815
+
816
+ def getProtectedClassMethods(self) -> List[str]:
817
+ """
818
+ Get all protected class method names of the instance.
819
+
820
+ Returns
821
+ -------
822
+ List[str]
823
+ List of protected class method names
824
+ """
825
+ class_name = self.getClassName()
826
+ cls = self._instance.__class__
827
+ protected_class_methods = []
828
+
829
+ # Iterate over all attributes of the class
830
+ for name in dir(cls):
831
+
832
+ # Get the attribute using getattr_static to avoid triggering property getters
833
+ attr = inspect.getattr_static(cls, name)
834
+
835
+ # Check if the attribute is a class method
836
+ if isinstance(attr, classmethod):
837
+
838
+ # Get the underlying function
839
+ func = attr.__func__
840
+
841
+ # Check if it's NOT a coroutine function (i.e., synchronous)
842
+ if not inspect.iscoroutinefunction(func) and name.startswith(f"_") and not name.startswith("__") and not name.startswith(f"_{class_name}"):
843
+ protected_class_methods.append(str(name).replace(f"_{class_name}", ""))
844
+
845
+ # Return the list of protected class method names
846
+ return protected_class_methods
847
+
848
+ def getPrivateClassMethods(self) -> List[str]:
849
+ """
850
+ Get all private class method names of the instance.
851
+
852
+ Returns
853
+ -------
854
+ List[str]
855
+ List of private class method names
856
+ """
857
+ class_name = self.getClassName()
858
+ cls = self._instance.__class__
859
+ private_class_methods = []
860
+
861
+ # Iterate over all attributes of the class
862
+ for name in dir(cls):
863
+
864
+ # Get the attribute using getattr_static to avoid triggering property getters
865
+ attr = inspect.getattr_static(cls, name)
866
+
867
+ # Check if the attribute is a class method
868
+ if isinstance(attr, classmethod):
869
+
870
+ # Get the underlying function
871
+ func = attr.__func__
872
+
873
+ # Check if it's NOT a coroutine function (i.e., synchronous)
874
+ if not inspect.iscoroutinefunction(func) and name.startswith(f"_{class_name}"):
875
+ private_class_methods.append(str(name).replace(f"_{class_name}", ""))
876
+
877
+ # Return the list of protected class method names
878
+ return private_class_methods
879
+
880
+
881
+
882
+
883
+
884
+
885
+
886
+
887
+
888
+
889
+
890
+
891
+
892
+
893
+
894
+
895
+
896
+ def getClassSyncMethods(self) -> List[str]:
897
+ """
898
+ Get all synchronous class method names of the instance.
899
+
900
+ Returns
901
+ -------
902
+ List[str]
903
+ List of synchronous class method names
904
+ """
905
+ class_name = self.getClassName()
906
+ cls = self._instance.__class__
907
+ class_sync_methods = []
908
+
909
+ # Iterate over all attributes of the class
910
+ for name in dir(cls):
911
+
912
+ # Get the attribute using getattr_static to avoid triggering property getters
913
+ attr = inspect.getattr_static(cls, name)
914
+
915
+ # Check if the attribute is a class method
916
+ if isinstance(attr, classmethod):
917
+
918
+ # Get the underlying function
919
+ func = attr.__func__
920
+
921
+ # Check if it's NOT a coroutine function (i.e., synchronous)
922
+ if not inspect.iscoroutinefunction(func):
923
+ class_sync_methods.append(str(name).replace(f"_{class_name}", ""))
924
+
925
+ # Return the list of synchronous class method names
926
+ return class_sync_methods
927
+
928
+ def getClassAsyncMethods(self) -> List[str]:
929
+ """
930
+ Get all asynchronous class method names of the instance.
931
+
932
+ Returns
933
+ -------
934
+ List[str]
935
+ List of asynchronous class method names
936
+ """
937
+ class_name = self.getClassName()
938
+ cls = self._instance.__class__
939
+ class_async_methods = []
940
+
941
+ # Iterate over all attributes of the class
942
+ for name in dir(cls):
943
+
944
+ # Get the attribute using getattr_static to avoid triggering property getters
945
+ attr = inspect.getattr_static(cls, name)
946
+
947
+ # Check if the attribute is a class method
948
+ if isinstance(attr, classmethod):
949
+
950
+ # Get the underlying function
951
+ func = attr.__func__
952
+
953
+ # Check if it's a coroutine function (i.e., asynchronous)
954
+ if inspect.iscoroutinefunction(func):
955
+ class_async_methods.append(str(name).replace(f"_{class_name}", ""))
956
+
957
+ # Return the list of asynchronous class method names
958
+ return class_async_methods
959
+
960
+
961
+
962
+
963
+
964
+
965
+
966
+
967
+
968
+
969
+
970
+
971
+
972
+
973
+
974
+
975
+
976
+
977
+
978
+
979
+
980
+
981
+
982
+
983
+
984
+
985
+
986
+
987
+
988
+
989
+
990
+
991
+ def getDunderMethods(self) -> List[str]:
992
+ """
993
+ Get all dunder (double underscore) method names of the instance.
994
+
995
+ Returns
996
+ -------
997
+ List[str]
998
+ List of dunder method names
999
+ """
1000
+ dunder_methods = []
1001
+
1002
+ # Collect dunder methods (starting and ending with double underscores)
1003
+ for name in dir(self._instance):
1004
+ if name.startswith("__") and name.endswith("__"):
1005
+ dunder_methods.append(name)
1006
+
1007
+ return dunder_methods
1008
+
1009
+ def getMagicMethods(self) -> List[str]:
1010
+ """
1011
+ Get all magic method names of the instance.
1012
+
1013
+ Returns
1014
+ -------
1015
+ List[str]
1016
+ List of magic method names
1017
+ """
1018
+ return self.getDunderMethods()
1019
+
1020
+
1021
+
1022
+ def getStaticMethods(self) -> List[str]:
1023
+ """
1024
+ Get all static method names of the instance.
1025
+
1026
+ Returns
1027
+ -------
1028
+ List[str]
1029
+ List of static method names
1030
+ """
1031
+ class_name = self.getClassName()
1032
+ cls = self._instance.__class__
1033
+ static_methods = []
1034
+ for name in dir(cls):
1035
+ attr = inspect.getattr_static(cls, name)
1036
+ if isinstance(attr, staticmethod):
1037
+ static_methods.append(str(name).replace(f"_{class_name}", ""))
1038
+ return static_methods
1039
+
1040
+ def getStaticSyncMethods(self) -> List[str]:
1041
+ """
1042
+ Get all synchronous static method names of the instance.
1043
+
1044
+ Returns
1045
+ -------
1046
+ List[str]
1047
+ List of synchronous static method names
1048
+ """
1049
+ class_name = self.getClassName()
1050
+ cls = self._instance.__class__
1051
+ static_sync_methods = []
1052
+
1053
+ # Iterate over all attributes of the class
1054
+ for name in dir(cls):
1055
+
1056
+ # Get the attribute using getattr_static to avoid triggering property getters
1057
+ attr = inspect.getattr_static(cls, name)
1058
+
1059
+ # Check if the attribute is a static method
1060
+ if isinstance(attr, staticmethod):
1061
+
1062
+ # Get the underlying function
1063
+ func = attr.__func__
1064
+
1065
+ # Check if it's NOT a coroutine function (i.e., synchronous)
1066
+ if not inspect.iscoroutinefunction(func):
1067
+ static_sync_methods.append(str(name).replace(f"_{class_name}", ""))
1068
+
1069
+ # Return the list of synchronous static method names
1070
+ return static_sync_methods
1071
+
1072
+ def getStaticAsyncMethods(self) -> List[str]:
1073
+ """
1074
+ Get all asynchronous static method names of the instance.
1075
+
1076
+ Returns
1077
+ -------
1078
+ List[str]
1079
+ List of asynchronous static method names
1080
+ """
1081
+ class_name = self.getClassName()
1082
+ cls = self._instance.__class__
1083
+ static_async_methods = []
1084
+
1085
+ # Iterate over all attributes of the class
1086
+ for name in dir(cls):
1087
+
1088
+ # Get the attribute using getattr_static to avoid triggering property getters
1089
+ attr = inspect.getattr_static(cls, name)
1090
+
1091
+ # Check if the attribute is a static method
1092
+ if isinstance(attr, staticmethod):
1093
+
1094
+ # Get the underlying function
1095
+ func = attr.__func__
1096
+
1097
+ # Check if it's a coroutine function (i.e., asynchronous)
1098
+ if inspect.iscoroutinefunction(func):
1099
+ static_async_methods.append(str(name).replace(f"_{class_name}", ""))
1100
+
1101
+ # Return the list of asynchronous static method names
1102
+ return static_async_methods
1103
+
1104
+ # def getMethodDocstring(self, method_name: str) -> Optional[str]:
1105
+ # """
1106
+ # Get the docstring of a method.
1107
+
1108
+ # Parameters
1109
+ # ----------
1110
+ # method_name : str
1111
+ # Name of the method
1112
+
1113
+ # Returns
1114
+ # -------
1115
+ # Optional[str]
1116
+ # The docstring of the method, or None if not available
1117
+ # """
1118
+ # # Handle private method name mangling
1119
+ # if method_name in self.getPrivateMethods() or method_name in self.getClass
1120
+ # method_name = f"_{self.getClassName()}{method_name}"
1121
+
1122
+ # print(f"Getting docstring for method: {method_name} in class: {self.getPrivateMethods()}")
1123
+
1124
+ # # Try to get the method from the instance first
1125
+ # method = getattr(self._instance, method_name, None)
1126
+ # if method is None:
1127
+ # # Try to get the method from the class if not found on the instance
1128
+ # method = getattr(self._instance.__class__, method_name, None)
1129
+ # if method is None:
1130
+ # return None
1131
+
1132
+ # # If it's a staticmethod or classmethod, get the underlying function
1133
+ # if isinstance(method, (staticmethod, classmethod)):
1134
+ # method = method.__func__
1135
+
1136
+ # # Return the docstring if available
1137
+ # return getattr(method, "__doc__", None) or ""
1138
+
1139
+ # def getMethodSignature(self, method_name: str) -> inspect.Signature:
1140
+ # """
1141
+ # Get the signature of a method.
1142
+
1143
+ # Parameters
1144
+ # ----------
1145
+ # method_name : str
1146
+ # Name of the method
1147
+
1148
+ # Returns
1149
+ # -------
1150
+ # inspect.Signature
1151
+ # The method signature
1152
+
1153
+ # Raises
1154
+ # ------
1155
+ # ReflectionAttributeError
1156
+ # If the method does not exist on the instance
1157
+ # """
1158
+ # if method_name in self.getPrivateMethods():
1159
+ # method_name = f"_{self.getClassName()}{method_name}"
1160
+
1161
+ # method = getattr(self._instance, method_name, None)
1162
+ # if method is None or not callable(method):
1163
+ # raise ReflectionAttributeError(f"Method '{method_name}' not found in '{self.getClassName()}'.")
1164
+
1165
+ # return inspect.signature(method)
1166
+
1167
+
1168
+
1169
+
1170
+
1171
+
1172
+
1173
+
1174
+
1175
+
1176
+
1177
+
1178
+
1179
+
1180
+
1181
+
1182
+
1183
+
1184
+
1185
+
1186
+
1187
+
1188
+
1189
+
1190
+
1191
+
1192
+
1193
+
1194
+
1195
+
1196
+
1197
+
1198
+
1199
+
1200
+
1201
+
1202
+
1203
+
1204
+
1205
+
1206
+
1207
+
1208
+
1209
+
1210
+
1211
+
1212
+
1213
+
1214
+
1215
+
1216
+
1217
+
1218
+
1219
+
1220
+
1221
+
1222
+
1223
+
1224
+
1225
+
1226
+
1227
+
1228
+
1229
+
1230
+
1231
+
1232
+
1233
+
1234
+
1235
+
1236
+
1237
+
1238
+
1239
+
1240
+
1241
+
1242
+
1243
+
1244
+
1245
+
1246
+
1247
+ def getProperties(self) -> Dict[str, ClassProperty]:
1248
+ """
1249
+ Get all properties of the instance.
1250
+
1251
+ Returns
1252
+ -------
1253
+ List[str]
1254
+ List of property names
1255
+ """
1256
+
1257
+ properties = {}
1258
+ for name, prop in self._instance.__class__.__dict__.items():
1259
+ if isinstance(prop, property):
1260
+ properties[name] = ClassProperty(
1261
+ name=name,
1262
+ value=getattr(self._instance, name, None),
1263
+ signature=inspect.signature(prop.fget) if prop.fget else None,
1264
+ doc=prop.__doc__ or ""
1265
+ )
1266
+ return properties
1267
+
1268
+ def getPropertyNames(self) -> List[str]:
1269
+ """
1270
+ Get all property names of the instance.
1271
+
1272
+ Returns
1273
+ -------
1274
+ List[str]
1275
+ List of property names
1276
+ """
1277
+ return self.getProperties().keys()
1278
+
1279
+ def getProperty(self, property_name: str) -> Any:
1280
+ """
1281
+ Get the value of a property.
1282
+
1283
+ Parameters
1284
+ ----------
1285
+ property_name : str
1286
+ Name of the property
1287
+
1288
+ Returns
1289
+ -------
1290
+ Any
1291
+ The value of the property
1292
+
1293
+ Raises
1294
+ ------
1295
+ AttributeError
1296
+ If the property doesn't exist or is not a property
1297
+ """
1298
+ all_prop = self.getProperties()
1299
+ if property_name not in all_prop:
1300
+ raise ReflectionValueError(f"Property '{property_name}' not found.")
1301
+ return all_prop[property_name].value
1302
+
1303
+ def getPropertySignature(self, property_name: str) -> inspect.Signature:
1304
+ """
1305
+ Get the signature of a property.
1306
+
1307
+ Parameters
1308
+ ----------
1309
+ property_name : str
1310
+ Name of the property
1311
+
1312
+ Returns
1313
+ -------
1314
+ inspect.Signature
1315
+ The property signature
1316
+
1317
+ Raises
1318
+ ------
1319
+ AttributeError
1320
+ If the property doesn't exist or is not a property
1321
+ """
1322
+ all_prop = self.getProperties()
1323
+ if property_name not in all_prop:
1324
+ raise ReflectionValueError(f"Property '{property_name}' not found.")
1325
+ return all_prop[property_name].signature
1326
+
1327
+ def getPropertyDoc(self, property_name: str) -> str:
1328
+ """
1329
+ Get the docstring of a property.
1330
+
1331
+ Parameters
1332
+ ----------
1333
+ property_name : str
1334
+ Name of the property
1335
+
1336
+ Returns
1337
+ -------
1338
+ str
1339
+ The docstring of the property
1340
+
1341
+ Raises
1342
+ ------
1343
+ AttributeError
1344
+ If the property doesn't exist or is not a property
1345
+ """
1346
+ all_prop = self.getProperties()
1347
+ if property_name not in all_prop:
1348
+ raise ReflectionValueError(f"Property '{property_name}' not found.")
1349
+ return all_prop[property_name].doc
1350
+
1351
+
1352
+
1353
+
1354
+
1355
+
1356
+
1357
+
1358
+
1359
+
1360
+
1361
+
1362
+
1363
+
1364
+
1365
+
1366
+
1367
+
1368
+
1369
+
1370
+
1371
+
1372
+
1373
+
1374
+
1375
+
1376
+
1377
+
1378
+
1379
+
1380
+
1381
+
1382
+
1383
+
1384
+
1385
+
1386
+
1387
+
1388
+
1389
+
1390
+
1391
+
1392
+
1393
+
1394
+
1395
+
1396
+
1397
+
1398
+
1399
+
1400
+
1401
+
1402
+
1403
+
1404
+
1405
+ def getConstructorDependencies(self) -> ClassDependency:
1406
+ """
1407
+ Get the resolved and unresolved dependencies from the constructor of the instance's class.
1408
+
1409
+
1410
+
1411
+ Returns
1412
+ -------
1413
+ ClassDependency
1414
+ A structured representation of the constructor dependencies, containing:
1415
+ - resolved: Dictionary of resolved dependencies with their names and values.
1416
+ - unresolved: List of unresolved dependencies (parameter names without default values or annotations).
1417
+ """
1418
+ return ReflectDependencies(self._instance.__class__).getConstructorDependencies()
1419
+
1420
+ def getMethodDependencies(self, method_name: str) -> MethodDependency:
1421
+ """
1422
+ Get the resolved and unresolved dependencies from a method of the instance's class.
1423
+
1424
+ Parameters
1425
+ ----------
1426
+ method_name : str
1427
+ The name of the method to inspect
1428
+
1429
+ Returns
1430
+ -------
1431
+ MethodDependency
1432
+ A structured representation of the method dependencies, containing:
1433
+ - resolved: Dictionary of resolved dependencies with their names and values.
1434
+ - unresolved: List of unresolved dependencies (parameter names without default values or annotations).
1435
+ """
1436
+ return ReflectDependencies(self._instance).getMethodDependencies(method_name)