Skip to content

Callables

The ReflectionCallable class is an introspection utility designed to analyze Python functions, methods, and lambdas at runtime. It provides a clean and consistent API for extracting metadata, source code, signatures, and dependencies from any valid callable.

Unlike ReflectionAbstract, which operates on abstract classes, ReflectionCallable focuses exclusively on individual callable objects: regular functions, bound instance methods, static methods, and lambda expressions.

from orionis.services.introspection.callables.reflection import ReflectionCallable

The class receives a callable as its parameter. Regular functions, bound methods, static methods, and lambdas are accepted. If the argument is not a valid callable with a __code__ attribute, a TypeError will be raised.

from orionis.services.introspection.callables.reflection import ReflectionCallable
def my_function(x: int, y: str = "hello") -> bool:
"""Return whether x is positive."""
return x > 0
reflection = ReflectionCallable(my_function)
# Regular function
reflection = ReflectionCallable(my_function)
# Lambda
reflection = ReflectionCallable(lambda a, b: a + b)
# Bound method (instance method)
class MyClass:
def process(self, value: int) -> int:
return value * 2
obj = MyClass()
reflection = ReflectionCallable(obj.process)
# Static method (accessed from the class)
class Utils:
@staticmethod
def compute(n: int) -> int:
return n ** 2
reflection = ReflectionCallable(Utils.compute)

The following types are not accepted and will raise TypeError:

# Integer (not callable)
ReflectionCallable(42)
# TypeError: Expected a function, method, or lambda, got int
# Class (no __code__)
ReflectionCallable(MyClass)
# TypeError: Expected a function, method, or lambda, got type
# Built-in (no __code__)
ReflectionCallable(len)
# TypeError: Expected a function, method, or lambda, got builtin_function_or_method

The class implements the IReflectionCallable contract, which defines the complete introspection interface for callables:

from orionis.services.introspection.callables.contracts.reflection import IReflectionCallable

The abstract methods defined in the contract are: getCallable, getName, getModuleName, getModuleWithCallableName, getDocstring, getSourceCode, getFile, getSignature, getDependencies, and clearCache.

Methods for obtaining identification information about the callable.

Returns the original callable object passed to the constructor.

fn = reflection.getCallable()
# <function my_function at 0x...>

Returns the name of the callable as defined in its declaration.

name = reflection.getName()
# "my_function"

For lambdas, the name will be "<lambda>".

Returns the name of the module where the callable is defined.

module = reflection.getModuleName()
# "app.services.processor"

Returns the fully qualified name, combining the module and callable name separated by a dot.

fqn = reflection.getModuleWithCallableName()
# "app.services.processor.my_function"

Returns the callable’s docstring. If no docstring is defined, returns an empty string.

doc = reflection.getDocstring()
# "Return whether x is positive."
# Function without docstring
def no_doc():
pass
rc = ReflectionCallable(no_doc)
rc.getDocstring()
# ""

Retrieves the complete source code of the callable as a string. The result is automatically cached. Raises AttributeError if the source code is not available.

source = reflection.getSourceCode()
# "def my_function(x: int, y: str = \"hello\") -> bool:\n ..."

Returns the absolute file path where the callable is defined. The result is cached. Raises TypeError if the path cannot be determined (e.g., for built-ins).

path = reflection.getFile()
# "/path/to/app/services/processor.py"

Returns an inspect.Signature object describing the callable’s parameters, including names, default values, and type annotations.

import inspect
sig = reflection.getSignature()
# <Signature (x: int, y: str = 'hello') -> bool>
# Access individual parameters
params = sig.parameters
print(params["x"].annotation)
# <class 'int'>
print(params["y"].default)
# "hello"

For bound methods, the self parameter is automatically excluded from the signature.

class MyClass:
def process(self, value: int) -> int:
return value * 2
obj = MyClass()
rc = ReflectionCallable(obj.process)
sig = rc.getSignature()
# <Signature (value: int) -> int>
# 'self' does not appear in the signature

Analyzes the callable’s signature and returns a Signature object containing resolved dependencies (parameters with type annotations) and unresolved dependencies (parameters without annotations or default values).

This method is fundamental for the framework’s dependency injection system, as it determines which services a callable needs in order to be executed.

from orionis.services.introspection.callables.reflection import ReflectionCallable
def create_user(name: str, db: DatabaseService, logger: Logger = None) -> User:
...
rc = ReflectionCallable(create_user)
deps = rc.getDependencies()
# Signature(resolved=[...], unresolved=[...])

For a function without parameters, the dependency lists will be empty:

def simple():
pass
rc = ReflectionCallable(simple)
deps = rc.getDependencies()

ReflectionCallable implements an internal cache system that automatically stores the results of expensive operations such as getSourceCode, getFile, getSignature, and getDependencies. This prevents recalculating values on successive calls.

The class implements the special methods __getitem__, __setitem__, __contains__, and __delitem__, allowing you to interact with the internal cache as a dictionary:

# Check if a key exists in the cache
"source_code" in reflection
# Get a cached value
value = reflection["source_code"]
# Store a custom value
reflection["custom_key"] = "custom_value"
# Delete a cache entry
del reflection["custom_key"]
# Getting a non-existent key returns None
reflection["absent_key"] # None

Clears all reflection cache, forcing subsequent calls to recompute results from scratch.

# Force source code recomputation
reflection.getSourceCode() # computed and cached
reflection.clearCache() # all cache cleared
reflection.getSourceCode() # recomputed
from orionis.services.introspection.callables.reflection import ReflectionCallable
def process_payment(amount: float, currency: str = "USD") -> bool:
"""Process a payment transaction and return success status."""
return amount > 0
# Create reflection instance
rc = ReflectionCallable(process_payment)
# Identity
print(rc.getName())
# "process_payment"
print(rc.getModuleName())
# "__main__"
print(rc.getModuleWithCallableName())
# "__main__.process_payment"
# Metadata
print(rc.getDocstring())
# "Process a payment transaction and return success status."
print(rc.getFile())
# "/path/to/script.py"
# Signature
sig = rc.getSignature()
print(sig)
# (amount: float, currency: str = 'USD') -> bool
for name, param in sig.parameters.items():
print(f" {name}: annotation={param.annotation}, default={param.default}")
# amount: annotation=<class 'float'>, default=<class 'inspect._empty'>
# currency: annotation=<class 'str'>, default=USD
# Dependencies
deps = rc.getDependencies()
print(deps)
# Cache
rc.clearCache()
MethodReturnDescription
getCallable()callableReturns the original callable
getName()strCallable name
getModuleName()strModule where it is defined
getModuleWithCallableName()strFull name module.callable
getDocstring()strDocstring or empty string
getSourceCode()strComplete source code
getFile()strAbsolute file path
getSignature()inspect.SignatureSignature with parameters and types
getDependencies()SignatureResolved and unresolved dependencies
clearCache()NoneClears all internal cache