Coverage for src/_griffe/agents/nodes/exports.py: 92.98%
51 statements
« prev ^ index » next coverage.py v7.6.2, created at 2024-10-12 01:34 +0200
« prev ^ index » next coverage.py v7.6.2, created at 2024-10-12 01:34 +0200
1# This module contains utilities for extracting exports from `__all__` assignments.
3from __future__ import annotations
5import ast
6from contextlib import suppress
7from dataclasses import dataclass
8from typing import TYPE_CHECKING, Any, Callable
10from _griffe.agents.nodes.values import get_value
11from _griffe.enumerations import LogLevel
12from _griffe.expressions import ExprName
13from _griffe.logger import logger
15if TYPE_CHECKING:
16 from _griffe.models import Module
19# YORE: Bump 2: Remove block.
20@dataclass
21class ExportedName:
22 """Deprecated. An intermediate class to store names.
24 The [`get__all__`][griffe.get__all__] function now returns instances of [`ExprName`][griffe.ExprName] instead.
25 """
27 name: str
28 """The exported name."""
29 parent: Module
30 """The parent module."""
33def _extract_attribute(node: ast.Attribute, parent: Module) -> list[str | ExprName]:
34 return [ExprName(name=node.attr, parent=_extract(node.value, parent)[0])]
37def _extract_binop(node: ast.BinOp, parent: Module) -> list[str | ExprName]:
38 left = _extract(node.left, parent)
39 right = _extract(node.right, parent)
40 return left + right
43def _extract_constant(node: ast.Constant, parent: Module) -> list[str | ExprName]:
44 return [node.value]
47def _extract_name(node: ast.Name, parent: Module) -> list[str | ExprName]:
48 return [ExprName(node.id, parent)]
51def _extract_sequence(node: ast.List | ast.Set | ast.Tuple, parent: Module) -> list[str | ExprName]:
52 sequence = []
53 for elt in node.elts:
54 sequence.extend(_extract(elt, parent))
55 return sequence
58def _extract_starred(node: ast.Starred, parent: Module) -> list[str | ExprName]:
59 return _extract(node.value, parent)
62_node_map: dict[type, Callable[[Any, Module], list[str | ExprName]]] = {
63 ast.Attribute: _extract_attribute,
64 ast.BinOp: _extract_binop,
65 ast.Constant: _extract_constant,
66 ast.List: _extract_sequence,
67 ast.Name: _extract_name,
68 ast.Set: _extract_sequence,
69 ast.Starred: _extract_starred,
70 ast.Tuple: _extract_sequence,
71}
74def _extract(node: ast.AST, parent: Module) -> list[str | ExprName]:
75 return _node_map[type(node)](node, parent)
78def get__all__(node: ast.Assign | ast.AnnAssign | ast.AugAssign, parent: Module) -> list[str | ExprName]:
79 """Get the values declared in `__all__`.
81 Parameters:
82 node: The assignment node.
83 parent: The parent module.
85 Returns:
86 A set of names.
87 """
88 if node.value is None: 88 ↛ 89line 88 didn't jump to line 89 because the condition on line 88 was never true
89 return []
90 return _extract(node.value, parent)
93def safe_get__all__(
94 node: ast.Assign | ast.AnnAssign | ast.AugAssign,
95 parent: Module,
96 log_level: LogLevel = LogLevel.debug, # TODO: set to error when we handle more things
97) -> list[str | ExprName]:
98 """Safely (no exception) extract values in `__all__`.
100 Parameters:
101 node: The `__all__` assignment node.
102 parent: The parent used to resolve the names.
103 log_level: Log level to use to log a message.
105 Returns:
106 A list of strings or resovable names.
107 """
108 try:
109 return get__all__(node, parent)
110 except Exception as error: # noqa: BLE001
111 message = f"Failed to extract `__all__` value: {get_value(node.value)}"
112 with suppress(Exception):
113 message += f" at {parent.relative_filepath}:{node.lineno}"
114 if isinstance(error, KeyError): 114 ↛ 117line 114 didn't jump to line 117 because the condition on line 114 was always true
115 message += f": unsupported node {error}"
116 else:
117 message += f": {error}"
118 getattr(logger, log_level.value)(message)
119 return []