Python's inspect module helps you understand live objects at runtime. It can identify functions and classes, read a callable's signature, list members, and retrieve source code when source is available. It is useful for debugging, documentation tools, plugin systems, and tests. Inspection is not the same as a stable public API, so code that depends on introspection should handle built-ins, generated objects, wrappers, and unavailable source gracefully.
Quick answer
Use inspect.signature(callable) to inspect parameters, inspect.getmembers(obj) to list attributes, inspect.isfunction(obj) to identify Python functions, and inspect.isclass(obj) to identify classes. Use inspect.getsource(obj) only when source code is available. Wrap source inspection in an exception handler because built-in and dynamically generated objects may not have retrievable source.
The official inspect documentation covers the module's callable, source, member, and predicate helpers. The safest pattern is to inspect a known object, request one specific fact, and provide a fallback when that fact is unavailable.

Inspect a callable signature
inspect.signature() returns a Signature object describing parameters and defaults for many Python callables. It is valuable when a decorator, command builder, or documentation generator needs to understand how a function is called.
import inspect
def connect(host, port=443, *, timeout=10):
return host, port, timeout
signature = inspect.signature(connect)
print(signature)
for name, parameter in signature.parameters.items():
print(name, parameter.kind, parameter.default)
Signatures can be customized by decorators or unavailable for some extension types. Treat the result as introspection data, not a guarantee that every implementation detail is stable across versions.

Identify functions and classes
The predicate helpers make type checks readable when the application accepts a broad object. isfunction() identifies user-defined Python functions, while isclass() identifies class objects. Other predicates such as ismethod() and isbuiltin() can distinguish related callable forms.
import inspect
def helper():
return "ok"
class Worker:
pass
objects = [helper, Worker, len, Worker()]
for obj in objects:
print(
type(obj).__name__,
inspect.isfunction(obj),
inspect.isclass(obj),
inspect.isbuiltin(obj),
)
Do not use the predicate result as a substitute for a protocol check when the code only needs a capability. If the object can be called or has a specific method, checking that contract may be more flexible than requiring one exact Python type.
List members with getmembers()
inspect.getmembers() returns sorted name-value pairs for attributes found on an object. An optional predicate can narrow the list, such as functions or methods. This is useful for plugin discovery and diagnostics, but property access can execute code on custom objects.
import inspect
class Service:
def start(self):
return "started"
def stop(self):
return "stopped"
service_methods = inspect.getmembers(service := Service(), inspect.ismethod)
print([name for name, value in service_methods])
Keep member scans bounded and avoid exposing arbitrary object internals to untrusted input. A property or descriptor may have side effects or raise an exception when accessed. For a plugin system, prefer an explicit registration interface when possible.

Read source code when it exists
inspect.getsource() can return the source text for a Python function, class, or module when the source file is available. It may raise OSError or TypeError for built-ins, interactive definitions, generated functions, and extension objects.
import inspect
def sample(value):
return value * 2
try:
source = inspect.getsource(sample)
except (OSError, TypeError) as error:
source = f"source unavailable: {error}"
print(source)
Source retrieval is best for developer tools and local diagnostics, not as a runtime dependency for core business behavior. Packaging, optimization, decorators, and deployment can change what source is available.
Find the defining module
inspect.getmodule() can identify the module associated with an object, while inspect.getfile() and inspect.getsourcefile() can provide file information when available. These helpers should be treated as hints because built-in and extension objects may not map to a Python source file.
import inspect
import math
def local_function():
return None
for obj in [local_function, math.sqrt, len]:
print(
getattr(inspect.getmodule(obj), "__name__", None),
inspect.getsourcefile(obj),
)
Do not print filesystem paths in a public error response. Paths can reveal deployment details. Use them in controlled logs or developer tools and redact sensitive directory names when sharing diagnostics.

Inspect annotations and parameters
Function annotations are available through inspect.signature() and the function's __annotations__ attribute. An annotation is metadata, not runtime validation. A tool can display it, but the application should still validate values at the boundary.
import inspect
def add(left: int, right: int = 0) -> int:
return left + right
signature = inspect.signature(add)
print(signature.return_annotation)
for parameter in signature.parameters.values():
print(parameter.name, parameter.annotation)
When annotations are postponed or stored as strings, use the documented annotation-resolution tools carefully. Evaluation can execute names or imports, so do not resolve annotations from untrusted modules merely to display them.
Build a safe inspection helper
A small helper can return structured, bounded facts rather than dumping an entire object. This makes logs easier to read and reduces the chance that inspection triggers expensive or sensitive behavior.
import inspect
def describe(obj):
result = {
"type": type(obj).__qualname__,
"is_function": inspect.isfunction(obj),
"is_class": inspect.isclass(obj),
"callable": callable(obj),
}
if callable(obj):
try:
result["signature"] = str(inspect.signature(obj))
except (TypeError, ValueError):
result["signature"] = None
return result
print(describe(len))
print(describe(lambda value: value))
Keep the output contract stable if another tool consumes it. Inspection code should degrade gracefully when the object is a built-in, a proxy, a decorated function, or an object whose attributes are not safe to enumerate.

Common mistakes
- Assuming every callable has retrievable Python source.
- Using introspection instead of an explicit plugin or protocol contract.
- Scanning arbitrary members and accidentally triggering properties.
- Logging source paths or source text that contains sensitive information.
- Treating annotations or signatures as runtime type validation.
The pragmatic use of inspect is targeted and defensive: identify the object, request the smallest fact needed, handle unavailable metadata, and keep introspection outside the critical path when possible. That gives developers useful runtime visibility without coupling the application to implementation details.
Keep introspection out of critical behavior
Inspection is most valuable at development and integration boundaries, where it can explain an object that a developer did not expect. Core application behavior should prefer explicit interfaces because decorators, packaging, alternative Python implementations, and native extensions can all change what inspect can see.
When inspection output is saved, include the Python version and a stable object identifier when possible. This gives a future reader context without requiring the original process, filesystem, or source layout to remain available.
For runtime introspection, compare inspect with help() and traceback diagnostics. Read python help function and python traceback for the related workflow.
Frequently Asked Questions
Frequently Asked Questions
What is the inspect module used for in Python?
inspect provides runtime tools for examining callable signatures, source, members, and object or class types.
How do I inspect a function signature?
Call inspect.signature(function) and read the returned Signature and Parameter objects.
Why can inspect.getsource() fail?
Built-ins, extension objects, interactive definitions, and generated or wrapped objects may not expose retrievable Python source.
Does inspect perform runtime type validation?
No. Signatures and annotations are metadata; application code still needs explicit input validation.