ReflectionModule
ReflectionModule imports a Python module and classifies the objects in its namespace: classes, constants, functions and imported modules. Use it to inspect extensions or build a catalog of loaded components. Unlike ModuleInspector file analysis, construction can execute module code through Python’s normal import system.
Quick start
Section titled “Quick start”from orionis.introspection import ReflectionModule
metadata = ReflectionModule("orionis.metadata.framework")assert metadata.getConstant("NAME") == "orionis"Example module
Section titled “Example module”Save this file as app/plugins/catalog.py. The following examples reuse that module and the reflected wrapper.
import mathfrom pathlib import Path
PUBLIC_LIMIT = 10_INTERNAL_LIMIT = 20__PRIVATE_LIMIT = 30
class CatalogPlugin: def run(self) -> str: return "catalog"
class _InternalPlugin: pass
class __PrivatePlugin: pass
def list_items() -> list[str]: return ["book"]
async def refresh_items() -> int: return 1
def _prepare() -> bool: return True
async def _prepare_async() -> bool: return True
def __sign() -> str: return "signed"
async def __sign_async() -> str: return "signed"from orionis.introspection import ReflectionModule
reflected = ReflectionModule("app.plugins.catalog")__init__
Section titled “__init__”def __init__(self, module: str) -> None: ...module is a required string containing an importable name, not a file path or module object. An empty string, whitespace-only string or another type raises TypeError. Any exception raised during import is wrapped in TypeError, preserving its original cause. This does not reload a module already present in sys.modules.
reflected = ReflectionModule("app.plugins.catalog")getModule
Section titled “getModule”def getModule(self) -> object: ...Take no arguments. Return the imported module object (object in the signature), shared with other consumers of that module.
import importlib
assert reflected.getModule() is importlib.import_module("app.plugins.catalog")Classes and class aliases
Section titled “Classes and class aliases”Any namespace value satisfying isinstance(value, type) is a class. Imported classes are included: value.__module__ need not match the inspected module. Keys are variable names or aliases in that module; they need not equal Class.__name__.
getClasses
Section titled “getClasses”def getClasses(self) -> dict: ...With no arguments, return a dict of all classes and aliases, including names beginning with _.
assert "CatalogPlugin" in reflected.getClasses()assert "Path" in reflected.getClasses()getPublicClasses
Section titled “getPublicClasses”def getPublicClasses(self) -> dict: ...With no arguments, return a dict whose names do not start with _.
assert "CatalogPlugin" in reflected.getPublicClasses()assert "_InternalPlugin" not in reflected.getPublicClasses()getProtectedClasses
Section titled “getProtectedClasses”def getProtectedClasses(self) -> dict: ...With no arguments, return a dict whose names start with _, but not __.
assert "_InternalPlugin" in reflected.getProtectedClasses()getPrivateClasses
Section titled “getPrivateClasses”def getPrivateClasses(self) -> dict: ...With no arguments, return a dict whose names start with __ and do not end with __. These are module names, without class name mangling.
assert "__PrivatePlugin" in reflected.getPrivateClasses()hasClass
Section titled “hasClass”def hasClass(self, class_name: str) -> bool: ...class_name is required. Return bool indicating whether that name is in getClasses().
assert reflected.hasClass("CatalogPlugin")assert not reflected.hasClass("missing")getClass
Section titled “getClass”def getClass(self, class_name: str) -> type | None: ...class_name is required. Return the class object, or None when absent. This does not construct an instance.
plugin_type = reflected.getClass("CatalogPlugin")assert plugin_type().run() == "catalog"assert reflected.getClass("missing") is NoneConstants
Section titled “Constants”Any name satisfying str.isupper() whose value is not callable is classified as a constant. This does not check Final or enforce immutability. Methods return actual values, not copies; a constant can contain a list or another mutable object.
getConstant
Section titled “getConstant”def getConstant(self, constant_name: str) -> object | None: ...constant_name is required. Return its value (object), or None when absent; a constant set to None yields the same result.
assert reflected.getConstant("PUBLIC_LIMIT") == 10assert reflected.getConstant("missing") is NonegetConstants
Section titled “getConstants”def getConstants(self) -> dict: ...With no arguments, return a dict of all recognized constants, including names beginning with underscores.
assert reflected.getConstants()["__PRIVATE_LIMIT"] == 30getPublicConstants
Section titled “getPublicConstants”def getPublicConstants(self) -> dict: ...With no arguments, return a dict of constants without an _ prefix.
assert reflected.getPublicConstants()["PUBLIC_LIMIT"] == 10getProtectedConstants
Section titled “getProtectedConstants”def getProtectedConstants(self) -> dict: ...With no arguments, return a dict of constants starting with _, but not __.
assert reflected.getProtectedConstants()["_INTERNAL_LIMIT"] == 20getPrivateConstants
Section titled “getPrivateConstants”def getPrivateConstants(self) -> dict: ...With no arguments, return a dict of constants starting with __ but not ending with __.
assert reflected.getPrivateConstants()["__PRIVATE_LIMIT"] == 30Sync and async functions
Section titled “Sync and async functions”Filters include only types.FunctionType instances: locally defined or imported Python functions. Classes, bound methods, callable objects and built-in functions are excluded. Async classification uses inspect.iscoroutinefunction; it does not execute functions or inspect their potential return values. An async generator is not a coroutine function and remains in the sync filter.
All these methods take no arguments and return a dict mapping names to functions. To run an async function, invoke it and await its result in your asynchronous flow.
getFunctions
Section titled “getFunctions”def getFunctions(self) -> dict: ...Return all Python functions, of every visibility.
assert "list_items" in reflected.getFunctions()assert "__sign" in reflected.getFunctions()getPublicFunctions(self) -> dict — Return public functions, both sync and async.
assert "list_items" in reflected.getPublicFunctions()getPublicSyncFunctions(self) -> dict — Return public functions that are synchronous.
assert "list_items" in reflected.getPublicSyncFunctions()getPublicAsyncFunctions(self) -> dict — Return public functions that are asynchronous.
assert "refresh_items" in reflected.getPublicAsyncFunctions()getProtectedFunctions(self) -> dict — Return protected functions, both sync and async.
assert "_prepare" in reflected.getProtectedFunctions()getProtectedSyncFunctions(self) -> dict — Return protected functions that are synchronous.
assert "_prepare" in reflected.getProtectedSyncFunctions()getProtectedAsyncFunctions(self) -> dict — Return protected functions that are asynchronous.
assert "_prepare_async" in reflected.getProtectedAsyncFunctions()getPrivateFunctions(self) -> dict — Return private functions, both sync and async.
assert "__sign" in reflected.getPrivateFunctions()getPrivateSyncFunctions(self) -> dict — Return private functions that are synchronous.
assert "__sign" in reflected.getPrivateSyncFunctions()getPrivateAsyncFunctions(self) -> dict — Return private functions that are asynchronous.
assert "__sign_async" in reflected.getPrivateAsyncFunctions()async def inspect_and_refresh() -> int: functions = reflected.getPublicAsyncFunctions() return await functions["refresh_items"]()Imports and source code
Section titled “Imports and source code”getImports
Section titled “getImports”def getImports(self) -> dict: ...With no arguments, return a dict of names whose values are ModuleType objects. import math appears as math; from pathlib import Path is not a module-object import, although Path appears among classes. This does not analyze file statements.
assert "math" in reflected.getImports()assert "Path" not in reflected.getImports()getFile
Section titled “getFile”def getFile(self) -> str: ...With no arguments, return str using inspect.getfile and memoize the path. TypeError propagates for modules without an inspectable file, such as some built-in or dynamically created modules.
assert reflected.getFile().endswith("catalog.py")getSourceCode
Section titled “getSourceCode”def getSourceCode(self) -> str: ...With no arguments, open getFile() as UTF-8, return the complete contents (str) and memoize them. File, decoding and inspection errors are wrapped in ValueError, preserving the cause.
assert "class CatalogPlugin" in reflected.getSourceCode()Change the namespace
Section titled “Change the namespace”These operations modify the module object in memory for all its consumers. They do not edit the Python file or update references already imported into another module. Use them when an extension needs to register or remove a class alias during initialization.
setClass
Section titled “setClass”def setClass(self, class_name: str, cls: type) -> bool: ...class_name: str and cls: type are required. Require a class (TypeError) and a valid identifier that is not a keyword (ValueError). Assign or replace the alias and return True. Only the classes entry is invalidated; call clearCache() before reusing previously calculated visibility filters.
plugin_type = reflected.getClass("CatalogPlugin")assert reflected.setClass("ExtraPlugin", plugin_type) is Truereflected.clearCache()assert reflected.getPublicClasses()["ExtraPlugin"] is plugin_typeremoveClass
Section titled “removeClass”def removeClass(self, class_name: str) -> bool: ...class_name is required. Delete an existing class alias and return True. Names absent from getClasses() raise ValueError. Like setClass, only classes is invalidated; clear the cache to refresh other filters.
assert reflected.removeClass("ExtraPlugin") is Truereflected.clearCache()assert not reflected.hasClass("ExtraPlugin")Wrapper cache
Section titled “Wrapper cache”Result dictionaries are memoized and returned by reference. Do not modify them as if they were independent copies. Each wrapper owns its cache; external module changes do not invalidate it. The dictionary protocol operates on this cache, not on module attributes. No files are written.
clearCache
Section titled “clearCache”def clearCache(self) -> None: ...With no arguments, remove all cached results and return None. This neither reloads the module through importlib.reload nor clears the ModuleInspector class cache.
assert reflected.clearCache() is Noneassert "CatalogPlugin" in reflected.getClasses()__setitem__
Section titled “__setitem__”def __setitem__(self, key: str, value: object) -> None: ...Store value under key: str and return None.
reflected["custom:catalog"] = "loaded"__getitem__
Section titled “__getitem__”def __getitem__(self, key: str) -> object | None: ...Look up key: str and return the value or None; this does not raise KeyError.
assert reflected["custom:catalog"] == "loaded"assert reflected["custom:missing"] is None__contains__
Section titled “__contains__”def __contains__(self, key: str) -> bool: ...Return whether key: str is present in the cache.
assert "custom:catalog" in reflected__delitem__
Section titled “__delitem__”def __delitem__(self, key: str) -> None: ...Remove key: str if present; return None and do not fail for an absent key.
del reflected["custom:catalog"]del reflected["custom:missing"]Avoid writing internal keys such as classes or functions: they directly control wrapper reads.
Use from injected components
Section titled “Use from injected components”The target module is application data. Encapsulate wrapper construction in a service and inject that service through Orionis. Do not register a generic ReflectionModule instance without a module name; reflection itself does not automatically register plugins.
from orionis.http import JSONResponsefrom orionis.introspection import ReflectionModule
class PluginCatalog: def names(self) -> list[str]: module = ReflectionModule("app.plugins.catalog") return sorted(module.getPublicClasses())
class PluginController: async def __call__(self, catalog: PluginCatalog) -> JSONResponse: return JSONResponse({"classes": catalog.names()})The wrapper contract is IReflectionModule, importable from orionis.introspection.modules.contracts.reflection. To inject that contract, explicitly register an instance or factory constructing the wrapper for the chosen module name.