ReflectionAbstract
ReflectionAbstract inspects a class without creating an instance. Use it to build action catalogs, verify conventions and inspect signatures for development tools. It can also mutate data and remove methods on the original class.
The following examples form an executable walkthrough: save the initial block and subsequent snippets, in order, to a .py file. Source retrieval requires this file; a class created in an interactive console may not have retrievable source.
Construction and example class
Section titled “Construction and example class”__init__(self, abstract: type) -> NoneRequired abstract must satisfy inspect.isabstract(): inheriting from ABC with no remaining abstract methods is insufficient. A non-abstract class raises TypeError; an invalid input lacking __name__ can also raise AttributeError while constructing the error message. The contract is never instantiated.
from abc import ABC, abstractmethodfrom orionis.introspection import ReflectionAbstract
class Parent: inherited = "base"
def inheritedMethod(self) -> str: return "base"
class ReportContract(Parent, ABC): """Report endpoint metadata."""
format: str = "json" _label: str = "reports" __token: str = "secret" __feature__ = "reports" declared_only: str
def __init__(self, limit: int = 10) -> None: self.instance_only = limit
@abstractmethod def render(self, item_id: int, mode: str = "brief") -> str: return f"{item_id}:{mode}"
def summary(self) -> str: return "summary"
async def fetch(self) -> str: return "data"
def _check(self) -> bool: return True
async def _fetch(self) -> str: return "data"
def __sign(self) -> str: return "signature"
async def __fetch(self) -> str: return "data"
@staticmethod def normalize() -> str: return "json"
@staticmethod async def normalizeAsync() -> str: return "json"
@classmethod def build(cls) -> str: return cls.__name__
@classmethod async def buildAsync(cls) -> str: return cls.__name__
@staticmethod def _normalize() -> str: return "json"
@staticmethod async def _normalizeAsync() -> str: return "json"
@classmethod def _build(cls) -> str: return cls.__name__
@classmethod async def _buildAsync(cls) -> str: return cls.__name__
@staticmethod def __normalize() -> str: return "json"
@staticmethod async def __normalizeAsync() -> str: return "json"
@classmethod def __build(cls) -> str: return cls.__name__
@classmethod async def __buildAsync(cls) -> str: return cls.__name__
@property def title(self) -> str: """Report title.""" return self.format
@property def _title(self) -> str: """Report title.""" return self.format
@property def __title(self) -> str: """Report title.""" return self.format
def __repr__(self) -> str: return "Report"
r = ReflectionAbstract(ReportContract)Import from orionis.introspection; the direct orionis.introspection.abstract.reflection path is also available. Query methods are synchronous even when inspected members are async.
Classification rules
Section titled “Classification rules”The scan traverses only Class.__dict__, not the MRO. Collection getters exclude inherited members; use getBaseClasses() to visit bases explicitly. A field created as self.field is not class data, and an annotation without a value is absent from attributes.
Classification distinguishes data, property, staticmethod, classmethod and other callables. It does not require every callable to be a conventional instance function. Async detection uses inspect.iscoroutinefunction() without execution: an async generator or a synchronous function returning an awaitable is not automatically classified as a coroutine function.
Public names do not start with _; protected names use _; private names are recognized through the class mangling prefix and exposed as __name. Dunder names also end in __ and are queried separately. Normalization follows the prefix Python generates after removing leading underscores from the class name.
Use private names as written in source ("__sign"), rather than "_ReportContract__sign". Classification compares prefixes; it is not access control and does not prevent private member access.
Identity, documentation and source
Section titled “Identity, documentation and source”getClass
getClass(self) -> typeReturns the original class object (type) without constructing it.
assert r.getClass() is ReportContractgetClassName
getClassName(self) -> strReturns the __name__ captured when constructing the wrapper as str, rather than __qualname__. clearCache() does not capture it again.
assert r.getClassName() == "ReportContract"getModuleName
getModuleName(self) -> strReturns the module name captured when constructing the wrapper as str. clearCache() does not capture it again.
assert r.getModuleName() == ReportContract.__module__getModuleWithClassName
getModuleWithClassName(self) -> strReturns module.ClassName as str, using names captured when constructing the wrapper. It does not add the enclosing scopes of a nested class.
assert r.getModuleWithClassName() == f"{ReportContract.__module__}.ReportContract"getDocstring
getDocstring(self) -> str | NoneReturns __doc__ or None; it neither cleans the text nor inherits another class docstring.
assert r.getDocstring() == "Report endpoint metadata."getBaseClasses
getBaseClasses(self) -> list[type]Returns a list of direct base classes (type); it does not traverse the complete MRO.
assert Parent in r.getBaseClasses()getSourceCode
getSourceCode(self) -> strAccepts no arguments. Returns the complete class source as str using inspect.getsource(). Converts OSError and TypeError to ValueError when unavailable. It does not accept a method selector.
assert "class ReportContract" in r.getSourceCode()getFile
getFile(self) -> strReturns the class file path as str. It converts a TypeError from inspect.getfile() into ValueError; it does not guarantee the file still exists.
assert r.getFile().endswith(".py")getAnnotations
getAnnotations(self) -> dictReturns a dict of class annotations, including fields annotated without a value. It removes the class prefix from private names and does not inspect method annotations.
assert "declared_only" in r.getAnnotations()assert "__token" in r.getAnnotations()Class data
Section titled “Class data”hasAttribute
hasAttribute(self, attribute: str) -> boolattribute is the required name of a class data attribute. Returns bool; methods, properties, inherited members and values only created by the constructor are excluded.
assert r.hasAttribute("format")assert not r.hasAttribute("declared_only")assert not r.hasAttribute("inherited")assert not r.hasAttribute("instance_only")getAttribute
getAttribute(self, attribute: str) -> object | NoneRequired attribute is looked up only in classified data. Returns its value or None if missing. No default argument is supported. Use hasAttribute() to distinguish a stored None from missing data.
assert r.getAttribute("format") == "json"assert r.getAttribute("missing") is Noneassert r.getAttribute("inherited") is NonegetAttributes
getAttributes(self) -> dictReturns a dict combining public, protected, normalized private and non-excluded dunder data, in that order. Instance fields and annotations without a value are excluded.
attributes = r.getAttributes()assert attributes["format"] == "json"assert attributes["__token"] == "secret"getPublicAttributes
getPublicAttributes(self) -> dictReturns a dict of data whose names do not begin with _.
assert r.getPublicAttributes()["format"] == "json"getProtectedAttributes
getProtectedAttributes(self) -> dictReturns a dict of data starting with _ that are neither private nor dunder.
assert r.getProtectedAttributes()["_label"] == "reports"getPrivateAttributes
getPrivateAttributes(self) -> dictReturns a dict of private data. Keys retain __ while omitting the class prefix introduced by Python.
assert r.getPrivateAttributes()["__token"] == "secret"getDunderAttributes
getDunderAttributes(self) -> dictReturns a dict of custom __name__ data. The implementation excludes Python metadata such as __dict__, __module__, __doc__ and __annotations__.
assert r.getDunderAttributes()["__feature__"] == "reports"assert "__module__" not in r.getDunderAttributes()getMagicAttributes
getMagicAttributes(self) -> dictAlias of getDunderAttributes(); returns the same cached dictionary.
assert r.getMagicAttributes() is r.getDunderAttributes()setAttribute
setAttribute(self, name: str, value: object) -> boolname must be a valid Python identifier that is not a keyword (otherwise ValueError). Required value must not be callable (TypeError). Mutates the original class and returns True; private __names are supported. It neither validates annotations nor updates __abstractmethods__.
assert r.setAttribute("extra_label", "export") is Trueassert ReportContract.extra_label == "export"assert r.removeAttribute("extra_label") is TrueremoveAttribute
removeAttribute(self, name: str) -> boolname identifies an own data attribute, using its original name when private. Removes the actual attribute and returns True. Raises ValueError when absent from the data classification; metaclass restrictions may propagate.
r.setAttribute("__temporary", 20)assert r.removeAttribute("__temporary") is Trueassert not r.hasAttribute("__temporary")Although the callable error from setAttribute() mentions setMethod, ReflectionAbstract does not implement setMethod(). This wrapper provides no method for adding functions.
Instance methods
Section titled “Instance methods”getMethods
getMethods(self) -> list[str]Returns list[str]: instance, class and static methods, each category ordered by public, protected and private visibility. Properties and dunder methods are excluded; names are not alphabetically sorted.
methods = r.getMethods()assert "render" in methods and "build" in methods and "normalize" in methodsassert "title" not in methods and "__repr__" not in methodsgetPublicMethods
getPublicMethods(self) -> list[str]Returns list[str] of public instance methods, including synchronous and asynchronous functions. It does not execute methods. No arguments are accepted.
assert "summary" in r.getPublicMethods()getPublicSyncMethods
getPublicSyncMethods(self) -> list[str]Returns list[str] of public instance methods that are not coroutine functions. It does not execute methods. No arguments are accepted.
assert "summary" in r.getPublicSyncMethods()assert "fetch" not in r.getPublicSyncMethods()getPublicAsyncMethods
getPublicAsyncMethods(self) -> list[str]Returns list[str] of public instance methods recognized by inspect.iscoroutinefunction(). It does not execute methods. No arguments are accepted.
assert "fetch" in r.getPublicAsyncMethods()assert "summary" not in r.getPublicAsyncMethods()getProtectedMethods
getProtectedMethods(self) -> list[str]Returns list[str] of protected instance methods, including synchronous and asynchronous functions. It does not execute methods. No arguments are accepted.
assert "_check" in r.getProtectedMethods()getProtectedSyncMethods
getProtectedSyncMethods(self) -> list[str]Returns list[str] of protected instance methods that are not coroutine functions. It does not execute methods. No arguments are accepted.
assert "_check" in r.getProtectedSyncMethods()assert "_fetch" not in r.getProtectedSyncMethods()getProtectedAsyncMethods
getProtectedAsyncMethods(self) -> list[str]Returns list[str] of protected instance methods recognized by inspect.iscoroutinefunction(). It does not execute methods. No arguments are accepted.
assert "_fetch" in r.getProtectedAsyncMethods()assert "_check" not in r.getProtectedAsyncMethods()getPrivateMethods
getPrivateMethods(self) -> list[str]Returns list[str] of private instance methods, including synchronous and asynchronous functions. Results use __name, without the class prefix. It does not execute methods. No arguments are accepted.
assert "__sign" in r.getPrivateMethods()getPrivateSyncMethods
getPrivateSyncMethods(self) -> list[str]Returns list[str] of private instance methods that are not coroutine functions. Results use __name, without the class prefix. It does not execute methods. No arguments are accepted.
assert "__sign" in r.getPrivateSyncMethods()assert "__fetch" not in r.getPrivateSyncMethods()getPrivateAsyncMethods
getPrivateAsyncMethods(self) -> list[str]Returns list[str] of private instance methods recognized by inspect.iscoroutinefunction(). Results use __name, without the class prefix. It does not execute methods. No arguments are accepted.
assert "__fetch" in r.getPrivateAsyncMethods()assert "__sign" not in r.getPrivateAsyncMethods()Class methods
Section titled “Class methods”getPublicClassMethods
getPublicClassMethods(self) -> list[str]Returns list[str] of public class (@classmethod) methods, including synchronous and asynchronous functions. It does not execute methods. No arguments are accepted.
assert "build" in r.getPublicClassMethods()getPublicClassSyncMethods
getPublicClassSyncMethods(self) -> list[str]Returns list[str] of public class (@classmethod) methods that are not coroutine functions. It does not execute methods. No arguments are accepted.
assert "build" in r.getPublicClassSyncMethods()assert "buildAsync" not in r.getPublicClassSyncMethods()getPublicClassAsyncMethods
getPublicClassAsyncMethods(self) -> list[str]Returns list[str] of public class (@classmethod) methods recognized by inspect.iscoroutinefunction(). It does not execute methods. No arguments are accepted.
assert "buildAsync" in r.getPublicClassAsyncMethods()assert "build" not in r.getPublicClassAsyncMethods()getProtectedClassMethods
getProtectedClassMethods(self) -> list[str]Returns list[str] of protected class (@classmethod) methods, including synchronous and asynchronous functions. It does not execute methods. No arguments are accepted.
assert "_build" in r.getProtectedClassMethods()getProtectedClassSyncMethods
getProtectedClassSyncMethods(self) -> list[str]Returns list[str] of protected class (@classmethod) methods that are not coroutine functions. It does not execute methods. No arguments are accepted.
assert "_build" in r.getProtectedClassSyncMethods()assert "_buildAsync" not in r.getProtectedClassSyncMethods()getProtectedClassAsyncMethods
getProtectedClassAsyncMethods(self) -> list[str]Returns list[str] of protected class (@classmethod) methods recognized by inspect.iscoroutinefunction(). It does not execute methods. No arguments are accepted.
assert "_buildAsync" in r.getProtectedClassAsyncMethods()assert "_build" not in r.getProtectedClassAsyncMethods()getPrivateClassMethods
getPrivateClassMethods(self) -> list[str]Returns list[str] of private class (@classmethod) methods, including synchronous and asynchronous functions. Results use __name, without the class prefix. It does not execute methods. No arguments are accepted.
assert "__build" in r.getPrivateClassMethods()getPrivateClassSyncMethods
getPrivateClassSyncMethods(self) -> list[str]Returns list[str] of private class (@classmethod) methods that are not coroutine functions. Results use __name, without the class prefix. It does not execute methods. No arguments are accepted.
assert "__build" in r.getPrivateClassSyncMethods()assert "__buildAsync" not in r.getPrivateClassSyncMethods()getPrivateClassAsyncMethods
getPrivateClassAsyncMethods(self) -> list[str]Returns list[str] of private class (@classmethod) methods recognized by inspect.iscoroutinefunction(). Results use __name, without the class prefix. It does not execute methods. No arguments are accepted.
assert "__buildAsync" in r.getPrivateClassAsyncMethods()assert "__build" not in r.getPrivateClassAsyncMethods()Static methods
Section titled “Static methods”getPublicStaticMethods
getPublicStaticMethods(self) -> list[str]Returns list[str] of public static (@staticmethod) methods, including synchronous and asynchronous functions. It does not execute methods. No arguments are accepted.
assert "normalize" in r.getPublicStaticMethods()getPublicStaticSyncMethods
getPublicStaticSyncMethods(self) -> list[str]Returns list[str] of public static (@staticmethod) methods that are not coroutine functions. It does not execute methods. No arguments are accepted.
assert "normalize" in r.getPublicStaticSyncMethods()assert "normalizeAsync" not in r.getPublicStaticSyncMethods()getPublicStaticAsyncMethods
getPublicStaticAsyncMethods(self) -> list[str]Returns list[str] of public static (@staticmethod) methods recognized by inspect.iscoroutinefunction(). It does not execute methods. No arguments are accepted.
assert "normalizeAsync" in r.getPublicStaticAsyncMethods()assert "normalize" not in r.getPublicStaticAsyncMethods()getProtectedStaticMethods
getProtectedStaticMethods(self) -> list[str]Returns list[str] of protected static (@staticmethod) methods, including synchronous and asynchronous functions. It does not execute methods. No arguments are accepted.
assert "_normalize" in r.getProtectedStaticMethods()getProtectedStaticSyncMethods
getProtectedStaticSyncMethods(self) -> list[str]Returns list[str] of protected static (@staticmethod) methods that are not coroutine functions. It does not execute methods. No arguments are accepted.
assert "_normalize" in r.getProtectedStaticSyncMethods()assert "_normalizeAsync" not in r.getProtectedStaticSyncMethods()getProtectedStaticAsyncMethods
getProtectedStaticAsyncMethods(self) -> list[str]Returns list[str] of protected static (@staticmethod) methods recognized by inspect.iscoroutinefunction(). It does not execute methods. No arguments are accepted.
assert "_normalizeAsync" in r.getProtectedStaticAsyncMethods()assert "_normalize" not in r.getProtectedStaticAsyncMethods()getPrivateStaticMethods
getPrivateStaticMethods(self) -> list[str]Returns list[str] of private static (@staticmethod) methods, including synchronous and asynchronous functions. Results use __name, without the class prefix. It does not execute methods. No arguments are accepted.
assert "__normalize" in r.getPrivateStaticMethods()getPrivateStaticSyncMethods
getPrivateStaticSyncMethods(self) -> list[str]Returns list[str] of private static (@staticmethod) methods that are not coroutine functions. Results use __name, without the class prefix. It does not execute methods. No arguments are accepted.
assert "__normalize" in r.getPrivateStaticSyncMethods()assert "__normalizeAsync" not in r.getPrivateStaticSyncMethods()getPrivateStaticAsyncMethods
getPrivateStaticAsyncMethods(self) -> list[str]Returns list[str] of private static (@staticmethod) methods recognized by inspect.iscoroutinefunction(). Results use __name, without the class prefix. It does not execute methods. No arguments are accepted.
assert "__normalizeAsync" in r.getPrivateStaticAsyncMethods()assert "__normalize" not in r.getPrivateStaticAsyncMethods()Existence, mutations and dunder methods
Section titled “Existence, mutations and dunder methods”hasMethod
hasMethod(self, name: str) -> boolRequired name is checked against own instance, class and static methods. Returns bool; private names use their unmangled spelling. Dunder methods are separate and return False here.
assert r.hasMethod("render")assert r.hasMethod("__sign")assert not r.hasMethod("__repr__")assert not r.hasMethod("inheritedMethod")removeMethod
removeMethod(self, name: str) -> boolRemoves own method name from the class and returns True; raises ValueError when hasMethod() is false. Private names and static/class descriptors are supported. It cannot remove dunder or inherited methods.
original_summary = ReportContract.__dict__["summary"]assert r.removeMethod("summary") is Trueassert not r.hasMethod("summary")setattr(ReportContract, "summary", original_summary)r.clearCache()getDunderMethods
getDunderMethods(self) -> list[str]Returns list[str] of own __name__ callables that are not properties, staticmethods or classmethods. Includes __init__ when defined on the class.
assert "__repr__" in r.getDunderMethods()assert "__init__" in r.getDunderMethods()getMagicMethods
getMagicMethods(self) -> list[str]Alias of getDunderMethods(); returns the same cached list.
assert r.getMagicMethods() is r.getDunderMethods()Properties without constructing the class
Section titled “Properties without constructing the class”getProperties
getProperties(self) -> list[str]Returns list[str] of own property descriptors. It preserves declaration order, normalizes private names and does not execute getters.
assert r.getProperties() == ["title", "_title", "__title"]getPublicProperties
getPublicProperties(self) -> list[str]Returns list[str] of public properties.
assert r.getPublicProperties() == ["title"]getProtectedProperties
getProtectedProperties(self) -> list[str]Returns list[str] of protected properties.
assert r.getProtectedProperties() == ["_title"]getPrivateProperties
getPrivateProperties(self) -> list[str]Returns list[str] of private properties using their original spelling.
assert r.getPrivateProperties() == ["__title"]getPropertySignature
getPropertySignature(self, name: str) -> inspect.SignatureRequired name returns inspect.Signature for the fget getter, including self, without executing it. Supports private and inherited properties accessible through getattr(). Raises ValueError when missing, and TypeError for non-properties or uninspectable getters.
assert list(r.getPropertySignature("title").parameters) == ["self"]assert list(r.getPropertySignature("__title").parameters) == ["self"]getPropertyDocstring
getPropertyDocstring(self, name: str) -> str | NoneRequired name returns str | None containing the fget docstring; a missing getter yields None. Supports private and inherited properties. Raises ValueError when absent and TypeError when the attribute is not a property.
assert r.getPropertyDocstring("title") == "Report title."assert r.getPropertyDocstring("__title") == "Report title."Signatures and dependency metadata
Section titled “Signatures and dependency metadata”getMethodSignature
getMethodSignature(self, name: str) -> inspect.SignatureRequired name returns inspect.Signature without invoking the method: instance signatures retain self, whereas bound classmethods omit cls. Raises ValueError if not classified and TypeError if no longer callable; inspection errors can also propagate.
signature = r.getMethodSignature("render")assert list(signature.parameters) == ["self", "item_id", "mode"]assert "cls" not in r.getMethodSignature("build").parametersassert "self" not in r.getMethodSignature("normalize").parametersconstructorSignature
constructorSignature(self) -> SignatureReturns the Orionis Signature describing __init__ dependencies without constructing the class. Its resolved, unresolved and ordered maps contain Argument objects. This is neither inspect.Signature nor service instance resolution.
analysis = r.constructorSignature()assert list(analysis.ordered) == ["limit"]assert analysis.resolved["limit"].default == 10methodSignature
methodSignature(self, method_name: str) -> SignatureRequired method_name returns a dependency Signature for an own method, omitting self/cls and classifying arguments without execution. Raises AttributeError when hasMethod() is false; analyzer errors propagate. Private names are supported.
analysis = r.methodSignature("render")assert list(analysis.ordered) == ["item_id", "mode"]assert "item_id" in analysis.unresolvedassert analysis.resolved["mode"].default == "brief"The last two methods delegate to ReflectDependencies. resolved means sufficient type or default-value metadata exists; it does not mean a service has been created or registered. Required built-in item_id: int remains unresolved, whereas mode="brief" has a known default.
Local cache and access operators
Section titled “Local cache and access operators”The first member getter performs the scan. Returned lists and dictionaries are the cached objects rather than defensive copies. Do not modify them to “edit” the class: that alters the cached view and may make queries inconsistent. Use mutation methods or copy returned results.
setAttribute(), removeAttribute() and removeMethod() invalidate member classifications but can retain previously computed signatures and other results. Call clearCache() after changing signatures, annotations or metadata. Direct changes to the class are not detected automatically either.
The class name, module name and private prefix are captured during construction and do not change after clearCache(). Construct another wrapper after changing __name__ or __module__. The docstring is read directly on each call.
The following operators access the wrapper cache, rather than class attributes. Use custom keys distinct from internal keys (attributes, methods, etc.); overwriting or deleting internal keys can break scan consistency.
__setitem__
__setitem__(self, key: str, value: object) -> NoneStores required value under required key; returns None. It does not change the class.
r["app:note"] = "reviewed"assert r["app:note"] == "reviewed"__getitem__
__getitem__(self, key: str) -> object | NoneReturns the value under required key, or None when absent; it does not raise KeyError. Use in to distinguish a key storing None.
assert r["app:note"] == "reviewed"assert r["app:absent"] is None__contains__
__contains__(self, key: str) -> boolReturns bool indicating whether required key is stored, including when its value is None.
r["app:empty"] = Noneassert "app:empty" in rassert "app:absent" not in r__delitem__
__delitem__(self, key: str) -> NoneRemoves required key, returns None and ignores missing keys.
del r["app:note"]del r["app:absent"]assert "app:note" not in rclearCache
clearCache(self) -> NoneReturns None. Discards all data cached in this wrapper, including custom bracket keys. The next query recomputes reflection; this neither reverses class mutations nor empties the global dependency cache.
r["app:note"] = "cached"assert r.clearCache() is Noneassert "app:note" not in rassert "render" in r.getPublicMethods()Dependency injection integration
Section titled “Dependency injection integration”The wrapper must know which class to inspect. Annotating a parameter as ReflectionAbstract does not automatically select your application contract or component. Construct the reflector when the target is known, or encapsulate it in your own resolvable service. No native binding chooses that target for you.
from orionis.http import JSONResponse
class ReportCatalog: def __init__(self) -> None: self.reflection = ReflectionAbstract(ReportContract)
def actions(self) -> list[str]: return list(self.reflection.getPublicMethods())
class ReportExporter: def __init__(self, catalog: ReportCatalog) -> None: self.catalog = catalog
def actions(self) -> list[str]: return self.catalog.actions()
class MetadataController: async def __call__(self, catalog: ReportCatalog) -> JSONResponse: return JSONResponse({"actions": catalog.actions()})Here, ReportCatalog is an application service with a no-argument constructor. The catalog explicitly creates the reflector and returns a list copy. Constructor injection into another service uses the same catalog: ReportCatalog annotation. Direct access remains valid outside controllers. See the container for registrations, lifetimes and custom contracts.
Errors and practical limits
Section titled “Errors and practical limits”Queries do not execute actions or turn annotations into data validation. Mutations are real and affect every consumer of the class in the process; no class copy is created, and deleting the reflector does not undo changes. Descriptor or metaclass restrictions can make an operation fail; those exceptions propagate.
Method getters do not separate abstract declarations from concrete implementations: both appear according to visibility and descriptor type. The wrapper does not call abc.update_abstractmethods() after a mutation.
A name returned by getDunderMethods() is not automatically valid for hasMethod(), removeMethod(), getMethodSignature() or methodSignature(): these operations use the non-dunder method list. Properties have their own APIs.
Clearing this wrapper does not empty the shared ReflectDependencies caches after dynamic function changes. New functions are analyzed by identity, whereas editing annotations or defaults on the same function can leave earlier analysis cached; see the dependency guide.