Coverage for packages / griffelib / src / griffe / _internal / models.py: 82.73%
1063 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-11 11:48 +0100
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-11 11:48 +0100
1# This module contains our models definitions,
2# to represent Python objects (and other aspects of Python APIs)... in Python.
4from __future__ import annotations
6import inspect
7from collections import defaultdict
8from contextlib import suppress
9from dataclasses import asdict
10from pathlib import Path
11from textwrap import dedent
12from typing import TYPE_CHECKING, Any, Literal, cast
14from griffe._internal.c3linear import c3linear_merge
15from griffe._internal.docstrings.parsers import DocstringOptions, DocstringStyle, parse
16from griffe._internal.enumerations import Kind, ParameterKind, Parser, TypeParameterKind
17from griffe._internal.exceptions import AliasResolutionError, BuiltinModuleError, CyclicAliasError, NameResolutionError
18from griffe._internal.expressions import ExprCall, ExprName, ExprTuple
19from griffe._internal.logger import logger
20from griffe._internal.mixins import ObjectAliasMixin
22if TYPE_CHECKING:
23 from collections.abc import Callable, Sequence
25 from griffe._internal.collections import LinesCollection, ModulesCollection
26 from griffe._internal.docstrings.models import DocstringSection
27 from griffe._internal.expressions import Expr
28 from griffe._internal.git import GitInfo
31from functools import cached_property
34class Decorator:
35 """This class represents decorators."""
37 def __init__(self, value: str | Expr, *, lineno: int | None, endlineno: int | None) -> None:
38 """Initialize the decorator.
40 Parameters:
41 value: The decorator code.
42 lineno: The starting line number.
43 endlineno: The ending line number.
44 """
45 self.value: str | Expr = value
46 """The decorator value (as a Griffe expression or string)."""
47 self.lineno: int | None = lineno
48 """The starting line number of the decorator."""
49 self.endlineno: int | None = endlineno
50 """The ending line number of the decorator."""
52 @property
53 def callable_path(self) -> str:
54 """The path of the callable used as decorator."""
55 value = self.value.function if isinstance(self.value, ExprCall) else self.value
56 return value if isinstance(value, str) else value.canonical_path
58 def as_dict(self, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002
59 """Return this decorator's data as a dictionary.
61 Parameters:
62 **kwargs: Additional serialization options.
64 Returns:
65 A dictionary.
66 """
67 return {
68 "value": self.value,
69 "lineno": self.lineno,
70 "endlineno": self.endlineno,
71 }
74class Docstring:
75 """This class represents docstrings."""
77 def __init__(
78 self,
79 value: str,
80 *,
81 lineno: int | None = None,
82 endlineno: int | None = None,
83 parent: Object | None = None,
84 parser: DocstringStyle | Parser | None = None,
85 parser_options: DocstringOptions | None = None,
86 ) -> None:
87 """Initialize the docstring.
89 Parameters:
90 value: The docstring value.
91 lineno: The starting line number.
92 endlineno: The ending line number.
93 parent: The parent object on which this docstring is attached.
94 parser: The docstring parser to use. By default, no parsing is done.
95 parser_options: Additional docstring parsing options.
96 """
97 self.value: str = inspect.cleandoc(value.rstrip())
98 """The original value of the docstring, cleaned by `inspect.cleandoc`.
100 See also: [`source`][griffe.Docstring.source].
101 """
103 self.lineno: int | None = lineno
104 """The starting line number of the docstring.
106 See also: [`endlineno`][griffe.Docstring.endlineno]."""
108 self.endlineno: int | None = endlineno
109 """The ending line number of the docstring.
111 See also: [`lineno`][griffe.Docstring.lineno]."""
113 self.parent: Object | None = parent
114 """The object this docstring is attached to."""
116 self.parser: DocstringStyle | Parser | None = parser
117 """The selected docstring parser.
119 See also: [`parser_options`][griffe.Docstring.parser_options],
120 [`parse`][griffe.Docstring.parse].
121 """
123 self.parser_options: DocstringOptions = parser_options or {}
124 """The configured parsing options.
126 See also: [`parser`][griffe.Docstring.parser],
127 [`parse`][griffe.Docstring.parse].
128 """
130 @property
131 def lines(self) -> list[str]:
132 """The lines of the docstring.
134 See also: [`source`][griffe.Docstring.source].
135 """
136 return self.value.split("\n")
138 @property
139 def source(self) -> str:
140 """The original, uncleaned value of the docstring as written in the source.
142 It is a simple concatenation of the source lines. These source lines will include
143 quotes (single/double/triple) and might include leading whitespace and indentation,
144 as well as trailing comments.
146 Raises:
147 ValueError: If the original docstring cannot be retrieved
148 (no parent, no line numbers, or attached to namespace package).
150 See also: [`value`][griffe.Docstring.value].
151 """
152 if self.parent is None:
153 raise ValueError("Cannot get original docstring without parent object")
154 if isinstance(self.parent.filepath, list):
155 raise ValueError("Cannot get original docstring for namespace package") # noqa: TRY004
156 if self.lineno is None or self.endlineno is None:
157 raise ValueError("Cannot get original docstring without line numbers")
158 return "\n".join(self.parent.lines_collection[self.parent.filepath][self.lineno - 1 : self.endlineno])
160 @cached_property
161 def parsed(self) -> list[DocstringSection]:
162 """The docstring sections, parsed into structured data."""
163 return self.parse()
165 def parse(
166 self,
167 parser: DocstringStyle | Parser | None = None,
168 **options: Any,
169 ) -> list[DocstringSection]:
170 """Parse the docstring into structured data.
172 See also: [`parser`][griffe.Docstring.parser],
173 [`parser_options`][griffe.Docstring.parser_options].
175 Parameters:
176 parser: The docstring parser to use.
177 In order: use the given parser, or the self parser, or no parser (return a single text section).
178 **options: Additional docstring parsing options.
180 Returns:
181 The parsed docstring as a list of sections.
182 """
183 return parse(self, parser or self.parser, **(options or self.parser_options))
185 def as_dict(
186 self,
187 *,
188 full: bool = False,
189 **kwargs: Any, # noqa: ARG002
190 ) -> dict[str, Any]:
191 """Return this docstring's data as a dictionary.
193 Parameters:
194 full: Whether to return full info, or just base info.
195 **kwargs: Additional serialization options.
197 Returns:
198 A dictionary.
199 """
200 base: dict[str, Any] = {
201 "value": self.value,
202 "lineno": self.lineno,
203 "endlineno": self.endlineno,
204 }
205 if full:
206 base["parsed"] = self.parsed
207 return base
210class Parameter: # noqa: PLW1641
211 """This class represent a function parameter.
213 See also: [`Parameters`][griffe.Parameters].
214 """
216 def __init__(
217 self,
218 name: str,
219 *,
220 annotation: str | Expr | None = None,
221 kind: ParameterKind | None = None,
222 default: str | Expr | None = None,
223 docstring: Docstring | None = None,
224 ) -> None:
225 """Initialize the parameter.
227 Parameters:
228 name: The parameter name, without leading stars (`*` or `**`).
229 annotation: The parameter annotation, if any.
230 kind: The parameter kind.
231 default: The parameter default, if any.
232 docstring: The parameter docstring.
233 """
234 self.name: str = name
235 """The parameter name."""
236 self.annotation: str | Expr | None = annotation
237 """The parameter type annotation."""
238 self.kind: ParameterKind | None = kind
239 """The parameter kind."""
240 self.default: str | Expr | None = default
241 """The parameter default value."""
242 self.docstring: Docstring | None = docstring
243 """The parameter docstring."""
244 # The parent function is set in `Function.__init__`,
245 # when the parameters are assigned to the function.
246 self.function: Function | None = None
247 """The parent function of the parameter."""
249 def __str__(self) -> str:
250 param = f"{self.name}: {self.annotation} = {self.default}"
251 if self.kind:
252 return f"[{self.kind.value}] {param}"
253 return param
255 def __repr__(self) -> str:
256 return f"Parameter(name={self.name!r}, annotation={self.annotation!r}, kind={self.kind!r}, default={self.default!r})"
258 def __eq__(self, value: object, /) -> bool:
259 """Parameters are equal if all their attributes except `docstring` and `function` are equal."""
260 if not isinstance(value, Parameter): 260 ↛ 261line 260 didn't jump to line 261 because the condition on line 260 was never true
261 return NotImplemented
262 return (
263 self.name == value.name
264 and self.annotation == value.annotation
265 and self.kind == value.kind
266 and self.default == value.default
267 )
269 @property
270 def required(self) -> bool:
271 """Whether this parameter is required."""
272 return self.default is None
274 def as_dict(self, *, full: bool = False, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002
275 """Return this parameter's data as a dictionary.
277 Parameters:
278 **kwargs: Additional serialization options.
280 Returns:
281 A dictionary.
282 """
283 base: dict[str, Any] = {
284 "name": self.name,
285 "annotation": self.annotation,
286 "kind": self.kind,
287 "default": self.default,
288 }
289 if self.docstring:
290 base["docstring"] = self.docstring.as_dict(full=full)
291 return base
294class Parameters:
295 """This class is a container for parameters.
297 It allows to get parameters using their position (index) or their name:
299 ```pycon
300 >>> parameters = Parameters(Parameter("hello"))
301 >>> parameters[0] is parameters["hello"]
302 True
303 ```
305 See also: [`Parameter`][griffe.Parameter].
306 """
308 def __init__(self, *parameters: Parameter) -> None:
309 """Initialize the parameters container.
311 Parameters:
312 *parameters: The initial parameters to add to the container.
313 """
314 self._params: list[Parameter] = list(parameters)
316 def __repr__(self) -> str:
317 return f"Parameters({', '.join(repr(param) for param in self._params)})"
319 def __getitem__(self, name_or_index: int | str) -> Parameter:
320 """Get a parameter by index or name."""
321 if isinstance(name_or_index, int):
322 return self._params[name_or_index]
323 name = name_or_index.lstrip("*")
324 try:
325 return next(param for param in self._params if param.name == name)
326 except StopIteration as error:
327 raise KeyError(f"parameter {name_or_index} not found") from error
329 def __setitem__(self, name_or_index: int | str, parameter: Parameter) -> None:
330 """Set a parameter by index or name."""
331 if isinstance(name_or_index, int):
332 self._params[name_or_index] = parameter
333 else:
334 name = name_or_index.lstrip("*")
335 try:
336 index = next(idx for idx, param in enumerate(self._params) if param.name == name)
337 except StopIteration:
338 self._params.append(parameter)
339 else:
340 self._params[index] = parameter
342 def __delitem__(self, name_or_index: int | str) -> None:
343 """Delete a parameter by index or name."""
344 if isinstance(name_or_index, int):
345 del self._params[name_or_index]
346 else:
347 name = name_or_index.lstrip("*")
348 try:
349 index = next(idx for idx, param in enumerate(self._params) if param.name == name)
350 except StopIteration as error:
351 raise KeyError(f"parameter {name_or_index} not found") from error
352 del self._params[index]
354 def __len__(self):
355 """The number of parameters."""
356 return len(self._params)
358 def __iter__(self):
359 """Iterate over the parameters, in order."""
360 return iter(self._params)
362 def __contains__(self, param_name: str):
363 """Whether a parameter with the given name is present."""
364 try:
365 next(param for param in self._params if param.name == param_name.lstrip("*"))
366 except StopIteration:
367 return False
368 return True
370 def add(self, parameter: Parameter) -> None:
371 """Add a parameter to the container.
373 Parameters:
374 parameter: The function parameter to add.
376 Raises:
377 ValueError: When a parameter with the same name is already present.
378 """
379 if parameter.name in self:
380 raise ValueError(f"parameter {parameter.name} already present")
381 self._params.append(parameter)
384class TypeParameter:
385 """This class represents a type parameter."""
387 def __init__(
388 self,
389 name: str,
390 *,
391 kind: TypeParameterKind,
392 bound: str | Expr | None = None,
393 constraints: Sequence[str | Expr] | None = None,
394 default: str | Expr | None = None,
395 ) -> None:
396 """Initialize the type parameter.
398 Parameters:
399 name: The type parameter name, without leading stars (`*` or `**`).
400 kind: The type parameter kind.
401 bound: The type parameter bound, if any.
402 Mutually exclusive with `constraints`.
403 constraints: The type parameter constraints, if any.
404 Mutually exclusive with `bound`.
405 default: The type parameter default, if any.
407 Raises:
408 ValueError: When more than one of `bound` and `constraints` is set.
409 """
410 if bound is not None and constraints: 410 ↛ 411line 410 didn't jump to line 411 because the condition on line 410 was never true
411 raise ValueError("bound and constraints are mutually exclusive")
413 self.name: str = name
414 """The type parameter name."""
416 self.kind: TypeParameterKind = kind
417 """The type parameter kind."""
419 self.annotation: str | Expr | None
420 """The type parameter bound or constraints."""
422 if constraints:
423 self.constraints = constraints
424 else:
425 self.bound = bound
427 self.default: str | Expr | None = default
428 """The type parameter default value."""
430 def __repr__(self) -> str:
431 return f"TypeParameter(name={self.name!r}, kind={self.kind!r}, bound={self.annotation!r}, default={self.default!r})"
433 @property
434 def bound(self) -> str | Expr | None:
435 """The type parameter bound."""
436 if not isinstance(self.annotation, ExprTuple):
437 return self.annotation
438 return None
440 @bound.setter
441 def bound(self, bound: str | Expr | None) -> None:
442 self.annotation = bound
444 @property
445 def constraints(self) -> tuple[str | Expr, ...] | None:
446 """The type parameter constraints."""
447 if isinstance(self.annotation, ExprTuple):
448 return tuple(self.annotation.elements)
449 return None
451 @constraints.setter
452 def constraints(self, constraints: Sequence[str | Expr] | None) -> None:
453 if constraints is not None: 453 ↛ 456line 453 didn't jump to line 456 because the condition on line 453 was always true
454 self.annotation = ExprTuple(constraints)
455 else:
456 self.annotation = None
458 def as_dict(self, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002
459 """Return this type parameter's data as a dictionary.
461 Parameters:
462 **kwargs: Additional serialization options.
464 Returns:
465 A dictionary.
466 """
467 base: dict[str, Any] = {
468 "name": self.name,
469 "kind": self.kind,
470 "annotation": self.annotation,
471 "default": self.default,
472 }
473 return base
476class TypeParameters:
477 """This class is a container for type parameters.
479 It allows to get type parameters using their position (index) or their name:
481 ```pycon
482 >>> type_parameters = TypeParameters(TypeParameter("hello"), kind=TypeParameterKind.type_var)
483 >>> type_parameters[0] is type_parameters["hello"]
484 True
485 ```
486 """
488 def __init__(self, *type_parameters: TypeParameter) -> None:
489 """Initialize the type parameters container.
491 Parameters:
492 *type_parameters: The initial type parameters to add to the container.
493 """
494 self._type_params: list[TypeParameter] = list(type_parameters)
496 def __repr__(self) -> str:
497 return f"TypeParameters({', '.join(repr(type_param) for type_param in self._type_params)})"
499 def __getitem__(self, name_or_index: int | str) -> TypeParameter:
500 """Get a type parameter by index or name."""
501 if isinstance(name_or_index, int):
502 return self._type_params[name_or_index]
503 name = name_or_index.lstrip("*")
504 try:
505 return next(param for param in self._type_params if param.name == name)
506 except StopIteration as error:
507 raise KeyError(f"type parameter {name_or_index} not found") from error
509 def __setitem__(self, name_or_index: int | str, type_parameter: TypeParameter) -> None:
510 """Set a type parameter by index or name."""
511 if isinstance(name_or_index, int):
512 self._type_params[name_or_index] = type_parameter
513 else:
514 name = name_or_index.lstrip("*")
515 try:
516 index = next(idx for idx, param in enumerate(self._type_params) if param.name == name)
517 except StopIteration:
518 self._type_params.append(type_parameter)
519 else:
520 self._type_params[index] = type_parameter
522 def __delitem__(self, name_or_index: int | str) -> None:
523 """Delete a type parameter by index or name."""
524 if isinstance(name_or_index, int):
525 del self._type_params[name_or_index]
526 else:
527 name = name_or_index.lstrip("*")
528 try:
529 index = next(idx for idx, param in enumerate(self._type_params) if param.name == name)
530 except StopIteration as error:
531 raise KeyError(f"type parameter {name_or_index} not found") from error
532 del self._type_params[index]
534 def __len__(self):
535 """The number of type parameters."""
536 return len(self._type_params)
538 def __iter__(self):
539 """Iterate over the type parameters, in order."""
540 return iter(self._type_params)
542 def __contains__(self, type_param_name: str):
543 """Whether a type parameter with the given name is present."""
544 try:
545 next(param for param in self._type_params if param.name == type_param_name.lstrip("*"))
546 except StopIteration:
547 return False
548 return True
550 def add(self, type_parameter: TypeParameter) -> None:
551 """Add a type parameter to the container.
553 Parameters:
554 type_parameter: The function parameter to add.
556 Raises:
557 ValueError: When a type parameter with the same name is already present.
558 """
559 if type_parameter.name in self:
560 raise ValueError(f"type parameter {type_parameter.name} already present")
561 self._type_params.append(type_parameter)
564class Object(ObjectAliasMixin):
565 """An abstract class representing a Python object."""
567 kind: Kind
568 """The object kind."""
569 is_alias: bool = False
570 """Always false for objects."""
571 is_collection: bool = False
572 """Always false for objects."""
573 inherited: bool = False
574 """Always false for objects.
576 Only aliases can be marked as inherited.
577 """
579 def __init__(
580 self,
581 name: str,
582 *,
583 lineno: int | None = None,
584 endlineno: int | None = None,
585 runtime: bool = True,
586 docstring: Docstring | None = None,
587 type_parameters: TypeParameters | None = None,
588 parent: Module | Class | None = None,
589 lines_collection: LinesCollection | None = None,
590 modules_collection: ModulesCollection | None = None,
591 git_info: GitInfo | None = None,
592 analysis: Literal["static", "dynamic"] | None = None,
593 ) -> None:
594 """Initialize the object.
596 Parameters:
597 name: The object name, as declared in the code.
598 lineno: The object starting line, or None for modules. Lines start at 1.
599 endlineno: The object ending line (inclusive), or None for modules.
600 runtime: Whether this object is present at runtime or not.
601 docstring: The object docstring.
602 type_parameters: The object type parameters, if any.
603 parent: The object parent.
604 lines_collection: A collection of source code lines.
605 modules_collection: A collection of modules.
606 git_info: Git information.
607 analysis: The type of analysis used to load this object.
608 None means the object was created manually.
609 """
610 self.name: str = name
611 """The object name."""
613 self.lineno: int | None = lineno
614 """The starting line number of the object.
616 See also: [`endlineno`][griffe.Object.endlineno].
617 """
619 self.endlineno: int | None = endlineno
620 """The ending line number of the object.
622 See also: [`lineno`][griffe.Object.lineno].
623 """
625 self.docstring: Docstring | None = docstring
626 """The object docstring.
628 See also: [`has_docstring`][griffe.Object.has_docstring],
629 [`has_docstrings`][griffe.Object.has_docstrings].
630 """
632 # TODO: Maybe move these into `Class` and `Function`.
633 # Then always return them in `Class` and `Function`'s `as_dict` methods,
634 # and remove the conditional in the `_load_class` and `_load_function` decoders.
635 self.type_parameters: TypeParameters = type_parameters or TypeParameters()
636 """The object type parameters."""
638 self.parent: Module | Class | None = parent
639 """The parent of the object (none if top module)."""
641 self.members: dict[str, Object | Alias] = {}
642 """The object members (modules, classes, functions, attributes, type aliases).
644 See also: [`inherited_members`][griffe.Object.inherited_members],
645 [`get_member`][griffe.Object.get_member],
646 [`set_member`][griffe.Object.set_member],
647 [`filter_members`][griffe.Object.filter_members].
648 """
650 self.labels: set[str] = set()
651 """The object labels (`property`, `dataclass`, etc.).
653 See also: [`has_labels`][griffe.Object.has_labels]."""
655 self.imports: dict[str, str] = {}
656 """The other objects imported by this object.
658 Keys are the names within the object (`from ... import ... as AS_NAME`),
659 while the values are the actual names of the objects (`from ... import REAL_NAME as ...`).
660 """
662 self.exports: list[str | ExprName] | None = None
663 """The names of the objects exported by this (module) object through the `__all__` variable.
665 Exports can contain string (object names) or resolvable names,
666 like other lists of exports coming from submodules:
668 ```python
669 from .submodule import __all__ as submodule_all
671 __all__ = ["hello", *submodule_all]
672 ```
674 Exports get expanded by the loader before it expands wildcards and resolves aliases.
676 See also: [`GriffeLoader.expand_exports`][griffe.GriffeLoader.expand_exports].
677 """
679 self.aliases: dict[str, Alias] = {}
680 """The aliases pointing to this object."""
682 self.runtime: bool = runtime
683 """Whether this object is available at runtime.
685 Typically, type-guarded objects (under an `if TYPE_CHECKING` condition)
686 are not available at runtime.
687 """
689 self.extra: dict[str, dict[str, Any]] = defaultdict(dict)
690 """Namespaced dictionaries storing extra metadata for this object, used by extensions."""
692 self.public: bool | None = None
693 """Whether this object is public."""
695 self.deprecated: bool | str | None = None
696 """Whether this object is deprecated (boolean or deprecation message)."""
698 self.analysis: Literal["static", "dynamic"] | None = analysis
699 """The type of analysis used to load this object.
701 None means the object was created manually.
702 """
704 self._lines_collection: LinesCollection | None = lines_collection
705 self._modules_collection: ModulesCollection | None = modules_collection
706 self._git_info: GitInfo | None = git_info
707 self._source_link: str | None = None
709 # Attach the docstring to this object.
710 if docstring:
711 docstring.parent = self
713 def __repr__(self) -> str:
714 return f"{self.__class__.__name__}({self.name!r}, {self.lineno!r}, {self.endlineno!r})"
716 # Prevent using `__len__`.
717 def __bool__(self) -> bool:
718 """An object is always true-ish."""
719 return True
721 def __len__(self) -> int:
722 """The number of members in this object, recursively."""
723 return len(self.members) + sum(len(member) for member in self.members.values())
725 @property
726 def git_info(self) -> GitInfo | None:
727 """Git information for this object, if available."""
728 if self._git_info is not None or self.parent is None:
729 return self._git_info
730 return self.parent.git_info
732 @git_info.setter
733 def git_info(self, value: GitInfo | None) -> None:
734 """Set the Git information for this object."""
735 self._git_info = value
737 @property
738 def source_link(self) -> str | None:
739 """Source link for this object, if available."""
740 if self._source_link is not None: 740 ↛ 741line 740 didn't jump to line 741 because the condition on line 740 was never true
741 return self._source_link
742 with suppress(BuiltinModuleError, ValueError):
743 if (git_info := self.git_info) and isinstance(self.filepath, Path):
744 # We don't use `self.relative_filepath` because it is computed
745 # relative to the current working directory, which isn't what we want.
746 filepath = self.filepath.relative_to(git_info.repository)
747 if self.lineno is not None and self.endlineno is not None:
748 self._source_link = git_info.get_source_link(filepath, self.lineno, self.endlineno)
749 return self._source_link
751 @source_link.setter
752 def source_link(self, value: str | None) -> None:
753 """Set the source link for this object."""
754 self._source_link = value
756 @property
757 def has_docstring(self) -> bool:
758 """Whether this object has a docstring (empty or not).
760 See also: [`docstring`][griffe.Object.docstring],
761 [`has_docstrings`][griffe.Object.has_docstrings].
762 """
763 return bool(self.docstring)
765 # NOTE: (pawamoy) I'm not happy with `has_docstrings`.
766 # It currently recurses into submodules, but that doesn't make sense
767 # if downstream projects use it to know if they should render an init module
768 # while not rendering submodules too: the property could tell them there are
769 # docstrings, but they could be in submodules, not in the init module.
770 # Maybe we should derive it into new properties: `has_local_docstrings`,
771 # `has_docstrings`, `has_public_docstrings`... Maybe we should make it a function?`
772 # For now it's used in mkdocstrings-python so we must be careful with changes.
773 @property
774 def has_docstrings(self) -> bool:
775 """Whether this object or any of its members has a docstring (empty or not).
777 Inherited members are not considered. Imported members are not considered,
778 unless they are also public.
780 See also: [`docstring`][griffe.Object.docstring],
781 [`has_docstring`][griffe.Object.has_docstring].
782 """
783 if self.has_docstring:
784 return True
785 for member in self.members.values():
786 try:
787 if (not member.is_imported or member.is_public) and member.has_docstrings:
788 return True
789 except AliasResolutionError:
790 continue
791 return False
793 def is_kind(self, kind: str | Kind | set[str | Kind]) -> bool:
794 """Tell if this object is of the given kind.
796 See also: [`is_module`][griffe.Object.is_module],
797 [`is_class`][griffe.Object.is_class],
798 [`is_function`][griffe.Object.is_function],
799 [`is_attribute`][griffe.Object.is_attribute],
800 [`is_type_alias`][griffe.Object.is_type_alias],
801 [`is_alias`][griffe.Object.is_alias].
803 Parameters:
804 kind: An instance or set of kinds (strings or enumerations).
806 Raises:
807 ValueError: When an empty set is given as argument.
809 Returns:
810 True or False.
811 """
812 if isinstance(kind, set):
813 if not kind:
814 raise ValueError("kind must not be an empty set")
815 return self.kind in (knd if isinstance(knd, Kind) else Kind(knd) for knd in kind)
816 if isinstance(kind, str):
817 kind = Kind(kind)
818 return self.kind is kind
820 @property
821 def inherited_members(self) -> dict[str, Alias]:
822 """Members that are inherited from base classes.
824 This method is part of the consumer API:
825 do not use when producing Griffe trees!
827 See also: [`members`][griffe.Object.members].
828 """
829 if not isinstance(self, Class): 829 ↛ 830line 829 didn't jump to line 830 because the condition on line 829 was never true
830 return {}
831 try:
832 mro = self.mro()
833 except ValueError as error:
834 logger.debug(error)
835 return {}
836 inherited_members = {}
837 for base in reversed(mro):
838 for name, member in base.members.items():
839 if name not in self.members:
840 inherited_members[name] = Alias(name, member, parent=self, inherited=True)
841 return inherited_members
843 @property
844 def is_module(self) -> bool:
845 """Whether this object is a module.
847 See also: [`is_init_module`][griffe.Object.is_init_module].
848 [`is_class`][griffe.Object.is_class],
849 [`is_function`][griffe.Object.is_function],
850 [`is_attribute`][griffe.Object.is_attribute],
851 [`is_type_alias`][griffe.Object.is_type_alias],
852 [`is_alias`][griffe.Object.is_alias],
853 [`is_kind`][griffe.Object.is_kind].
854 """
855 return self.kind is Kind.MODULE
857 @property
858 def is_class(self) -> bool:
859 """Whether this object is a class.
861 See also: [`is_module`][griffe.Object.is_module].
862 [`is_function`][griffe.Object.is_function],
863 [`is_attribute`][griffe.Object.is_attribute],
864 [`is_type_alias`][griffe.Object.is_type_alias],
865 [`is_alias`][griffe.Object.is_alias],
866 [`is_kind`][griffe.Object.is_kind].
867 """
868 return self.kind is Kind.CLASS
870 @property
871 def is_function(self) -> bool:
872 """Whether this object is a function.
874 See also: [`is_module`][griffe.Object.is_module].
875 [`is_class`][griffe.Object.is_class],
876 [`is_attribute`][griffe.Object.is_attribute],
877 [`is_type_alias`][griffe.Object.is_type_alias],
878 [`is_alias`][griffe.Object.is_alias],
879 [`is_kind`][griffe.Object.is_kind].
880 """
881 return self.kind is Kind.FUNCTION
883 @property
884 def is_attribute(self) -> bool:
885 """Whether this object is an attribute.
887 See also: [`is_module`][griffe.Object.is_module].
888 [`is_class`][griffe.Object.is_class],
889 [`is_function`][griffe.Object.is_function],
890 [`is_type_alias`][griffe.Object.is_type_alias],
891 [`is_alias`][griffe.Object.is_alias],
892 [`is_kind`][griffe.Object.is_kind].
893 """
894 return self.kind is Kind.ATTRIBUTE
896 @property
897 def is_type_alias(self) -> bool:
898 """Whether this object is a type alias.
900 See also: [`is_module`][griffe.Object.is_module].
901 [`is_class`][griffe.Object.is_class],
902 [`is_function`][griffe.Object.is_function],
903 [`is_attribute`][griffe.Object.is_attribute],
904 [`is_alias`][griffe.Object.is_alias],
905 [`is_kind`][griffe.Object.is_kind].
906 """
907 return self.kind is Kind.TYPE_ALIAS
909 @property
910 def is_init_method(self) -> bool:
911 """Whether this function is an `__init__` method."""
912 return False
914 @property
915 def is_init_module(self) -> bool:
916 """Whether this object is an `__init__.py` module.
918 See also: [`is_module`][griffe.Object.is_module].
919 """
920 return False
922 @property
923 def is_package(self) -> bool:
924 """Whether this object is a package (top module).
926 See also: [`is_subpackage`][griffe.Object.is_subpackage].
927 """
928 return False
930 @property
931 def is_subpackage(self) -> bool:
932 """Whether this object is a subpackage.
934 See also: [`is_package`][griffe.Object.is_package].
935 """
936 return False
938 @property
939 def is_namespace_package(self) -> bool:
940 """Whether this object is a namespace package (top folder, no `__init__.py`).
942 See also: [`is_namespace_subpackage`][griffe.Object.is_namespace_subpackage].
943 """
944 return False
946 @property
947 def is_namespace_subpackage(self) -> bool:
948 """Whether this object is a namespace subpackage.
950 See also: [`is_namespace_package`][griffe.Object.is_namespace_package].
951 """
952 return False
954 def has_labels(self, *labels: str) -> bool:
955 """Tell if this object has all the given labels.
957 See also: [`labels`][griffe.Object.labels].
959 Parameters:
960 *labels: Labels that must be present.
962 Returns:
963 True or False.
964 """
965 return set(labels).issubset(self.labels)
967 def filter_members(self, *predicates: Callable[[Object | Alias], bool]) -> dict[str, Object | Alias]:
968 """Filter and return members based on predicates.
970 See also: [`members`][griffe.Object.members].
972 Parameters:
973 *predicates: A list of predicates, i.e. callables accepting a member as argument and returning a boolean.
975 Returns:
976 A dictionary of members.
977 """
978 if not predicates:
979 return self.members
980 members: dict[str, Object | Alias] = {
981 name: member for name, member in self.members.items() if all(predicate(member) for predicate in predicates)
982 }
983 return members
985 @property
986 def module(self) -> Module:
987 """The parent module of this object.
989 See also: [`package`][griffe.Object.package].
991 Examples:
992 >>> import griffe
993 >>> markdown = griffe.load("markdown")
994 >>> markdown["core.Markdown.references"].module
995 Module(PosixPath('~/project/.venv/lib/python3.11/site-packages/markdown/core.py'))
996 >>> # The `module` of a module is itself.
997 >>> markdown["core"].module
998 Module(PosixPath('~/project/.venv/lib/python3.11/site-packages/markdown/core.py'))
1000 Raises:
1001 ValueError: When the object is not a module and does not have a parent.
1002 """
1003 if isinstance(self, Module):
1004 return self
1005 if self.parent is not None:
1006 return self.parent.module
1007 raise ValueError(f"Object {self.name} does not have a parent module")
1009 @property
1010 def package(self) -> Module:
1011 """The absolute top module (the package) of this object.
1013 See also: [`module`][griffe.Object.module].
1015 Examples:
1016 >>> import griffe
1017 >>> markdown = griffe.load("markdown")
1018 >>> markdown["core.Markdown.references"].package
1019 Module(PosixPath('~/project/.venv/lib/python3.11/site-packages/markdown/__init__.py'))
1020 """
1021 module = self.module
1022 while module.parent:
1023 module = module.parent
1024 return module # ty:ignore[invalid-return-type]
1026 @property
1027 def filepath(self) -> Path | list[Path]:
1028 """The file path (or directory list for namespace packages) where this object was defined.
1030 See also: [`relative_filepath`][griffe.Object.relative_filepath],
1031 [`relative_package_filepath`][griffe.Object.relative_package_filepath].
1033 Examples:
1034 >>> import griffe
1035 >>> markdown = griffe.load("markdown")
1036 >>> markdown.filepath
1037 PosixPath('~/project/.venv/lib/python3.11/site-packages/markdown/__init__.py')
1038 """
1039 return self.module.filepath
1041 @property
1042 def relative_package_filepath(self) -> Path:
1043 """The file path where this object was defined, relative to the top module path.
1045 See also: [`filepath`][griffe.Object.filepath],
1046 [`relative_filepath`][griffe.Object.relative_filepath].
1048 Raises:
1049 ValueError: When the relative path could not be computed.
1050 """
1051 package_path = self.package.filepath
1053 # Current "module" is a namespace package.
1054 if isinstance(self.filepath, list):
1055 # Current package is a namespace package.
1056 if isinstance(package_path, list): 1056 ↛ 1066line 1056 didn't jump to line 1066 because the condition on line 1056 was always true
1057 for pkg_path in package_path: 1057 ↛ 1069line 1057 didn't jump to line 1069 because the loop on line 1057 didn't complete
1058 for self_path in self.filepath: 1058 ↛ 1057line 1058 didn't jump to line 1057 because the loop on line 1058 didn't complete
1059 with suppress(ValueError):
1060 return self_path.relative_to(pkg_path.parent)
1062 # Current package is a regular package.
1063 # NOTE: Technically it makes no sense to have a namespace package
1064 # under a non-namespace one, so we should never enter this branch.
1065 else:
1066 for self_path in self.filepath:
1067 with suppress(ValueError):
1068 return self_path.relative_to(package_path.parent.parent)
1069 raise ValueError
1071 # Current package is a namespace package,
1072 # and current module is a regular module or package.
1073 if isinstance(package_path, list): 1073 ↛ 1074line 1073 didn't jump to line 1074 because the condition on line 1073 was never true
1074 for pkg_path in package_path:
1075 with suppress(ValueError):
1076 return self.filepath.relative_to(pkg_path.parent)
1077 raise ValueError
1079 # Current package is a regular package,
1080 # and current module is a regular module or package,
1081 # try to compute the path relative to the parent folder
1082 # of the package (search path).
1083 return self.filepath.relative_to(package_path.parent.parent)
1085 @property
1086 def relative_filepath(self) -> Path:
1087 """The file path where this object was defined, relative to the current working directory.
1089 If this object's file path is not relative to the current working directory, return its absolute path.
1091 See also: [`filepath`][griffe.Object.filepath],
1092 [`relative_package_filepath`][griffe.Object.relative_package_filepath].
1094 Raises:
1095 ValueError: When the relative path could not be computed.
1096 """
1097 cwd = Path.cwd()
1098 if isinstance(self.filepath, list):
1099 for self_path in self.filepath:
1100 with suppress(ValueError):
1101 return self_path.relative_to(cwd)
1102 raise ValueError(f"No directory in {self.filepath!r} is relative to the current working directory {cwd}")
1103 try:
1104 return self.filepath.relative_to(cwd)
1105 except ValueError:
1106 return self.filepath
1108 @property
1109 def path(self) -> str:
1110 """The dotted path of this object.
1112 On regular objects (not aliases), the path is the canonical path.
1114 See also: [`canonical_path`][griffe.Object.canonical_path].
1116 Examples:
1117 >>> import griffe
1118 >>> markdown = griffe.load("markdown")
1119 >>> markdown["core.Markdown.references"].path
1120 'markdown.core.Markdown.references'
1121 """
1122 return self.canonical_path
1124 @property
1125 def canonical_path(self) -> str:
1126 """The full dotted path of this object.
1128 The canonical path is the path where the object was defined (not imported).
1130 See also: [`path`][griffe.Object.path].
1131 """
1132 if self.parent is None:
1133 return self.name
1134 return f"{self.parent.path}.{self.name}"
1136 @property
1137 def modules_collection(self) -> ModulesCollection:
1138 """The modules collection attached to this object or its parents.
1140 Raises:
1141 ValueError: When no modules collection can be found in the object or its parents.
1142 """
1143 if self._modules_collection is not None:
1144 return self._modules_collection
1145 if self.parent is None: 1145 ↛ 1146line 1145 didn't jump to line 1146 because the condition on line 1145 was never true
1146 raise ValueError("no modules collection in this object or its parents")
1147 return self.parent.modules_collection
1149 @property
1150 def lines_collection(self) -> LinesCollection:
1151 """The lines collection attached to this object or its parents.
1153 See also: [`lines`][griffe.Object.lines],
1154 [`source`][griffe.Object.source].
1156 Raises:
1157 ValueError: When no modules collection can be found in the object or its parents.
1158 """
1159 if self._lines_collection is not None:
1160 return self._lines_collection
1161 if self.parent is None: 1161 ↛ 1162line 1161 didn't jump to line 1162 because the condition on line 1161 was never true
1162 raise ValueError("no lines collection in this object or its parents")
1163 return self.parent.lines_collection
1165 @property
1166 def lines(self) -> list[str]:
1167 """The lines containing the source of this object.
1169 See also: [`lines_collection`][griffe.Object.lines_collection],
1170 [`source`][griffe.Object.source].
1171 """
1172 try:
1173 filepath = self.filepath
1174 except BuiltinModuleError:
1175 return []
1176 if isinstance(filepath, list): 1176 ↛ 1177line 1176 didn't jump to line 1177 because the condition on line 1176 was never true
1177 return []
1178 try:
1179 lines = self.lines_collection[filepath]
1180 except KeyError:
1181 return []
1182 if self.is_module:
1183 return lines
1184 if self.lineno is None or self.endlineno is None:
1185 return []
1186 return lines[self.lineno - 1 : self.endlineno]
1188 @property
1189 def source(self) -> str:
1190 """The source code of this object.
1192 See also: [`lines`][griffe.Object.lines],
1193 [`lines_collection`][griffe.Object.lines_collection].
1194 """
1195 return dedent("\n".join(self.lines))
1197 def resolve(self, name: str) -> str:
1198 """Resolve a name within this object's and parents' scope.
1200 Parameters:
1201 name: The name to resolve.
1203 Raises:
1204 NameResolutionError: When the name could not be resolved.
1206 Returns:
1207 The resolved name.
1208 """
1209 # TODO: Better match Python's own scoping rules?
1210 # Also, maybe return regular paths instead of canonical ones?
1212 # Name is a type parameter.
1213 if name in self.type_parameters:
1214 type_parameter = self.type_parameters[name]
1215 if type_parameter.kind is TypeParameterKind.type_var_tuple: 1215 ↛ 1216line 1215 didn't jump to line 1216 because the condition on line 1215 was never true
1216 prefix = "*"
1217 elif type_parameter.kind is TypeParameterKind.param_spec: 1217 ↛ 1218line 1217 didn't jump to line 1218 because the condition on line 1217 was never true
1218 prefix = "**"
1219 else:
1220 prefix = ""
1221 return f"{self.path}[{prefix}{name}]"
1223 # Name is a member of this object.
1224 if name in self.members:
1225 if self.members[name].is_alias:
1226 return self.members[name].target_path # ty:ignore[possibly-missing-attribute]
1227 return self.members[name].path
1229 # Name unknown and no more parent scope, could be a built-in.
1230 if self.parent is None:
1231 raise NameResolutionError(f"{name} could not be resolved in the scope of {self.path}")
1233 # Name is parent, non-module object.
1234 if name == self.parent.name and not self.parent.is_module:
1235 return self.parent.path
1237 # Recurse in parent.
1238 return self.parent.resolve(name)
1240 def as_dict(self, *, full: bool = False, **kwargs: Any) -> dict[str, Any]:
1241 """Return this object's data as a dictionary.
1243 See also: [`as_json`][griffe.Object.as_json].
1245 Parameters:
1246 full: Whether to return full info, or just base info.
1247 **kwargs: Additional serialization options.
1249 Returns:
1250 A dictionary.
1251 """
1252 base: dict[str, Any] = {
1253 "kind": self.kind,
1254 "name": self.name,
1255 "runtime": self.runtime,
1256 }
1258 if self.public is not None: 1258 ↛ 1259line 1258 didn't jump to line 1259 because the condition on line 1258 was never true
1259 base["public"] = self.public
1260 if self.exports is not None:
1261 base["exports"] = [str(export) for export in self.exports]
1262 if self.imports:
1263 base["imports"] = self.imports
1264 if self.deprecated is not None: 1264 ↛ 1265line 1264 didn't jump to line 1265 because the condition on line 1264 was never true
1265 base["deprecated"] = self.deprecated
1266 if self.lineno is not None:
1267 base["lineno"] = self.lineno
1268 if self.endlineno is not None:
1269 base["endlineno"] = self.endlineno
1270 if self.docstring:
1271 base["docstring"] = self.docstring
1272 if self.type_parameters:
1273 base["type_parameters"] = [type_param.as_dict(**kwargs) for type_param in self.type_parameters]
1274 if self.labels:
1275 base["labels"] = self.labels
1276 if self.members:
1277 base["members"] = {name: member.as_dict(full=full, **kwargs) for name, member in self.members.items()}
1278 if self.analysis:
1279 base["analysis"] = self.analysis
1280 if self._git_info is not None:
1281 base["git_info"] = asdict(self._git_info)
1282 if self._source_link is not None: 1282 ↛ 1283line 1282 didn't jump to line 1283 because the condition on line 1282 was never true
1283 base["source_link"] = self._source_link
1284 # TODO: Include `self.extra`?
1286 if full:
1287 base.update(
1288 {
1289 "path": self.path,
1290 "filepath": self.filepath,
1291 "relative_package_filepath": self.relative_package_filepath,
1292 "is_public": self.is_public,
1293 "is_deprecated": self.is_deprecated,
1294 "is_private": self.is_private,
1295 "is_class_private": self.is_class_private,
1296 "is_special": self.is_special,
1297 "is_imported": self.is_imported,
1298 "is_exported": self.is_exported,
1299 "is_wildcard_exposed": self.is_wildcard_exposed,
1300 # TODO: Add these properties?
1301 # "is_alias": self.is_alias,
1302 # "is_collection": self.is_collection,
1303 # "is_module": self.is_module,
1304 # "is_class": self.is_class,
1305 # "is_function": self.is_function,
1306 # "is_attribute": self.is_attribute,
1307 # "is_type_alias": self.is_type_alias,
1308 # "is_init_module": self.is_init_module,
1309 # "is_package": self.is_package,
1310 # "is_subpackage": self.is_subpackage,
1311 # "is_namespace_package": self.is_namespace_package,
1312 # "is_namespace_subpackage": self.is_namespace_subpackage,
1313 # "has_docstring": self.has_docstring,
1314 # "has_docstrings": self.has_docstrings,
1315 },
1316 )
1318 with suppress(ValueError):
1319 base["relative_filepath"] = self.relative_filepath
1321 if "source_link" not in base and (source_link := self.source_link) is not None:
1322 base["source_link"] = source_link
1324 return base
1327class Alias(ObjectAliasMixin):
1328 """This class represents an alias, or indirection, to an object declared in another module.
1330 Aliases represent objects that are in the scope of a module or class,
1331 but were imported from another module.
1333 They behave almost exactly like regular objects, to a few exceptions:
1335 - line numbers are those of the alias, not the target
1336 - the path is the alias path, not the canonical one
1337 - the name can be different from the target's
1338 - if the target can be resolved, the kind is the target's kind
1339 - if the target cannot be resolved, the kind becomes [Kind.ALIAS][griffe.Kind]
1340 """
1342 is_alias: bool = True
1343 """Always true for aliases."""
1344 is_collection: bool = False
1345 """Always false for aliases.
1347 See also: [`ModulesCollection`][griffe.ModulesCollection].
1348 """
1350 def __init__(
1351 self,
1352 name: str,
1353 target: str | Object | Alias,
1354 *,
1355 lineno: int | None = None,
1356 endlineno: int | None = None,
1357 runtime: bool = True,
1358 parent: Module | Class | Alias | None = None,
1359 inherited: bool = False,
1360 wildcard_imported: bool = False,
1361 analysis: Literal["static", "dynamic"] | None = None,
1362 ) -> None:
1363 """Initialize the alias.
1365 Parameters:
1366 name: The alias name.
1367 target: If it's a string, the target resolution is delayed until accessing the target property.
1368 If it's an object, or even another alias, the target is immediately set.
1369 lineno: The alias starting line number.
1370 endlineno: The alias ending line number.
1371 runtime: Whether this alias is present at runtime or not.
1372 parent: The alias parent.
1373 inherited: Whether this alias wraps an inherited member.
1374 wildcard_imported: Whether this alias was created using a wildcard import.
1375 analysis: The type of analysis used to load this alias.
1376 None means the alias was created manually.
1377 """
1378 self.name: str = name
1379 """The alias name."""
1381 self.alias_lineno: int | None = lineno
1382 """The starting line number of the alias."""
1384 self.alias_endlineno: int | None = endlineno
1385 """The ending line number of the alias."""
1387 self.runtime: bool = runtime
1388 """Whether this alias is available at runtime."""
1390 self.inherited: bool = inherited
1391 """Whether this alias represents an inherited member."""
1393 self.wildcard_imported: bool = wildcard_imported
1394 """Whether this alias was created using a wildcard import."""
1396 self.public: bool | None = None
1397 """Whether this alias is public."""
1399 self.deprecated: str | bool | None = None
1400 """Whether this alias is deprecated (boolean or deprecation message)."""
1402 self.analysis: Literal["static", "dynamic"] | None = analysis
1403 """The type of analysis used to load this alias.
1405 None means the alias was created manually.
1406 """
1408 self._parent: Module | Class | Alias | None = parent
1409 self._passed_through: bool = False
1411 self.target_path: str
1412 """The path of this alias' target."""
1414 if isinstance(target, str):
1415 self._target: Object | Alias | None = None
1416 self.target_path = target
1417 else:
1418 self._target = target
1419 self.target_path = target.path
1420 self._update_target_aliases()
1422 def __repr__(self) -> str:
1423 return f"Alias({self.name!r}, {self.target_path!r})"
1425 # Prevent using `__len__`.
1426 def __bool__(self) -> bool:
1427 """An alias is always true-ish."""
1428 return True
1430 def __len__(self) -> int:
1431 """The length of an alias is always 1."""
1432 return 1
1434 # SPECIAL PROXIES -------------------------------
1435 # The following methods and properties exist on the target(s),
1436 # but we must handle them in a special way.
1438 @property
1439 def kind(self) -> Kind:
1440 """The target's kind, or `Kind.ALIAS` if the target cannot be resolved.
1442 See also: [`is_kind`][griffe.Alias.is_kind].
1443 """
1444 # custom behavior to avoid raising exceptions
1445 try:
1446 return self.final_target.kind
1447 except (AliasResolutionError, CyclicAliasError):
1448 return Kind.ALIAS
1450 @property
1451 def has_docstring(self) -> bool:
1452 """Whether this alias' target has a non-empty docstring.
1454 See also: [`has_docstrings`][griffe.Alias.has_docstrings],
1455 [`docstring`][griffe.Alias.docstring].
1456 """
1457 try:
1458 return self.final_target.has_docstring
1459 except (AliasResolutionError, CyclicAliasError):
1460 return False
1462 @property
1463 def has_docstrings(self) -> bool:
1464 """Whether this alias' target or any of its members has a non-empty docstring.
1466 See also: [`has_docstring`][griffe.Alias.has_docstring],
1467 [`docstring`][griffe.Alias.docstring].
1468 """
1469 try:
1470 return self.final_target.has_docstrings
1471 except (AliasResolutionError, CyclicAliasError):
1472 return False
1474 @property
1475 def parent(self) -> Module | Class | Alias | None:
1476 """The parent of this alias."""
1477 return self._parent
1479 @parent.setter
1480 def parent(self, value: Module | Class | Alias) -> None:
1481 self._parent = value
1482 self._update_target_aliases()
1484 @property
1485 def path(self) -> str:
1486 """The dotted path / import path of this object.
1488 See also: [`canonical_path`][griffe.Alias.canonical_path].
1489 """
1490 return f"{self.parent.path}.{self.name}" # ty:ignore[possibly-missing-attribute]
1492 @property
1493 def modules_collection(self) -> ModulesCollection:
1494 """The modules collection attached to the alias parents."""
1495 # No need to forward to the target.
1496 return self.parent.modules_collection # ty:ignore[possibly-missing-attribute]
1498 @property
1499 def members(self) -> dict[str, Object | Alias]:
1500 """The target's members (modules, classes, functions, attributes, type aliases).
1502 See also: [`inherited_members`][griffe.Alias.inherited_members],
1503 [`get_member`][griffe.Alias.get_member],
1504 [`set_member`][griffe.Alias.set_member],
1505 [`filter_members`][griffe.Alias.filter_members].
1506 """
1507 final_target = self.final_target
1509 # We recreate aliases to maintain a correct hierarchy,
1510 # and therefore correct paths. The path of an alias member
1511 # should be the path of the alias plus the member's name,
1512 # not the original member's path.
1513 return {
1514 name: Alias(name, target=member, parent=self, inherited=False)
1515 for name, member in final_target.members.items()
1516 }
1518 @property
1519 def inherited_members(self) -> dict[str, Alias]:
1520 """Members that are inherited from base classes.
1522 Each inherited member of the target will be wrapped in an alias,
1523 to preserve correct object access paths.
1525 This method is part of the consumer API:
1526 do not use when producing Griffe trees!
1528 See also: [`members`][griffe.Alias.members].
1529 """
1530 final_target = self.final_target
1532 # We recreate aliases to maintain a correct hierarchy,
1533 # and therefore correct paths. The path of an alias member
1534 # should be the path of the alias plus the member's name,
1535 # not the original member's path.
1536 return {
1537 name: Alias(name, target=member, parent=self, inherited=True)
1538 for name, member in final_target.inherited_members.items()
1539 }
1541 def as_json(self, *, full: bool = False, **kwargs: Any) -> str:
1542 """Return this target's data as a JSON string.
1544 See also: [`as_dict`][griffe.Alias.as_dict].
1546 Parameters:
1547 full: Whether to return full info, or just base info.
1548 **kwargs: Additional serialization options passed to encoder.
1550 Returns:
1551 A JSON string.
1552 """
1553 try:
1554 return self.final_target.as_json(full=full, **kwargs)
1555 except (AliasResolutionError, CyclicAliasError):
1556 return super().as_json(full=full, **kwargs)
1558 # GENERIC OBJECT PROXIES --------------------------------
1559 # The following methods and properties exist on the target(s).
1560 # We first try to reach the final target, triggering alias resolution errors
1561 # and cyclic aliases errors early. We avoid recursing in the alias chain.
1563 @property
1564 def git_info(self) -> GitInfo | None:
1565 """Get the Git information for this object, if available."""
1566 return self.final_target.git_info
1568 @git_info.setter
1569 def git_info(self, value: GitInfo | None) -> None:
1570 """Set the Git information for this object."""
1571 self.final_target.git_info = value
1573 @property
1574 def source_link(self) -> str | None:
1575 """Get the source link for this object, if available."""
1576 return self.final_target.source_link
1578 @source_link.setter
1579 def source_link(self, value: str | None) -> None:
1580 """Set the source link for this object."""
1581 self.final_target.source_link = value
1583 @property
1584 def extra(self) -> dict:
1585 """Namespaced dictionaries storing extra metadata for this object, used by extensions."""
1586 return self.final_target.extra
1588 @property
1589 def lineno(self) -> int | None:
1590 """The starting line number of the target object.
1592 See also: [`endlineno`][griffe.Alias.endlineno].
1593 """
1594 return self.final_target.lineno
1596 @lineno.setter
1597 def lineno(self, lineno: int | None) -> None:
1598 self.final_target.lineno = lineno
1600 @property
1601 def endlineno(self) -> int | None:
1602 """The ending line number of the target object.
1604 See also: [`lineno`][griffe.Alias.lineno].
1605 """
1606 return self.final_target.endlineno
1608 @endlineno.setter
1609 def endlineno(self, endlineno: int | None) -> None:
1610 self.final_target.endlineno = endlineno
1612 @property
1613 def docstring(self) -> Docstring | None:
1614 """The target docstring.
1616 See also: [`has_docstring`][griffe.Alias.has_docstring],
1617 [`has_docstrings`][griffe.Alias.has_docstrings].
1618 """
1619 return self.final_target.docstring
1621 @docstring.setter
1622 def docstring(self, docstring: Docstring | None) -> None:
1623 self.final_target.docstring = docstring
1625 @property
1626 def type_parameters(self) -> TypeParameters:
1627 """The target type parameters."""
1628 return self.final_target.type_parameters
1630 @property
1631 def labels(self) -> set[str]:
1632 """The target labels (`property`, `dataclass`, etc.).
1634 See also: [`has_labels`][griffe.Alias.has_labels].
1635 """
1636 return self.final_target.labels
1638 @property
1639 def imports(self) -> dict[str, str]:
1640 """The other objects imported by this alias' target.
1642 Keys are the names within the object (`from ... import ... as AS_NAME`),
1643 while the values are the actual names of the objects (`from ... import REAL_NAME as ...`).
1645 See also: [`is_imported`][griffe.Alias.is_imported].
1646 """
1647 return self.final_target.imports
1649 @property
1650 def exports(self) -> list[str | ExprName] | None:
1651 """The names of the objects exported by this (module) object through the `__all__` variable.
1653 Exports can contain string (object names) or resolvable names,
1654 like other lists of exports coming from submodules:
1656 ```python
1657 from .submodule import __all__ as submodule_all
1659 __all__ = ["hello", *submodule_all]
1660 ```
1662 Exports get expanded by the loader before it expands wildcards and resolves aliases.
1664 See also: [`GriffeLoader.expand_exports`][griffe.GriffeLoader.expand_exports].
1665 """
1666 return self.final_target.exports
1668 @property
1669 def aliases(self) -> dict[str, Alias]:
1670 """The aliases pointing to this object."""
1671 return self.final_target.aliases
1673 def is_kind(self, kind: str | Kind | set[str | Kind]) -> bool:
1674 """Tell if this object is of the given kind.
1676 See also: [`is_module`][griffe.Alias.is_module],
1677 [`is_class`][griffe.Alias.is_class],
1678 [`is_function`][griffe.Alias.is_function],
1679 [`is_attribute`][griffe.Alias.is_attribute],
1680 [`is_type_alias`][griffe.Alias.is_type_alias],
1681 [`is_alias`][griffe.Alias.is_alias].
1683 Parameters:
1684 kind: An instance or set of kinds (strings or enumerations).
1686 Raises:
1687 ValueError: When an empty set is given as argument.
1689 Returns:
1690 True or False.
1691 """
1692 return self.final_target.is_kind(kind)
1694 @property
1695 def is_module(self) -> bool:
1696 """Whether this object is a module.
1698 See also: [`is_init_module`][griffe.Alias.is_init_module].
1699 [`is_class`][griffe.Alias.is_class],
1700 [`is_function`][griffe.Alias.is_function],
1701 [`is_attribute`][griffe.Alias.is_attribute],
1702 [`is_type_alias`][griffe.Alias.is_type_alias],
1703 [`is_alias`][griffe.Alias.is_alias],
1704 [`is_kind`][griffe.Alias.is_kind].
1705 """
1706 return self.final_target.is_module
1708 @property
1709 def is_class(self) -> bool:
1710 """Whether this object is a class.
1712 See also: [`is_module`][griffe.Alias.is_module],
1713 [`is_function`][griffe.Alias.is_function],
1714 [`is_attribute`][griffe.Alias.is_attribute],
1715 [`is_type_alias`][griffe.Alias.is_type_alias],
1716 [`is_alias`][griffe.Alias.is_alias],
1717 [`is_kind`][griffe.Alias.is_kind].
1718 """
1719 return self.final_target.is_class
1721 @property
1722 def is_function(self) -> bool:
1723 """Whether this object is a function.
1725 See also: [`is_module`][griffe.Alias.is_module],
1726 [`is_class`][griffe.Alias.is_class],
1727 [`is_attribute`][griffe.Alias.is_attribute],
1728 [`is_type_alias`][griffe.Alias.is_type_alias],
1729 [`is_alias`][griffe.Alias.is_alias],
1730 [`is_kind`][griffe.Alias.is_kind].
1731 """
1732 return self.final_target.is_function
1734 @property
1735 def is_attribute(self) -> bool:
1736 """Whether this object is an attribute.
1738 See also: [`is_module`][griffe.Alias.is_module],
1739 [`is_class`][griffe.Alias.is_class],
1740 [`is_function`][griffe.Alias.is_function],
1741 [`is_type_alias`][griffe.Alias.is_type_alias],
1742 [`is_alias`][griffe.Alias.is_alias],
1743 [`is_kind`][griffe.Alias.is_kind].
1744 """
1745 return self.final_target.is_attribute
1747 @property
1748 def is_type_alias(self) -> bool:
1749 """Whether this object is a type alias.
1751 See also: [`is_module`][griffe.Alias.is_module],
1752 [`is_class`][griffe.Alias.is_class],
1753 [`is_function`][griffe.Alias.is_function],
1754 [`is_attribute`][griffe.Alias.is_attribute],
1755 [`is_alias`][griffe.Alias.is_alias],
1756 [`is_kind`][griffe.Alias.is_kind].
1757 """
1758 return self.final_target.is_type_alias
1760 def has_labels(self, *labels: str) -> bool:
1761 """Tell if this object has all the given labels.
1763 See also: [`labels`][griffe.Alias.labels].
1765 Parameters:
1766 *labels: Labels that must be present.
1768 Returns:
1769 True or False.
1770 """
1771 return self.final_target.has_labels(*labels)
1773 def filter_members(self, *predicates: Callable[[Object | Alias], bool]) -> dict[str, Object | Alias]:
1774 """Filter and return members based on predicates.
1776 See also: [`members`][griffe.Alias.members],
1777 [`get_member`][griffe.Alias.get_member],
1778 [`set_member`][griffe.Alias.set_member].
1780 Parameters:
1781 *predicates: A list of predicates, i.e. callables accepting a member as argument and returning a boolean.
1783 Returns:
1784 A dictionary of members.
1785 """
1786 return self.final_target.filter_members(*predicates)
1788 @property
1789 def module(self) -> Module:
1790 """The parent module of this object.
1792 See also: [`package`][griffe.Alias.package].
1794 Raises:
1795 ValueError: When the object is not a module and does not have a parent.
1796 """
1797 return self.final_target.module
1799 @property
1800 def package(self) -> Module:
1801 """The absolute top module (the package) of this object.
1803 See also: [`module`][griffe.Alias.module].
1804 """
1805 return self.final_target.package
1807 @property
1808 def filepath(self) -> Path | list[Path]:
1809 """The file path (or directory list for namespace packages) where this object was defined.
1811 See also: [`relative_filepath`][griffe.Alias.relative_filepath],
1812 [`relative_package_filepath`][griffe.Alias.relative_package_filepath].
1813 """
1814 return self.final_target.filepath
1816 @property
1817 def relative_filepath(self) -> Path:
1818 """The file path where this object was defined, relative to the current working directory.
1820 If this object's file path is not relative to the current working directory, return its absolute path.
1822 See also: [`filepath`][griffe.Alias.filepath],
1823 [`relative_package_filepath`][griffe.Alias.relative_package_filepath].
1825 Raises:
1826 ValueError: When the relative path could not be computed.
1827 """
1828 return self.final_target.relative_filepath
1830 @property
1831 def relative_package_filepath(self) -> Path:
1832 """The file path where this object was defined, relative to the top module path.
1834 See also: [`filepath`][griffe.Alias.filepath],
1835 [`relative_filepath`][griffe.Alias.relative_filepath].
1837 Raises:
1838 ValueError: When the relative path could not be computed.
1839 """
1840 return self.final_target.relative_package_filepath
1842 @property
1843 def canonical_path(self) -> str:
1844 """The full dotted path of this object.
1846 The canonical path is the path where the object was defined (not imported).
1848 See also: [`path`][griffe.Alias.path].
1849 """
1850 return self.final_target.canonical_path
1852 @property
1853 def lines_collection(self) -> LinesCollection:
1854 """The lines collection attached to this object or its parents.
1856 See also: [`lines`][griffe.Alias.lines],
1857 [`source`][griffe.Alias.source].
1859 Raises:
1860 ValueError: When no modules collection can be found in the object or its parents.
1861 """
1862 return self.final_target.lines_collection
1864 @property
1865 def lines(self) -> list[str]:
1866 """The lines containing the source of this object.
1868 See also: [`source`][griffe.Alias.source],
1869 [`lines_collection`][griffe.Alias.lines_collection].
1870 """
1871 return self.final_target.lines
1873 @property
1874 def source(self) -> str:
1875 """The source code of this object.
1877 See also: [`lines`][griffe.Alias.lines],
1878 [`lines_collection`][griffe.Alias.lines_collection].
1879 """
1880 return self.final_target.source
1882 def resolve(self, name: str) -> str:
1883 """Resolve a name within this object's and parents' scope.
1885 Parameters:
1886 name: The name to resolve.
1888 Raises:
1889 NameResolutionError: When the name could not be resolved.
1891 Returns:
1892 The resolved name.
1893 """
1894 return self.final_target.resolve(name)
1896 # SPECIFIC MODULE/CLASS/FUNCTION/ATTRIBUTE/TYPE ALIAS PROXIES ---------------
1897 # These methods and properties exist on targets of specific kind.
1898 # We first try to reach the final target, triggering alias resolution errors
1899 # and cyclic aliases errors early. We avoid recursing in the alias chain.
1901 @property
1902 def _filepath(self) -> Path | list[Path] | None:
1903 return cast("Module", self.final_target)._filepath
1905 @property
1906 def bases(self) -> list[Expr | str]:
1907 """The class bases.
1909 See also: [`Class`][griffe.Class],
1910 [`resolved_bases`][griffe.Alias.resolved_bases],
1911 [`mro`][griffe.Alias.mro].
1912 """
1913 return cast("Class", self.final_target).bases
1915 @property
1916 def keywords(self) -> dict[str, Expr | str]:
1917 """The class keywords."""
1918 return cast("Class", self.final_target).keywords
1920 @property
1921 def decorators(self) -> list[Decorator]:
1922 """The class/function decorators.
1924 See also: [`Function`][griffe.Function],
1925 [`Class`][griffe.Class].
1926 """
1927 return cast("Class | Function", self.target).decorators
1929 @property
1930 def imports_future_annotations(self) -> bool:
1931 """Whether this module import future annotations."""
1932 return cast("Module", self.final_target).imports_future_annotations
1934 @property
1935 def is_init_method(self) -> bool:
1936 """Whether this method is an `__init__` method."""
1937 return cast("Function", self.final_target).is_init_method
1939 @property
1940 def is_init_module(self) -> bool:
1941 """Whether this module is an `__init__.py` module.
1943 See also: [`is_module`][griffe.Alias.is_module].
1944 """
1945 return cast("Module", self.final_target).is_init_module
1947 @property
1948 def is_package(self) -> bool:
1949 """Whether this module is a package (top module).
1951 See also: [`is_subpackage`][griffe.Alias.is_subpackage].
1952 """
1953 return cast("Module", self.final_target).is_package
1955 @property
1956 def is_subpackage(self) -> bool:
1957 """Whether this module is a subpackage.
1959 See also: [`is_package`][griffe.Alias.is_package].
1960 """
1961 return cast("Module", self.final_target).is_subpackage
1963 @property
1964 def is_namespace_package(self) -> bool:
1965 """Whether this module is a namespace package (top folder, no `__init__.py`).
1967 See also: [`is_namespace_subpackage`][griffe.Alias.is_namespace_subpackage].
1968 """
1969 return cast("Module", self.final_target).is_namespace_package
1971 @property
1972 def is_namespace_subpackage(self) -> bool:
1973 """Whether this module is a namespace subpackage.
1975 See also: [`is_namespace_package`][griffe.Alias.is_namespace_package].
1976 """
1977 return cast("Module", self.final_target).is_namespace_subpackage
1979 @property
1980 def overloads(self) -> dict[str, list[Function]] | list[Function] | None:
1981 """The overloaded signatures declared in this class/module or for this function."""
1982 return cast("Module | Class | Function", self.final_target).overloads
1984 @overloads.setter
1985 def overloads(self, overloads: list[Function] | None) -> None:
1986 cast("Module | Class | Function", self.final_target).overloads = overloads # ty:ignore[invalid-assignment]
1988 @property
1989 def parameters(self) -> Parameters:
1990 """The parameters of the current function or `__init__` method for classes.
1992 This property can fetch inherited members,
1993 and therefore is part of the consumer API:
1994 do not use when producing Griffe trees!
1995 """
1996 return cast("Class | Function", self.final_target).parameters
1998 @property
1999 def returns(self) -> str | Expr | None:
2000 """The function return type annotation."""
2001 return cast("Function", self.final_target).returns
2003 @returns.setter
2004 def returns(self, returns: str | Expr | None) -> None:
2005 cast("Function", self.final_target).returns = returns
2007 @property
2008 def setter(self) -> Function | None:
2009 """The setter linked to this function (property)."""
2010 return cast("Attribute", self.final_target).setter
2012 @property
2013 def deleter(self) -> Function | None:
2014 """The deleter linked to this function (property)."""
2015 return cast("Attribute", self.final_target).deleter
2017 @property
2018 def value(self) -> str | Expr | None:
2019 """The attribute or type alias value."""
2020 return cast("Attribute | TypeAlias", self.final_target).value
2022 @value.setter
2023 def value(self, value: str | Expr | None) -> None:
2024 cast("Attribute", self.final_target).value = value
2026 @property
2027 def annotation(self) -> str | Expr | None:
2028 """The attribute type annotation."""
2029 return cast("Attribute", self.final_target).annotation
2031 @annotation.setter
2032 def annotation(self, annotation: str | Expr | None) -> None:
2033 cast("Attribute", self.final_target).annotation = annotation
2035 @property
2036 def resolved_bases(self) -> list[Object]:
2037 """Resolved class bases.
2039 This method is part of the consumer API:
2040 do not use when producing Griffe trees!
2041 """
2042 return cast("Class", self.final_target).resolved_bases
2044 def mro(self) -> list[Class]:
2045 """Return a list of classes in order corresponding to Python's MRO."""
2046 return cast("Class", self.final_target).mro()
2048 def signature(self, *, return_type: bool = False, name: str | None = None) -> str:
2049 """Construct the class/function signature.
2051 Parameters:
2052 return_type: Whether to include the return type in the signature.
2053 name: The name of the class/function to use in the signature.
2055 Returns:
2056 A string representation of the class/function signature.
2057 """
2058 return cast("Class | Function", self.final_target).signature(return_type=return_type, name=name)
2060 # SPECIFIC ALIAS METHOD AND PROPERTIES -----------------
2061 # These methods and properties do not exist on targets,
2062 # they are specific to aliases.
2064 @property
2065 def target(self) -> Object | Alias:
2066 """The resolved target (actual object), if possible.
2068 Upon accessing this property, if the target is not already resolved,
2069 a lookup is done using the modules collection to find the target.
2071 See also: [`final_target`][griffe.Alias.final_target],
2072 [`resolve_target`][griffe.Alias.resolve_target],
2073 [`resolved`][griffe.Alias.resolved].
2074 """
2075 if not self.resolved:
2076 self.resolve_target()
2077 return self._target # ty:ignore[invalid-return-type]
2079 @target.setter
2080 def target(self, value: Object | Alias) -> None:
2081 if value is self or value.path == self.path:
2082 raise CyclicAliasError([self.target_path])
2083 self._target = value
2084 self.target_path = value.path
2085 if self.parent is not None:
2086 self._target.aliases[self.path] = self
2088 @property
2089 def final_target(self) -> Object:
2090 """The final, resolved target, if possible.
2092 This will iterate through the targets until a non-alias object is found.
2094 See also: [`target`][griffe.Alias.target],
2095 [`resolve_target`][griffe.Alias.resolve_target],
2096 [`resolved`][griffe.Alias.resolved].
2097 """
2098 # Here we quickly iterate on the alias chain,
2099 # remembering which path we've seen already to detect cycles.
2101 # The cycle detection is needed because alias chains can be created
2102 # as already resolved, and can contain cycles.
2104 # Using a dict as an ordered set.
2105 paths_seen: dict[str, None] = {}
2106 target = self
2107 while target.is_alias:
2108 if target.path in paths_seen: 2108 ↛ 2109line 2108 didn't jump to line 2109 because the condition on line 2108 was never true
2109 raise CyclicAliasError([*paths_seen, target.path])
2110 paths_seen[target.path] = None
2111 target = target.target
2112 return target # ty:ignore[invalid-return-type]
2114 def resolve_target(self) -> None:
2115 """Resolve the target.
2117 See also: [`target`][griffe.Alias.target],
2118 [`final_target`][griffe.Alias.final_target],
2119 [`resolved`][griffe.Alias.resolved].
2121 Raises:
2122 AliasResolutionError: When the target cannot be resolved.
2123 It happens when the target does not exist,
2124 or could not be loaded (unhandled dynamic object?),
2125 or when the target is from a module that was not loaded
2126 and added to the collection.
2127 CyclicAliasError: When the resolved target is the alias itself.
2128 """
2129 # Here we try to resolve the whole alias chain recursively.
2130 # We detect cycles by setting a "passed through" state variable
2131 # on each alias as we pass through it. Passing a second time
2132 # through an alias will raise a CyclicAliasError.
2134 # If a single link of the chain cannot be resolved,
2135 # the whole chain stays unresolved. This prevents
2136 # bad surprises later, in code that checks if
2137 # an alias is resolved by checking only
2138 # the first link of the chain.
2139 if self._passed_through: 2139 ↛ 2140line 2139 didn't jump to line 2140 because the condition on line 2139 was never true
2140 raise CyclicAliasError([self.target_path])
2141 self._passed_through = True
2142 try:
2143 self._resolve_target()
2144 finally:
2145 self._passed_through = False
2147 def _resolve_target(self) -> None:
2148 try:
2149 resolved = self.modules_collection.get_member(self.target_path)
2150 except KeyError as error:
2151 raise AliasResolutionError(self) from error
2152 if resolved is self: 2152 ↛ 2153line 2152 didn't jump to line 2153 because the condition on line 2152 was never true
2153 raise CyclicAliasError([self.target_path])
2154 if resolved.is_alias and not resolved.resolved:
2155 try:
2156 resolved.resolve_target()
2157 except CyclicAliasError as error:
2158 raise CyclicAliasError([self.target_path, *error.chain]) from error
2159 self._target = resolved
2160 if self.parent is not None: 2160 ↛ exitline 2160 didn't return from function '_resolve_target' because the condition on line 2160 was always true
2161 self._target.aliases[self.path] = self
2163 def _update_target_aliases(self) -> None:
2164 with suppress(AttributeError, AliasResolutionError, CyclicAliasError):
2165 self._target.aliases[self.path] = self # ty:ignore[possibly-missing-attribute]
2167 @property
2168 def resolved(self) -> bool:
2169 """Whether this alias' target is resolved."""
2170 return self._target is not None
2172 @property
2173 def wildcard(self) -> str | None:
2174 """The module on which the wildcard import is performed (if any).
2176 See also: [`GriffeLoader.expand_wildcards`][griffe.GriffeLoader.expand_wildcards].
2177 """
2178 if self.name.endswith("/*"):
2179 return self.target_path
2180 return None
2182 def as_dict(self, *, full: bool = False, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002
2183 """Return this alias' data as a dictionary.
2185 See also: [`as_json`][griffe.Alias.as_json].
2187 Parameters:
2188 full: Whether to return full info, or just base info.
2189 **kwargs: Additional serialization options.
2191 Returns:
2192 A dictionary.
2193 """
2194 base: dict[str, Any] = {
2195 "kind": Kind.ALIAS,
2196 "name": self.name,
2197 "target_path": self.target_path,
2198 "runtime": self.runtime,
2199 "inherited": self.inherited,
2200 }
2202 if self.public is not None: 2202 ↛ 2203line 2202 didn't jump to line 2203 because the condition on line 2202 was never true
2203 base["public"] = self.public
2204 if self.deprecated is not None: 2204 ↛ 2205line 2204 didn't jump to line 2205 because the condition on line 2204 was never true
2205 base["deprecated"] = self.deprecated
2206 if self.alias_lineno:
2207 base["lineno"] = self.alias_lineno
2208 if self.alias_endlineno:
2209 base["endlineno"] = self.alias_endlineno
2210 if self.analysis: 2210 ↛ 2213line 2210 didn't jump to line 2213 because the condition on line 2210 was always true
2211 base["analysis"] = self.analysis
2213 if full:
2214 base.update(
2215 {
2216 "path": self.path,
2217 "is_public": self.is_public,
2218 "is_deprecated": self.is_deprecated,
2219 "is_private": self.is_private,
2220 "is_class_private": self.is_class_private,
2221 "is_special": self.is_special,
2222 "is_imported": self.is_imported,
2223 "is_exported": self.is_exported,
2224 "is_wildcard_exposed": self.is_wildcard_exposed,
2225 },
2226 )
2228 return base
2231class Module(Object):
2232 """The class representing a Python module."""
2234 kind = Kind.MODULE
2236 def __init__(self, *args: Any, filepath: Path | list[Path] | None = None, **kwargs: Any) -> None:
2237 """Initialize the module.
2239 Parameters:
2240 *args: See [`griffe.Object`][].
2241 filepath: The module file path (directory for namespace [sub]packages, none for builtin modules).
2242 **kwargs: See [`griffe.Object`][].
2243 """
2244 super().__init__(*args, **kwargs)
2245 self._filepath: Path | list[Path] | None = filepath
2246 self.overloads: dict[str, list[Function]] = defaultdict(list)
2247 """The overloaded signatures declared in this module."""
2249 def __repr__(self) -> str:
2250 try:
2251 return f"Module({self.filepath!r})"
2252 except BuiltinModuleError:
2253 return f"Module({self.name!r})"
2255 @property
2256 def filepath(self) -> Path | list[Path]:
2257 """The file path of this module.
2259 Raises:
2260 BuiltinModuleError: When the instance filepath is None.
2261 """
2262 if self._filepath is None:
2263 raise BuiltinModuleError(self.name)
2264 return self._filepath
2266 @property
2267 def imports_future_annotations(self) -> bool:
2268 """Whether this module import future annotations."""
2269 return (
2270 "annotations" in self.members
2271 and self.members["annotations"].is_alias
2272 and self.members["annotations"].target_path == "__future__.annotations" # ty:ignore[possibly-missing-attribute]
2273 )
2275 @property
2276 def is_init_module(self) -> bool:
2277 """Whether this module is an `__init__.py` module.
2279 See also: [`is_module`][griffe.Module.is_module].
2280 """
2281 if isinstance(self.filepath, list): 2281 ↛ 2282line 2281 didn't jump to line 2282 because the condition on line 2281 was never true
2282 return False
2283 try:
2284 return self.filepath.name.split(".", 1)[0] == "__init__"
2285 except BuiltinModuleError:
2286 return False
2288 @property
2289 def is_package(self) -> bool:
2290 """Whether this module is a package (top module).
2292 See also: [`is_subpackage`][griffe.Module.is_subpackage].
2293 """
2294 return not bool(self.parent) and self.is_init_module
2296 @property
2297 def is_subpackage(self) -> bool:
2298 """Whether this module is a subpackage.
2300 See also: [`is_package`][griffe.Module.is_package].
2301 """
2302 return bool(self.parent) and self.is_init_module
2304 @property
2305 def is_namespace_package(self) -> bool:
2306 """Whether this module is a namespace package (top folder, no `__init__.py`).
2308 See also: [`is_namespace_subpackage`][griffe.Module.is_namespace_subpackage].
2309 """
2310 try:
2311 return self.parent is None and isinstance(self.filepath, list)
2312 except BuiltinModuleError:
2313 return False
2315 @property
2316 def is_namespace_subpackage(self) -> bool:
2317 """Whether this module is a namespace subpackage.
2319 See also: [`is_namespace_package`][griffe.Module.is_namespace_package].
2320 """
2321 try:
2322 return (
2323 self.parent is not None
2324 and isinstance(self.filepath, list)
2325 and (
2326 cast("Module", self.parent).is_namespace_package
2327 or cast("Module", self.parent).is_namespace_subpackage
2328 )
2329 )
2330 except BuiltinModuleError:
2331 return False
2333 def as_dict(self, **kwargs: Any) -> dict[str, Any]:
2334 """Return this module's data as a dictionary.
2336 See also: [`as_json`][griffe.Module.as_json].
2338 Parameters:
2339 **kwargs: Additional serialization options.
2341 Returns:
2342 A dictionary.
2343 """
2344 base = super().as_dict(**kwargs)
2345 if isinstance(self._filepath, list):
2346 base["filepath"] = [str(path) for path in self._filepath]
2347 elif self._filepath: 2347 ↛ 2350line 2347 didn't jump to line 2350 because the condition on line 2347 was always true
2348 base["filepath"] = str(self._filepath)
2349 else:
2350 base["filepath"] = None
2351 return base
2354class Class(Object):
2355 """The class representing a Python class."""
2357 kind = Kind.CLASS
2359 def __init__(
2360 self,
2361 *args: Any,
2362 bases: Sequence[Expr | str] | None = None,
2363 decorators: list[Decorator] | None = None,
2364 keywords: dict[str, Any] | None = None,
2365 **kwargs: Any,
2366 ) -> None:
2367 """Initialize the class.
2369 Parameters:
2370 *args: See [`griffe.Object`][].
2371 bases: The list of base classes, if any.
2372 decorators: The class decorators, if any.
2373 keywords: The class keywords arguments, if any.
2374 **kwargs: See [`griffe.Object`][].
2375 """
2376 super().__init__(*args, **kwargs)
2378 self.bases: list[Expr | str] = list(bases) if bases else []
2379 """The class bases.
2381 See also: [`resolved_bases`][griffe.Class.resolved_bases],
2382 [`mro`][griffe.Class.mro].
2383 """
2385 self.decorators: list[Decorator] = decorators or []
2386 """The class decorators."""
2388 self.keywords: dict[str, Any] = keywords or {}
2389 """The class keywords arguments."""
2391 self.overloads: dict[str, list[Function]] = defaultdict(list)
2392 """The overloaded signatures declared in this class."""
2394 @property
2395 def parameters(self) -> Parameters:
2396 """The parameters of this class' `__init__` method, if any.
2398 This property fetches inherited members,
2399 and therefore is part of the consumer API:
2400 do not use when producing Griffe trees!
2401 """
2402 try:
2403 return self.all_members["__init__"].parameters # ty:ignore[possibly-missing-attribute]
2404 except KeyError:
2405 return Parameters()
2407 def signature(self, *, return_type: bool = False, name: str | None = None) -> str:
2408 """Construct the class signature.
2410 Parameters:
2411 return_type: Whether to include the return type in the signature.
2412 name: The name of the class to use in the signature.
2414 Returns:
2415 A string representation of the class signature.
2416 """
2417 all_members = self.all_members
2418 if "__init__" in all_members: 2418 ↛ 2422line 2418 didn't jump to line 2422 because the condition on line 2418 was always true
2419 init = all_members["__init__"]
2420 if isinstance(init, Function): 2420 ↛ 2422line 2420 didn't jump to line 2422 because the condition on line 2420 was always true
2421 return init.signature(return_type=return_type, name=name or self.name)
2422 return ""
2424 @property
2425 def resolved_bases(self) -> list[Object]:
2426 """Resolved class bases.
2428 This method is part of the consumer API:
2429 do not use when producing Griffe trees!
2431 See also: [`bases`][griffe.Class.bases],
2432 [`mro`][griffe.Class.mro].
2433 """
2434 resolved_bases = []
2435 for base in self.bases:
2436 base_path = base if isinstance(base, str) else base.canonical_path
2437 try:
2438 resolved_base = self.modules_collection.get_member(base_path)
2439 if resolved_base.is_alias:
2440 resolved_base = resolved_base.final_target
2441 except (AliasResolutionError, CyclicAliasError, KeyError):
2442 logger.debug("Base class %s is not loaded, or not static, it cannot be resolved", base_path)
2443 else:
2444 resolved_bases.append(resolved_base)
2445 return resolved_bases
2447 def _mro(self, seen: tuple[str, ...] = ()) -> list[Class]:
2448 seen = (*seen, self.path)
2449 bases: list[Class] = [base for base in self.resolved_bases if base.is_class] # ty:ignore[invalid-assignment]
2450 if not bases:
2451 return [self]
2452 for base in bases:
2453 if base.path in seen:
2454 cycle = " -> ".join(seen) + f" -> {base.path}"
2455 raise ValueError(f"Cannot compute C3 linearization, inheritance cycle detected: {cycle}")
2456 return [self, *c3linear_merge(*[base._mro(seen) for base in bases], bases)]
2458 def mro(self) -> list[Class]:
2459 """Return a list of classes in order corresponding to Python's MRO.
2461 See also: [`bases`][griffe.Class.bases],
2462 [`resolved_bases`][griffe.Class.resolved_bases].
2463 """
2464 return self._mro()[1:] # Remove self.
2466 def as_dict(self, **kwargs: Any) -> dict[str, Any]:
2467 """Return this class' data as a dictionary.
2469 See also: [`as_json`][griffe.Class.as_json].
2471 Parameters:
2472 **kwargs: Additional serialization options.
2474 Returns:
2475 A dictionary.
2476 """
2477 base = super().as_dict(**kwargs)
2478 base["bases"] = self.bases
2479 base["decorators"] = [dec.as_dict(**kwargs) for dec in self.decorators]
2480 return base
2483class Function(Object):
2484 """The class representing a Python function."""
2486 kind = Kind.FUNCTION
2488 def __init__(
2489 self,
2490 *args: Any,
2491 parameters: Parameters | None = None,
2492 returns: str | Expr | None = None,
2493 decorators: list[Decorator] | None = None,
2494 **kwargs: Any,
2495 ) -> None:
2496 """Initialize the function.
2498 Parameters:
2499 *args: See [`griffe.Object`][].
2500 parameters: The function parameters.
2501 returns: The function return annotation.
2502 decorators: The function decorators, if any.
2503 **kwargs: See [`griffe.Object`][].
2504 """
2505 super().__init__(*args, **kwargs)
2506 self.parameters: Parameters = parameters or Parameters()
2507 """The function parameters."""
2508 self.returns: str | Expr | None = returns
2509 """The function return type annotation."""
2510 self.decorators: list[Decorator] = decorators or []
2511 """The function decorators."""
2512 self.overloads: list[Function] | None = None
2513 """The overloaded signatures of this function."""
2515 for parameter in self.parameters:
2516 parameter.function = self
2518 @property
2519 def annotation(self) -> str | Expr | None:
2520 """The type annotation of the returned value."""
2521 return self.returns
2523 def resolve(self, name: str) -> str:
2524 """Resolve a name within this object's and parents' scope.
2526 Parameters:
2527 name: The name to resolve.
2529 Raises:
2530 NameResolutionError: When the name could not be resolved.
2532 Returns:
2533 The resolved name.
2534 """
2535 # We're in an `__init__` method...
2536 if self.parent and self.name == "__init__":
2537 # ...and name is a parameter name: resolve to the parameter.
2538 if name in self.parameters:
2539 return f"{self.parent.path}({name})"
2541 # Kind of a special case: we avoid resolving to instance-attributes from a function scope.
2542 # See issue https://github.com/mkdocstrings/griffe/issues/367.
2543 resolved = super().resolve(name)
2544 try:
2545 obj = self.modules_collection.get_member(resolved)
2546 except KeyError:
2547 return resolved
2548 try:
2549 if obj.is_attribute and "instance-attribute" in obj.labels:
2550 raise NameResolutionError(name)
2551 except AliasResolutionError:
2552 pass
2553 return resolved
2554 return super().resolve(name)
2556 @property
2557 def is_init_method(self) -> bool:
2558 """Whether this function is an `__init__` method."""
2559 return bool(self.parent and self.parent.is_class and self.name == "__init__")
2561 def as_dict(self, **kwargs: Any) -> dict[str, Any]:
2562 """Return this function's data as a dictionary.
2564 See also: [`as_json`][griffe.Function.as_json].
2566 Parameters:
2567 **kwargs: Additional serialization options.
2569 Returns:
2570 A dictionary.
2571 """
2572 base = super().as_dict(**kwargs)
2573 base["decorators"] = [dec.as_dict(**kwargs) for dec in self.decorators]
2574 base["parameters"] = [param.as_dict(**kwargs) for param in self.parameters]
2575 base["returns"] = self.returns
2576 return base
2578 def signature(self, *, return_type: bool = True, name: str | None = None) -> str:
2579 """Construct the function signature.
2581 Parameters:
2582 return_type: Whether to include the return type in the signature.
2583 name: The name of the function to use in the signature.
2585 Returns:
2586 A string representation of the function signature.
2587 """
2588 signature = f"{name or self.name}("
2590 has_pos_only = any(p.kind == ParameterKind.positional_only for p in self.parameters)
2591 render_pos_only_separator = True
2592 render_kw_only_separator = True
2594 param_strs = []
2596 for index, param in enumerate(self.parameters):
2597 # Skip 'self' or 'cls' for class methods if it's the first parameter.
2598 if index == 0 and param.name in ("self", "cls") and self.parent and self.parent.is_class: 2598 ↛ 2599line 2598 didn't jump to line 2599 because the condition on line 2598 was never true
2599 continue
2601 param_str = ""
2603 # Handle parameter kind and separators.
2604 if param.kind != ParameterKind.positional_only:
2605 if has_pos_only and render_pos_only_separator:
2606 render_pos_only_separator = False
2607 param_strs.append("/")
2609 if param.kind == ParameterKind.keyword_only and render_kw_only_separator: 2609 ↛ 2610line 2609 didn't jump to line 2610 because the condition on line 2609 was never true
2610 render_kw_only_separator = False
2611 param_strs.append("*")
2613 # Handle variadic parameters.
2614 if param.kind == ParameterKind.var_positional:
2615 param_str = "*"
2616 render_kw_only_separator = False
2617 elif param.kind == ParameterKind.var_keyword:
2618 param_str = "**"
2620 # Add parameter name.
2621 param_str += param.name
2623 # Handle type annotation
2624 if param.annotation is not None:
2625 param_str += f": {param.annotation}"
2626 equal = " = " # Space around equal when annotation is present.
2627 else:
2628 equal = "=" # No space when no annotation.
2630 # Handle default value.
2631 if param.default is not None and param.kind not in {
2632 ParameterKind.var_positional,
2633 ParameterKind.var_keyword,
2634 }:
2635 param_str += f"{equal}{param.default}"
2637 param_strs.append(param_str)
2639 # If we have positional-only parameters but no '/' was added yet
2640 if has_pos_only and render_pos_only_separator: 2640 ↛ 2641line 2640 didn't jump to line 2641 because the condition on line 2640 was never true
2641 param_strs.append("/")
2643 signature += ", ".join(param_strs)
2644 signature += ")"
2646 # Add return type if present.
2647 if return_type and self.annotation:
2648 signature += f" -> {self.annotation}"
2650 return signature
2653class Attribute(Object):
2654 """The class representing a Python module/class/instance attribute."""
2656 kind = Kind.ATTRIBUTE
2658 def __init__(
2659 self,
2660 *args: Any,
2661 value: str | Expr | None = None,
2662 annotation: str | Expr | None = None,
2663 **kwargs: Any,
2664 ) -> None:
2665 """Initialize the function.
2667 Parameters:
2668 *args: See [`griffe.Object`][].
2669 value: The attribute value, if any.
2670 annotation: The attribute annotation, if any.
2671 **kwargs: See [`griffe.Object`][].
2672 """
2673 super().__init__(*args, **kwargs)
2674 self.value: str | Expr | None = value
2675 """The attribute value."""
2676 self.annotation: str | Expr | None = annotation
2677 """The attribute type annotation."""
2678 self.setter: Function | None = None
2679 """The setter linked to this property."""
2680 self.deleter: Function | None = None
2681 """The deleter linked to this property."""
2683 def as_dict(self, **kwargs: Any) -> dict[str, Any]:
2684 """Return this attribute's data as a dictionary.
2686 See also: [`as_json`][griffe.Attribute.as_json].
2688 Parameters:
2689 **kwargs: Additional serialization options.
2691 Returns:
2692 A dictionary.
2693 """
2694 base = super().as_dict(**kwargs)
2695 if self.value is not None:
2696 base["value"] = self.value
2697 if self.annotation is not None:
2698 base["annotation"] = self.annotation
2699 return base
2702class TypeAlias(Object):
2703 """The class representing a Python type alias."""
2705 kind = Kind.TYPE_ALIAS
2707 def __init__(
2708 self,
2709 *args: Any,
2710 value: str | Expr | None = None,
2711 **kwargs: Any,
2712 ) -> None:
2713 """Initialize the function.
2715 Parameters:
2716 *args: See [`griffe.Object`][].
2717 value: The type alias value.
2718 **kwargs: See [`griffe.Object`][].
2719 """
2720 super().__init__(*args, **kwargs)
2721 self.value: str | Expr | None = value
2722 """The type alias value."""
2724 def as_dict(self, **kwargs: Any) -> dict[str, Any]:
2725 """Return this type alias's data as a dictionary.
2727 Parameters:
2728 **kwargs: Additional serialization options.
2730 Returns:
2731 A dictionary.
2732 """
2733 base = super().as_dict(**kwargs)
2734 base["value"] = self.value
2735 return base