Skip to main content
doc0blade47/fumadocsdocumentation home
Sign in

Python Docstring Extractor

Last updated Oct 12, 2026100% grounded
Relevant source files

The following files were used as context for generating this wiki page:

Overview

The fumapy Python package provides built-in docgen capabilities for extracting API documentation from Python source code and docstrings, serving as the official Python extension for Fumadocs. It bridges raw Python modules and documentation frontends by inspecting AST structures, simplifying docstring content, and serializing parsed metadata into JSON format for static site generation.

Sources: packages/python/pyproject.toml:4-4, packages/python/package.json:4-4

Command Line and Execution Workflow

CLI Entrypoint and Argument Configuration

The package registers a command-line script named fumapy-generate through pyproject.toml, pointing directly to the generate function within the main package module.

[project.scripts]
fumapy-generate = "fumapy:generate"

Sources: packages/python/pyproject.toml:10-11

The generate function builds an argparse.ArgumentParser configured with positional and optional parameters to control target selection and output paths.

Argument / FlagTypeDefaultDescription
modulestr(Required)The target Python module name to generate documentation for.
--dir, -dstr"."The target directory where the resulting JSON documentation file is saved.

Sources: packages/python/fumapy/init.py:30-41

Execution Lifecycle and Serialization

Once command-line arguments are parsed, the execution workflow initializes griffe with the TypingDocExtension extension. It loads the target module using griffe.load, setting docstring_parser="auto" and store_source=True based on module-level constants DOCSTRING_TYPE and STORE_SOURCE.

    extensions = griffe.load_extensions(TypingDocExtension)
    pkg = parse_module(
        griffe.load(
            args.module, docstring_parser="auto", store_source=STORE_SOURCE, extensions=extensions
        )
    )
    api_filename = f"{args.module}.json"
 
    with open(os.path.join(args.dir, api_filename), "w") as file:
        json.dump(pkg, file, cls=CustomEncoder, indent=2, full=True)

Sources: packages/python/fumapy/init.py:10-12, packages/python/fumapy/init.py:43-52

Note

The generated documentation file is written as {module}.json inside the directory specified by --dir. The serialization process leverages CustomEncoder and enables full=True during json.dump to capture complete AST and docstring metadata.

Sources: packages/python/fumapy/init.py:49-52

Griffe Inspection and AST Traversal

Module AST Parsing

The parse_module function accepts a griffe.Object and verifies that it is an instance of griffe.Module, raising a ValueError otherwise. It processes module docstrings via simplify_docstring and builds a structured Module dictionary containing name, path, filepath, description, remaining docstring, attributes, submodules, classes, and functions while filtering out any aliases (if not value.is_alias).

def parse_module(m: griffe.Object) -> Module:
    if not isinstance(m, griffe.Module):
        raise ValueError("Module must be a module")
 
    out = simplify_docstring(m.docstring, m)
    res: Module = {
        "name": m.name,
        "path": m.path,
        "filepath": m.filepath,
        "description": out.description,
        "docstring": out.remainder,
        "attributes": out.attributes,
        "modules": {
            name: parse_module(value)
            for name, value in m.modules.items()
            if not value.is_alias
        },
        "classes": {
            name: parse_class(value)
            for name, value in m.classes.items()
            if not value.is_alias
        },
        "functions": {
            name: parse_function(value)
            for name, value in m.functions.items()
            if not value.is_alias
        },
    }
    if m.is_package:
        try:
            res["version"] = version(m.name)
        except AttributeError:
            res["version"] = "unknown"
 
    return res

Sources: packages/python/fumapy/mksource/document_module.py:12-46

Note

When m.is_package evaluates to true, parse_module attempts to dynamically retrieve the package version via importlib.metadata.version(m.name), falling back to "unknown" if an AttributeError occurs.

Sources: packages/python/fumapy/mksource/document_module.py:40-44

Class and Function AST Extraction

The parse_class function processes a griffe.Class object to extract its name, path, description, parameters, attributes, docstring, source code, non-alias functions, and inherited members. It parses inherited members by inspecting c.inherited_members.values(), extracting their canonical paths and kinds, and grouping them by their parent paths.

