Reflecting on an Instance
In this guide, we will explore how to leverage the reflection capabilities provided by the Orionis Framework to inspect and interact with a class instance. This feature is particularly valuable for dynamically accessing attributes, invoking methods, and building advanced tools such as ORMs, API generators, or documentation utilities.
Why Use Reflection?
Reflection enables developers to examine the structure and behavior of objects at runtime. With the Orionis Framework, you can effortlessly retrieve information about an instance’s attributes, methods, and even its class inheritance hierarchy.
Example Class for Demonstration
To demonstrate the concepts covered in this guide, we will use the following example class:
import asyncio
class BaseFakeClass:
pass
class FakeClass(BaseFakeClass):
"""This is a test class for ReflexionInstance."""
class_attr: str = "class_value"
def __init__(self) -> None:
self.public_attr = 42
self._private_attr = "secret"
self.dynamic_attr = None
def instanceMethod(self, x: int, y: int) -> int:
"""Adds two numbers."""
return x + y
@property
def computed_property(self) -> str:
"""A computed property."""
return f"Value: {self.public_attr}"
@classmethod
def classMethod(cls) -> str:
"""A class method."""
return f"Class attr: {cls.class_attr}"
@staticmethod
def staticMethod(text: str) -> str:
"""A static method."""
return text.upper()
@staticmethod
async def staticAsyncMethod(text: str) -> str:
"""An asynchronous static method."""
await asyncio.sleep(0.1)
return text.upper()
def __privateMethod(self) -> str:
"""A 'private' method."""
return "This is private"
def _protectedMethod(self) -> str:
"""A 'protected' method."""
return "This is protected"
async def asyncMethod(self) -> str:
"""An async method."""
return "This is async"
Reflecting on an Instance
getClassName
Method
The getClassName
method provides a simple way to retrieve the name of an instance’s class. This is especially useful when working with dynamic or polymorphic objects.
from orionis.luminate.support.inspection.reflection import Reflection
from folder.module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Retrieve the class name
class_name = reflex.getClassName()
print(class_name) # Output: "FakeClass"
getClass
Method
The getClass
method grants access to the class of the instantiated object. This can be useful for performing introspection or when you need to work directly with the class rather than the instance.
from orionis.luminate.support.inspection.reflection import Reflection
from folder.module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Get the class of the instance
clazz = reflex.getClass()
print(clazz) # Output: <class 'folder.module.FakeClass'>
getModuleName
Method
The getModuleName
method returns the name of the module where the class is defined. This is useful for identifying the code's location and improving module organization in large-scale projects.
from orionis.luminate.support.inspection.reflection import Reflection
from folder.module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Retrieve the module name
module_name = reflex.getModuleName()
print(module_name) # Output: "folder.module"
getAttributes
Method
The getAttributes
method returns a dictionary containing all attributes of an instance, including both public and private ones. This is particularly useful for dynamically inspecting an object’s state or for building advanced tools such as serializers or debuggers.
In the following example, we use the getAttributes
method to retrieve the attributes of a FakeClass
instance:
from orionis.luminate.support.inspection.reflection import Reflection
from folder.module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Retrieve the instance attributes
attributes = reflex.getAttributes()
# Print the retrieved attributes
print(attributes)
# Output:
# {
# "public_attr": 42,
# "_private_attr": "secret",
# "dynamic_attr": None
# }
getMethods
Method
The getMethods
method retrieves a list of all methods defined in an instance, including both public and private ones. This is especially useful for dynamically inspecting an object’s behavior, building advanced tools such as documentation generators or code analyzers, and gaining a deeper understanding of a class’s functionality.
In the following example, we use the getMethods
method to retrieve the methods of a FakeClass
instance:
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Retrieve the instance methods
methods = reflex.getMethods()
# Print the retrieved methods
print(methods)
# Output:
# ['__privateMethod', '__init__', '_protectedMethod', 'asyncMethod', 'classMethod', 'instanceMethod']
In this example, the getMethods
method returns a list of all method names available on the instance, including protected and private ones. This allows for detailed analysis of a class's behavior and structure.
getProtectedMethods
Method
The getProtectedMethods
method returns a list of all protected methods in an instance. This is helpful for identifying methods that are not intended to be accessed externally, but may still be used by subclasses or within the class itself.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Retrieve the protected methods
methods = reflex.getProtectedMethods()
# Print the retrieved methods
print(methods)
# Output:
# ['_protectedMethod']
getPrivateMethods
Method
The getPrivateMethods
method returns a list of all private methods defined in an instance. This is useful for identifying methods that are strictly intended for internal use within the class.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Retrieve the private methods
methods = reflex.getPrivateMethods()
# Print the retrieved methods
print(methods)
# Output:
# ['__privateMethod']
getAsyncMethods
Method
The getAsyncMethods
method returns a list of all asynchronous methods defined in an instance. This is useful for identifying methods that can be invoked asynchronously, which is especially relevant in applications that rely on asynchronous programming or concurrency.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Retrieve the asynchronous methods
methods = reflex.getAsyncMethods()
# Print the retrieved methods
print(methods)
# Output:
# ['asyncMethod']
getSyncMethods
Method
The getSyncMethods
method returns a list of all synchronous methods defined in an instance. This is useful for identifying methods that execute sequentially and do not involve async
or await
.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Retrieve the synchronous methods
methods = reflex.getSyncMethods()
# Print the retrieved methods
print(methods)
# Output:
# ['__privateMethod', '__init__', '_protectedMethod', 'classMethod', 'instanceMethod']
getClassMethods
Method
The getClassMethods
method returns a list of all class methods defined in an instance. This is helpful for identifying methods that are bound to the class rather than the instance and do not rely on instance-specific data.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Retrieve the class methods
methods = reflex.getClassMethods()
# Print the retrieved methods
print(methods)
# Output:
# ['classMethod']
getStaticMethods
Method
The getStaticMethods
method returns a list of all static methods defined in an instance. This is useful for identifying methods that do not depend on the instance's state and can be invoked without creating an object.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Retrieve the static methods
methods = reflex.getStaticMethods()
# Print the retrieved methods
print(methods)
# Output:
# ['staticAsyncMethod', 'staticMethod']
getAsyncStaticMethods
Method
The getAsyncStaticMethods
method returns a list of all asynchronous static methods defined in an instance. This is helpful for identifying methods that can be invoked asynchronously and do not rely on instance-level data.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Retrieve the asynchronous static methods
methods = reflex.getAsyncStaticMethods()
# Print the retrieved methods
print(methods)
# Output:
# ['staticAsyncMethod']
getSyncStaticMethods
Method
The getSyncStaticMethods
method returns a list of all synchronous static methods defined in an instance. This is useful for identifying static methods that execute sequentially and do not require async
or await
.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Retrieve the synchronous static methods
methods = reflex.getSyncStaticMethods()
# Print the retrieved methods
print(methods)
# Output:
# ['staticMethod']
getPropertyNames
Method
The getPropertyNames
method returns a list of the names of all properties defined in an instance. This is especially useful for identifying properties that can be accessed or modified directly, facilitating object introspection and dynamic manipulation.
In the following example, we use the getPropertyNames
method to retrieve the properties of an instance of the FakeClass
:
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Retrieve the names of the instance's properties
properties = reflex.getPropertyNames()
# Print the retrieved property names
print(properties)
# Output:
# [
# 'computed_property'
# ]
Note: In Python, properties are special attributes defined using decorators like @property
. These properties encapsulate logic that executes when getting or setting a value, making them ideal for use cases requiring controlled data access.
getProperty
Method
The getProperty
method returns the value of a specific property of an instance. This is useful for accessing properties that may have custom logic during read or write operations.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Retrieve the value of a specific property
prop = reflex.getProperty("computed_property")
# Print the value
print(prop)
# Output:
# "Value: 42"
getPropertySignature
Method
The getPropertySignature
method returns the signature of a specific property of an instance. This is helpful for understanding the structure and parameters of a property without needing to inspect its implementation.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Retrieve the signature of a specific property
sign = reflex.getPropertySignature("computed_property")
# Print the property signature
print(sign)
# Output:
# "(self) -> str"
callMethod
Method
The callMethod
method dynamically invokes a method on an instance. This is useful when you need to execute methods without knowing their names at compile time, enabling the development of generic tools or dynamic object manipulation.
This method handles both synchronous and asynchronous method execution depending on the nature of the target method.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
# Call a synchronous method
result = reflex.callMethod("instanceMethod", 1, 2)
print(result) # Output: 3
# Call an asynchronous method
async def main():
result = await reflex.callMethod("asyncMethod")
print(result) # Output: "This is async"
Note:
- If the method is asynchronous and called inside an event loop, it must be awaited.
- If the method is asynchronous and no event loop is running, a new loop will be created and executed automatically.
- If the method is synchronous, it will be executed immediately without
await
.
Understanding these differences is crucial to avoid runtime errors when working with both synchronous and asynchronous code.
getMethodSignature
Method
The getMethodSignature
method returns the signature of a specific method of an instance. This helps to understand the parameters and return type of the method without inspecting the actual implementation.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
signature = reflex.getMethodSignature("instanceMethod")
print(signature) # Output: "(x: int, y: int) -> int"
signature = reflex.getMethodSignature("staticMethod")
print(signature) # Output: "(text: str) -> str"
signature = reflex.getMethodSignature("__privateMethod")
print(signature) # Output: "() -> str"
getMethodDocstring
Method
The getMethodDocstring
method retrieves the docstring of a specific method of an instance. This is useful to understand the purpose and usage of a method without looking at its code.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
docstring = reflex.getDocstring()
print(docstring) # Output: "This is a test class for ReflexionInstance."
getBaseClasses
Method
The getBaseClasses
method returns a tuple of all base classes of an instance. This is helpful for understanding class hierarchy and inheritance relationships.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
reflex = Reflection.instance(FakeClass())
base_classes = reflex.getBaseClasses()
print(base_classes) # Output: (<class 'folder.module.BaseFakeClass'>,)
isInstanceOf
Method
The isInstanceOf
method checks if the instance is of a specific type or a subclass thereof. This is useful for runtime type validation and enforcing interface contracts.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass, BaseFakeClass
reflex = Reflection.instance(FakeClass())
verify = reflex.isInstanceOf(BaseFakeClass)
print(verify) # Output: True
getSourceCode
Method
The getSourceCode
method returns the source code of the instance's class. This is particularly useful for documentation, debugging, or educational tools.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
reflex = Reflection.instance(FakeClass())
code = reflex.getSourceCode()
print(code) # Output: "class FakeClass(BaseFakeClass): ..."
getFileLocation
Method
The getFileLocation
method returns the file path where the class of the instance is defined. This is helpful for navigating codebases and organizing modules.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
reflex = Reflection.instance(FakeClass())
file = reflex.getFileLocation()
print(file) # Output: "/path/to/folder/module.py"
getAnnotations
Method
The getAnnotations
method returns a dictionary containing all type annotations defined in the instance. This includes both public and private annotations, and is useful for dynamic inspection or tools like serializers and debuggers.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
reflex = Reflection.instance(FakeClass())
annotations = reflex.getAnnotations()
print(annotations) # Output: {'class_attr': <class 'str'>}
hasAttribute
Method
The hasAttribute
method checks whether an instance contains a specific attribute. This helps to avoid runtime errors by verifying the existence of attributes before accessing them.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
reflex = Reflection.instance(FakeClass())
print(reflex.hasAttribute("public_attr")) # Output: True
print(reflex.hasAttribute("non_existent_attr")) # Output: False
getAttribute
Method
The getAttribute
method retrieves the value of a specific attribute from an instance. This is particularly useful when accessing attributes that may involve property logic or dynamic resolution.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
# Create a Reflection instance for the target object
reflex = Reflection.instance(FakeClass())
attr_value = reflex.getAttribute("public_attr")
print(attr_value) # Output: 42
This method safely accesses the underlying value, even if it’s managed by a property or descriptor.
setAttribute
Method
The setAttribute
method sets the value of a specific attribute on an instance. This is useful for modifying internal state, even when attributes are controlled via property setters or other custom logic.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
reflex = Reflection.instance(FakeClass())
reflex.setAttribute("new_attr", "Orionis")
attr_value = reflex.getAttribute("new_attr")
print(attr_value) # Output: "Orionis"
This method supports creating or overwriting existing attributes, making it useful for injecting values or mocking behavior in test scenarios.
removeAttribute
Method
The removeAttribute
method deletes a specific attribute from an instance. This is useful for cleaning up dynamic attributes or resetting the state of an object.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
reflex = Reflection.instance(FakeClass())
reflex.setAttribute("temp_attr", "Temporary")
attr_value = reflex.getAttribute("temp_attr")
print(attr_value) # Output: "Temporary"
This method allows you to remove attributes that were added dynamically or to reset the state of an object.
setMacro
Method
The setMacro
method dynamically attaches a method to an instance. This enables runtime augmentation of an object’s behavior, ideal for plugin systems, mocking, or extending functionality without modifying the class definition.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
import asyncio
reflex = Reflection.instance(FakeClass())
# Define an asynchronous macro method
async def asyncMacro(cls: FakeClass, num):
await asyncio.sleep(0.1)
return cls.instanceMethod(10, 12) + num
# Register the macro on the instance
reflex.setMacro("asyncMacro", asyncMacro)
# Call the macro using callMethod
result = await reflex.callMethod("asyncMacro", reflex._instance, 3)
print(result) # Output: 25
# Define a synchronous macro method
def syncMacro(cls: FakeClass, num):
return cls.instanceMethod(10, 12) + num
reflex.setMacro("syncMacro", syncMacro)
result = reflex.callMethod("syncMacro", reflex._instance, 2)
print(result) # Output: 24
Macros can be either asynchronous or synchronous and are attached at runtime using a simple interface. They behave like native methods for the duration of their registration.
removeMacro
Method
The removeMacro
method detaches a previously registered macro from an instance. This is useful for undoing dynamic changes and ensuring that objects revert to their original state.
from orionis.luminate.support.inspection.reflection import Reflection
from origin_module import FakeClass
import asyncio
reflex = Reflection.instance(FakeClass())
async def asyncMacro(cls: FakeClass, num):
await asyncio.sleep(0.1)
return cls.instanceMethod(10, 12) + num
reflex.setMacro("asyncMacro", asyncMacro)
result = await reflex.callMethod("asyncMacro", reflex._instance, 3)
print(result) # Output: 25
# Remove the macro from the instance
reflex.removeMacro("asyncMacro")