Skip to content

Agents¤

Griffe is able to analyze code both statically and dynamically.

Main API¤

visit ¤

visit(
    module_name: str,
    filepath: Path,
    code: str,
    *,
    extensions: Extensions | None = None,
    parent: Module | None = None,
    docstring_parser: DocstringStyle | Parser | None = None,
    docstring_options: DocstringOptions | None = None,
    lines_collection: LinesCollection | None = None,
    modules_collection: ModulesCollection | None = None,
) -> Module

Parse and visit a module file.

We provide this function for static analysis. It uses a NodeVisitor-like class, the Visitor, to compile and parse code (using compile) then visit the resulting AST (Abstract Syntax Tree).

Important

This function is generally not used directly. In most cases, users can rely on the GriffeLoader and its accompanying load shortcut and their respective options to load modules using static analysis.

Parameters:

  • module_name ¤

    (str) –

    The module name (as when importing [from] it).

  • filepath ¤

    (Path) –

    The module file path.

  • code ¤

    (str) –

    The module contents.

  • extensions ¤

    (Extensions | None, default: None ) –

    The extensions to use when visiting the AST.

  • parent ¤

    (Module | None, default: None ) –

    The optional parent of this module.

  • docstring_parser ¤

    (DocstringStyle | Parser | None, default: None ) –

    The docstring parser to use. By default, no parsing is done.

  • docstring_options ¤

    (DocstringOptions | None, default: None ) –

    Docstring parsing options.

  • lines_collection ¤

    (LinesCollection | None, default: None ) –

    A collection of source code lines.

  • modules_collection ¤

    (ModulesCollection | None, default: None ) –

    A collection of modules.

Returns:

  • Module

    The module, with its members populated.

Source code in src/griffe/_internal/agents/visitor.py
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
def visit(
    module_name: str,
    filepath: Path,
    code: str,
    *,
    extensions: Extensions | None = None,
    parent: Module | None = None,
    docstring_parser: DocstringStyle | Parser | None = None,
    docstring_options: DocstringOptions | None = None,
    lines_collection: LinesCollection | None = None,
    modules_collection: ModulesCollection | None = None,
) -> Module:
    """Parse and visit a module file.

    We provide this function for static analysis. It uses a [`NodeVisitor`][ast.NodeVisitor]-like class,
    the [`Visitor`][griffe.Visitor], to compile and parse code (using [`compile`][])
    then visit the resulting AST (Abstract Syntax Tree).

    Important:
        This function is generally not used directly.
        In most cases, users can rely on the [`GriffeLoader`][griffe.GriffeLoader]
        and its accompanying [`load`][griffe.load] shortcut and their respective options
        to load modules using static analysis.

    Parameters:
        module_name: The module name (as when importing [from] it).
        filepath: The module file path.
        code: The module contents.
        extensions: The extensions to use when visiting the AST.
        parent: The optional parent of this module.
        docstring_parser: The docstring parser to use. By default, no parsing is done.
        docstring_options: Docstring parsing options.
        lines_collection: A collection of source code lines.
        modules_collection: A collection of modules.

    Returns:
        The module, with its members populated.
    """
    return Visitor(
        module_name,
        filepath,
        code,
        extensions or load_extensions(),
        parent,
        docstring_parser=docstring_parser,
        docstring_options=docstring_options,
        lines_collection=lines_collection,
        modules_collection=modules_collection,
    ).get_module()

inspect ¤

inspect(
    module_name: str,
    *,
    filepath: Path | None = None,
    import_paths: Sequence[str | Path] | None = None,
    extensions: Extensions | None = None,
    parent: Module | None = None,
    docstring_parser: DocstringStyle | Parser | None = None,
    docstring_options: DocstringOptions | None = None,
    lines_collection: LinesCollection | None = None,
    modules_collection: ModulesCollection | None = None,
) -> Module

Inspect a module.

Sometimes we cannot get the source code of a module or an object, typically built-in modules like itertools. The only way to know what they are made of is to actually import them and inspect their contents.

Sometimes, even if the source code is available, loading the object is desired because it was created or modified dynamically, and our static agent is not powerful enough to infer all these dynamic modifications. In this case, we load the module using introspection.

Griffe therefore provides this function for dynamic analysis. It uses a NodeVisitor-like class, the Inspector, to inspect the module with inspect.getmembers().

The inspection agent works similarly to the regular Visitor agent, in that it maintains a state with the current object being handled, and recursively handle its members.

Important

This function is generally not used directly. In most cases, users can rely on the GriffeLoader and its accompanying load shortcut and their respective options to load modules using dynamic analysis.

Parameters:

  • module_name ¤

    (str) –

    The module name (as when importing [from] it).

  • filepath ¤

    (Path | None, default: None ) –

    The module file path.

  • import_paths ¤

    (Sequence[str | Path] | None, default: None ) –

    Paths to import the module from.

  • extensions ¤

    (Extensions | None, default: None ) –

    The extensions to use when inspecting the module.

  • parent ¤

    (Module | None, default: None ) –

    The optional parent of this module.

  • docstring_parser ¤

    (DocstringStyle | Parser | None, default: None ) –

    The docstring parser to use. By default, no parsing is done.

  • docstring_options ¤

    (DocstringOptions | None, default: None ) –

    Docstring parsing options.

  • lines_collection ¤

    (LinesCollection | None, default: None ) –

    A collection of source code lines.

  • modules_collection ¤

    (ModulesCollection | None, default: None ) –

    A collection of modules.

Returns:

  • Module

    The module, with its members populated.

Source code in src/griffe/_internal/agents/inspector.py
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
def inspect(
    module_name: str,
    *,
    filepath: Path | None = None,
    import_paths: Sequence[str | Path] | None = None,
    extensions: Extensions | None = None,
    parent: Module | None = None,
    docstring_parser: DocstringStyle | Parser | None = None,
    docstring_options: DocstringOptions | None = None,
    lines_collection: LinesCollection | None = None,
    modules_collection: ModulesCollection | None = None,
) -> Module:
    """Inspect a module.

    Sometimes we cannot get the source code of a module or an object,
    typically built-in modules like `itertools`.
    The only way to know what they are made of is to actually import them and inspect their contents.

    Sometimes, even if the source code is available,
    loading the object is desired because it was created or modified dynamically,
    and our static agent is not powerful enough to infer all these dynamic modifications.
    In this case, we load the module using introspection.

    Griffe therefore provides this function for dynamic analysis.
    It uses a [`NodeVisitor`][ast.NodeVisitor]-like class, the [`Inspector`][griffe.Inspector],
    to inspect the module with [`inspect.getmembers()`][inspect.getmembers].

    The inspection agent works similarly to the regular [`Visitor`][griffe.Visitor] agent,
    in that it maintains a state with the current object being handled, and recursively handle its members.

    Important:
        This function is generally not used directly.
        In most cases, users can rely on the [`GriffeLoader`][griffe.GriffeLoader]
        and its accompanying [`load`][griffe.load] shortcut and their respective options
        to load modules using dynamic analysis.

    Parameters:
        module_name: The module name (as when importing [from] it).
        filepath: The module file path.
        import_paths: Paths to import the module from.
        extensions: The extensions to use when inspecting the module.
        parent: The optional parent of this module.
        docstring_parser: The docstring parser to use. By default, no parsing is done.
        docstring_options: Docstring parsing options.
        lines_collection: A collection of source code lines.
        modules_collection: A collection of modules.

    Returns:
        The module, with its members populated.
    """
    return Inspector(
        module_name,
        filepath,
        extensions or load_extensions(),
        parent,
        docstring_parser=docstring_parser,
        docstring_options=docstring_options,
        lines_collection=lines_collection,
        modules_collection=modules_collection,
    ).get_module(import_paths)

Advanced API¤

Visitor ¤

This class is used to instantiate a visitor.

Visitors iterate on AST nodes to extract data from them.

Parameters:

  • module_name ¤

    (str) –

    The module name.

  • filepath ¤

    (Path) –

    The module filepath.

  • code ¤

    (str) –

    The module source code.

  • extensions ¤

    (Extensions) –

    The extensions to use when visiting.

  • parent ¤

    (Module | None, default: None ) –

    An optional parent for the final module object.

  • docstring_parser ¤

    (DocstringStyle | Parser | None, default: None ) –

    The docstring parser to use.

  • docstring_options ¤

    (DocstringOptions | None, default: None ) –

    Docstring parsing options.

  • lines_collection ¤

    (LinesCollection | None, default: None ) –

    A collection of source code lines.

  • modules_collection ¤

    (ModulesCollection | None, default: None ) –

    A collection of modules.

Methods:

Attributes:

Source code in src/griffe/_internal/agents/visitor.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def __init__(
    self,
    module_name: str,
    filepath: Path,
    code: str,
    extensions: Extensions,
    parent: Module | None = None,
    docstring_parser: DocstringStyle | Parser | None = None,
    docstring_options: DocstringOptions | None = None,
    lines_collection: LinesCollection | None = None,
    modules_collection: ModulesCollection | None = None,
) -> None:
    """Initialize the visitor.

    Parameters:
        module_name: The module name.
        filepath: The module filepath.
        code: The module source code.
        extensions: The extensions to use when visiting.
        parent: An optional parent for the final module object.
        docstring_parser: The docstring parser to use.
        docstring_options: Docstring parsing options.
        lines_collection: A collection of source code lines.
        modules_collection: A collection of modules.
    """
    super().__init__()

    self.module_name: str = module_name
    """The module name."""

    self.filepath: Path = filepath
    """The module filepath."""

    self.code: str = code
    """The module source code."""

    self.extensions: Extensions = extensions
    """The extensions to use when visiting the AST."""

    self.parent: Module | None = parent
    """An optional parent for the final module object."""

    self.current: Module | Class = None  # type: ignore[assignment]
    """The current object being visited."""

    self.docstring_parser: DocstringStyle | Parser | None = docstring_parser
    """The docstring parser to use."""

    self.docstring_options: DocstringOptions = docstring_options or {}
    """The docstring parsing options."""

    self.lines_collection: LinesCollection = lines_collection or LinesCollection()
    """A collection of source code lines."""

    self.modules_collection: ModulesCollection = modules_collection or ModulesCollection()
    """A collection of modules."""

    self.type_guarded: bool = False
    """Whether the current code branch is type-guarded."""

