griffe

repository·main·Indexed 18 days ago

https://github.com/mkdocstrings/griffe

A tool for extracting Python API signatures to generate API documentation and detect breaking changes. Griffe allows developers to inspect project structures, export signatures as JSON via the `griffe dump` command, and identify API regressions using `griffe check`. It provides a Python API for programmatic loading and includes an extension system with built-in support for dataclasses and TypedDict, as well as official extensions for inherited docstrings and automatic docstring style detection.

Tokens
60.7K
Snippets
198
Records
316
Agent score
71%

What's inside griffe

  1. What is Griffe?

    main

    Griffe is a Python tool and library designed to extract the signatures, structure, and skeleton of entire Python programs. It is primarily used for:

    • Generating API documentation: For example, the mkdocstrings Python handler uses Griffe to collect API data for HTML rendering.
    • Finding breaking changes: It can compare different versions of an API to identify breaking changes.
    • Data serialization: It can load and serialize API data into JSON format for programmatic use or inspection.
  2. Explore Griffe API manipulation capabilities

    main

    Griffe provides several core workflows for working with Python API data:

    • Loading: Find and scan Python packages and modules either statically or dynamically to extract API information.
    • Navigating: Access extracted API information through structured data models.
    • Serializing: Convert API data into JSON format for use by other tools.
    • Checking: Compare snapshots of the same API to detect breaking changes.
    • Extending: Use Griffe's extension system to augment or modify API data.
  3. Explore official Griffe extensions

    main

    Griffe supports several official extensions maintained by the mkdocstrings organization. These extensions provide specialized support for third-party libraries (like Pydantic), Python standard library features (like PEP 727 or PEP 702), or specific documentation styles (like Sphinx comments).

    | Extension | Description |
    | --------- | ----------- |
    | [`autodocstringstyle`](official/autodocstringstyle.md) | Set docstring style to `auto` for external packages. |
    | [`inherited-docstrings`](official/inherited-docstrings.md) | Inherit docstrings from parent classes. |
    | [`public-redundant-aliases`](official/public-redundant-aliases.md) | Mark objects imported with redundant aliases as public. |
    | [`public-wildcard-imports`](official/public-wildcard-imports.md) | Mark wildcard imported objects as public. |
    | [`pydantic`](official/pydantic.md) | Support for [Pydantic](https://docs.pydantic.dev/latest/) models. |
    | [`runtime-objects`](official/runtime-objects.md) | Access runtime objects corresponding to each loaded Griffe object through their `extra` attribute. |
    | [`sphinx`](official/sphinx.md) | Parse [Sphinx](https://www.sphinx-doc.org/)-comments above attributes (`#:`) as docstrings. |
    | [`typing-doc`](official/typingdoc.md) | Support for [PEP 727](https://peps.python.org/pep-0727/)'s [`typing.Doc`][typing_extensions.Doc], "Documentation in Annotated Metadata". |
    | [`warnings-deprecated`](official/warnings-deprecated.md) | Support for [PEP 702](https://peps.python.org/pep-0702/)'s [`warnings.deprecated`][], "Marking deprecations using the type system". |
  4. What is a public API and how to communicate it

    main

    A public API is the interface through which developers interact with your software (modules, classes, functions, etc.). In Python, distinguishing public objects from internal ones is a matter of communication and convention, as the language does not strictly prevent access to internal objects.

    Key components of a public API include:

    • Module layout
    • Functions and their signatures
    • Classes (including inheritance), methods, and signatures
    • Module or class attributes (types and values)
    • Exceptions raised (crucial for users to catch errors)
    • CLI options and logger names

    Best Practices:

    • Communicate clearly: Explicitly define what is public and what is subject to unnotified changes.
    • Use Deprecation Periods: When changing a public object, provide a period where the old version still works but emits deprecation warnings.
    • Automate Verification: Use tools like Griffe to automate checks around your public API to ensure you don't accidentally break it.
  5. Work with Expressions and enhanced ASTs

    main

    Griffe builds enhanced ASTs called Expressions for type annotations, decorators, and values. Unlike standard Python ast objects, Griffe expressions:

    • Flatten attributes (e.g., a.b.c is a single attribute).
    • Attach a parent object to names, allowing resolution to a full path within the current scope.

    These expressions enable downstream tools like mkdocstrings to handle cross-references and allow developers to build robust extensions for analyzing decorators and dataclasses.

    from griffe import temporary_visited_module
    from rich.pretty import pprint
    
    code = """
        from dataclasses import dataclass
        from random import randint
    
        @dataclass
        class Bar:
            baz: int
    
        def get_some_baz() -> int:
            return randint(0, 10)
    
        foo: Bar = Bar(baz=get_some_baz())
    """
    
    with temporary_visited_module(code) as module:
        pprint(module["foo"].annotation)
        pprint(module["foo"].value)
  6. Run project tasks via duties.py

    main
    The project uses duty to define and run specific tasks. These tasks are defined in duties.py and require development dependencies (listed in devdeps.txt) to be installed. Tool-specific configuration files are stored in the config folder to keep the repository root clean; the tasks automatically locate these files when executed.
  7. How Google-style admonitions work

    main

    When a section identifier does not match a supported section, it is parsed as an admonition (or callout).

    • Kind derivation: The admonition kind is created by lower-casing the identifier and replacing spaces with dashes (e.g., See also: becomes see-also).
    • Singular vs Plural: Identifiers are case-insensitive, but singular and plural forms are distinct. For example, Note: is a specific admonition, while Notes: is not. Example is an admonition of kind example, whereas Examples is a dedicated Examples section.
    • Custom Titles: You can include custom titles in admonitions, such as Tip: Check this out:, which results in a tip admonition with the title Check this out:.
  8. Understand detected API breakages in Griffe

    main

    Griffe detects changes in your Python API that can either trigger immediate errors (like TypeError or ImportError) or silently change the behavior of user code. While the definition of a 'public API' varies by project, Griffe focuses on identifying changes that impact the contract between your code and its consumers.

    Breakages are categorized into:

    • Immediate Errors: Removing parameters, adding required parameters, changing parameter kinds, or removing public objects.
    • Silent Behavior Changes: Moving positional parameters, changing parameter defaults, changing object kinds, changing attribute values, or removing base classes.
  9. Navigate up the object tree using parents

    main

    Every object in the Griffe tree (except the top-level module) holds a reference to its [parent][griffe.Object.parent]. You can use the following shortcuts to climb the tree:

    • parent: The immediate parent object.
    • module: The parent module.
    • package: The top-level package.
    • modules_collection: Access the collection of all loaded modules.
  10. Ensure unique names and single public locations

    main

    To improve usability and documentation clarity, follow these two principles:

    1. Use Unique and Meaningful Names: Avoid names that require module context to understand (e.g., use docstring_warning instead of just warning). Unique names prevent users from needing to use aliases (import x as y) when importing multiple objects.
    2. Expose Objects in a Single Location: Avoid exposing the same object in multiple places (e.g., both in package.module and package). This removes ambiguity for users and helps documentation generators correctly identify the canonical reference path.

    Best Practices for Single Location

    • If using a hidden layout: The object should only exist in the top-level __init__.py.
    • If using a public layout: Declare the object in its specific module and do not re-export it in the top-level __init__.py.
    • If you must use the top-level: Make the source module private (e.g., _module.py) to signal it is not a public entry point.
  11. Use the underscore prefix to mark internal objects

    main

    A common Python convention is to prefix object names with a single underscore (_) to mark them as internal or private. Objects without this prefix are implicitly considered public.

    Example:

    def public_function():
        ...
    
    def _internal_function():
        ...

    Note on Imports: Objects imported from other modules (e.g., from elsewhere import something) are generally not considered part of the current module's public API, even if they do not start with an underscore.