Claude Code for inspect: Runtime Introspection in Python — Claude Skills 360 Blog
Blog / AI / Claude Code for inspect: Runtime Introspection in Python
AI

Claude Code for inspect: Runtime Introspection in Python

Published: July 29, 2028
Read time: 5 min read
By: Claude Skills 360

Python’s inspect module examines live objects: classes, functions, frames, and source code. import inspect. getmembers: inspect.getmembers(obj) → list of (name, value) pairs. getmembers_static: inspect.getmembers_static(obj) (3.11+) — no descriptors triggered. isfunction: inspect.isfunction(obj). ismethod: inspect.ismethod(obj). isclass: inspect.isclass(obj). ismodule: inspect.ismodule(obj). iscoroutinefunction: inspect.iscoroutinefunction(fn). isgeneratorfunction: inspect.isgeneratorfunction(fn). isasyncgenfunction: inspect.isasyncgenfunction(fn). isbuiltin: inspect.isbuiltin(obj). signature: sig = inspect.signature(fn)Signature; sig.parametersOrderedDict[str, Parameter]. Parameter: .name, .default, .annotation, .kind (POSITIONAL_ONLY, POSITIONAL_OR_KEYWORD, VAR_POSITIONAL, KEYWORD_ONLY, VAR_KEYWORD). bind: ba = sig.bind(1, x=2)BoundArguments; .arguments, .apply_defaults(). getsource: inspect.getsource(obj) → source string. getsourcelines: (lines, lineno). getsourcefile: source file path. getdoc: inspect.getdoc(obj) → cleaned docstring or None. cleandoc: inspect.cleandoc(raw). stack: inspect.stack() → list of FrameInfo; [0] is current frame. currentframe: inspect.currentframe() → frame object. getframeinfo: inspect.getframeinfo(frame)Traceback(filename, lineno, function, code_context, index). getannotations: inspect.get_annotations(obj) (3.10+). Claude Code generates plugin loaders, dependency injectors, validation decorators, and debug helpers.

CLAUDE.md for inspect

## inspect Stack
- Stdlib: import inspect
- Signature: sig = inspect.signature(fn); sig.parameters
- Source:    inspect.getsource(cls_or_fn)
- Type test: inspect.isfunction/isclass/iscoroutinefunction(obj)
- Docstring: inspect.getdoc(obj)
- Stack:     inspect.stack()[1]  — caller frame info
- Members:   inspect.getmembers(obj, predicate)

inspect Introspection Pipeline

# app/reflectutil.py — signature, members, source, stack, docstring, injection
from __future__ import annotations

import inspect
import textwrap
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any, get_type_hints


# ─────────────────────────────────────────────────────────────────────────────
# 1. Callable introspection
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class ParamInfo:
    name:       str
    kind:       str          # "positional", "keyword", "var_positional", "var_keyword"
    annotation: Any          # inspect.Parameter.empty if unannotated
    default:    Any          # inspect.Parameter.empty if required
    required:   bool

    @classmethod
    def from_param(cls, p: inspect.Parameter) -> "ParamInfo":
        kind_map = {
            inspect.Parameter.POSITIONAL_ONLY:          "positional",
            inspect.Parameter.POSITIONAL_OR_KEYWORD:    "positional_or_keyword",
            inspect.Parameter.VAR_POSITIONAL:           "var_positional",
            inspect.Parameter.KEYWORD_ONLY:             "keyword_only",
            inspect.Parameter.VAR_KEYWORD:              "var_keyword",
        }
        return cls(
            name=p.name,
            kind=kind_map.get(p.kind, str(p.kind)),
            annotation=p.annotation,
            default=p.default,
            required=p.default is inspect.Parameter.empty and p.kind not in (
                inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD
            ),
        )


def signature_info(fn: Callable) -> dict[str, Any]:
    """
    Return structured information about a callable's signature.

    Example:
        def greet(name: str, greeting: str = "Hello") -> str: ...
        info = signature_info(greet)
        info["params"]["name"].required   # True
        info["params"]["greeting"].default  # "Hello"
        info["return_annotation"]           # str
    """
    sig = inspect.signature(fn)
    params = {name: ParamInfo.from_param(p) for name, p in sig.parameters.items()}
    return {
        "name":              getattr(fn, "__name__", repr(fn)),
        "params":            params,
        "return_annotation": sig.return_annotation,
        "is_async":          inspect.iscoroutinefunction(fn),
        "is_generator":      inspect.isgeneratorfunction(fn),
        "is_method":         inspect.ismethod(fn),
        "docstring":         inspect.getdoc(fn) or "",
    }