def parse_class(c: griffe.Class) -> Class:
    out = simplify_docstring(c.docstring, c)
    res: Class = {
        "name": c.name,
        "path": c.path,
        "description": out.description,
        "parameters": out.parameters,
        "attributes": out.attributes,
        "docstring": out.remainder,
        "functions": {
            name: parse_function(value)
            for name, value in c.functions.items()
            if not value.is_alias
        },
        "source": c.source,
        "inherited_members": {},
    }
    for member in c.inherited_members.values():
        parent_path = ".".join(member.canonical_path.split(".")[:-1])
        member_info = {"kind": member.kind, "path": member.canonical_path}
        if parent_path not in res["inherited_members"]:
            res["inherited_members"][parent_path] = []
        res["inherited_members"][parent_path].append(member_info)
    return res

Sources: packages/python/fumapy/mksource/document_module.py:49-72

The parse_function function inspects a griffe.Function object, generating its signature via build_signature(f) and extracting its name, path, description, parameters, return annotations, docstring remainder, and source code.

def parse_function(f: griffe.Function) -> Function:
    out = simplify_docstring(f.docstring, f)
    res: Function = {
        "name": f.name,
        "path": f.path,
        "signature": build_signature(f),
        "description": out.description,
        "parameters": out.parameters,
        "returns": out.returns,
        "docstring": out.remainder,
        "source": f.source,
    }
    return res

Sources: packages/python/fumapy/mksource/document_module.py:75-86

Utility Functions and Helpers

Utility helpers support AST inspection by filtering non-imported objects, stringifying expressions, and building accurate Python callable signatures.

FunctionParametersReturn TypeDescription
build_signaturefunc: griffe.FunctionstrConstructs a full function signature string, handling positional-only (/), keyword-only (*), varargs (*args), kwargs (**kwargs), defaults, and return annotations.
filter_non_importedd: dict[str, griffe.Object]dictFilters a dictionary of Griffe objects to retain only those where not v.is_imported.
stringify_exprexpr: griffe.Expr | strstrConverts a Griffe expression into its string path representation or returns the string as-is.

Sources: packages/python/fumapy/mksource/utils.py:4-49

Docstring Simplification and Signature Normalization

Simplified Docstring Structure and Processing

The docstring transformation pipeline converts raw Griffe docstrings and AST inspection objects into structured dictionaries and named tuples via simplify_docstring. The SimplifiedDocstring named tuple contains five core fields: description, parameters, returns, attributes, and remainder.

class SimplifiedDocstring(t.NamedTuple):
    description: str | None
    parameters: list[dict[str, str | None]]
    returns: dict[str, str | None]
    attributes: list[dict[str, str | None]]
    remainder: list[griffe.DocstringSection]
 

Sources: packages/python/fumapy/mksource/simplify_docstring.py:6-12

When processing a module, class, or function, helper routines initialize fields directly from the parent AST signatures before parsing individual docstring sections. Parameter annotations, default values, and attribute visibility filters are established during this initial signature pass.

def get_parameters_from_signature(parent: griffe.Class | griffe.Function):
    return [
        {
            "name": p.name,
            "annotation": p.annotation,
            "description": None,
            "value": p.default,
        }
        for p in parent.parameters
    ]
 
def get_returns_from_signature(parent: griffe.Function):
    return {
        "name": "",
        "annotation": (
            parent.returns
            if isinstance(parent.returns, (str, type(None)))
            else "".join(
                elem if isinstance(elem, str) else elem.canonical_path
                for elem in parent.returns.iterate(flat=True)
            )
        ),
        "description": None,
    }
 
def get_attributes_from_signature(parent: griffe.Module | griffe.Class):
    return [
        {
            "name": attr.name,
            "annotation": attr.annotation,
            "description": attr.docstring.parsed if attr.docstring else None,
            "value": attr.value,
        }
        for attr in parent.attributes.values()
        if (not attr.is_alias and not attr.is_private)
    ]

Sources: packages/python/fumapy/mksource/simplify_docstring.py:17-52

Warning

When iterating over parent attributes in get_attributes_from_signature, both attr.is_alias and attr.is_private items are explicitly excluded from the resulting list to prevent duplicate or private documentation entries.

