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