code instance-attribute ¤

code: str = code

The module source code.

current instance-attribute ¤

current: Module | Class = None

The current object being visited.

docstring_options instance-attribute ¤

docstring_options: DocstringOptions = (
    docstring_options or {}
)

The docstring parsing options.

docstring_parser instance-attribute ¤

docstring_parser: DocstringStyle | Parser | None = (
    docstring_parser
)

The docstring parser to use.

extensions instance-attribute ¤

extensions: Extensions = extensions

The extensions to use when visiting the AST.

filepath instance-attribute ¤

filepath: Path = filepath

The module filepath.

lines_collection instance-attribute ¤

lines_collection: LinesCollection = (
    lines_collection or LinesCollection()
)

A collection of source code lines.

module_name instance-attribute ¤

module_name: str = module_name

The module name.

modules_collection instance-attribute ¤

A collection of modules.

parent instance-attribute ¤

parent: Module | None = parent

An optional parent for the final module object.

type_guarded instance-attribute ¤

type_guarded: bool = False

Whether the current code branch is type-guarded.

decorators_to_labels ¤

decorators_to_labels(
    decorators: list[Decorator],
) -> set[str]

Build and return a set of labels based on decorators.

Parameters:

Returns:

  • set[str]

    A set of labels.

Source code in src/griffe/_internal/agents/visitor.py
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
def decorators_to_labels(self, decorators: list[Decorator]) -> set[str]:
    """Build and return a set of labels based on decorators.

    Parameters:
        decorators: The decorators to check.

    Returns:
        A set of labels.
    """
    labels = set()
    for decorator in decorators:
        callable_path = decorator.callable_path
        if callable_path in builtin_decorators:
            labels.add(builtin_decorators[callable_path])
        elif callable_path in stdlib_decorators:
            labels |= stdlib_decorators[callable_path]
    return labels

generic_visit ¤

generic_visit(node: AST) -> None

Extend the base generic visit with extensions.

Parameters:

  • node ¤

    (AST) –

    The node to visit.

Source code in src/griffe/_internal/agents/visitor.py
268
269
270
271
272
273
274
275
def generic_visit(self, node: ast.AST) -> None:
    """Extend the base generic visit with extensions.

    Parameters:
        node: The node to visit.
    """
    for child in ast_children(node):
        self.visit(child)

get_base_property ¤

get_base_property(
    decorators: list[Decorator], function: Function
) -> str | None

Check decorators to return the base property in case of setters and deleters.

Parameters:

Returns:

  • base_property ( str | None ) –

    The property for which the setter/deleted is set.

  • property_function ( str | None ) –

    Either "setter" or "deleter".

Source code in src/griffe/_internal/agents/visitor.py
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
def get_base_property(self, decorators: list[Decorator], function: Function) -> str | None:
    """Check decorators to return the base property in case of setters and deleters.

    Parameters:
        decorators: The decorators to check.

    Returns:
        base_property: The property for which the setter/deleted is set.
        property_function: Either `"setter"` or `"deleter"`.
    """
    for decorator in decorators:
        try:
            path, prop_function = decorator.callable_path.rsplit(".", 1)
        except ValueError:
            continue
        property_setter_or_deleter = (
            prop_function in {"setter", "deleter"}
            and path == function.path
            and self.current.get_member(function.name).has_labels("property")
        )
        if property_setter_or_deleter:
            return prop_function
    return None

get_module ¤

get_module() -> Module

Build and return the object representing the module attached to this visitor.

This method triggers a complete visit of the module nodes.

Returns:

  • Module

    A module instance.

Source code in src/griffe/_internal/agents/visitor.py
246
247
248
249
250
251
252
253
254
255
256
257
258
def get_module(self) -> Module:
    """Build and return the object representing the module attached to this visitor.

    This method triggers a complete visit of the module nodes.

    Returns:
        A module instance.
    """
    # Optimization: equivalent to `ast.parse`, but with `optimize=1` to remove assert statements.
    # TODO: With options, could use `optimize=2` to remove docstrings.
    top_node = compile(self.code, mode="exec", filename=str(self.filepath), flags=ast.PyCF_ONLY_AST, optimize=1)
    self.visit(top_node)
    return self.current.module

handle_attribute ¤

handle_attribute(
    node: Assign | AnnAssign,
    annotation: str | Expr | None = None,
) -> None

Handle an attribute (assignment) node.

Parameters:

  • node ¤

    (Assign | AnnAssign) –

    The node to visit.

  • annotation ¤

    (str | Expr | None, default: None ) –

    A potential annotation.

Source code in src/griffe/_internal/agents/visitor.py
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
def handle_attribute(
    self,
    node: ast.Assign | ast.AnnAssign,
    annotation: str | Expr | None = None,
) -> None:
    """Handle an attribute (assignment) node.

    Parameters:
        node: The node to visit.
        annotation: A potential annotation.
    """
    self.extensions.call("on_node", node=node, agent=self)
    self.extensions.call("on_attribute_node", node=node, agent=self)
    parent = self.current
    labels = set()
    names = None

    if parent.kind is Kind.MODULE:
        try:
            names = get_names(node)
        except KeyError:  # Unsupported nodes, like subscript.
            return
        labels.add("module-attribute")
    elif parent.kind is Kind.CLASS:
        try:
            names = get_names(node)
        except KeyError:  # Unsupported nodes, like subscript.
            return

        if isinstance(annotation, Expr) and annotation.is_classvar:
            # Explicit `ClassVar`: class attribute only.
            annotation = annotation.slice  # type: ignore[attr-defined]
            labels.add("class-attribute")
        elif node.value:
            # Attribute assigned at class-level: available in instances as well.
            labels.add("class-attribute")
            labels.add("instance-attribute")
        else:
            # Annotated attribute only: not available at class-level.
            labels.add("instance-attribute")

    elif parent.kind is Kind.FUNCTION:
        if parent.name != "__init__":
            return
        try:
            names = get_instance_names(node)
        except KeyError:  # Unsupported nodes, like subscript.
            return
        parent = parent.parent  # type: ignore[assignment]
        if parent is None:
            return
        labels.add("instance-attribute")

    if not names:
        return

    value = safe_get_expression(node.value, parent=self.current, parse_strings=False)

    try:
        docstring = self._get_docstring(ast_next(node), strict=True)
    except (LastNodeError, AttributeError):
        docstring = None

    for name in names:
        # TODO: Handle assigns like `x.y = z`.
        # We need to resolve `x.y` and add `z` in its members.
        if "." in name:
            continue

        if name in parent.members:
            # Assigning multiple times.
            # TODO: Might be better to inspect.
            if isinstance(node.parent, (ast.If, ast.ExceptHandler)):  # type: ignore[union-attr]
                continue  # Prefer "no-exception" case.

            existing_member = parent.members[name]
            with suppress(AliasResolutionError, CyclicAliasError):
                labels |= existing_member.labels
                # Forward previous docstring and annotation instead of erasing them.
                if existing_member.docstring and not docstring:
                    docstring = existing_member.docstring
                with suppress(AttributeError):
                    if existing_member.annotation and not annotation:  # type: ignore[union-attr]
                        annotation = existing_member.annotation  # type: ignore[union-attr]

        attribute = Attribute(
            name=name,
            value=value,
            annotation=annotation,
            lineno=node.lineno,
            endlineno=node.end_lineno,
            docstring=docstring,
            runtime=not self.type_guarded,
            analysis="static",
        )
        attribute.labels |= labels
        parent.set_member(name, attribute)

        if name == "__all__":
            with suppress(AttributeError):
                parent.exports = [
                    name if isinstance(name, str) else ExprName(name.name, parent=name.parent)
                    for name in safe_get__all__(node, self.current)  # type: ignore[arg-type]
                ]
        self.extensions.call("on_instance", node=node, obj=attribute, agent=self)
        self.extensions.call("on_attribute_instance", node=node, attr=attribute, agent=self)

handle_function ¤

handle_function(
    node: AsyncFunctionDef | FunctionDef,
    labels: set | None = None,
) -> None

Handle a function definition node.

Parameters:

Source code in src/griffe/_internal/agents/visitor.py
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
def handle_function(self, node: ast.AsyncFunctionDef | ast.FunctionDef, labels: set | None = None) -> None:
    """Handle a function definition node.

    Parameters:
        node: The node to visit.
        labels: Labels to add to the data object.
    """
    self.extensions.call("on_node", node=node, agent=self)
    self.extensions.call("on_function_node", node=node, agent=self)

    labels = labels or set()

    # Handle decorators.
    decorators = []
    overload = False
    if node.decorator_list:
        lineno = node.decorator_list[0].lineno
        for decorator_node in node.decorator_list:
            decorator_value = safe_get_expression(decorator_node, parent=self.current, parse_strings=False)
            if decorator_value is None:
                continue
            decorator = Decorator(
                decorator_value,
                lineno=decorator_node.lineno,
                endlineno=decorator_node.end_lineno,
            )
            decorators.append(decorator)
            overload |= decorator.callable_path in typing_overload
    else:
        lineno = node.lineno

    labels |= self.decorators_to_labels(decorators)

    if "property" in labels:
        attribute = Attribute(
            name=node.name,
            value=None,
            annotation=safe_get_annotation(node.returns, parent=self.current, member=node.name),
            lineno=node.lineno,
            endlineno=node.end_lineno,
            docstring=self._get_docstring(node),
            runtime=not self.type_guarded,
            analysis="static",
        )
        attribute.labels |= labels
        self.current.set_member(node.name, attribute)
        self.extensions.call("on_instance", node=node, obj=attribute, agent=self)
        self.extensions.call("on_attribute_instance", node=node, attr=attribute, agent=self)
        return

    # Handle parameters.
    parameters = Parameters(
        *[
            Parameter(
                name,
                kind=kind,
                annotation=safe_get_annotation(annotation, parent=self.current, member=node.name),
                default=default
                if isinstance(default, str)
                else safe_get_expression(default, parent=self.current, parse_strings=False),
            )
            for name, annotation, kind, default in get_parameters(node.args)
        ],
    )

    function = Function(
        name=node.name,
        lineno=lineno,
        endlineno=node.end_lineno,
        parameters=parameters,
        returns=safe_get_annotation(node.returns, parent=self.current, member=node.name),
        decorators=decorators,
        type_parameters=TypeParameters(*self._get_type_parameters(node, scope=node.name)),
        docstring=self._get_docstring(node),
        runtime=not self.type_guarded,
        parent=self.current,
        analysis="static",
    )

    property_function = self.get_base_property(decorators, function)

    if overload:
        self.current.overloads[function.name].append(function)
    elif property_function:
        base_property: Attribute = self.current.members[node.name]  # type: ignore[assignment]
        if property_function == "setter":
            base_property.setter = function
            base_property.labels.add("writable")
        elif property_function == "deleter":
            base_property.deleter = function
            base_property.labels.add("deletable")
    else:
        self.current.set_member(node.name, function)
        if self.current.kind in {Kind.MODULE, Kind.CLASS} and self.current.overloads[function.name]:
            function.overloads = self.current.overloads[function.name]
            del self.current.overloads[function.name]

    function.labels |= labels

    self.extensions.call("on_instance", node=node, obj=function, agent=self)
    self.extensions.call("on_function_instance", node=node, func=function, agent=self)
    if self.current.kind is Kind.CLASS and function.name == "__init__":
        self.current = function  # type: ignore[assignment]
        self.generic_visit(node)
        self.current = self.current.parent  # type: ignore[assignment]

visit ¤

visit(node: AST) -> None

Extend the base visit with extensions.

Parameters:

  • node ¤

    (AST) –

    The node to visit.

Source code in src/griffe/_internal/agents/visitor.py
260
261
262
263
264
265
266
def visit(self, node: ast.AST) -> None:
    """Extend the base visit with extensions.

    Parameters:
        node: The node to visit.
    """
    getattr(self, f"visit_{ast_kind(node)}", self.generic_visit)(node)

visit_annassign ¤

visit_annassign(node: AnnAssign) -> None

Visit an annotated assignment node.

Parameters:

Source code in src/griffe/_internal/agents/visitor.py
728
729
730
731
732
733
734
def visit_annassign(self, node: ast.AnnAssign) -> None:
    """Visit an annotated assignment node.

    Parameters:
        node: The node to visit.
    """
    self.handle_attribute(node, safe_get_annotation(node.annotation, parent=self.current))

visit_assign ¤

visit_assign(node: Assign) -> None

Visit an assignment node.

Parameters:

  • node ¤

    (Assign) –

    The node to visit.

Source code in src/griffe/_internal/agents/visitor.py
720
721
722
723
724
725
726
def visit_assign(self, node: ast.Assign) -> None:
    """Visit an assignment node.

    Parameters:
        node: The node to visit.
    """
    self.handle_attribute(node)

visit_asyncfunctiondef ¤

visit_asyncfunctiondef(node: AsyncFunctionDef) -> None

Visit an async function definition node.

Parameters:

Source code in src/griffe/_internal/agents/visitor.py
509
510
511
512
513
514
515
def visit_asyncfunctiondef(self, node: ast.AsyncFunctionDef) -> None:
    """Visit an async function definition node.

    Parameters:
        node: The node to visit.
    """
    self.handle_function(node, labels={"async"})

visit_augassign ¤

visit_augassign(node: AugAssign) -> None

Visit an augmented assignment node.

Parameters:

Source code in src/griffe/_internal/agents/visitor.py
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
def visit_augassign(self, node: ast.AugAssign) -> None:
    """Visit an augmented assignment node.

    Parameters:
        node: The node to visit.
    """
    with suppress(AttributeError):
        all_augment = (
            node.target.id == "__all__"  # type: ignore[union-attr]
            and self.current.is_module
            and isinstance(node.op, ast.Add)
        )
        if all_augment:
            # We assume `exports` is not `None` at this point.
            self.current.exports.extend(  # type: ignore[union-attr]
                [
                    name if isinstance(name, str) else ExprName(name.name, parent=name.parent)
                    for name in safe_get__all__(node, self.current)  # type: ignore[arg-type]
                ],
            )

visit_classdef ¤

visit_classdef(node: ClassDef) -> None

Visit a class definition node.

Parameters:

Source code in src/griffe/_internal/agents/visitor.py
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
def visit_classdef(self, node: ast.ClassDef) -> None:
    """Visit a class definition node.

    Parameters:
        node: The node to visit.
    """
    self.extensions.call("on_node", node=node, agent=self)
    self.extensions.call("on_class_node", node=node, agent=self)

    # Handle decorators.
    decorators: list[Decorator] = []
    if node.decorator_list:
        lineno = node.decorator_list[0].lineno
        decorators.extend(
            Decorator(
                safe_get_expression(decorator_node, parent=self.current, parse_strings=False),  # type: ignore[arg-type]
                lineno=decorator_node.lineno,
                endlineno=decorator_node.end_lineno,
            )
            for decorator_node in node.decorator_list
        )
    else:
        lineno = node.lineno

    # Handle base classes and keywords.
    bases = [safe_get_base_class(base, parent=self.current, member=node.name) for base in node.bases]
    keywords = {
        kw.arg: safe_get_class_keyword(kw.value, parent=self.current) for kw in node.keywords if kw.arg is not None
    }

    class_ = Class(
        name=node.name,
        lineno=lineno,
        endlineno=node.end_lineno,
        docstring=self._get_docstring(node),
        decorators=decorators,
        type_parameters=TypeParameters(*self._get_type_parameters(node, scope=node.name)),
        bases=bases,  # type: ignore[arg-type]
        keywords=keywords,
        runtime=not self.type_guarded,
        analysis="static",
    )
    class_.labels |= self.decorators_to_labels(decorators)

    self.current.set_member(node.name, class_)
    self.current = class_
    self.extensions.call("on_instance", node=node, obj=class_, agent=self)
    self.extensions.call("on_class_instance", node=node, cls=class_, agent=self)
    self.generic_visit(node)
    self.extensions.call("on_members", node=node, obj=class_, agent=self)
    self.extensions.call("on_class_members", node=node, cls=class_, agent=self)
    self.current = self.current.parent  # type: ignore[assignment]

visit_functiondef ¤

visit_functiondef(node: FunctionDef) -> None

Visit a function definition node.

Parameters:

Source code in src/griffe/_internal/agents/visitor.py
501
502
503
504
505
506
507
def visit_functiondef(self, node: ast.FunctionDef) -> None:
    """Visit a function definition node.

    Parameters:
        node: The node to visit.
    """
    self.handle_function(node)

visit_if ¤

visit_if(node: If) -> None

Visit an "if" node.

Parameters:

  • node ¤

    (If) –

    The node to visit.

Source code in src/griffe/_internal/agents/visitor.py
757
758
759
760
761
762
763
764
765
766
767
768
def visit_if(self, node: ast.If) -> None:
    """Visit an "if" node.

    Parameters:
        node: The node to visit.
    """
    if isinstance(node.parent, (ast.Module, ast.ClassDef)):  # type: ignore[attr-defined]
        condition = safe_get_condition(node.test, parent=self.current, log_level=None)
        if str(condition) in {"typing.TYPE_CHECKING", "TYPE_CHECKING"}:
            self.type_guarded = True
    self.generic_visit(node)
    self.type_guarded = False

visit_import ¤

visit_import(node: Import) -> None

Visit an import node.

Parameters:

  • node ¤

    (Import) –

    The node to visit.

Source code in src/griffe/_internal/agents/visitor.py
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
def visit_import(self, node: ast.Import) -> None:
    """Visit an import node.

    Parameters:
        node: The node to visit.
    """
    for name in node.names:
        alias_path = name.name if name.asname else name.name.split(".", 1)[0]
        alias_name = name.asname or alias_path.split(".", 1)[0]
        self.current.imports[alias_name] = alias_path
        alias = Alias(
            alias_name,
            alias_path,
            lineno=node.lineno,
            endlineno=node.end_lineno,
            runtime=not self.type_guarded,
            analysis="static",
        )
        self.current.set_member(alias_name, alias)
        self.extensions.call("on_alias_instance", alias=alias, node=node, agent=self)

visit_importfrom ¤

visit_importfrom(node: ImportFrom) -> None

Visit an "import from" node.

Parameters:

Source code in src/griffe/_internal/agents/visitor.py
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
def visit_importfrom(self, node: ast.ImportFrom) -> None:
    """Visit an "import from" node.

    Parameters:
        node: The node to visit.
    """
    for name in node.names:
        if not node.module and node.level == 1 and not name.asname and self.current.module.is_init_module:
            # Special case: when being in `a/__init__.py` and doing `from . import b`,
            # we are effectively creating a member `b` in `a` that is pointing to `a.b`
            # -> cyclic alias! In that case, we just skip it, as both the member and module
            # have the same name and can be accessed the same way.
            continue

        alias_path = relative_to_absolute(node, name, self.current.module)
        if name.name == "*":
            alias_name = alias_path.replace(".", "/")
            alias_path = alias_path.replace(".*", "")
        else:
            alias_name = name.asname or name.name
            self.current.imports[alias_name] = alias_path
        # Do not create aliases pointing to themselves (it happens with
        # `from package.current_module import Thing as Thing` or
        # `from . import thing as thing`).
        if alias_path != f"{self.current.path}.{alias_name}":
            alias = Alias(
                alias_name,
                alias_path,
                lineno=node.lineno,
                endlineno=node.end_lineno,
                runtime=not self.type_guarded,
                analysis="static",
            )
            self.current.set_member(alias_name, alias)
            self.extensions.call("on_alias_instance", alias=alias, node=node, agent=self)

visit_module ¤

visit_module(node: Module) -> None

Visit a module node.

Parameters:

  • node ¤

    (Module) –

    The node to visit.

Source code in src/griffe/_internal/agents/visitor.py
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
def visit_module(self, node: ast.Module) -> None:
    """Visit a module node.

    Parameters:
        node: The node to visit.
    """
    self.extensions.call("on_node", node=node, agent=self)
    self.extensions.call("on_module_node", node=node, agent=self)
    self.current = module = Module(
        name=self.module_name,
        filepath=self.filepath,
        parent=self.parent,
        docstring=self._get_docstring(node),
        lines_collection=self.lines_collection,
        modules_collection=self.modules_collection,
        analysis="static",
    )
    self.extensions.call("on_instance", node=node, obj=module, agent=self)
    self.extensions.call("on_module_instance", node=node, mod=module, agent=self)
    self.generic_visit(node)
    self.extensions.call("on_members", node=node, obj=module, agent=self)
    self.extensions.call("on_module_members", node=node, mod=module, agent=self)

visit_typealias ¤

visit_typealias(node: TypeAlias) -> None

Visit a type alias node.

Parameters:

Source code in src/griffe/_internal/agents/visitor.py
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
def visit_typealias(self, node: ast.TypeAlias) -> None:
    """Visit a type alias node.

    Parameters:
        node: The node to visit.
    """
    self.extensions.call("on_node", node=node, agent=self)
    self.extensions.call("on_type_alias_node", node=node, agent=self)

    # A type alias's name attribute is syntactically a single NAME,
    # but represented as an expression in the AST.
    # https://jellezijlstra.github.io/pep695#ast

    name = node.name.id

    value = safe_get_expression(node.value, parent=self.current, member=name)

    try:
        docstring = self._get_docstring(ast_next(node), strict=True)
    except (LastNodeError, AttributeError):
        docstring = None

    type_alias = TypeAlias(
        name=name,
        value=value,
        lineno=node.lineno,
        endlineno=node.end_lineno,
        type_parameters=TypeParameters(*self._get_type_parameters(node, scope=name)),
        docstring=docstring,
        parent=self.current,
        analysis="static",
    )
    self.current.set_member(name, type_alias)
    self.extensions.call("on_instance", node=node, obj=type_alias, agent=self)
    self.extensions.call("on_type_alias_instance", node=node, type_alias=type_alias, agent=self)

Inspector ¤

This class is used to instantiate an inspector.

Inspectors iterate on objects members to extract data from them.

Parameters:

  • module_name ¤

    (str) –

    The module name.

  • filepath ¤

    (Path | None) –

    The optional filepath.

  • extensions ¤

    (Extensions) –

    Extensions to use when inspecting.

  • parent ¤

    (Module | None, default: None ) –

    The module parent.

  • docstring_parser ¤

    (DocstringStyle | Parser | None, default: None ) –

    The docstring parser to use.

  • docstring_options ¤

    (DocstringOptions | None, default: None ) –

    Docstring parsing options.

  • lines_collection ¤

    (LinesCollection | None, default: None ) –

    A collection of source code lines.

  • modules_collection ¤

    (ModulesCollection | None, default: None ) –

    A collection of modules.

Methods:

Attributes:

Source code in src/griffe/_internal/agents/inspector.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
def __init__(
    self,
    module_name: str,
    filepath: Path | None,
    extensions: Extensions,
    parent: Module | None = None,
    docstring_parser: DocstringStyle | Parser | None = None,
    docstring_options: DocstringOptions | None = None,
    lines_collection: LinesCollection | None = None,
    modules_collection: ModulesCollection | None = None,
) -> None:
    """Initialize the inspector.

    Parameters:
        module_name: The module name.
        filepath: The optional filepath.
        extensions: Extensions to use when inspecting.
        parent: The module parent.
        docstring_parser: The docstring parser to use.
        docstring_options: Docstring parsing options.
        lines_collection: A collection of source code lines.
        modules_collection: A collection of modules.
    """
    super().__init__()

    self.module_name: str = module_name
    """The module name."""

    self.filepath: Path | None = filepath
    """The module file path."""

    self.extensions: Extensions = extensions
    """The extensions to use when inspecting."""

    self.parent: Module | None = parent
    """An optional parent for the final module object."""

    self.current: Module | Class = None  # type: ignore[assignment]
    """The current object being inspected."""

    self.docstring_parser: DocstringStyle | Parser | None = docstring_parser
    """The docstring parser to use."""

    self.docstring_options: DocstringOptions = docstring_options or {}
    """The docstring parsing options."""

    self.lines_collection: LinesCollection = lines_collection or LinesCollection()
    """A collection of source code lines."""

    self.modules_collection: ModulesCollection = modules_collection or ModulesCollection()
    """A collection of modules."""

current instance-attribute ¤

current: Module | Class = None

The current object being inspected.

docstring_options instance-attribute ¤

docstring_options: DocstringOptions = (
    docstring_options or {}
)

The docstring parsing options.

docstring_parser instance-attribute ¤

docstring_parser: DocstringStyle | Parser | None = (
    docstring_parser
)

The docstring parser to use.

extensions instance-attribute ¤

extensions: Extensions = extensions

The extensions to use when inspecting.

filepath instance-attribute ¤

filepath: Path | None = filepath

The module file path.

lines_collection instance-attribute ¤

lines_collection: LinesCollection = (
    lines_collection or LinesCollection()
)

A collection of source code lines.

module_name instance-attribute ¤

module_name: str = module_name

The module name.

modules_collection instance-attribute ¤

A collection of modules.

parent instance-attribute ¤

parent: Module | None = parent

An optional parent for the final module object.

generic_inspect ¤

generic_inspect(node: ObjectNode) -> None

Extend the base generic inspection with extensions.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
def generic_inspect(self, node: ObjectNode) -> None:
    """Extend the base generic inspection with extensions.

    Parameters:
        node: The node to inspect.
    """
    for child in node.children:
        if target_path := child.alias_target_path:
            # If the child is an actual submodule of the current module,
            # and has no `__file__` set, we won't find it on the disk so we must inspect it now.
            # For that we instantiate a new inspector and use it to inspect the submodule,
            # then assign the submodule as member of the current module.
            # If the submodule has a `__file__` set, the loader should find it on the disk,
            # so we skip it here (no member, no alias, just skip it).
            if child.is_module and target_path == f"{self.current.path}.{child.name}":
                if not hasattr(child.obj, "__file__"):
                    logger.debug("Module %s is not discoverable on disk, inspecting right now", target_path)
                    inspector = Inspector(
                        child.name,
                        filepath=None,
                        parent=self.current.module,
                        extensions=self.extensions,
                        docstring_parser=self.docstring_parser,
                        docstring_options=self.docstring_options,
                        lines_collection=self.lines_collection,
                        modules_collection=self.modules_collection,
                    )
                    inspector.inspect_module(child)
                    self.current.set_member(child.name, inspector.current.module)
            # Otherwise, alias the object.
            else:
                alias = Alias(child.name, target_path, analysis="dynamic")
                self.current.set_member(child.name, alias)
                self.extensions.call("on_alias_instance", alias=alias, node=node, agent=self)
        else:
            self.inspect(child)

get_module ¤

get_module(
    import_paths: Sequence[str | Path] | None = None,
) -> Module

Build and return the object representing the module attached to this inspector.

This method triggers a complete inspection of the module members.

Parameters:

  • import_paths ¤

    (Sequence[str | Path] | None, default: None ) –

    Paths replacing sys.path to import the module.

Returns:

  • Module

    A module instance.

Source code in src/griffe/_internal/agents/inspector.py
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
def get_module(self, import_paths: Sequence[str | Path] | None = None) -> Module:
    """Build and return the object representing the module attached to this inspector.

    This method triggers a complete inspection of the module members.

    Parameters:
        import_paths: Paths replacing `sys.path` to import the module.

    Returns:
        A module instance.
    """
    import_path = self.module_name
    if self.parent is not None:
        import_path = f"{self.parent.path}.{import_path}"

    # Make sure `import_paths` is a list, in case we want to `insert` into it.
    import_paths = list(import_paths or ())

    # If the thing we want to import has a filepath,
    # we make sure to insert the right parent directory
    # at the front of our list of import paths.
    # We do this by counting the number of dots `.` in the import path,
    # corresponding to slashes `/` in the filesystem,
    # and go up in the file tree the same number of times.
    if self.filepath:
        parent_path = self.filepath.parent
        for _ in range(import_path.count(".")):
            parent_path = parent_path.parent
        # Climb up one more time for `__init__` modules.
        if self.filepath.stem == "__init__":
            parent_path = parent_path.parent
        if parent_path not in import_paths:
            import_paths.insert(0, parent_path)

    value = dynamic_import(import_path, import_paths)

    # We successfully imported the given object,
    # and we now create the object tree with all the necessary nodes,
    # from the root of the package to this leaf object.
    parent_node = None
    if self.parent is not None:
        for part in self.parent.path.split("."):
            parent_node = ObjectNode(None, name=part, parent=parent_node)
    module_node = ObjectNode(value, self.module_name, parent=parent_node)

    self.inspect(module_node)
    return self.current.module

handle_attribute ¤

handle_attribute(
    node: ObjectNode, annotation: str | Expr | None = None
) -> None

Handle an attribute.

Parameters:

  • node ¤

    (ObjectNode) –

    The node to inspect.

  • annotation ¤

    (str | Expr | None, default: None ) –

    A potential annotation.

Source code in src/griffe/_internal/agents/inspector.py
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
def handle_attribute(self, node: ObjectNode, annotation: str | Expr | None = None) -> None:
    """Handle an attribute.

    Parameters:
        node: The node to inspect.
        annotation: A potential annotation.
    """
    self.extensions.call("on_node", node=node, agent=self)
    self.extensions.call("on_attribute_node", node=node, agent=self)

    # TODO: To improve.
    parent = self.current
    labels: set[str] = set()

    if parent.kind is Kind.MODULE:
        labels.add("module-attribute")
    elif parent.kind is Kind.CLASS:
        labels.add("class-attribute")
    elif parent.kind is Kind.FUNCTION:
        if parent.name != "__init__":
            return
        parent = parent.parent  # type: ignore[assignment]
        labels.add("instance-attribute")

    try:
        value = repr(node.obj)
    except Exception:  # noqa: BLE001
        value = None
    try:
        docstring = self._get_docstring(node)
    except Exception:  # noqa: BLE001
        docstring = None

    attribute = Attribute(
        name=node.name,
        value=value,
        annotation=annotation,
        docstring=docstring,
        analysis="dynamic",
    )
    attribute.labels |= labels
    parent.set_member(node.name, attribute)

    if node.name == "__all__":
        parent.exports = list(node.obj)
    self.extensions.call("on_instance", node=node, obj=attribute, agent=self)
    self.extensions.call("on_attribute_instance", node=node, attr=attribute, agent=self)

handle_function ¤

handle_function(
    node: ObjectNode, labels: set | None = None
) -> None

Handle a function.

Parameters:

  • node ¤

    (ObjectNode) –

    The node to inspect.

  • labels ¤

    (set | None, default: None ) –

    Labels to add to the data object.

Source code in src/griffe/_internal/agents/inspector.py
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
def handle_function(self, node: ObjectNode, labels: set | None = None) -> None:
    """Handle a function.

    Parameters:
        node: The node to inspect.
        labels: Labels to add to the data object.
    """
    self.extensions.call("on_node", node=node, agent=self)
    self.extensions.call("on_function_node", node=node, agent=self)

    try:
        signature = getsignature(node.obj)
    except Exception:  # noqa: BLE001
        # So many exceptions can be raised here:
        # AttributeError, NameError, RuntimeError, ValueError, TokenError, TypeError...
        parameters = None
        returns = None
    else:
        parameters = Parameters(
            *[
                _convert_parameter(parameter, parent=self.current, member=node.name)
                for parameter in signature.parameters.values()
            ],
        )
        return_annotation = signature.return_annotation
        returns = (
            None
            if return_annotation is _empty
            else _convert_object_to_annotation(return_annotation, parent=self.current, member=node.name)
        )

    lineno, endlineno = self._get_linenos(node)

    obj: Attribute | Function
    labels = labels or set()
    if "property" in labels:
        obj = Attribute(
            name=node.name,
            value=None,
            annotation=returns,
            docstring=self._get_docstring(node),
            lineno=lineno,
            endlineno=endlineno,
            analysis="dynamic",
        )
    else:
        obj = Function(
            name=node.name,
            parameters=parameters,
            returns=returns,
            type_parameters=TypeParameters(
                *_convert_type_parameters(node.obj, parent=self.current, member=node.name),
            ),
            docstring=self._get_docstring(node),
            lineno=lineno,
            endlineno=endlineno,
            analysis="dynamic",
        )
    obj.labels |= labels
    self.current.set_member(node.name, obj)
    self.extensions.call("on_instance", node=node, obj=obj, agent=self)
    if obj.is_attribute:
        self.extensions.call("on_attribute_instance", node=node, attr=obj, agent=self)
    else:
        self.extensions.call("on_function_instance", node=node, func=obj, agent=self)

inspect ¤

inspect(node: ObjectNode) -> None

Extend the base inspection with extensions.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
255
256
257
258
259
260
261
def inspect(self, node: ObjectNode) -> None:
    """Extend the base inspection with extensions.

    Parameters:
        node: The node to inspect.
    """
    getattr(self, f"inspect_{node.kind}", self.generic_inspect)(node)

inspect_attribute ¤

inspect_attribute(node: ObjectNode) -> None

Inspect an attribute.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
536
537
538
539
540
541
542
def inspect_attribute(self, node: ObjectNode) -> None:
    """Inspect an attribute.

    Parameters:
        node: The node to inspect.
    """
    self.handle_attribute(node)

inspect_builtin_function ¤

inspect_builtin_function(node: ObjectNode) -> None

Inspect a builtin function.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
405
406
407
408
409
410
411
def inspect_builtin_function(self, node: ObjectNode) -> None:
    """Inspect a builtin function.

    Parameters:
        node: The node to inspect.
    """
    self.handle_function(node, {"builtin"})

inspect_builtin_method ¤

inspect_builtin_method(node: ObjectNode) -> None

Inspect a builtin method.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
381
382
383
384
385
386
387
def inspect_builtin_method(self, node: ObjectNode) -> None:
    """Inspect a builtin method.

    Parameters:
        node: The node to inspect.
    """
    self.handle_function(node, {"builtin"})

inspect_cached_property ¤

inspect_cached_property(node: ObjectNode) -> None

Inspect a cached property.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
421
422
423
424
425
426
427
def inspect_cached_property(self, node: ObjectNode) -> None:
    """Inspect a cached property.

    Parameters:
        node: The node to inspect.
    """
    self.handle_function(node, {"cached", "property"})

inspect_class ¤

inspect_class(node: ObjectNode) -> None

Inspect a class.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
def inspect_class(self, node: ObjectNode) -> None:
    """Inspect a class.

    Parameters:
        node: The node to inspect.
    """
    self.extensions.call("on_node", node=node, agent=self)
    self.extensions.call("on_class_node", node=node, agent=self)

    bases = []
    for base in node.obj.__bases__:
        if base is object:
            continue
        bases.append(f"{base.__module__}.{base.__qualname__}")

    lineno, endlineno = self._get_linenos(node)
    class_ = Class(
        name=node.name,
        docstring=self._get_docstring(node),
        bases=bases,
        type_parameters=TypeParameters(*_convert_type_parameters(node.obj, parent=self.current, member=node.name)),
        lineno=lineno,
        endlineno=endlineno,
        analysis="dynamic",
    )
    self.current.set_member(node.name, class_)
    self.current = class_
    self.extensions.call("on_instance", node=node, obj=class_, agent=self)
    self.extensions.call("on_class_instance", node=node, cls=class_, agent=self)
    self.generic_inspect(node)
    self.extensions.call("on_members", node=node, obj=class_, agent=self)
    self.extensions.call("on_class_members", node=node, cls=class_, agent=self)
    self.current = self.current.parent  # type: ignore[assignment]

inspect_classmethod ¤

inspect_classmethod(node: ObjectNode) -> None

Inspect a class method.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
365
366
367
368
369
370
371
def inspect_classmethod(self, node: ObjectNode) -> None:
    """Inspect a class method.

    Parameters:
        node: The node to inspect.
    """
    self.handle_function(node, {"classmethod"})

inspect_coroutine ¤

inspect_coroutine(node: ObjectNode) -> None

Inspect a coroutine.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
397
398
399
400
401
402
403
def inspect_coroutine(self, node: ObjectNode) -> None:
    """Inspect a coroutine.

    Parameters:
        node: The node to inspect.
    """
    self.handle_function(node, {"async"})

inspect_function ¤

inspect_function(node: ObjectNode) -> None

Inspect a function.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
413
414
415
416
417
418
419
def inspect_function(self, node: ObjectNode) -> None:
    """Inspect a function.

    Parameters:
        node: The node to inspect.
    """
    self.handle_function(node)

inspect_getset_descriptor ¤

inspect_getset_descriptor(node: ObjectNode) -> None

Inspect a get/set descriptor.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
437
438
439
440
441
442
443
def inspect_getset_descriptor(self, node: ObjectNode) -> None:
    """Inspect a get/set descriptor.

    Parameters:
        node: The node to inspect.
    """
    self.handle_function(node, {"property"})

inspect_method ¤

inspect_method(node: ObjectNode) -> None

Inspect a method.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
389
390
391
392
393
394
395
def inspect_method(self, node: ObjectNode) -> None:
    """Inspect a method.

    Parameters:
        node: The node to inspect.
    """
    self.handle_function(node)

inspect_method_descriptor ¤

inspect_method_descriptor(node: ObjectNode) -> None

Inspect a method descriptor.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
373
374
375
376
377
378
379
def inspect_method_descriptor(self, node: ObjectNode) -> None:
    """Inspect a method descriptor.

    Parameters:
        node: The node to inspect.
    """
    self.handle_function(node, {"method descriptor"})

inspect_module ¤

inspect_module(node: ObjectNode) -> None

Inspect a module.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
def inspect_module(self, node: ObjectNode) -> None:
    """Inspect a module.

    Parameters:
        node: The node to inspect.
    """
    self.extensions.call("on_node", node=node, agent=self)
    self.extensions.call("on_module_node", node=node, agent=self)
    self.current = module = Module(
        name=self.module_name,
        filepath=self.filepath,
        parent=self.parent,
        docstring=self._get_docstring(node),
        lines_collection=self.lines_collection,
        modules_collection=self.modules_collection,
        analysis="dynamic",
    )
    self.extensions.call("on_instance", node=node, obj=module, agent=self)
    self.extensions.call("on_module_instance", node=node, mod=module, agent=self)
    self.generic_inspect(node)
    self.extensions.call("on_members", node=node, obj=module, agent=self)
    self.extensions.call("on_module_members", node=node, mod=module, agent=self)

inspect_property ¤

inspect_property(node: ObjectNode) -> None

Inspect a property.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
429
430
431
432
433
434
435
def inspect_property(self, node: ObjectNode) -> None:
    """Inspect a property.

    Parameters:
        node: The node to inspect.
    """
    self.handle_function(node, {"property"})

inspect_staticmethod ¤

inspect_staticmethod(node: ObjectNode) -> None

Inspect a static method.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
357
358
359
360
361
362
363
def inspect_staticmethod(self, node: ObjectNode) -> None:
    """Inspect a static method.

    Parameters:
        node: The node to inspect.
    """
    self.handle_function(node, {"staticmethod"})

inspect_type_alias ¤

inspect_type_alias(node: ObjectNode) -> None

Inspect a type alias.

Parameters:

Source code in src/griffe/_internal/agents/inspector.py
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
def inspect_type_alias(self, node: ObjectNode) -> None:
    """Inspect a type alias.

    Parameters:
        node: The node to inspect.
    """
    self.extensions.call("on_node", node=node, agent=self)
    self.extensions.call("on_type_alias_node", node=node, agent=self)

    lineno, endlineno = self._get_linenos(node)

    type_alias = TypeAlias(
        name=node.name,
        value=_convert_type_to_annotation(node.obj.__value__, parent=self.current, member=node.name),
        lineno=lineno,
        endlineno=endlineno,
        type_parameters=TypeParameters(*_convert_type_parameters(node.obj, parent=self.current, member=node.name)),
        docstring=self._get_docstring(node),
        parent=self.current,
        analysis="dynamic",
    )
    self.current.set_member(node.name, type_alias)
    self.extensions.call("on_instance", node=node, obj=type_alias, agent=self)
    self.extensions.call("on_type_alias_instance", node=node, type_alias=type_alias, agent=self)

Dynamic analysis helpers¤

sys_path ¤

sys_path(*paths: str | Path) -> Iterator[None]

Redefine sys.path temporarily.

Parameters:

  • *paths ¤

    (str | Path, default: () ) –

    The paths to use when importing modules. If no paths are given, keep sys.path untouched.

Yields:

  • None

    Nothing.

Source code in src/griffe/_internal/importer.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@contextmanager
def sys_path(*paths: str | Path) -> Iterator[None]:
    """Redefine `sys.path` temporarily.

    Parameters:
        *paths: The paths to use when importing modules.
            If no paths are given, keep `sys.path` untouched.

    Yields:
        Nothing.
    """
    if not paths:
        yield
        return
    old_path = sys.path
    sys.path = [str(path) for path in paths]
    try:
        yield
    finally:
        sys.path = old_path

dynamic_import ¤

dynamic_import(
    import_path: str,
    import_paths: Sequence[str | Path] | None = None,
) -> Any

Dynamically import the specified object.

It can be a module, class, method, function, attribute, type alias, nested arbitrarily.

It works like this:

  • for a given object path a.b.x.y
  • it tries to import a.b.x.y as a module (with importlib.import_module)
  • if it fails, it tries again with a.b.x, storing y
  • then a.b, storing x.y
  • then a, storing b.x.y
  • if nothing worked, it raises an error
  • if one of the iteration worked, it moves on, and...
  • it tries to get the remaining (stored) parts with getattr
  • for example it gets b from a, then x from b, etc.
  • if a single attribute access fails, it raises an error
  • if everything worked, it returns the last obtained attribute

Since the function potentially tries multiple things before succeeding, all errors happening along the way are recorded, and re-emitted with an ImportError when it fails, to let users know what was tried.

Important

The paths given through the import_paths parameter are used to temporarily patch sys.path: this function is therefore not thread-safe.

Important

The paths given as import_paths must be correct. The contents of sys.path must be consistent to what a user of the imported code would expect. Given a set of paths, if the import fails for a user, it will fail here too, with potentially unintuitive errors. If we wanted to make this function more robust, we could add a loop to "roll the window" of given paths, shifting them to the left (for example: ("/a/a", "/a/b", "/a/c/"), then ("/a/b", "/a/c", "/a/a/"), then ("/a/c", "/a/a", "/a/b/")), to make sure each entry is given highest priority at least once, maintaining relative order, but we deem this unnecessary for now.

Parameters:

  • import_path ¤

    (str) –

    The path of the object to import.

  • import_paths ¤

    (Sequence[str | Path] | None, default: None ) –

    The (sys) paths to import the object from.

Raises:

  • ModuleNotFoundError

    When the object's module could not be found.

  • ImportError

    When there was an import error or when couldn't get the attribute.

Returns:

  • Any

    The imported object.

Source code in src/griffe/_internal/importer.py
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
def dynamic_import(import_path: str, import_paths: Sequence[str | Path] | None = None) -> Any:
    """Dynamically import the specified object.

    It can be a module, class, method, function, attribute, type alias,
    nested arbitrarily.

    It works like this:

    - for a given object path `a.b.x.y`
    - it tries to import `a.b.x.y` as a module (with `importlib.import_module`)
    - if it fails, it tries again with `a.b.x`, storing `y`
    - then `a.b`, storing `x.y`
    - then `a`, storing `b.x.y`
    - if nothing worked, it raises an error
    - if one of the iteration worked, it moves on, and...
    - it tries to get the remaining (stored) parts with `getattr`
    - for example it gets `b` from `a`, then `x` from `b`, etc.
    - if a single attribute access fails, it raises an error
    - if everything worked, it returns the last obtained attribute

    Since the function potentially tries multiple things before succeeding,
    all errors happening along the way are recorded, and re-emitted with
    an `ImportError` when it fails, to let users know what was tried.

    IMPORTANT: The paths given through the `import_paths` parameter are used
    to temporarily patch `sys.path`: this function is therefore not thread-safe.

    IMPORTANT: The paths given as `import_paths` must be *correct*.
    The contents of `sys.path` must be consistent to what a user of the imported code
    would expect. Given a set of paths, if the import fails for a user, it will fail here too,
    with potentially unintuitive errors. If we wanted to make this function more robust,
    we could add a loop to "roll the window" of given paths, shifting them to the left
    (for example: `("/a/a", "/a/b", "/a/c/")`, then `("/a/b", "/a/c", "/a/a/")`,
    then `("/a/c", "/a/a", "/a/b/")`), to make sure each entry is given highest priority
    at least once, maintaining relative order, but we deem this unnecessary for now.

    Parameters:
        import_path: The path of the object to import.
        import_paths: The (sys) paths to import the object from.

    Raises:
        ModuleNotFoundError: When the object's module could not be found.
        ImportError: When there was an import error or when couldn't get the attribute.

    Returns:
        The imported object.
    """
    module_parts: list[str] = import_path.split(".")
    object_parts: list[str] = []
    errors = []

    with sys_path(*(import_paths or ())):
        while module_parts:
            module_path = ".".join(module_parts)
            try:
                module = import_module(module_path)
            except BaseException as error:  # noqa: BLE001
                # pyo3's PanicException can only be caught with BaseException.
                # We do want to catch base exceptions anyway (exit, interrupt, etc.).
                errors.append(_error_details(error, module_path))
                object_parts.insert(0, module_parts.pop(-1))
            else:
                break
        else:
            raise ImportError("; ".join(errors))

        # Sometimes extra dependencies are not installed,
        # so importing the leaf module fails with a ModuleNotFoundError,
        # or later `getattr` triggers additional code that fails.
        # In these cases, and for consistency, we always re-raise an ImportError
        # instead of an any other exception (it's called "dynamic import" after all).
        # See https://github.com/mkdocstrings/mkdocstrings/issues/380.
        value = module
        for part in object_parts:
            try:
                value = getattr(value, part)
            except BaseException as error:  # noqa: BLE001
                errors.append(_error_details(error, module_path + ":" + ".".join(object_parts)))
                raise ImportError("; ".join(errors))  # noqa: B904

    return value

ObjectNode ¤

ObjectNode(
    obj: Any, name: str, parent: ObjectNode | None = None
)

Helper class to represent an object tree.

It's not really a tree but more a backward-linked list: each node has a reference to its parent, but not to its child (for simplicity purposes and to avoid bugs).

Each node stores an object, its name, and a reference to its parent node.

Parameters:

  • obj ¤

    (Any) –

    A Python object.

  • name ¤

    (str) –

    The object's name.

  • parent ¤

    (ObjectNode | None, default: None ) –

    The object's parent node.

Attributes:

Source code in src/griffe/_internal/agents/nodes/runtime.py
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def __init__(self, obj: Any, name: str, parent: ObjectNode | None = None) -> None:
    """Initialize the object.

    Parameters:
        obj: A Python object.
        name: The object's name.
        parent: The object's parent node.
    """
    # Unwrap object.
    try:
        obj = inspect.unwrap(obj)
    except Exception as error:  # noqa: BLE001
        # `inspect.unwrap` at some point runs `hasattr(obj, "__wrapped__")`,
        # which triggers the `__getattr__` method of the object, which in
        # turn can raise various exceptions. Probably not just `__getattr__`.
        # See https://github.com/pawamoy/pytkdocs/issues/45.
        logger.debug("Could not unwrap %s: %r", name, error)

    # Unwrap cached properties (`inspect.unwrap` doesn't do that).
    if isinstance(obj, cached_property):
        is_cached_property = True
        obj = obj.func
    else:
        is_cached_property = False

    self.obj: Any = obj
    """The actual Python object."""
    self.name: str = name
    """The Python object's name."""
    self.parent: ObjectNode | None = parent
    """The parent node."""
    self.is_cached_property: bool = is_cached_property
    """Whether this node's object is a cached property."""

alias_target_path cached property ¤

alias_target_path: str | None

Alias target path of this node, if the node should be an alias.

children cached property ¤

children: Sequence[ObjectNode]

The children of this node.

exclude_specials class-attribute ¤

exclude_specials: set[str] = {
    "__builtins__",
    "__loader__",
    "__spec__",
}

Low level attributes known to cause issues when resolving aliases.

is_attribute cached property ¤

is_attribute: bool

Whether this node's object is an attribute.

is_builtin_function cached property ¤

is_builtin_function: bool

Whether this node's object is a builtin function.

is_builtin_method cached property ¤

is_builtin_method: bool

Whether this node's object is a builtin method.

is_cached_property instance-attribute ¤

is_cached_property: bool = is_cached_property

Whether this node's object is a cached property.

is_class cached property ¤

is_class: bool

Whether this node's object is a class.

is_classmethod cached property ¤

is_classmethod: bool

Whether this node's object is a classmethod.

is_coroutine cached property ¤

is_coroutine: bool

Whether this node's object is a coroutine.

is_function cached property ¤

is_function: bool

Whether this node's object is a function.

is_getset_descriptor cached property ¤

is_getset_descriptor: bool

Whether this node's object is a get/set descriptor.

is_method cached property ¤

is_method: bool

Whether this node's object is a method.

is_method_descriptor cached property ¤

is_method_descriptor: bool

Whether this node's object is a method descriptor.

Built-in methods (e.g. those implemented in C/Rust) are often method descriptors, rather than normal methods.

is_module cached property ¤

is_module: bool

Whether this node's object is a module.

is_property cached property ¤

is_property: bool

Whether this node's object is a property.

is_staticmethod cached property ¤

is_staticmethod: bool

Whether this node's object is a staticmethod.

is_type_alias cached property ¤

is_type_alias: bool

Whether this node's object is a type alias.

kind property ¤

kind: ObjectKind

The kind of this node.

module property ¤

module: ObjectNode

The object's module, fetched from the node tree.

module_path property ¤

module_path: str | None

The object's module path.

name instance-attribute ¤

name: str = name

The Python object's name.

obj instance-attribute ¤

obj: Any = obj

The actual Python object.

parent instance-attribute ¤

parent: ObjectNode | None = parent

The parent node.

parent_is_class cached property ¤

parent_is_class: bool

Whether the object of this node's parent is a class.

path property ¤

path: str

The object's (Python) path.

ObjectKind ¤

Bases: str, Enum


              flowchart TD
              griffe.ObjectKind[ObjectKind]

              

              click griffe.ObjectKind href "" "griffe.ObjectKind"
            

Enumeration of the different runtime object kinds.

Attributes:

ATTRIBUTE class-attribute instance-attribute ¤

ATTRIBUTE = 'attribute'

Attributes.

BUILTIN_FUNCTION class-attribute instance-attribute ¤

BUILTIN_FUNCTION = 'builtin_function'

Built-in functions.

BUILTIN_METHOD class-attribute instance-attribute ¤

BUILTIN_METHOD = 'builtin_method'

Built-in methods.

CACHED_PROPERTY class-attribute instance-attribute ¤

CACHED_PROPERTY = 'cached_property'

Cached properties.

CLASS class-attribute instance-attribute ¤

CLASS = 'class'

Classes.

CLASSMETHOD class-attribute instance-attribute ¤

CLASSMETHOD = 'classmethod'

Class methods.

COROUTINE class-attribute instance-attribute ¤

COROUTINE = 'coroutine'

Coroutines

FUNCTION class-attribute instance-attribute ¤

FUNCTION = 'function'

Functions.

GETSET_DESCRIPTOR class-attribute instance-attribute ¤

GETSET_DESCRIPTOR = 'getset_descriptor'

Get/set descriptors.

METHOD class-attribute instance-attribute ¤

METHOD = 'method'

Methods.

METHOD_DESCRIPTOR class-attribute instance-attribute ¤

METHOD_DESCRIPTOR = 'method_descriptor'

Method descriptors.

MODULE class-attribute instance-attribute ¤

MODULE = 'module'

Modules.

PROPERTY class-attribute instance-attribute ¤

PROPERTY = 'property'

Properties.

STATICMETHOD class-attribute instance-attribute ¤

STATICMETHOD = 'staticmethod'

Static methods.

TYPE_ALIAS class-attribute instance-attribute ¤

TYPE_ALIAS = 'type_alias'

Type aliases.

Static analysis helpers¤

builtin_decorators module-attribute ¤

builtin_decorators = {
    "property": "property",
    "staticmethod": "staticmethod",
    "classmethod": "classmethod",
}

Mapping of builtin decorators to labels.

stdlib_decorators module-attribute ¤

stdlib_decorators = {
    "abc.abstractmethod": {"abstractmethod"},
    "functools.cache": {"cached"},
    "functools.cached_property": {"cached", "property"},
    "cached_property.cached_property": {
        "cached",
        "property",
    },
    "functools.lru_cache": {"cached"},
    "dataclasses.dataclass": {"dataclass"},
}

Mapping of standard library decorators to labels.

typing_overload module-attribute ¤

typing_overload = {
    "typing.overload",
    "typing_extensions.overload",
}

Set of recognized typing overload decorators.

When such a decorator is found, the decorated function becomes an overload.

ast_kind ¤

ast_kind(node: AST) -> str

Return the kind of an AST node.

Parameters:

  • node ¤

    (AST) –

    The AST node.

Returns:

  • str

    The node kind.

Source code in src/griffe/_internal/agents/nodes/ast.py
14
15
16
17
18
19
20
21
22
23
def ast_kind(node: AST) -> str:
    """Return the kind of an AST node.

    Parameters:
        node: The AST node.

    Returns:
        The node kind.
    """
    return node.__class__.__name__.lower()

ast_children ¤

ast_children(node: AST) -> Iterator[AST]

Return the children of an AST node.

Parameters:

  • node ¤

    (AST) –

    The AST node.

Yields:

  • AST

    The node children.

Source code in src/griffe/_internal/agents/nodes/ast.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def ast_children(node: AST) -> Iterator[AST]:
    """Return the children of an AST node.

    Parameters:
        node: The AST node.

    Yields:
        The node children.
    """
    for field_name in node._fields:
        try:
            field = getattr(node, field_name)
        except AttributeError:
            continue
        if isinstance(field, AST):
            field.parent = node  # type: ignore[attr-defined]
            yield field
        elif isinstance(field, list):
            for child in field:
                if isinstance(child, AST):
                    child.parent = node  # type: ignore[attr-defined]
                    yield child

ast_previous_siblings ¤

ast_previous_siblings(node: AST) -> Iterator[AST]

Return the previous siblings of this node, starting from the closest.

Parameters:

  • node ¤

    (AST) –

    The AST node.

Yields:

  • AST

    The previous siblings.

Source code in src/griffe/_internal/agents/nodes/ast.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
def ast_previous_siblings(node: AST) -> Iterator[AST]:
    """Return the previous siblings of this node, starting from the closest.

    Parameters:
        node: The AST node.

    Yields:
        The previous siblings.
    """
    for sibling in ast_children(node.parent):  # type: ignore[attr-defined]
        if sibling is not node:
            yield sibling
        else:
            return

ast_next_siblings ¤

ast_next_siblings(node: AST) -> Iterator[AST]

Return the next siblings of this node, starting from the closest.

Parameters:

  • node ¤

    (AST) –

    The AST node.

Yields:

  • AST

    The next siblings.

Source code in src/griffe/_internal/agents/nodes/ast.py
66
67
68
69
70
71
72
73
74
75
76
77
78
79
def ast_next_siblings(node: AST) -> Iterator[AST]:
    """Return the next siblings of this node, starting from the closest.

    Parameters:
        node: The AST node.

    Yields:
        The next siblings.
    """
    siblings = ast_children(node.parent)  # type: ignore[attr-defined]
    for sibling in siblings:
        if sibling is node:
            break
    yield from siblings

ast_siblings ¤

ast_siblings(node: AST) -> Iterator[AST]

Return the siblings of this node.

Parameters:

  • node ¤

    (AST) –

    The AST node.

Yields:

  • AST

    The siblings.

Source code in src/griffe/_internal/agents/nodes/ast.py
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
def ast_siblings(node: AST) -> Iterator[AST]:
    """Return the siblings of this node.

    Parameters:
        node: The AST node.

    Yields:
        The siblings.
    """
    siblings = ast_children(node.parent)  # type: ignore[attr-defined]
    for sibling in siblings:
        if sibling is not node:
            yield sibling
        else:
            break
    yield from siblings

ast_previous ¤

ast_previous(node: AST) -> AST

Return the previous sibling of this node.

Parameters:

  • node ¤

    (AST) –

    The AST node.

Raises:

  • LastNodeError

    When the node does not have previous siblings.

Returns:

  • AST

    The sibling.

Source code in src/griffe/_internal/agents/nodes/ast.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
def ast_previous(node: AST) -> AST:
    """Return the previous sibling of this node.

    Parameters:
        node: The AST node.

    Raises:
        LastNodeError: When the node does not have previous siblings.

    Returns:
        The sibling.
    """
    try:
        *_, last = ast_previous_siblings(node)
    except ValueError:
        raise LastNodeError("there is no previous node") from None
    return last

ast_next ¤

ast_next(node: AST) -> AST

Return the next sibling of this node.

Parameters:

  • node ¤

    (AST) –

    The AST node.

Raises:

Returns:

  • AST

    The sibling.

Source code in src/griffe/_internal/agents/nodes/ast.py
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def ast_next(node: AST) -> AST:
    """Return the next sibling of this node.

    Parameters:
        node: The AST node.

    Raises:
        LastNodeError: When the node does not have next siblings.

    Returns:
        The sibling.
    """
    try:
        return next(ast_next_siblings(node))
    except StopIteration:
        raise LastNodeError("there is no next node") from None

ast_first_child ¤

ast_first_child(node: AST) -> AST

Return the first child of this node.

Parameters:

  • node ¤

    (AST) –

    The AST node.

Raises:

Returns:

  • AST

    The child.

Source code in src/griffe/_internal/agents/nodes/ast.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
def ast_first_child(node: AST) -> AST:
    """Return the first child of this node.

    Parameters:
        node: The AST node.

    Raises:
        LastNodeError: When the node does not have children.

    Returns:
        The child.
    """
    try:
        return next(ast_children(node))
    except StopIteration as error:
        raise LastNodeError("there are no children node") from error

ast_last_child ¤

ast_last_child(node: AST) -> AST

Return the lasts child of this node.

Parameters:

  • node ¤

    (AST) –

    The AST node.