Sources: packages/python/fumapy/mksource/simplify_docstring.py:50-52

Section Parsing and AST Merging

As simplify_docstring iterates through doc.parsed, it handles specific section kinds (text, parameters, returns, and attributes) while appending unhandled sections to remainder. Text sections appearing at index 0 populate the main description. Parameter and attribute sections merge docstring descriptions with runtime signature data by matching names against dictionaries constructed from parsed docstring section values.

for i, sec in enumerate(doc.parsed):
    if sec.kind == "text" and i == 0:
        description = sec.value
        continue
 
    # Sort the parameters with the real signature
    if sec.kind == "parameters":
        map = {i.name: i for i in sec.value}
        params_list = []
        for param in parent.parameters:
            if param.name in map:
                docstring = map[param.name]
                try:
                    docstring.description = griffe.parse_google(
                        griffe.Docstring(docstring.description)
                    )
                except AttributeError:
                    pass
                params_list.append(docstring)
            else:
                params_list.append(
                    {
                        "name": param.name,
                        "annotation": param.annotation,
                        "description": None,
                        "value": param.default,
                    }
                )
 
        parameters = params_list
        continue
 
    if sec.kind == "returns":
        returns: griffe.DocstringReturn = sec.value[0]
        returns.annotation = (
            returns.annotation.canonical_path
            if isinstance(returns.annotation, griffe.Expr)
            else returns.annotation
        )
        continue

Sources: packages/python/fumapy/mksource/simplify_docstring.py:76-115

Tip

Parameter and attribute descriptions extracted from docstrings are re-parsed using griffe.parse_google(griffe.Docstring(...)) to normalize Google-style formatting within individual item docstrings.

Sources: packages/python/fumapy/mksource/simplify_docstring.py:89-91, packages/python/fumapy/mksource/simplify_docstring.py:135-137

TypedDict definitions in models.py mirror these structures for downstream serialization and documentation rendering, ensuring strict type safety for parameters, attributes, functions, classes, and modules.

Type DefinitionFieldsDescription
Parametername, annotation, description, valueRepresents a single function or method parameter.
Attributename, annotation, description, valueRepresents a class or module attribute.
DocstringSectionkind, valueRepresents an unparsed or remaining docstring section.
Functionname, path, signature, description, parameters, returns, docstring, sourceTyped dictionary capturing function metadata.

Sources: packages/python/fumapy/mksource/models.py:30-60

Serialization Models and JSON Encoding

TypedDict Schema Models

TypedDict schemas define the structure for serialized Python modules, classes, and functions, mapping documentation metadata into strict dictionary types. The Module schema aggregates nested modules, classes, functions, attributes, and docstrings alongside metadata fields. The Class and Function schemas capture signature information, source code strings, parameters, and return mappings.

class Module(t.TypedDict):
    name: str
    path: str
    filepath: str
    description: str | None
    docstring: Docstring
    attributes: list[Attribute]
    modules: dict[str, Module]
    classes: dict[str, Class]
    functions: dict[str, Function]
    version: str | None
 
 
class Class(t.TypedDict):
    name: str
    path: str
    description: str | None
    parameters: list[Parameter]
    attributes: list[Attribute]
    docstring: Docstring
    functions: dict[str, Function]
    source: str
 
 
class Function(t.TypedDict):
    name: str
    path: str
    signature: str
    description: str | None
    parameters: list[Parameter]
    returns: dict[str, str | None]
    docstring: Docstring
    source: str
 

Sources: packages/python/fumapy/mksource/models.py:6-39

Note

Docstring is defined as a list of DocstringSection typed dictionaries, where each section holds a specific kind string and a value containing either plain text or a list of parameters.

Sources: packages/python/fumapy/mksource/models.py:41-46

Custom JSON Encoder

The CustomEncoder class extends griffe.JSONEncoder to serialize Python path objects and expressions during export. A mapping table (_json_encoder_map) directs Path, PosixPath, and WindowsPath instances to str conversion, and Python set objects to sorted lists.

_json_encoder_map: dict[type, t.Callable[[t.Any], t.Any]] = {
    Path: str,
    PosixPath: str,
    WindowsPath: str,
    set: sorted,
}

