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