Raises:

Returns:

  • AST

    The child.

Source code in src/griffe/_internal/agents/nodes/ast.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
def ast_last_child(node: AST) -> AST:
    """Return the lasts child of this node.

    Parameters:
        node: The AST node.

    Raises:
        LastNodeError: When the node does not have children.

    Returns:
        The child.
    """
    try:
        *_, last = ast_children(node)
    except ValueError as error:
        raise LastNodeError("there are no children node") from error
    return last

get_docstring ¤

get_docstring(
    node: AST, *, strict: bool = False
) -> tuple[str | None, int | None, int | None]

Extract a docstring.

Parameters:

  • node ¤

    (AST) –

    The node to extract the docstring from.

  • strict ¤

    (bool, default: False ) –

    Whether to skip searching the body (functions).

Returns:

  • tuple[str | None, int | None, int | None]

    A tuple with the value and line numbers of the docstring.

Source code in src/griffe/_internal/agents/nodes/docstrings.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def get_docstring(
    node: ast.AST,
    *,
    strict: bool = False,
) -> tuple[str | None, int | None, int | None]:
    """Extract a docstring.

    Parameters:
        node: The node to extract the docstring from.
        strict: Whether to skip searching the body (functions).

    Returns:
        A tuple with the value and line numbers of the docstring.
    """
    # TODO: Possible optimization using a type map.
    if isinstance(node, ast.Expr):
        doc = node.value
    elif not strict and node.body and isinstance(node.body, list) and isinstance(node.body[0], ast.Expr):  # type: ignore[attr-defined]
        doc = node.body[0].value  # type: ignore[attr-defined]
    else:
        return None, None, None
    if isinstance(doc, ast.Constant) and isinstance(doc.value, str):
        return doc.value, doc.lineno, doc.end_lineno
    return None, None, None

get_name ¤

get_name(node: AST) -> str

Extract name from an assignment node.

Parameters:

  • node ¤

    (AST) –

    The node to extract names from.

Returns:

  • str

    A list of names.

Source code in src/griffe/_internal/agents/nodes/assignments.py
23
24
25
26
27
28
29
30
31
32
def get_name(node: ast.AST) -> str:
    """Extract name from an assignment node.

    Parameters:
        node: The node to extract names from.

    Returns:
        A list of names.
    """
    return _node_name_map[type(node)](node)

get_names ¤

get_names(node: AST) -> list[str]

Extract names from an assignment node.

Parameters:

  • node ¤

    (AST) –

    The node to extract names from.

Returns:

Source code in src/griffe/_internal/agents/nodes/assignments.py
51
52
53
54
55
56
57
58
59
60
def get_names(node: ast.AST) -> list[str]:
    """Extract names from an assignment node.

    Parameters:
        node: The node to extract names from.

    Returns:
        A list of names.
    """
    return _node_names_map[type(node)](node)

get_instance_names ¤

get_instance_names(node: AST) -> list[str]

Extract names from an assignment node, only for instance attributes.

Parameters:

  • node ¤

    (AST) –

    The node to extract names from.

Returns:

Source code in src/griffe/_internal/agents/nodes/assignments.py
63
64
65
66
67
68
69
70
71
72
def get_instance_names(node: ast.AST) -> list[str]:
    """Extract names from an assignment node, only for instance attributes.

    Parameters:
        node: The node to extract names from.

    Returns:
        A list of names.
    """
    return [name.split(".", 1)[1] for name in get_names(node) if name.startswith("self.")]

get__all__ ¤

Get the values declared in __all__.

Parameters:

Returns:

Source code in src/griffe/_internal/agents/nodes/exports.py
78
79
80
81
82
83
84
85
86
87
88
89
90
def get__all__(node: ast.Assign | ast.AnnAssign | ast.AugAssign, parent: Module) -> list[str | ExprName]:
    """Get the values declared in `__all__`.

    Parameters:
        node: The assignment node.
        parent: The parent module.

    Returns:
        A set of names.
    """
    if node.value is None:
        return []
    return _extract(node.value, parent)

safe_get__all__ ¤

Safely (no exception) extract values in __all__.

Parameters:

Returns:

Source code in src/griffe/_internal/agents/nodes/exports.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def safe_get__all__(
    node: ast.Assign | ast.AnnAssign | ast.AugAssign,
    parent: Module,
    log_level: LogLevel = LogLevel.debug,  # TODO: Set to error when we handle more things?
) -> list[str | ExprName]:
    """Safely (no exception) extract values in `__all__`.

    Parameters:
        node: The `__all__` assignment node.
        parent: The parent used to resolve the names.
        log_level: Log level to use to log a message.

    Returns:
        A list of strings or resolvable names.
    """
    try:
        return get__all__(node, parent)
    except Exception as error:  # noqa: BLE001
        message = f"Failed to extract `__all__` value: {get_value(node.value)}"
        with suppress(Exception):
            message += f" at {parent.relative_filepath}:{node.lineno}"
        if isinstance(error, KeyError):
            message += f": unsupported node {error}"
        else:
            message += f": {error}"
        getattr(logger, log_level.value)(message)
        return []

relative_to_absolute ¤

relative_to_absolute(
    node: ImportFrom, name: alias, current_module: Module
) -> str

Convert a relative import path to an absolute one.

Parameters:

  • node ¤

    (ImportFrom) –

    The "from ... import ..." AST node.

  • name ¤

    (alias) –

    The imported name.

  • current_module ¤

    (Module) –

    The module in which the import happens.

Returns:

  • str

    The absolute import path.

Source code in src/griffe/_internal/agents/nodes/imports.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def relative_to_absolute(node: ast.ImportFrom, name: ast.alias, current_module: Module) -> str:
    """Convert a relative import path to an absolute one.

    Parameters:
        node: The "from ... import ..." AST node.
        name: The imported name.
        current_module: The module in which the import happens.

    Returns:
        The absolute import path.
    """
    level = node.level
    if (level > 0 and current_module.is_package) or current_module.is_subpackage:
        level -= 1
    while level > 0 and current_module.parent is not None:
        current_module = current_module.parent  # type: ignore[assignment]
        level -= 1
    base = current_module.path + "." if node.level > 0 else ""
    node_module = node.module + "." if node.module else ""
    return base + node_module + name.name

get_parameters ¤

get_parameters(node: arguments) -> ParametersType
Source code in src/griffe/_internal/agents/nodes/parameters.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
def get_parameters(node: ast.arguments) -> ParametersType:
    parameters: ParametersType = []

    # TODO: Probably some optimizations to do here.
    args_kinds_defaults: Iterable = reversed(
        (
            *zip_longest(
                reversed(
                    (
                        *zip_longest(
                            node.posonlyargs,
                            [],
                            fillvalue=ParameterKind.positional_only,
                        ),
                        *zip_longest(node.args, [], fillvalue=ParameterKind.positional_or_keyword),
                    ),
                ),
                reversed(node.defaults),
                fillvalue=None,
            ),
        ),
    )
    arg: ast.arg
    kind: ParameterKind
    arg_default: ast.AST | None
    for (arg, kind), arg_default in args_kinds_defaults:
        parameters.append((arg.arg, arg.annotation, kind, arg_default))

    if node.vararg:
        parameters.append(
            (
                node.vararg.arg,
                node.vararg.annotation,
                ParameterKind.var_positional,
                "()",
            ),
        )

    # TODO: Probably some optimizations to do here.
    kwargs_defaults: Iterable = reversed(
        (
            *zip_longest(
                reversed(node.kwonlyargs),
                reversed(node.kw_defaults),
                fillvalue=None,
            ),
        ),
    )
    kwarg: ast.arg
    kwarg_default: ast.AST | None
    for kwarg, kwarg_default in kwargs_defaults:
        parameters.append(
            (kwarg.arg, kwarg.annotation, ParameterKind.keyword_only, kwarg_default),
        )

    if node.kwarg:
        parameters.append(
            (
                node.kwarg.arg,
                node.kwarg.annotation,
                ParameterKind.var_keyword,
                "{}",
            ),
        )

    return parameters

get_value ¤

get_value(node: AST | None) -> str | None

Get the string representation of a node.

Parameters:

  • node ¤

    (AST | None) –

    The node to represent.

Returns:

  • str | None

    The representing code for the node.

Source code in src/griffe/_internal/agents/nodes/values.py
15
16
17
18
19
20
21
22
23
24
25
26
def get_value(node: ast.AST | None) -> str | None:
    """Get the string representation of a node.

    Parameters:
        node: The node to represent.

    Returns:
        The representing code for the node.
    """
    if node is None:
        return None
    return unparse(node)

safe_get_value ¤

safe_get_value(
    node: AST | None, filepath: str | Path | None = None
) -> str | None

Safely (no exception) get the string representation of a node.

Parameters:

  • node ¤

    (AST | None) –

    The node to represent.

  • filepath ¤

    (str | Path | None, default: None ) –

    An optional filepath from where the node comes.

Returns:

  • str | None

    The representing code for the node.

Source code in src/griffe/_internal/agents/nodes/values.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def safe_get_value(node: ast.AST | None, filepath: str | Path | None = None) -> str | None:
    """Safely (no exception) get the string representation of a node.

    Parameters:
        node: The node to represent.
        filepath: An optional filepath from where the node comes.

    Returns:
        The representing code for the node.
    """
    try:
        return get_value(node)
    except Exception as error:  # noqa: BLE001
        message = f"Failed to represent node {node}"
        if filepath:
            message += f" at {filepath}:{node.lineno}"  # type: ignore[union-attr]
        message += f": {error}"
        logger.exception(message)
        return None

Deprecated API¤

ExportedName dataclass ¤

ExportedName(name: str, parent: Module)

Deprecated. An intermediate class to store names.

The get__all__ function now returns instances of ExprName instead.

Attributes:

name instance-attribute ¤

name: str

The exported name.

parent instance-attribute ¤

parent: Module

The parent module.