Sources: packages/python/fumapy/mksource/json_encoder.py:5-10

Within CustomEncoder.default, objects matching griffe.Expr are converted directly to strings via str(obj), while standard inspected nodes invoke obj.as_dict(full=self.full). Unrecognized types fall back to _json_encoder_map lookups or super().default.

class CustomEncoder(griffe.JSONEncoder):
    def default(self, obj: t.Any) -> t.Any:
        """Return a serializable representation of the given object.
 
        Parameters:
            obj: The object to serialize.
 
        Returns:
            A serializable representation.
        """
 
        try:
            if isinstance(obj, griffe.Expr):
                return str(obj)
            return obj.as_dict(full=self.full)
        except AttributeError:
            return _json_encoder_map.get(type(obj), super().default)(obj)

Sources: packages/python/fumapy/mksource/json_encoder.py:13-29

Caution

If obj.as_dict() raises an AttributeError, the encoder catches the exception and attempts fallback serialization through _json_encoder_map before resorting to the parent encoder.

Sources: packages/python/fumapy/mksource/json_encoder.py:24-29

Frontend Type Mapping and Rendering

MDX Element Conversion and Layout

The convert function processes structured module objects into an array of output files by evaluating descriptions, attributes, nested classes, functions, and submodules. When classes or submodules are present, it dynamically builds layout structures using Tabs and Cards components, mapping entity paths with custom URL builders.

Function / UtilityTarget ElementPurpose
convertTabs, Tab, Cards, CardAssembles module documentation into tabbed layouts and card grids.
convertClassMarkdown file (.mdx)Generates standalone class documentation files with attribute and function sections.
convertFunctionPyFunctionWraps function definitions, signatures, source code blocks, parameters, and return types.
convertParameterPyParameterRenders parameter entries with type annotations and default values.
convertAttributePyAttributeRenders module or class attributes with optional annotations and default values.

Sources: packages/python/src/convert.ts:20-94, packages/python/src/convert.ts:96-126, packages/python/src/convert.ts:128-157, packages/python/src/convert.ts:159-171, packages/python/src/convert.ts:173-183

Warning

Text encoding performed by encodeText explicitly escapes less-than characters and curly braces to prevent syntax collision with MDX expressions during string serialization.

Sources: packages/python/src/convert.ts:249-251

React Documentation Components

The rendering layer defines specialized React components styled with Tailwind CSS and class-variance-authority variants. Badge variants distinguish component categories using color themes for functions, attributes, classes, and primary parameters.

const cardVariants = cva('bg-fd-card rounded-lg text-sm my-6 p-3 border');
const badgeVariants = cva('text-xs font-medium border p-1 rounded-lg not-prose', {
  variants: {
    color: {
      func: 'bg-fdpy-func/10 text-fdpy-func border-fdpy-func/50',
      attribute: 'bg-fdpy-attribute/10 text-fdpy-attribute border-fdpy-attribute/50',
      class: 'bg-fdpy-class/10 text-fdpy-class border-fdpy-class/50',
      primary: 'bg-fd-primary/10 text-fd-primary border-fd-primary/10',
    },
  },
});

Sources: packages/python/src/components/index.tsx:13-23

The PySourceCode component encapsulates function source blocks within collapsible containers that expose toggle triggers with chevron rotation indicators.

export function PySourceCode({ children }: { children: ReactNode }) {
  return (
    <Collapsible className="my-6">
      <CollapsibleTrigger
        className={cn(
          buttonVariants({
            color: 'secondary',
            size: 'sm',
            className: 'group',
          }),
        )}
      >
        Source Code
        <ChevronRight className="size-3.5 text-fd-muted-foreground group-data-[state=open]:rotate-90" />
      </CollapsibleTrigger>
      <CollapsibleContent className="prose-no-margin">{children}</CollapsibleContent>
    </Collapsible>
  );
}

Sources: packages/python/src/components/index.tsx:103-121

Tip

The InlineCode helper leverages syntax highlighting on python code strings, customising pre-formatted spans to render inline type signatures cleanly.

Sources: packages/python/src/components/index.tsx:135-149

Was this page helpful?