How Python Inspect Module Introspects Objects
Python's built-in inspect module provides a
comprehensive suite of tools to examine live objects, classes,
functions, and active execution frames at runtime. By tapping directly
into the CPython runtime's internal data structures, type systems, and
evaluation stack, it translates raw interpreter state into
human-readable, programmatic metadata. This article explores how
inspect works under the hood to extract object attributes,
trace class hierarchies, inspect execution frames, and retrieve original
source code.
Inspecting Live Objects and Types
At its core, Python treats everything as an object, backed by a
C-level structure (like PyObject) containing a reference
count, a type pointer (ob_type), and often an internal
dictionary (__dict__). The inspect module
leverages these native hooks:
- Type Predicates: Functions such as
inspect.isfunction(),inspect.isclass(), andinspect.isgenerator()check the object's__class__or match its internalPyTypeObjectflags against standard types in thetypesmodule (e.g.,types.FunctionType). - Member Retrieval:
inspect.getmembers()traverses an object's namespace by combiningdir(object)withgetattr(), resolving descriptors and filtering results using custom predicates. - Signatures and Callables:
inspect.signature()analyzes callable objects by inspecting their underlying code object (__code__), default argument tuples (__defaults__and__kwdefaults__), and type annotations (__annotations__). It builds an immutableSignatureobject mapping each parameter name, kind (positional, keyword, variadic), and default value.
Class Inspection and Method Resolution
When analyzing classes, inspect inspects the structure
of Python's inheritance system:
- MRO Resolution: The module uses the class's
__mro__attribute (Method Resolution Order) to determine the exact order in which attributes and methods are inherited according to the C3 linearization algorithm. - Class Hierarchies: Functions like
inspect.getclasstree()walk the__bases__attribute of given classes, constructing nested list structures that represent inheritance trees. - Method Classification:
inspect.classify_class_attrs()distinguishes between regular methods, class methods, static methods, properties, and plain data attributes by analyzing descriptor objects directly inside the class dictionary without triggering descriptor__get__calls.
Execution Frames and Call Stack Introspection
Python executes code within a stack of frame objects
(PyFrameObject), which represent the execution context of
functions or modules. The inspect module accesses this
stack through low-level hooks like sys._getframe():
- Frame Objects: An individual frame contains the
current execution state, including local variables
(
f_locals), global variables (f_globals), the currently executing code object (f_code), the line number (f_lineno), and a reference to the calling frame (f_back). - Stack Traversal: Functions like
inspect.currentframe()andinspect.stack()start from the active frame and traverse the linked list off_backpointers backward to the root of the call stack. - Traceback Extraction: When combined with
tracebacks,
inspectmaps the current bytecode instruction pointer (f_lasti) to concrete line numbers, allowing detailed debugging and dynamic runtime logging.
Source Code Retrieval and Line Caching
One of inspect's most powerful capabilities is
retrieving the original source code of live objects:
- Locating the File: The module retrieves the
co_filenameandco_firstlinenofields from a callable's__code__attribute or searches a class's module namespace (__module__). - Reading the Code: It uses Python's internal
linecachemodule to read the source file from disk into memory, preventing redundant I/O operations. - Tokenizing and Parsing: To locate where a
multi-line function or class ends,
inspect.getsource()uses thetokenizemodule to tokenize the Python code starting fromco_firstlineno. It tracks indentation levels and statement boundaries, ensuring that only the relevant block of code is returned.