def required_params(fn: Callable) -> list[str]:
    """
    Return names of all required (non-default, non-*args/**kwargs) parameters.

    Example:
        required_params(open)  # ["file"]
    """
    sig = inspect.signature(fn)
    return [
        name for name, p in sig.parameters.items()
        if p.default is inspect.Parameter.empty and p.kind not in (
            inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD
        )
    ]


def call_with_subset(fn: Callable, available: dict[str, Any]) -> Any:
    """
    Call fn using only the keyword arguments from `available` that fn accepts.
    Useful for dependency injection / loose coupling.

    Example:
        def handler(user_id: int, db): ...
        result = call_with_subset(handler, {"user_id": 1, "db": db, "extra": "ignored"})
    """
    sig = inspect.signature(fn)
    accepted  = {
        name for name, p in sig.parameters.items()
        if p.kind in (
            inspect.Parameter.POSITIONAL_OR_KEYWORD,
            inspect.Parameter.KEYWORD_ONLY,
        )
    }
    has_var_kw = any(
        p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()
    )
    if has_var_kw:
        kwargs = available
    else:
        kwargs = {k: v for k, v in available.items() if k in accepted}
    return fn(**kwargs)


def annotated_params(fn: Callable) -> dict[str, type]:
    """
    Return a dict of {param_name: annotation} for annotated parameters.
    Resolves forward references using get_type_hints.

    Example:
        def f(x: int, y: str = "") -> bool: ...
        annotated_params(f)  # {"x": int, "y": str}
    """
    try:
        hints = get_type_hints(fn)
    except Exception:
        hints = {}
    hints.pop("return", None)
    return hints


# ─────────────────────────────────────────────────────────────────────────────
# 2. Class and object inspection
# ─────────────────────────────────────────────────────────────────────────────

def public_methods(obj: Any) -> list[str]:
    """
    Return names of all public callable members (methods) of obj.

    Example:
        public_methods(MyClass())  # ["compute", "reset", "save"]
    """
    return [
        name for name, val in inspect.getmembers(obj, predicate=inspect.ismethod)
        if not name.startswith("_")
    ]


def public_functions(module: Any) -> list[str]:
    """
    Return names of all public functions defined in a module (not imported).

    Example:
        import mymodule
        public_functions(mymodule)  # ["do_thing", "process"]
    """
    mod_file = getattr(module, "__file__", None)
    return [
        name for name, fn in inspect.getmembers(module, predicate=inspect.isfunction)
        if not name.startswith("_")
        and (mod_file is None or inspect.getfile(fn) == mod_file)
    ]


def class_hierarchy(cls: type) -> list[str]:
    """
    Return the MRO class names for a class.

    Example:
        class_hierarchy(int)  # ["int", "object"]
    """
    return [c.__name__ for c in inspect.getmro(cls)]


def list_subclasses(cls: type, recursive: bool = True) -> list[type]:
    """
    Return all known subclasses of cls loaded in the current runtime.

    Example:
        list_subclasses(BaseHandler)  # [JSONHandler, HTMLHandler, ...]
    """
    direct = cls.__subclasses__()
    if not recursive:
        return direct
    all_subs: list[type] = []
    queue = list(direct)
    while queue:
        sub = queue.pop()
        all_subs.append(sub)
        queue.extend(sub.__subclasses__())
    return all_subs


def object_summary(obj: Any) -> dict[str, Any]:
    """
    Return a summary dict describing obj's type and public interface.

    Example:
        summary = object_summary(some_instance)
    """
    cls = type(obj)
    return {
        "type":        cls.__name__,
        "module":      cls.__module__,
        "mro":         class_hierarchy(cls),
        "methods":     public_methods(obj),
        "is_async":    inspect.iscoroutinefunction(obj),
        "docstring":   inspect.getdoc(cls) or "",
        "source_file": inspect.getsourcefile(cls) or "built-in",
    }


# ─────────────────────────────────────────────────────────────────────────────
# 3. Source code retrieval
# ─────────────────────────────────────────────────────────────────────────────

def get_source(obj: Any) -> str | None:
    """
    Return the source code of a function, class, or method.
    Returns None if source is unavailable (built-in, compiled extension, REPL).

    Example:
        print(get_source(MyClass.my_method))
    """
    try:
        return inspect.getsource(obj)
    except (OSError, TypeError):
        return None


def get_source_lines(obj: Any) -> tuple[list[str], int] | None:
    """
    Return (source_lines, start_lineno) or None if unavailable.

    Example:
        lines, lineno = get_source_lines(fn) or ([], 0)
    """
    try:
        return inspect.getsourcelines(obj)
    except (OSError, TypeError):
        return None


def source_location(obj: Any) -> str:
    """
    Return a 'file:lineno' string for obj or '<unknown>' if unavailable.

    Example:
        source_location(MyClass.method)  # "src/myclass.py:42"
    """
    try:
        file  = inspect.getsourcefile(obj) or inspect.getfile(obj)
        lines, lineno = inspect.getsourcelines(obj)
        return f"{file}:{lineno}"
    except (OSError, TypeError):
        return "<unknown>"


# ─────────────────────────────────────────────────────────────────────────────
# 4. Call stack helpers
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class FrameSnapshot:
    filename: str
    lineno:   int
    function: str
    code:     str


def caller_info(depth: int = 1) -> FrameSnapshot:
    """
    Return info about the caller at the given stack depth.
    depth=1 → immediate caller; depth=2 → caller's caller, etc.

    Example:
        def log(msg):
            loc = caller_info(depth=1)
            print(f"[{loc.filename}:{loc.lineno}] {msg}")
    """
    frame = inspect.stack()[depth + 1]
    code = (frame.code_context[0].strip() if frame.code_context else "")
    return FrameSnapshot(
        filename=frame.filename,
        lineno=frame.lineno,
        function=frame.function,
        code=code,
    )


def call_stack(max_depth: int = 10, skip: int = 1) -> list[FrameSnapshot]:
    """
    Return a list of FrameSnapshot for the current call stack.

    Example:
        for frame in call_stack():
            print(f"  {frame.function} at {frame.filename}:{frame.lineno}")
    """
    frames = inspect.stack()[skip:skip + max_depth]
    result = []
    for f in frames:
        code = (f.code_context[0].strip() if f.code_context else "")
        result.append(FrameSnapshot(f.filename, f.lineno, f.function, code))
    return result


def format_stack(max_depth: int = 8) -> str:
    """
    Return a formatted multi-line string of the current call stack (for logging).

    Example:
        logger.debug("stack at save():\n" + format_stack())
    """
    lines = []
    for snap in call_stack(max_depth=max_depth, skip=2):
        lines.append(f"  {snap.function}() @ {snap.filename}:{snap.lineno}")
        if snap.code:
            lines.append(f"    → {snap.code}")
    return "\n".join(lines)


# ─────────────────────────────────────────────────────────────────────────────
# 5. Plugin / registry helpers
# ─────────────────────────────────────────────────────────────────────────────

def find_implementations(base_cls: type, module: Any) -> list[type]:
    """
    Find all concrete subclasses of base_cls defined in module.

    Example:
        handlers = find_implementations(BaseHandler, handlers_module)
    """
    return [
        cls for _, cls in inspect.getmembers(module, predicate=inspect.isclass)
        if issubclass(cls, base_cls) and cls is not base_cls and not inspect.isabstract(cls)
        and cls.__module__ == getattr(module, "__name__", None)
    ]


def auto_wire(cls: type, registry: dict[str, Any]) -> Any:
    """
    Instantiate cls by injecting constructor parameters from registry by name.

    Example:
        registry = {"db": db_conn, "cache": cache, "logger": logger}
        service = auto_wire(UserService, registry)
    """
    return call_with_subset(cls, registry)


def method_table(obj: Any) -> dict[str, dict[str, Any]]:
    """
    Return a dict mapping public method names to their signature info.

    Example:
        table = method_table(my_service)
        for name, info in table.items():
            print(f"  {name}({', '.join(info['params'])})")
    """
    return {
        name: {"params": list(signature_info(method)["params"].keys()),
               "is_async": inspect.iscoroutinefunction(method),
               "docstring": inspect.getdoc(method) or ""}
        for name in public_methods(obj)
        for method in [getattr(obj, name)]
    }


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    import asyncio

    print("=== inspect demo ===")

    print("\n--- signature_info ---")
    def process(
        items: list[str],
        limit: int = 100,
        *,
        verbose: bool = False,
        callback: Callable | None = None,
    ) -> dict:
        """Process a list of items up to limit."""
        ...

    info = signature_info(process)
    print(f"  name: {info['name']}")
    for name, p in info["params"].items():
        req = "required" if p.required else f"default={p.default!r}"
        print(f"  param {name!r}: kind={p.kind}  {req}")
    print(f"  return: {info['return_annotation']}")
    print(f"  is_async: {info['is_async']}")

    print("\n--- required_params ---")
    print(f"  required_params(process): {required_params(process)}")
    print(f"  required_params(print):   {required_params(print)}")

    print("\n--- call_with_subset ---")
    def add(x: int, y: int) -> int:
        return x + y

    result = call_with_subset(add, {"x": 10, "y": 20, "z": 99, "extra": "ignored"})
    print(f"  add called with subset: {result}")

    print("\n--- annotated_params ---")
    def login(username: str, password: str, remember: bool = False) -> bool: ...
    print(f"  annotated_params(login): {annotated_params(login)}")

    print("\n--- class_hierarchy ---")
    print(f"  class_hierarchy(bool): {class_hierarchy(bool)}")

    print("\n--- list_subclasses ---")
    class Animal:
        pass
    class Dog(Animal):
        pass
    class Cat(Animal):
        pass
    class Labrador(Dog):
        pass

    subs = list_subclasses(Animal)
    print(f"  subclasses of Animal: {[c.__name__ for c in subs]}")

    print("\n--- source code ---")
    src = get_source(add)
    if src:
        print(f"  source of add():\n{textwrap.indent(src.strip(), '    ')}")
    loc = source_location(process)
    print(f"  source_location(process): {loc}")

    print("\n--- call_stack ---")
    def inner():
        return call_stack(max_depth=3)

    def outer():
        return inner()

    stack = outer()
    for snap in stack:
        print(f"  {snap.function}() @ ...:{snap.lineno}")

    print("\n--- method_table ---")
    class Calculator:
        """Simple calculator."""
        def add(self, a: int, b: int) -> int:
            """Add two numbers."""
            return a + b
        def multiply(self, a: int, b: int) -> int:
            """Multiply two numbers."""
            return a * b
        async def async_compute(self, x: float) -> float:
            """Async compute."""
            return x * 2.0

    calc = Calculator()
    table = method_table(calc)
    for name, meta in table.items():
        async_label = " [async]" if meta["is_async"] else ""
        print(f"  {name}({', '.join(meta['params'])}){async_label}{meta['docstring']}")

    print("\n--- auto_wire ---")
    class Logger:
        def log(self, msg: str) -> None:
            print(f"  [LOG] {msg}")

    class ReportService:
        def __init__(self, logger: Logger, title: str = "Report") -> None:
            self.logger = logger
            self.title  = title
        def run(self) -> None:
            self.logger.log(f"Running '{self.title}'")

    logger_instance = Logger()
    svc = auto_wire(ReportService, {"logger": logger_instance, "title": "Sales Report"})
    svc.run()

    print("\n=== done ===")

For the dir / vars alternative — Python’s built-ins dir(obj) returns all attribute names including inherited ones, vars(obj) returns obj.__dict__, and type(obj).__mro__ gives the method resolution order; they are fast and always available but return raw names with no metadata about kinds, defaults, or annotations; inspect provides structured Parameter, Signature, FrameInfo, and MemberDescriptorType objects with full annotations — use dir/vars for quick interactive exploration or simple attribute checks, inspect when you need structural information for frameworks, decorators, code generators, or debugging tools. For the ast alternative — Python’s ast module parses source text into an Abstract Syntax Tree for static analysis: ast.parse(source), ast.walk(tree), ast.NodeVisitor; inspect works on live objects and retrieves source via getsource (which may fail for REPL/eval code); ast does not require importing the module — use ast for linters, code transformers, security scanners, and static import graph analysis, inspect for runtime plugins, dependency injectors, debuggers, and reflection APIs that operate on already-loaded objects. The Claude Skills 360 bundle includes inspect skill sets covering ParamInfo/signature_info()/required_params() signature introspection, call_with_subset()/annotated_params() dependency injection helpers, public_methods()/public_functions()/class_hierarchy()/list_subclasses()/object_summary() class reflection, get_source()/source_location() source retrieval, FrameSnapshot/caller_info()/call_stack()/format_stack() stack inspection, and find_implementations()/auto_wire()/method_table() plugin and registry utilities. Start with the free tier to try runtime introspection and inspect pipeline code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free