Extract Python API signatures and detect breaking changes for documentation generation.
npx @tessl/cli install tessl/pypi-griffe@1.13.0Griffe is a comprehensive Python library that extracts signatures and API information from entire Python programs. It provides both static analysis (AST parsing) and dynamic analysis (runtime inspection) capabilities to generate API documentation, detect breaking changes, and support development workflows requiring detailed code structure analysis.
pip install griffegriffe command-line toolpip install griffe[pypi] for enhanced Git/PyPI integrationimport griffeCommon imports for specific functionality:
# Loading packages
from griffe import load, load_git, load_pypi
# CLI functions
from griffe import main, check, dump
# Core models
from griffe import Module, Class, Function, Attribute, Alias
# Analysis agents
from griffe import visit, inspect
# Breaking change detection
from griffe import find_breaking_changes, Breakage
# Docstring parsing
from griffe import parse, parse_auto, parse_google, parse_numpy, parse_sphinx
# Serialization
from griffe import JSONEncoder, json_decoderimport griffe
# Load a package from the current environment
package = griffe.load("requests")
print(f"Package: {package.name}")
print(f"Modules: {list(package.modules.keys())}")
# Load from specific Git reference
old_version = griffe.load_git("mypackage", ref="v1.0.0")
current_version = griffe.load("mypackage")
# Load from PyPI
pypi_package = griffe.load_pypi("django", "Django", "4.2.0")import griffe
# Compare two versions of a package
old_api = griffe.load_git("mypackage", ref="v1.0.0")
new_api = griffe.load("mypackage")
# Find breaking changes
breakages = griffe.find_breaking_changes(old_api, new_api)
for breakage in breakages:
print(f"Breaking change: {breakage}")# Dump package API as JSON
griffe dump fastapi httpx
# Check for breaking changes
griffe check mypackage --verbose
# With src layout
griffe check --search src mypackage --verboseGriffe is built around several key architectural concepts:
visit() (AST parsing) and dynamic analysis via inspect() (runtime inspection)Module, Class, Function, Attribute, Alias) that form the API structureload, load_git, load_pypi) that orchestrate the analysis processCommand-line interface functions that can be called from Python or as CLI commands.
def main() -> int:
"""
Run the main griffe program.
Returns:
int: Exit code (0 for success)
"""
def check(*args, **kwargs) -> int:
"""
Check for API breaking changes between versions.
Returns:
int: Exit code (0 if no breaking changes)
"""
def dump(*args, **kwargs) -> int:
"""
Load packages and dump API data as JSON.
Returns:
int: Exit code (0 for success)
"""High-level functions for loading Python packages and modules from different sources.
def load(
objspec: str | Path | None = None,
/,
*,
submodules: bool = True,
try_relative_path: bool = True,
extensions: Extensions | None = None,
search_paths: Sequence[str | Path] | None = None,
docstring_parser: DocstringStyle | Parser | None = None,
docstring_options: dict[str, Any] | None = None,
lines_collection: LinesCollection | None = None,
modules_collection: ModulesCollection | None = None,
allow_inspection: bool = True,
force_inspection: bool = False,
store_source: bool = True,
find_stubs_package: bool = False,
resolve_aliases: bool = False,
resolve_external: bool | None = None,
resolve_implicit: bool = False,
) -> Object | Alias: ...
def load_git(
objspec: str,
ref: str = "HEAD",
*,
repo: str | Path = ".",
submodules: bool = True,
**load_kwargs: Any,
) -> Object | Alias: ...
def load_pypi(
package: str,
distribution: str,
version_spec: str,
*,
submodules: bool = True,
extensions: Extensions | None = None,
search_paths: Sequence[str | Path] | None = None,
docstring_parser: DocstringStyle | Parser | None = None,
docstring_options: dict[str, Any] | None = None,
lines_collection: LinesCollection | None = None,
modules_collection: ModulesCollection | None = None,
allow_inspection: bool = True,
force_inspection: bool = False,
find_stubs_package: bool = False,
) -> Object | Alias: ...The primary classes representing Python API elements.
class Object:
"""Base class for all Griffe objects."""
@property
def name(self) -> str: ...
@property
def path(self) -> str: ...
def serialize(self, **kwargs: Any) -> dict[str, Any]: ...
class Module(Object):
"""A Python module."""
@property
def filepath(self) -> Path | None: ...
@property
def modules(self) -> dict[str, Module]: ...
@property
def classes(self) -> dict[str, Class]: ...
@property
def functions(self) -> dict[str, Function]: ...
@property
def attributes(self) -> dict[str, Attribute]: ...
class Class(Object):
"""A Python class."""
@property
def bases(self) -> list[Expr]: ...
@property
def decorators(self) -> list[Decorator]: ...
@property
def methods(self) -> dict[str, Function]: ...
class Function(Object):
"""A Python function or method."""
@property
def parameters(self) -> Parameters: ...
@property
def returns(self) -> Expr | None: ...
@property
def decorators(self) -> list[Decorator]: ...
class Attribute(Object):
"""A Python attribute."""
@property
def annotation(self) -> Expr | None: ...
@property
def value(self) -> Expr | None: ...
class Alias(Object):
"""An alias or import reference to an object."""
@property
def target_path(self) -> str: ...
def resolve(self) -> Object | None: ...Static and dynamic analysis functions for extracting API information.
def visit(
module: Module,
filepath: str | Path,
code: str | None = None,
extensions: Extensions | None = None,
parent: Module | None = None,
in_try_block: bool = False,
) -> Module: ...
def inspect(
module_name: str,
filepath: str | Path | None = None,
parent: Module | None = None,
) -> Module: ...Functions and classes for detecting breaking changes between API versions.
def find_breaking_changes(
old_object: Object,
new_object: Object,
**kwargs: Any,
) -> Iterator[Breakage]: ...
class Breakage:
"""Base class for API breakages."""
@property
def kind(self) -> BreakageKind: ...
@property
def object_path(self) -> str: ...
class ObjectRemovedBreakage(Breakage):
"""A public object was removed."""
class ParameterAddedRequiredBreakage(Breakage):
"""A required parameter was added to a function."""
class ReturnChangedTypeBreakage(Breakage):
"""A function's return type annotation changed."""JSON encoding and decoding for Griffe objects.
class JSONEncoder(json.JSONEncoder):
"""JSON encoder for Griffe objects."""
def default(self, obj: Any) -> Any: ...
def json_decoder(dct: dict[str, Any]) -> dict[str, Any] | Object:
"""
Decode Griffe objects from JSON.
Args:
dct: Dictionary from JSON decoder
Returns:
Decoded object or dictionary
"""Comprehensive docstring parsing supporting multiple formats.
def parse(
text: str,
parser: Parser | str | None = None,
**options: Any,
) -> list[DocstringSection]: ...
def parse_auto(text: str, **options: Any) -> list[DocstringSection]: ...
def parse_google(text: str, **options: Any) -> list[DocstringSection]: ...
def parse_numpy(text: str, **options: Any) -> list[DocstringSection]: ...
def parse_sphinx(text: str, **options: Any) -> list[DocstringSection]: ...Extensible plugin system for custom analysis.
class Extension:
"""Base class for Griffe extensions."""
def on_package_loaded(self, *, pkg: Module) -> None: ...
def on_module_loaded(self, *, mod: Module) -> None: ...
def on_class_loaded(self, *, cls: Class) -> None: ...
def on_function_loaded(self, *, func: Function) -> None: ...
def load_extensions(
extensions: list[LoadableExtensionType] | None = None
) -> Extensions: ...from pathlib import Path
from typing import Any, Sequence, IO
# Collection types
LinesCollection = dict[str, dict[int, str]]
ModulesCollection = dict[str, Module]
Extensions = dict[str, Extension]
# Docstring and parser types
DocstringStyle = str
ExplanationStyle = strfrom enum import Enum
class Kind(Enum):
"""Object kinds."""
MODULE = "module"
CLASS = "class"
FUNCTION = "function"
ATTRIBUTE = "attribute"
class ParameterKind(Enum):
"""Parameter kinds."""
POSITIONAL_ONLY = 0
POSITIONAL_OR_KEYWORD = 1
VAR_POSITIONAL = 2
KEYWORD_ONLY = 3
VAR_KEYWORD = 4
class Parser(Enum):
"""Docstring parser types."""
AUTO = "auto"
GOOGLE = "google"
NUMPY = "numpy"
SPHINX = "sphinx"
class BreakageKind(Enum):
"""API breakage types."""
OBJECT_REMOVED = "object_removed"
PARAMETER_ADDED_REQUIRED = "parameter_added_required"
PARAMETER_REMOVED = "parameter_removed"
RETURN_CHANGED_TYPE = "return_changed_type"class Parameter:
"""A function parameter."""
def __init__(
self,
name: str,
annotation: Expr | None = None,
default: Expr | None = None,
kind: ParameterKind = ParameterKind.POSITIONAL_OR_KEYWORD,
) -> None: ...
@property
def name(self) -> str: ...
@property
def annotation(self) -> Expr | None: ...
@property
def default(self) -> Expr | None: ...
class Parameters:
"""Container for function parameters."""
def __iter__(self) -> Iterator[Parameter]: ...
def __getitem__(self, key: int | str) -> Parameter: ...
def __len__(self) -> int: ...class Expr:
"""Base expression class."""
def __str__(self) -> str: ...
class ExprName(Expr):
"""A name expression."""
def __init__(self, name: str) -> None: ...
class ExprAttribute(Expr):
"""An attribute access expression like a.b."""
def __init__(self, value: Expr, attr: str) -> None: ...
class ExprCall(Expr):
"""A function call expression."""
def __init__(
self,
function: Expr,
arguments: list[Expr] | None = None,
) -> None: ...