The inspect module provides tools for examining live objects—functions, classes, modules, and stack frames. Essential for debugging, decorators, and metaprogramming.
Function Signatures
import inspect
def greet(name: str, greeting: str = "Hello") -> str:
"""Greet someone."""
return f"{greeting}, {name}!"
sig = inspect.signature(greet)
print(sig) # (name: str, greeting: str = 'Hello') -> str
# Examine parameters
for name, param in sig.parameters.items():
print(f"{name}: default={param.default}, annotation={param.annotation}")
# name: default=<class 'inspect._empty'>, annotation=<class 'str'>
# greeting: default=Hello, annotation=<class 'str'>Binding Arguments
import inspect
def process(a, b, *args, key=None, **kwargs):
pass
sig = inspect.signature(process)
# Bind arguments
bound = sig.bind(1, 2, 3, 4, key="value", extra="data")
print(bound.arguments)
# {'a': 1, 'b': 2, 'args': (3, 4), 'key': 'value', 'kwargs': {'extra': 'data'}}
# Apply defaults
bound.apply_defaults()
print(bound.arguments)Get Source Code
import inspect
def example_function():
"""Example docstring."""
x = 1
return x + 1
# Get source code
source = inspect.getsource(example_function)
print(source)
# Get just docstring
doc = inspect.getdoc(example_function)
print(doc) # "Example docstring."
# Get source file
file = inspect.getfile(example_function)
print(file)
# Get source lines with line numbers
lines, start_line = inspect.getsourcelines(example_function)Examining Classes
import inspect
class MyClass:
class_var = "hello"
def __init__(self, value):
self.value = value
def method(self):
pass
@classmethod
def class_method(cls):
pass
@staticmethod
def static_method():
pass
# Get all members
for name, value in inspect.getmembers(MyClass):
print(f"{name}: {type(value).__name__}")
# Filter by type
methods = inspect.getmembers(MyClass, predicate=inspect.ismethod)
functions = inspect.getmembers(MyClass, predicate=inspect.isfunction)Type Checking Predicates
import inspect
def func(): pass
class MyClass: pass
obj = MyClass()
inspect.isfunction(func) # True
inspect.ismethod(obj.method) # True (bound method)
inspect.isclass(MyClass) # True
inspect.ismodule(inspect) # True
inspect.isgenerator(x for x in []) # True
inspect.iscoroutine(async_func()) # True
inspect.isasyncgen(async_gen()) # TrueCall Stack Inspection
import inspect
def inner():
# Get current frame
frame = inspect.currentframe()
print(f"Current function: {frame.f_code.co_name}")
# Get caller's frame
caller = frame.f_back
print(f"Called by: {caller.f_code.co_name}")
# Get full stack
for frame_info in inspect.stack():
print(f" {frame_info.function} at {frame_info.filename}:{frame_info.lineno}")
def outer():
inner()
outer()FrameInfo Details
import inspect
def get_caller_info():
frame = inspect.currentframe().f_back
info = inspect.getframeinfo(frame)
return {
'filename': info.filename,
'lineno': info.lineno,
'function': info.function,
'code_context': info.code_context,
}
def example():
caller = get_caller_info()
print(f"Called from {caller['function']} at line {caller['lineno']}")
example()Decorator Introspection
import inspect
from functools import wraps
def log_calls(func):
sig = inspect.signature(func)
@wraps(func)
def wrapper(*args, **kwargs):
bound = sig.bind(*args, **kwargs)
bound.apply_defaults()
print(f"Calling {func.__name__} with:")
for name, value in bound.arguments.items():
print(f" {name} = {value!r}")
return func(*args, **kwargs)
return wrapper
@log_calls
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
greet("Alice")
# Calling greet with:
# name = 'Alice'
# greeting = 'Hello'Dependency Injection
import inspect
class Container:
def __init__(self):
self._services = {}
def register(self, cls):
self._services[cls] = cls
def resolve(self, cls):
if cls in self._services:
# Get constructor signature
sig = inspect.signature(cls.__init__)
# Resolve dependencies
kwargs = {}
for name, param in sig.parameters.items():
if name == 'self':
continue
if param.annotation in self._services:
kwargs[name] = self.resolve(param.annotation)
return cls(**kwargs)
raise KeyError(f"Service {cls} not registered")
class Database:
pass
class UserService:
def __init__(self, db: Database):
self.db = db
container = Container()
container.register(Database)
container.register(UserService)
service = container.resolve(UserService)
print(type(service.db)) # <class 'Database'>Getting Module Members
import inspect
import os
# Get all functions in a module
functions = inspect.getmembers(os, inspect.isfunction)
print(f"Functions in os: {len(functions)}")
# Get all classes
classes = inspect.getmembers(os, inspect.isclass)
# Get module from object
module = inspect.getmodule(os.path.join)
print(module.__name__) # posixpath or ntpathAsync Function Detection
import inspect
import asyncio
def sync_func():
pass
async def async_func():
pass
async def async_gen():
yield 1
print(inspect.iscoroutinefunction(sync_func)) # False
print(inspect.iscoroutinefunction(async_func)) # True
print(inspect.isasyncgenfunction(async_gen)) # TrueParameter Kinds
import inspect
def func(pos_only, /, pos_or_kw, *args, kw_only, **kwargs):
pass
sig = inspect.signature(func)
for name, param in sig.parameters.items():
print(f"{name}: {param.kind.name}")
# pos_only: POSITIONAL_ONLY
# pos_or_kw: POSITIONAL_OR_KEYWORD
# args: VAR_POSITIONAL
# kw_only: KEYWORD_ONLY
# kwargs: VAR_KEYWORDClass Hierarchy
import inspect
class A: pass
class B(A): pass
class C(B): pass
# Get base classes
print(inspect.getmro(C)) # (C, B, A, object)
# Check inheritance
print(issubclass(C, A)) # TrueCleandoc
import inspect
def example():
"""
This is a docstring.
With some indentation.
And more text.
"""
pass
# Clean up docstring formatting
clean = inspect.cleandoc(example.__doc__)
print(clean)
# This is a docstring.
#
# With some indentation.
#
# And more text.Practical: Auto-Documentation
import inspect
def document_class(cls):
"""Generate documentation for a class."""
doc = [f"# {cls.__name__}"]
if cls.__doc__:
doc.append(f"\n{inspect.cleandoc(cls.__doc__)}\n")
doc.append("\n## Methods\n")
for name, method in inspect.getmembers(cls, inspect.isfunction):
if name.startswith('_'):
continue
sig = inspect.signature(method)
doc.append(f"### {name}{sig}")
if method.__doc__:
doc.append(f"\n{inspect.cleandoc(method.__doc__)}\n")
return '\n'.join(doc)The inspect module reveals Python's internals at runtime. Use it for debugging tools, documentation generators, decorators, and framework development.
React to this post: