Coverage for src/griffe/_internal/models.py: 80.95%
995 statements
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-14 23:10 +0200
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-14 23:10 +0200
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 pathlib import Path
10from textwrap import dedent
11from typing import TYPE_CHECKING, Any, Callable, Union, cast
13from griffe._internal.c3linear import c3linear_merge
14from griffe._internal.docstrings.parsers import DocstringStyle, parse
15from griffe._internal.enumerations import Kind, ParameterKind, Parser, TypeParameterKind
16from griffe._internal.exceptions import AliasResolutionError, BuiltinModuleError, CyclicAliasError, NameResolutionError
17from griffe._internal.expressions import ExprCall, ExprName, ExprTuple
18from griffe._internal.logger import logger
19from griffe._internal.mixins import ObjectAliasMixin
21if TYPE_CHECKING:
22 from collections.abc import Sequence
24 from griffe._internal.collections import LinesCollection, ModulesCollection
25 from griffe._internal.docstrings.models import DocstringSection
26 from griffe._internal.expressions import Expr
29from functools import cached_property
32class Decorator:
33 """This class represents decorators."""
35 def __init__(self, value: str | Expr, *, lineno: int | None, endlineno: int | None) -> None:
36 """Initialize the decorator.
38 Parameters:
39 value: The decorator code.
40 lineno: The starting line number.
41 endlineno: The ending line number.
42 """
43 self.value: str | Expr = value
44 """The decorator value (as a Griffe expression or string)."""
45 self.lineno: int | None = lineno
46 """The starting line number of the decorator."""
47 self.endlineno: int | None = endlineno
48 """The ending line number of the decorator."""
50 @property
51 def callable_path(self) -> str:
52 """The path of the callable used as decorator."""
53 value = self.value.function if isinstance(self.value, ExprCall) else self.value
54 return value if isinstance(value, str) else value.canonical_path
56 def as_dict(self, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002
57 """Return this decorator's data as a dictionary.
59 Parameters:
60 **kwargs: Additional serialization options.
62 Returns:
63 A dictionary.
64 """
65 return {
66 "value": self.value,
67 "lineno": self.lineno,
68 "endlineno": self.endlineno,
69 }
72class Docstring:
73 """This class represents docstrings."""
75 def __init__(
76 self,
77 value: str,
78 *,
79 lineno: int | None = None,
80 endlineno: int | None = None,
81 parent: Object | None = None,
82 parser: DocstringStyle | Parser | None = None,
83 parser_options: dict[str, Any] | None = None,
84 ) -> None:
85 """Initialize the docstring.
87 Parameters:
88 value: The docstring value.
89 lineno: The starting line number.
90 endlineno: The ending line number.
91 parent: The parent object on which this docstring is attached.
92 parser: The docstring parser to use. By default, no parsing is done.
93 parser_options: Additional docstring parsing options.
94 """
95 self.value: str = inspect.cleandoc(value.rstrip())
96 """The original value of the docstring, cleaned by `inspect.cleandoc`.
98 See also: [`source`][griffe.Docstring.source].
99 """
101 self.lineno: int | None = lineno
102 """The starting line number of the docstring.
104 See also: [`endlineno`][griffe.Docstring.endlineno]."""
106 self.endlineno: int | None = endlineno
107 """The ending line number of the docstring.
109 See also: [`lineno`][griffe.Docstring.lineno]."""
111 self.parent: Object | None = parent
112 """The object this docstring is attached to."""
114 self.parser: DocstringStyle | Parser | None = parser
115 """The selected docstring parser.
117 See also: [`parser_options`][griffe.Docstring.parser_options],
118 [`parse`][griffe.Docstring.parse].
119 """
121 self.parser_options: dict[str, Any] = parser_options or {}
122 """The configured parsing options.
124 See also: [`parser`][griffe.Docstring.parser],
125 [`parse`][griffe.Docstring.parse].
126 """
128 @property
129 def lines(self) -> list[str]:
130 """The lines of the docstring.
132 See also: [`source`][griffe.Docstring.source].
133 """
134 return self.value.split("\n")
136 @property
137 def source(self) -> str:
138 """The original, uncleaned value of the docstring as written in the source.
140 See also: [`value`][griffe.Docstring.value].
141 """
142 if self.parent is None:
143 raise ValueError("Cannot get original docstring without parent object")
144 if isinstance(self.parent.filepath, list):
145 raise ValueError("Cannot get original docstring for namespace package") # noqa: TRY004
146 if self.lineno is None or self.endlineno is None:
147 raise ValueError("Cannot get original docstring without line numbers")
148 return "\n".join(self.parent.lines_collection[self.parent.filepath][self.lineno - 1 : self.endlineno])
150 @cached_property
151 def parsed(self) -> list[DocstringSection]:
152 """The docstring sections, parsed into structured data."""
153 return self.parse()
155 def parse(
156 self,
157 parser: DocstringStyle | Parser | None = None,
158 **options: Any,
159 ) -> list[DocstringSection]:
160 """Parse the docstring into structured data.
162 See also: [`parser`][griffe.Docstring.parser],
163 [`parser_options`][griffe.Docstring.parser_options].
165 Parameters:
166 parser: The docstring parser to use.
167 In order: use the given parser, or the self parser, or no parser (return a single text section).
168 **options: Additional docstring parsing options.
170 Returns:
171 The parsed docstring as a list of sections.
172 """
173 return parse(self, parser or self.parser, **(options or self.parser_options))
175 def as_dict(
176 self,
177 *,
178 full: bool = False,
179 **kwargs: Any, # noqa: ARG002
180 ) -> dict[str, Any]:
181 """Return this docstring's data as a dictionary.
183 Parameters:
184 full: Whether to return full info, or just base info.
185 **kwargs: Additional serialization options.
187 Returns:
188 A dictionary.
189 """
190 base: dict[str, Any] = {
191 "value": self.value,
192 "lineno": self.lineno,
193 "endlineno": self.endlineno,
194 }
195 if full:
196 base["parsed"] = self.parsed
197 return base
200class Parameter: # noqa: PLW1641
201 """This class represent a function parameter.
203 See also: [`Parameters`][griffe.Parameters].
204 """
206 def __init__(
207 self,
208 name: str,
209 *,
210 annotation: str | Expr | None = None,
211 kind: ParameterKind | None = None,
212 default: str | Expr | None = None,
213 docstring: Docstring | None = None,
214 ) -> None:
215 """Initialize the parameter.
217 Parameters:
218 name: The parameter name, without leading stars (`*` or `**`).
219 annotation: The parameter annotation, if any.
220 kind: The parameter kind.
221 default: The parameter default, if any.
222 docstring: The parameter docstring.
223 """
224 self.name: str = name
225 """The parameter name."""
226 self.annotation: str | Expr | None = annotation
227 """The parameter type annotation."""
228 self.kind: ParameterKind | None = kind
229 """The parameter kind."""
230 self.default: str | Expr | None = default
231 """The parameter default value."""
232 self.docstring: Docstring | None = docstring
233 """The parameter docstring."""
234 # The parent function is set in `Function.__init__`,
235 # when the parameters are assigned to the function.
236 self.function: Function | None = None
237 """The parent function of the parameter."""
239 def __str__(self) -> str:
240 param = f"{self.name}: {self.annotation} = {self.default}"
241 if self.kind:
242 return f"[{self.kind.value}] {param}"
243 return param
245 def __repr__(self) -> str:
246 return f"Parameter(name={self.name!r}, annotation={self.annotation!r}, kind={self.kind!r}, default={self.default!r})"
248 def __eq__(self, value: object, /) -> bool:
249 """Parameters are equal if all their attributes except `docstring` and `function` are equal."""
250 if not isinstance(value, Parameter): 250 ↛ 251line 250 didn't jump to line 251 because the condition on line 250 was never true
251 return NotImplemented
252 return (
253 self.name == value.name
254 and self.annotation == value.annotation
255 and self.kind == value.kind
256 and self.default == value.default
257 )
259 @property
260 def required(self) -> bool:
261 """Whether this parameter is required."""
262 return self.default is None
264 def as_dict(self, *, full: bool = False, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002
265 """Return this parameter's data as a dictionary.
267 Parameters:
268 **kwargs: Additional serialization options.
270 Returns:
271 A dictionary.
272 """
273 base: dict[str, Any] = {
274 "name": self.name,
275 "annotation": self.annotation,
276 "kind": self.kind,
277 "default": self.default,
278 }
279 if self.docstring:
280 base["docstring"] = self.docstring.as_dict(full=full)
281 return base
284class Parameters:
285 """This class is a container for parameters.
287 It allows to get parameters using their position (index) or their name:
289 ```pycon
290 >>> parameters = Parameters(Parameter("hello"))
291 >>> parameters[0] is parameters["hello"]
292 True
293 ```
295 See also: [`Parameter`][griffe.Parameter].
296 """
298 def __init__(self, *parameters: Parameter) -> None:
299 """Initialize the parameters container.
301 Parameters:
302 *parameters: The initial parameters to add to the container.
303 """
304 self._params: list[Parameter] = list(parameters)
306 def __repr__(self) -> str:
307 return f"Parameters({', '.join(repr(param) for param in self._params)})"
309 def __getitem__(self, name_or_index: int | str) -> Parameter:
310 """Get a parameter by index or name."""
311 if isinstance(name_or_index, int):
312 return self._params[name_or_index]
313 name = name_or_index.lstrip("*")
314 try:
315 return next(param for param in self._params if param.name == name)
316 except StopIteration as error:
317 raise KeyError(f"parameter {name_or_index} not found") from error
319 def __setitem__(self, name_or_index: int | str, parameter: Parameter) -> None:
320 """Set a parameter by index or name."""
321 if isinstance(name_or_index, int):
322 self._params[name_or_index] = parameter
323 else:
324 name = name_or_index.lstrip("*")
325 try:
326 index = next(idx for idx, param in enumerate(self._params) if param.name == name)
327 except StopIteration:
328 self._params.append(parameter)
329 else:
330 self._params[index] = parameter
332 def __delitem__(self, name_or_index: int | str) -> None:
333 """Delete a parameter by index or name."""
334 if isinstance(name_or_index, int):
335 del self._params[name_or_index]
336 else:
337 name = name_or_index.lstrip("*")
338 try:
339 index = next(idx for idx, param in enumerate(self._params) if param.name == name)
340 except StopIteration as error:
341 raise KeyError(f"parameter {name_or_index} not found") from error
342 del self._params[index]
344 def __len__(self):
345 """The number of parameters."""
346 return len(self._params)
348 def __iter__(self):
349 """Iterate over the parameters, in order."""
350 return iter(self._params)
352 def __contains__(self, param_name: str):
353 """Whether a parameter with the given name is present."""
354 try:
355 next(param for param in self._params if param.name == param_name.lstrip("*"))
356 except StopIteration:
357 return False
358 return True
360 def add(self, parameter: Parameter) -> None:
361 """Add a parameter to the container.
363 Parameters:
364 parameter: The function parameter to add.
366 Raises:
367 ValueError: When a parameter with the same name is already present.
368 """
369 if parameter.name in self:
370 raise ValueError(f"parameter {parameter.name} already present")
371 self._params.append(parameter)
374class TypeParameter:
375 """This class represents a type parameter."""
377 def __init__(
378 self,
379 name: str,
380 *,
381 kind: TypeParameterKind,
382 bound: str | Expr | None = None,
383 constraints: Sequence[str | Expr] | None = None,
384 default: str | Expr | None = None,
385 ) -> None:
386 """Initialize the type parameter.
388 Parameters:
389 name: The type parameter name, without leading stars (`*` or `**`).
390 kind: The type parameter kind.
391 bound: The type parameter bound, if any.
392 Mutually exclusive with `constraints`.
393 constraints: The type parameter constraints, if any.
394 Mutually exclusive with `bound`.
395 default: The type parameter default, if any.
397 Raises:
398 ValueError: When more than one of `bound` and `constraints` is set.
399 """
400 if bound is not None and constraints: 400 ↛ 401line 400 didn't jump to line 401 because the condition on line 400 was never true
401 raise ValueError("bound and constraints are mutually exclusive")
403 self.name: str = name
404 """The type parameter name."""
406 self.kind: TypeParameterKind = kind
407 """The type parameter kind."""
409 self.annotation: str | Expr | None
410 """The type parameter bound or constraints."""
412 if constraints:
413 self.constraints = constraints
414 else:
415 self.bound = bound
417 self.default: str | Expr | None = default
418 """The type parameter default value."""
420 def __repr__(self) -> str:
421 return f"TypeParameter(name={self.name!r}, kind={self.kind!r}, bound={self.annotation!r}, default={self.default!r})"
423 @property
424 def bound(self) -> str | Expr | None:
425 """The type parameter bound."""
426 if not isinstance(self.annotation, ExprTuple):
427 return self.annotation
428 return None
430 @bound.setter
431 def bound(self, bound: str | Expr | None) -> None:
432 self.annotation = bound
434 @property
435 def constraints(self) -> tuple[str | Expr, ...] | None:
436 """The type parameter constraints."""
437 if isinstance(self.annotation, ExprTuple):
438 return tuple(self.annotation.elements)
439 return None
441 @constraints.setter
442 def constraints(self, constraints: Sequence[str | Expr] | None) -> None:
443 if constraints is not None: 443 ↛ 446line 443 didn't jump to line 446 because the condition on line 443 was always true
444 self.annotation = ExprTuple(constraints)
445 else:
446 self.annotation = None
448 def as_dict(self, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002
449 """Return this type parameter's data as a dictionary.
451 Parameters:
452 **kwargs: Additional serialization options.
454 Returns:
455 A dictionary.
456 """
457 base: dict[str, Any] = {
458 "name": self.name,
459 "kind": self.kind,
460 "annotation": self.annotation,
461 "default": self.default,
462 }
463 return base
466class TypeParameters:
467 """This class is a container for type parameters.
469 It allows to get type parameters using their position (index) or their name:
471 ```pycon
472 >>> type_parameters = TypeParameters(TypeParameter("hello"), kind=TypeParameterKind.type_var)
473 >>> type_parameters[0] is type_parameters["hello"]
474 True
475 ```
476 """
478 def __init__(self, *type_parameters: TypeParameter) -> None:
479 """Initialize the type parameters container.
481 Parameters:
482 *type_parameters: The initial type parameters to add to the container.
483 """
484 self._type_params: list[TypeParameter] = list(type_parameters)
486 def __repr__(self) -> str:
487 return f"TypeParameters({', '.join(repr(type_param) for type_param in self._type_params)})"
489 def __getitem__(self, name_or_index: int | str) -> TypeParameter:
490 """Get a type parameter by index or name."""
491 if isinstance(name_or_index, int):
492 return self._type_params[name_or_index]
493 name = name_or_index.lstrip("*")
494 try:
495 return next(param for param in self._type_params if param.name == name)
496 except StopIteration as error:
497 raise KeyError(f"type parameter {name_or_index} not found") from error
499 def __setitem__(self, name_or_index: int | str, type_parameter: TypeParameter) -> None:
500 """Set a type parameter by index or name."""
501 if isinstance(name_or_index, int):
502 self._type_params[name_or_index] = type_parameter
503 else:
504 name = name_or_index.lstrip("*")
505 try:
506 index = next(idx for idx, param in enumerate(self._type_params) if param.name == name)
507 except StopIteration:
508 self._type_params.append(type_parameter)
509 else:
510 self._type_params[index] = type_parameter
512 def __delitem__(self, name_or_index: int | str) -> None:
513 """Delete a type parameter by index or name."""
514 if isinstance(name_or_index, int):
515 del self._type_params[name_or_index]
516 else:
517 name = name_or_index.lstrip("*")
518 try:
519 index = next(idx for idx, param in enumerate(self._type_params) if param.name == name)
520 except StopIteration as error:
521 raise KeyError(f"type parameter {name_or_index} not found") from error
522 del self._type_params[index]
524 def __len__(self):
525 """The number of type parameters."""
526 return len(self._type_params)
528 def __iter__(self):
529 """Iterate over the type parameters, in order."""
530 return iter(self._type_params)
532 def __contains__(self, type_param_name: str):
533 """Whether a type parameter with the given name is present."""
534 try:
535 next(param for param in self._type_params if param.name == type_param_name.lstrip("*"))
536 except StopIteration:
537 return False
538 return True
540 def add(self, type_parameter: TypeParameter) -> None:
541 """Add a type parameter to the container.
543 Parameters:
544 type_parameter: The function parameter to add.
546 Raises:
547 ValueError: When a type parameter with the same name is already present.
548 """
549 if type_parameter.name in self:
550 raise ValueError(f"type parameter {type_parameter.name} already present")
551 self._type_params.append(type_parameter)
554class Object(ObjectAliasMixin):
555 """An abstract class representing a Python object."""
557 kind: Kind
558 """The object kind."""
559 is_alias: bool = False
560 """Always false for objects."""
561 is_collection: bool = False
562 """Always false for objects."""
563 inherited: bool = False
564 """Always false for objects.
566 Only aliases can be marked as inherited.
567 """
569 def __init__(
570 self,
571 name: str,
572 *,
573 lineno: int | None = None,
574 endlineno: int | None = None,
575 runtime: bool = True,
576 docstring: Docstring | None = None,
577 type_parameters: TypeParameters | None = None,
578 parent: Module | Class | None = None,
579 lines_collection: LinesCollection | None = None,
580 modules_collection: ModulesCollection | None = None,
581 ) -> None:
582 """Initialize the object.
584 Parameters:
585 name: The object name, as declared in the code.
586 lineno: The object starting line, or None for modules. Lines start at 1.
587 endlineno: The object ending line (inclusive), or None for modules.
588 runtime: Whether this object is present at runtime or not.
589 docstring: The object docstring.
590 type_parameters: The object type parameters, if any.
591 parent: The object parent.
592 lines_collection: A collection of source code lines.
593 modules_collection: A collection of modules.
594 """
595 self.name: str = name
596 """The object name."""
598 self.lineno: int | None = lineno
599 """The starting line number of the object.
601 See also: [`endlineno`][griffe.Object.endlineno].
602 """
604 self.endlineno: int | None = endlineno
605 """The ending line number of the object.
607 See also: [`lineno`][griffe.Object.lineno].
608 """
610 self.docstring: Docstring | None = docstring
611 """The object docstring.
613 See also: [`has_docstring`][griffe.Object.has_docstring],
614 [`has_docstrings`][griffe.Object.has_docstrings].
615 """
617 # TODO: Maybe move these into `Class` and `Function`.
618 # Then always return them in `Class` and `Function`'s `as_dict` methods,
619 # and remove the conditional in the `_load_class` and `_load_function` decoders.
620 self.type_parameters: TypeParameters = type_parameters or TypeParameters()
621 """The object type parameters."""
623 self.parent: Module | Class | None = parent
624 """The parent of the object (none if top module)."""
626 self.members: dict[str, Object | Alias] = {}
627 """The object members (modules, classes, functions, attributes, type aliases).
629 See also: [`inherited_members`][griffe.Object.inherited_members],
630 [`get_member`][griffe.Object.get_member],
631 [`set_member`][griffe.Object.set_member],
632 [`filter_members`][griffe.Object.filter_members].
633 """
635 self.labels: set[str] = set()
636 """The object labels (`property`, `dataclass`, etc.).
638 See also: [`has_labels`][griffe.Object.has_labels]."""
640 self.imports: dict[str, str] = {}
641 """The other objects imported by this object.
643 Keys are the names within the object (`from ... import ... as AS_NAME`),
644 while the values are the actual names of the objects (`from ... import REAL_NAME as ...`).
645 """
647 self.exports: list[str | ExprName] | None = None
648 """The names of the objects exported by this (module) object through the `__all__` variable.
650 Exports can contain string (object names) or resolvable names,
651 like other lists of exports coming from submodules:
653 ```python
654 from .submodule import __all__ as submodule_all
656 __all__ = ["hello", *submodule_all]
657 ```
659 Exports get expanded by the loader before it expands wildcards and resolves aliases.
661 See also: [`GriffeLoader.expand_exports`][griffe.GriffeLoader.expand_exports].
662 """
664 self.aliases: dict[str, Alias] = {}
665 """The aliases pointing to this object."""
667 self.runtime: bool = runtime
668 """Whether this object is available at runtime.
670 Typically, type-guarded objects (under an `if TYPE_CHECKING` condition)
671 are not available at runtime.
672 """
674 self.extra: dict[str, dict[str, Any]] = defaultdict(dict)
675 """Namespaced dictionaries storing extra metadata for this object, used by extensions."""
677 self.public: bool | None = None
678 """Whether this object is public."""
680 self.deprecated: bool | str | None = None
681 """Whether this object is deprecated (boolean or deprecation message)."""
683 self._lines_collection: LinesCollection | None = lines_collection
684 self._modules_collection: ModulesCollection | None = modules_collection
686 # Attach the docstring to this object.
687 if docstring:
688 docstring.parent = self
690 def __repr__(self) -> str:
691 return f"{self.__class__.__name__}({self.name!r}, {self.lineno!r}, {self.endlineno!r})"
693 # Prevent using `__len__`.
694 def __bool__(self) -> bool:
695 """An object is always true-ish."""
696 return True
698 def __len__(self) -> int:
699 """The number of members in this object, recursively."""
700 return len(self.members) + sum(len(member) for member in self.members.values())
702 @property
703 def has_docstring(self) -> bool:
704 """Whether this object has a docstring (empty or not).
706 See also: [`docstring`][griffe.Object.docstring],
707 [`has_docstrings`][griffe.Object.has_docstrings].
708 """
709 return bool(self.docstring)
711 # NOTE: (pawamoy) I'm not happy with `has_docstrings`.
712 # It currently recurses into submodules, but that doesn't make sense
713 # if downstream projects use it to know if they should render an init module
714 # while not rendering submodules too: the property could tell them there are
715 # docstrings, but they could be in submodules, not in the init module.
716 # Maybe we should derive it into new properties: `has_local_docstrings`,
717 # `has_docstrings`, `has_public_docstrings`... Maybe we should make it a function?`
718 # For now it's used in mkdocstrings-python so we must be careful with changes.
719 @property
720 def has_docstrings(self) -> bool:
721 """Whether this object or any of its members has a docstring (empty or not).
723 Inherited members are not considered. Imported members are not considered,
724 unless they are also public.
726 See also: [`docstring`][griffe.Object.docstring],
727 [`has_docstring`][griffe.Object.has_docstring].
728 """
729 if self.has_docstring:
730 return True
731 for member in self.members.values():
732 try:
733 if (not member.is_imported or member.is_public) and member.has_docstrings:
734 return True
735 except AliasResolutionError:
736 continue
737 return False
739 def is_kind(self, kind: str | Kind | set[str | Kind]) -> bool:
740 """Tell if this object is of the given kind.
742 See also: [`is_module`][griffe.Object.is_module],
743 [`is_class`][griffe.Object.is_class],
744 [`is_function`][griffe.Object.is_function],
745 [`is_attribute`][griffe.Object.is_attribute],
746 [`is_type_alias`][griffe.Object.is_type_alias],
747 [`is_alias`][griffe.Object.is_alias].
749 Parameters:
750 kind: An instance or set of kinds (strings or enumerations).
752 Raises:
753 ValueError: When an empty set is given as argument.
755 Returns:
756 True or False.
757 """
758 if isinstance(kind, set):
759 if not kind:
760 raise ValueError("kind must not be an empty set")
761 return self.kind in (knd if isinstance(knd, Kind) else Kind(knd) for knd in kind)
762 if isinstance(kind, str):
763 kind = Kind(kind)
764 return self.kind is kind
766 @property
767 def inherited_members(self) -> dict[str, Alias]:
768 """Members that are inherited from base classes.
770 This method is part of the consumer API:
771 do not use when producing Griffe trees!
773 See also: [`members`][griffe.Object.members].
774 """
775 if not isinstance(self, Class): 775 ↛ 776line 775 didn't jump to line 776 because the condition on line 775 was never true
776 return {}
777 try:
778 mro = self.mro()
779 except ValueError as error:
780 logger.debug(error)
781 return {}
782 inherited_members = {}
783 for base in reversed(mro):
784 for name, member in base.members.items():
785 if name not in self.members:
786 inherited_members[name] = Alias(name, member, parent=self, inherited=True)
787 return inherited_members
789 @property
790 def is_module(self) -> bool:
791 """Whether this object is a module.
793 See also: [`is_init_module`][griffe.Object.is_init_module].
794 [`is_class`][griffe.Object.is_class],
795 [`is_function`][griffe.Object.is_function],
796 [`is_attribute`][griffe.Object.is_attribute],
797 [`is_type_alias`][griffe.Object.is_type_alias],
798 [`is_alias`][griffe.Object.is_alias],
799 [`is_kind`][griffe.Object.is_kind].
800 """
801 return self.kind is Kind.MODULE
803 @property
804 def is_class(self) -> bool:
805 """Whether this object is a class.
807 See also: [`is_module`][griffe.Object.is_module].
808 [`is_function`][griffe.Object.is_function],
809 [`is_attribute`][griffe.Object.is_attribute],
810 [`is_type_alias`][griffe.Object.is_type_alias],
811 [`is_alias`][griffe.Object.is_alias],
812 [`is_kind`][griffe.Object.is_kind].
813 """
814 return self.kind is Kind.CLASS
816 @property
817 def is_function(self) -> bool:
818 """Whether this object is a function.
820 See also: [`is_module`][griffe.Object.is_module].
821 [`is_class`][griffe.Object.is_class],
822 [`is_attribute`][griffe.Object.is_attribute],
823 [`is_type_alias`][griffe.Object.is_type_alias],
824 [`is_alias`][griffe.Object.is_alias],
825 [`is_kind`][griffe.Object.is_kind].
826 """
827 return self.kind is Kind.FUNCTION
829 @property
830 def is_attribute(self) -> bool:
831 """Whether this object is an attribute.
833 See also: [`is_module`][griffe.Object.is_module].
834 [`is_class`][griffe.Object.is_class],
835 [`is_function`][griffe.Object.is_function],
836 [`is_type_alias`][griffe.Object.is_type_alias],
837 [`is_alias`][griffe.Object.is_alias],
838 [`is_kind`][griffe.Object.is_kind].
839 """
840 return self.kind is Kind.ATTRIBUTE
842 @property
843 def is_type_alias(self) -> bool:
844 """Whether this object is a type alias.
846 See also: [`is_module`][griffe.Object.is_module].
847 [`is_class`][griffe.Object.is_class],
848 [`is_function`][griffe.Object.is_function],
849 [`is_attribute`][griffe.Object.is_attribute],
850 [`is_alias`][griffe.Object.is_alias],
851 [`is_kind`][griffe.Object.is_kind].
852 """
853 return self.kind is Kind.TYPE_ALIAS
855 @property
856 def is_init_module(self) -> bool:
857 """Whether this object is an `__init__.py` module.
859 See also: [`is_module`][griffe.Object.is_module].
860 """
861 return False
863 @property
864 def is_package(self) -> bool:
865 """Whether this object is a package (top module).
867 See also: [`is_subpackage`][griffe.Object.is_subpackage].
868 """
869 return False
871 @property
872 def is_subpackage(self) -> bool:
873 """Whether this object is a subpackage.
875 See also: [`is_package`][griffe.Object.is_package].
876 """
877 return False
879 @property
880 def is_namespace_package(self) -> bool:
881 """Whether this object is a namespace package (top folder, no `__init__.py`).
883 See also: [`is_namespace_subpackage`][griffe.Object.is_namespace_subpackage].
884 """
885 return False
887 @property
888 def is_namespace_subpackage(self) -> bool:
889 """Whether this object is a namespace subpackage.
891 See also: [`is_namespace_package`][griffe.Object.is_namespace_package].
892 """
893 return False
895 def has_labels(self, *labels: str) -> bool:
896 """Tell if this object has all the given labels.
898 See also: [`labels`][griffe.Object.labels].
900 Parameters:
901 *labels: Labels that must be present.
903 Returns:
904 True or False.
905 """
906 return set(labels).issubset(self.labels)
908 def filter_members(self, *predicates: Callable[[Object | Alias], bool]) -> dict[str, Object | Alias]:
909 """Filter and return members based on predicates.
911 See also: [`members`][griffe.Object.members].
913 Parameters:
914 *predicates: A list of predicates, i.e. callables accepting a member as argument and returning a boolean.
916 Returns:
917 A dictionary of members.
918 """
919 if not predicates:
920 return self.members
921 members: dict[str, Object | Alias] = {
922 name: member for name, member in self.members.items() if all(predicate(member) for predicate in predicates)
923 }
924 return members
926 @property
927 def module(self) -> Module:
928 """The parent module of this object.
930 See also: [`package`][griffe.Object.package].
932 Examples:
933 >>> import griffe
934 >>> markdown = griffe.load("markdown")
935 >>> markdown["core.Markdown.references"].module
936 Module(PosixPath('~/project/.venv/lib/python3.11/site-packages/markdown/core.py'))
937 >>> # The `module` of a module is itself.
938 >>> markdown["core"].module
939 Module(PosixPath('~/project/.venv/lib/python3.11/site-packages/markdown/core.py'))
941 Raises:
942 ValueError: When the object is not a module and does not have a parent.
943 """
944 if isinstance(self, Module):
945 return self
946 if self.parent is not None:
947 return self.parent.module
948 raise ValueError(f"Object {self.name} does not have a parent module")
950 @property
951 def package(self) -> Module:
952 """The absolute top module (the package) of this object.
954 See also: [`module`][griffe.Object.module].
956 Examples:
957 >>> import griffe
958 >>> markdown = griffe.load("markdown")
959 >>> markdown["core.Markdown.references"].package
960 Module(PosixPath('~/project/.venv/lib/python3.11/site-packages/markdown/__init__.py'))
961 """
962 module = self.module
963 while module.parent:
964 module = module.parent # type: ignore[assignment]
965 return module
967 @property
968 def filepath(self) -> Path | list[Path]:
969 """The file path (or directory list for namespace packages) where this object was defined.
971 See also: [`relative_filepath`][griffe.Object.relative_filepath],
972 [`relative_package_filepath`][griffe.Object.relative_package_filepath].
974 Examples:
975 >>> import griffe
976 >>> markdown = griffe.load("markdown")
977 >>> markdown.filepath
978 PosixPath('~/project/.venv/lib/python3.11/site-packages/markdown/__init__.py')
979 """
980 return self.module.filepath
982 @property
983 def relative_package_filepath(self) -> Path:
984 """The file path where this object was defined, relative to the top module path.
986 See also: [`filepath`][griffe.Object.filepath],
987 [`relative_filepath`][griffe.Object.relative_filepath].
989 Raises:
990 ValueError: When the relative path could not be computed.
991 """
992 package_path = self.package.filepath
994 # Current "module" is a namespace package.
995 if isinstance(self.filepath, list): 995 ↛ 997line 995 didn't jump to line 997 because the condition on line 995 was never true
996 # Current package is a namespace package.
997 if isinstance(package_path, list):
998 for pkg_path in package_path:
999 for self_path in self.filepath:
1000 with suppress(ValueError):
1001 return self_path.relative_to(pkg_path.parent)
1003 # Current package is a regular package.
1004 # NOTE: Technically it makes no sense to have a namespace package
1005 # under a non-namespace one, so we should never enter this branch.
1006 else:
1007 for self_path in self.filepath:
1008 with suppress(ValueError):
1009 return self_path.relative_to(package_path.parent.parent)
1010 raise ValueError
1012 # Current package is a namespace package,
1013 # and current module is a regular module or package.
1014 if isinstance(package_path, list): 1014 ↛ 1015line 1014 didn't jump to line 1015 because the condition on line 1014 was never true
1015 for pkg_path in package_path:
1016 with suppress(ValueError):
1017 return self.filepath.relative_to(pkg_path.parent)
1018 raise ValueError
1020 # Current package is a regular package,
1021 # and current module is a regular module or package,
1022 # try to compute the path relative to the parent folder
1023 # of the package (search path).
1024 return self.filepath.relative_to(package_path.parent.parent)
1026 @property
1027 def relative_filepath(self) -> Path:
1028 """The file path where this object was defined, relative to the current working directory.
1030 If this object's file path is not relative to the current working directory, return its absolute path.
1032 See also: [`filepath`][griffe.Object.filepath],
1033 [`relative_package_filepath`][griffe.Object.relative_package_filepath].
1035 Raises:
1036 ValueError: When the relative path could not be computed.
1037 """
1038 cwd = Path.cwd()
1039 if isinstance(self.filepath, list): 1039 ↛ 1040line 1039 didn't jump to line 1040 because the condition on line 1039 was never true
1040 for self_path in self.filepath:
1041 with suppress(ValueError):
1042 return self_path.relative_to(cwd)
1043 raise ValueError(f"No directory in {self.filepath!r} is relative to the current working directory {cwd}")
1044 try:
1045 return self.filepath.relative_to(cwd)
1046 except ValueError:
1047 return self.filepath
1049 @property
1050 def path(self) -> str:
1051 """The dotted path of this object.
1053 On regular objects (not aliases), the path is the canonical path.
1055 See also: [`canonical_path`][griffe.Object.canonical_path].
1057 Examples:
1058 >>> import griffe
1059 >>> markdown = griffe.load("markdown")
1060 >>> markdown["core.Markdown.references"].path
1061 'markdown.core.Markdown.references'
1062 """
1063 return self.canonical_path
1065 @property
1066 def canonical_path(self) -> str:
1067 """The full dotted path of this object.
1069 The canonical path is the path where the object was defined (not imported).
1071 See also: [`path`][griffe.Object.path].
1072 """
1073 if self.parent is None:
1074 return self.name
1075 return f"{self.parent.path}.{self.name}"
1077 @property
1078 def modules_collection(self) -> ModulesCollection:
1079 """The modules collection attached to this object or its parents.
1081 Raises:
1082 ValueError: When no modules collection can be found in the object or its parents.
1083 """
1084 if self._modules_collection is not None:
1085 return self._modules_collection
1086 if self.parent is None: 1086 ↛ 1087line 1086 didn't jump to line 1087 because the condition on line 1086 was never true
1087 raise ValueError("no modules collection in this object or its parents")
1088 return self.parent.modules_collection
1090 @property
1091 def lines_collection(self) -> LinesCollection:
1092 """The lines collection attached to this object or its parents.
1094 See also: [`lines`][griffe.Object.lines],
1095 [`source`][griffe.Object.source].
1097 Raises:
1098 ValueError: When no modules collection can be found in the object or its parents.
1099 """
1100 if self._lines_collection is not None:
1101 return self._lines_collection
1102 if self.parent is None: 1102 ↛ 1103line 1102 didn't jump to line 1103 because the condition on line 1102 was never true
1103 raise ValueError("no lines collection in this object or its parents")
1104 return self.parent.lines_collection
1106 @property
1107 def lines(self) -> list[str]:
1108 """The lines containing the source of this object.
1110 See also: [`lines_collection`][griffe.Object.lines_collection],
1111 [`source`][griffe.Object.source].
1112 """
1113 try:
1114 filepath = self.filepath
1115 except BuiltinModuleError:
1116 return []
1117 if isinstance(filepath, list): 1117 ↛ 1118line 1117 didn't jump to line 1118 because the condition on line 1117 was never true
1118 return []
1119 try:
1120 lines = self.lines_collection[filepath]
1121 except KeyError:
1122 return []
1123 if self.is_module:
1124 return lines
1125 if self.lineno is None or self.endlineno is None:
1126 return []
1127 return lines[self.lineno - 1 : self.endlineno]
1129 @property
1130 def source(self) -> str:
1131 """The source code of this object.
1133 See also: [`lines`][griffe.Object.lines],
1134 [`lines_collection`][griffe.Object.lines_collection].
1135 """
1136 return dedent("\n".join(self.lines))
1138 def resolve(self, name: str) -> str:
1139 """Resolve a name within this object's and parents' scope.
1141 Parameters:
1142 name: The name to resolve.
1144 Raises:
1145 NameResolutionError: When the name could not be resolved.
1147 Returns:
1148 The resolved name.
1149 """
1150 # TODO: Better match Python's own scoping rules?
1151 # Also, maybe return regular paths instead of canonical ones?
1153 # Name is a type parameter.
1154 if name in self.type_parameters:
1155 type_parameter = self.type_parameters[name]
1156 if type_parameter.kind is TypeParameterKind.type_var_tuple: 1156 ↛ 1157line 1156 didn't jump to line 1157 because the condition on line 1156 was never true
1157 prefix = "*"
1158 elif type_parameter.kind is TypeParameterKind.param_spec: 1158 ↛ 1159line 1158 didn't jump to line 1159 because the condition on line 1158 was never true
1159 prefix = "**"
1160 else:
1161 prefix = ""
1162 return f"{self.path}[{prefix}{name}]"
1164 # Name is a member of this object.
1165 if name in self.members:
1166 if self.members[name].is_alias:
1167 return self.members[name].target_path # type: ignore[union-attr]
1168 return self.members[name].path
1170 # Name unknown and no more parent scope, could be a built-in.
1171 if self.parent is None:
1172 raise NameResolutionError(f"{name} could not be resolved in the scope of {self.path}")
1174 # Name is parent, non-module object.
1175 if name == self.parent.name and not self.parent.is_module:
1176 return self.parent.path
1178 # Recurse in parent.
1179 return self.parent.resolve(name)
1181 def as_dict(self, *, full: bool = False, **kwargs: Any) -> dict[str, Any]:
1182 """Return this object's data as a dictionary.
1184 See also: [`as_json`][griffe.Object.as_json].
1186 Parameters:
1187 full: Whether to return full info, or just base info.
1188 **kwargs: Additional serialization options.
1190 Returns:
1191 A dictionary.
1192 """
1193 base: dict[str, Any] = {
1194 "kind": self.kind,
1195 "name": self.name,
1196 "runtime": self.runtime,
1197 }
1199 if self.public is not None: 1199 ↛ 1200line 1199 didn't jump to line 1200 because the condition on line 1199 was never true
1200 base["public"] = self.public
1201 if self.exports is not None:
1202 base["exports"] = [str(export) for export in self.exports]
1203 if self.imports:
1204 base["imports"] = self.imports
1205 if self.deprecated is not None: 1205 ↛ 1206line 1205 didn't jump to line 1206 because the condition on line 1205 was never true
1206 base["deprecated"] = self.deprecated
1207 if self.lineno is not None:
1208 base["lineno"] = self.lineno
1209 if self.endlineno is not None:
1210 base["endlineno"] = self.endlineno
1211 if self.docstring:
1212 base["docstring"] = self.docstring
1213 if self.type_parameters:
1214 base["type_parameters"] = [type_param.as_dict(**kwargs) for type_param in self.type_parameters]
1215 if self.labels:
1216 base["labels"] = self.labels
1217 if self.members:
1218 base["members"] = {name: member.as_dict(full=full, **kwargs) for name, member in self.members.items()}
1219 # TODO: Include `self.extra`?
1221 if full:
1222 base.update(
1223 {
1224 "path": self.path,
1225 "filepath": self.filepath,
1226 "relative_filepath": self.relative_filepath,
1227 "relative_package_filepath": self.relative_package_filepath,
1228 "is_public": self.is_public,
1229 "is_deprecated": self.is_deprecated,
1230 "is_private": self.is_private,
1231 "is_class_private": self.is_class_private,
1232 "is_special": self.is_special,
1233 "is_imported": self.is_imported,
1234 "is_exported": self.is_exported,
1235 "is_wildcard_exposed": self.is_wildcard_exposed,
1236 # TODO: Add these properties?
1237 # "is_alias": self.is_alias,
1238 # "is_collection": self.is_collection,
1239 # "is_module": self.is_module,
1240 # "is_class": self.is_class,
1241 # "is_function": self.is_function,
1242 # "is_attribute": self.is_attribute,
1243 # "is_type_alias": self.is_type_alias,
1244 # "is_init_module": self.is_init_module,
1245 # "is_package": self.is_package,
1246 # "is_subpackage": self.is_subpackage,
1247 # "is_namespace_package": self.is_namespace_package,
1248 # "is_namespace_subpackage": self.is_namespace_subpackage,
1249 # "has_docstring": self.has_docstring,
1250 # "has_docstrings": self.has_docstrings,
1251 },
1252 )
1254 return base
1257class Alias(ObjectAliasMixin):
1258 """This class represents an alias, or indirection, to an object declared in another module.
1260 Aliases represent objects that are in the scope of a module or class,
1261 but were imported from another module.
1263 They behave almost exactly like regular objects, to a few exceptions:
1265 - line numbers are those of the alias, not the target
1266 - the path is the alias path, not the canonical one
1267 - the name can be different from the target's
1268 - if the target can be resolved, the kind is the target's kind
1269 - if the target cannot be resolved, the kind becomes [Kind.ALIAS][griffe.Kind]
1270 """
1272 is_alias: bool = True
1273 """Always true for aliases."""
1274 is_collection: bool = False
1275 """Always false for aliases.
1277 See also: [`ModulesCollection`][griffe.ModulesCollection].
1278 """
1280 def __init__(
1281 self,
1282 name: str,
1283 target: str | Object | Alias,
1284 *,
1285 lineno: int | None = None,
1286 endlineno: int | None = None,
1287 runtime: bool = True,
1288 parent: Module | Class | Alias | None = None,
1289 inherited: bool = False,
1290 ) -> None:
1291 """Initialize the alias.
1293 Parameters:
1294 name: The alias name.
1295 target: If it's a string, the target resolution is delayed until accessing the target property.
1296 If it's an object, or even another alias, the target is immediately set.
1297 lineno: The alias starting line number.
1298 endlineno: The alias ending line number.
1299 runtime: Whether this alias is present at runtime or not.
1300 parent: The alias parent.
1301 inherited: Whether this alias wraps an inherited member.
1302 """
1303 self.name: str = name
1304 """The alias name."""
1306 self.alias_lineno: int | None = lineno
1307 """The starting line number of the alias."""
1309 self.alias_endlineno: int | None = endlineno
1310 """The ending line number of the alias."""
1312 self.runtime: bool = runtime
1313 """Whether this alias is available at runtime."""
1315 self.inherited: bool = inherited
1316 """Whether this alias represents an inherited member."""
1318 self.public: bool | None = None
1319 """Whether this alias is public."""
1321 self.deprecated: str | bool | None = None
1322 """Whether this alias is deprecated (boolean or deprecation message)."""
1324 self._parent: Module | Class | Alias | None = parent
1325 self._passed_through: bool = False
1327 self.target_path: str
1328 """The path of this alias' target."""
1330 if isinstance(target, str):
1331 self._target: Object | Alias | None = None
1332 self.target_path = target
1333 else:
1334 self._target = target
1335 self.target_path = target.path
1336 self._update_target_aliases()
1338 def __repr__(self) -> str:
1339 return f"Alias({self.name!r}, {self.target_path!r})"
1341 # Prevent using `__len__`.
1342 def __bool__(self) -> bool:
1343 """An alias is always true-ish."""
1344 return True
1346 def __len__(self) -> int:
1347 """The length of an alias is always 1."""
1348 return 1
1350 # SPECIAL PROXIES -------------------------------
1351 # The following methods and properties exist on the target(s),
1352 # but we must handle them in a special way.
1354 @property
1355 def kind(self) -> Kind:
1356 """The target's kind, or `Kind.ALIAS` if the target cannot be resolved.
1358 See also: [`is_kind`][griffe.Alias.is_kind].
1359 """
1360 # custom behavior to avoid raising exceptions
1361 try:
1362 return self.final_target.kind
1363 except (AliasResolutionError, CyclicAliasError):
1364 return Kind.ALIAS
1366 @property
1367 def has_docstring(self) -> bool:
1368 """Whether this alias' target has a non-empty docstring.
1370 See also: [`has_docstrings`][griffe.Alias.has_docstrings],
1371 [`docstring`][griffe.Alias.docstring].
1372 """
1373 try:
1374 return self.final_target.has_docstring
1375 except (AliasResolutionError, CyclicAliasError):
1376 return False
1378 @property
1379 def has_docstrings(self) -> bool:
1380 """Whether this alias' target or any of its members has a non-empty docstring.
1382 See also: [`has_docstring`][griffe.Alias.has_docstring],
1383 [`docstring`][griffe.Alias.docstring].
1384 """
1385 try:
1386 return self.final_target.has_docstrings
1387 except (AliasResolutionError, CyclicAliasError):
1388 return False
1390 @property
1391 def parent(self) -> Module | Class | Alias | None:
1392 """The parent of this alias."""
1393 return self._parent
1395 @parent.setter
1396 def parent(self, value: Module | Class | Alias) -> None:
1397 self._parent = value
1398 self._update_target_aliases()
1400 @property
1401 def path(self) -> str:
1402 """The dotted path / import path of this object.
1404 See also: [`canonical_path`][griffe.Alias.canonical_path].
1405 """
1406 return f"{self.parent.path}.{self.name}" # type: ignore[union-attr]
1408 @property
1409 def modules_collection(self) -> ModulesCollection:
1410 """The modules collection attached to the alias parents."""
1411 # No need to forward to the target.
1412 return self.parent.modules_collection # type: ignore[union-attr]
1414 @property
1415 def members(self) -> dict[str, Object | Alias]:
1416 """The target's members (modules, classes, functions, attributes, type aliases).
1418 See also: [`inherited_members`][griffe.Alias.inherited_members],
1419 [`get_member`][griffe.Alias.get_member],
1420 [`set_member`][griffe.Alias.set_member],
1421 [`filter_members`][griffe.Alias.filter_members].
1422 """
1423 final_target = self.final_target
1425 # We recreate aliases to maintain a correct hierarchy,
1426 # and therefore correct paths. The path of an alias member
1427 # should be the path of the alias plus the member's name,
1428 # not the original member's path.
1429 return {
1430 name: Alias(name, target=member, parent=self, inherited=False)
1431 for name, member in final_target.members.items()
1432 }
1434 @property
1435 def inherited_members(self) -> dict[str, Alias]:
1436 """Members that are inherited from base classes.
1438 Each inherited member of the target will be wrapped in an alias,
1439 to preserve correct object access paths.
1441 This method is part of the consumer API:
1442 do not use when producing Griffe trees!
1444 See also: [`members`][griffe.Alias.members].
1445 """
1446 final_target = self.final_target
1448 # We recreate aliases to maintain a correct hierarchy,
1449 # and therefore correct paths. The path of an alias member
1450 # should be the path of the alias plus the member's name,
1451 # not the original member's path.
1452 return {
1453 name: Alias(name, target=member, parent=self, inherited=True)
1454 for name, member in final_target.inherited_members.items()
1455 }
1457 def as_json(self, *, full: bool = False, **kwargs: Any) -> str:
1458 """Return this target's data as a JSON string.
1460 See also: [`as_dict`][griffe.Alias.as_dict].
1462 Parameters:
1463 full: Whether to return full info, or just base info.
1464 **kwargs: Additional serialization options passed to encoder.
1466 Returns:
1467 A JSON string.
1468 """
1469 try:
1470 return self.final_target.as_json(full=full, **kwargs)
1471 except (AliasResolutionError, CyclicAliasError):
1472 return super().as_json(full=full, **kwargs)
1474 # GENERIC OBJECT PROXIES --------------------------------
1475 # The following methods and properties exist on the target(s).
1476 # We first try to reach the final target, triggering alias resolution errors
1477 # and cyclic aliases errors early. We avoid recursing in the alias chain.
1479 @property
1480 def extra(self) -> dict:
1481 """Namespaced dictionaries storing extra metadata for this object, used by extensions."""
1482 return self.final_target.extra
1484 @property
1485 def lineno(self) -> int | None:
1486 """The starting line number of the target object.
1488 See also: [`endlineno`][griffe.Alias.endlineno].
1489 """
1490 return self.final_target.lineno
1492 @lineno.setter
1493 def lineno(self, lineno: int | None) -> None:
1494 self.final_target.lineno = lineno
1496 @property
1497 def endlineno(self) -> int | None:
1498 """The ending line number of the target object.
1500 See also: [`lineno`][griffe.Alias.lineno].
1501 """
1502 return self.final_target.endlineno
1504 @endlineno.setter
1505 def endlineno(self, endlineno: int | None) -> None:
1506 self.final_target.endlineno = endlineno
1508 @property
1509 def docstring(self) -> Docstring | None:
1510 """The target docstring.
1512 See also: [`has_docstring`][griffe.Alias.has_docstring],
1513 [`has_docstrings`][griffe.Alias.has_docstrings].
1514 """
1515 return self.final_target.docstring
1517 @docstring.setter
1518 def docstring(self, docstring: Docstring | None) -> None:
1519 self.final_target.docstring = docstring
1521 @property
1522 def type_parameters(self) -> TypeParameters:
1523 """The target type parameters."""
1524 return self.final_target.type_parameters
1526 @property
1527 def labels(self) -> set[str]:
1528 """The target labels (`property`, `dataclass`, etc.).
1530 See also: [`has_labels`][griffe.Alias.has_labels].
1531 """
1532 return self.final_target.labels
1534 @property
1535 def imports(self) -> dict[str, str]:
1536 """The other objects imported by this alias' target.
1538 Keys are the names within the object (`from ... import ... as AS_NAME`),
1539 while the values are the actual names of the objects (`from ... import REAL_NAME as ...`).
1541 See also: [`is_imported`][griffe.Alias.is_imported].
1542 """
1543 return self.final_target.imports
1545 @property
1546 def exports(self) -> list[str | ExprName] | None:
1547 """The names of the objects exported by this (module) object through the `__all__` variable.
1549 Exports can contain string (object names) or resolvable names,
1550 like other lists of exports coming from submodules:
1552 ```python
1553 from .submodule import __all__ as submodule_all
1555 __all__ = ["hello", *submodule_all]
1556 ```
1558 Exports get expanded by the loader before it expands wildcards and resolves aliases.
1560 See also: [`GriffeLoader.expand_exports`][griffe.GriffeLoader.expand_exports].
1561 """
1562 return self.final_target.exports
1564 @property
1565 def aliases(self) -> dict[str, Alias]:
1566 """The aliases pointing to this object."""
1567 return self.final_target.aliases
1569 def is_kind(self, kind: str | Kind | set[str | Kind]) -> bool:
1570 """Tell if this object is of the given kind.
1572 See also: [`is_module`][griffe.Alias.is_module],
1573 [`is_class`][griffe.Alias.is_class],
1574 [`is_function`][griffe.Alias.is_function],
1575 [`is_attribute`][griffe.Alias.is_attribute],
1576 [`is_type_alias`][griffe.Alias.is_type_alias],
1577 [`is_alias`][griffe.Alias.is_alias].
1579 Parameters:
1580 kind: An instance or set of kinds (strings or enumerations).
1582 Raises:
1583 ValueError: When an empty set is given as argument.
1585 Returns:
1586 True or False.
1587 """
1588 return self.final_target.is_kind(kind)
1590 @property
1591 def is_module(self) -> bool:
1592 """Whether this object is a module.
1594 See also: [`is_init_module`][griffe.Alias.is_init_module].
1595 [`is_class`][griffe.Alias.is_class],
1596 [`is_function`][griffe.Alias.is_function],
1597 [`is_attribute`][griffe.Alias.is_attribute],
1598 [`is_type_alias`][griffe.Alias.is_type_alias],
1599 [`is_alias`][griffe.Alias.is_alias],
1600 [`is_kind`][griffe.Alias.is_kind].
1601 """
1602 return self.final_target.is_module
1604 @property
1605 def is_class(self) -> bool:
1606 """Whether this object is a class.
1608 See also: [`is_module`][griffe.Alias.is_module],
1609 [`is_function`][griffe.Alias.is_function],
1610 [`is_attribute`][griffe.Alias.is_attribute],
1611 [`is_type_alias`][griffe.Alias.is_type_alias],
1612 [`is_alias`][griffe.Alias.is_alias],
1613 [`is_kind`][griffe.Alias.is_kind].
1614 """
1615 return self.final_target.is_class
1617 @property
1618 def is_function(self) -> bool:
1619 """Whether this object is a function.
1621 See also: [`is_module`][griffe.Alias.is_module],
1622 [`is_class`][griffe.Alias.is_class],
1623 [`is_attribute`][griffe.Alias.is_attribute],
1624 [`is_type_alias`][griffe.Alias.is_type_alias],
1625 [`is_alias`][griffe.Alias.is_alias],
1626 [`is_kind`][griffe.Alias.is_kind].
1627 """
1628 return self.final_target.is_function
1630 @property
1631 def is_attribute(self) -> bool:
1632 """Whether this object is an attribute.
1634 See also: [`is_module`][griffe.Alias.is_module],
1635 [`is_class`][griffe.Alias.is_class],
1636 [`is_function`][griffe.Alias.is_function],
1637 [`is_type_alias`][griffe.Alias.is_type_alias],
1638 [`is_alias`][griffe.Alias.is_alias],
1639 [`is_kind`][griffe.Alias.is_kind].
1640 """
1641 return self.final_target.is_attribute
1643 @property
1644 def is_type_alias(self) -> bool:
1645 """Whether this object is a type alias.
1647 See also: [`is_module`][griffe.Alias.is_module],
1648 [`is_class`][griffe.Alias.is_class],
1649 [`is_function`][griffe.Alias.is_function],
1650 [`is_attribute`][griffe.Alias.is_attribute],
1651 [`is_alias`][griffe.Alias.is_alias],
1652 [`is_kind`][griffe.Alias.is_kind].
1653 """
1654 return self.final_target.is_type_alias
1656 def has_labels(self, *labels: str) -> bool:
1657 """Tell if this object has all the given labels.
1659 See also: [`labels`][griffe.Alias.labels].
1661 Parameters:
1662 *labels: Labels that must be present.
1664 Returns:
1665 True or False.
1666 """
1667 return self.final_target.has_labels(*labels)
1669 def filter_members(self, *predicates: Callable[[Object | Alias], bool]) -> dict[str, Object | Alias]:
1670 """Filter and return members based on predicates.
1672 See also: [`members`][griffe.Alias.members],
1673 [`get_member`][griffe.Alias.get_member],
1674 [`set_member`][griffe.Alias.set_member].
1676 Parameters:
1677 *predicates: A list of predicates, i.e. callables accepting a member as argument and returning a boolean.
1679 Returns:
1680 A dictionary of members.
1681 """
1682 return self.final_target.filter_members(*predicates)
1684 @property
1685 def module(self) -> Module:
1686 """The parent module of this object.
1688 See also: [`package`][griffe.Alias.package].
1690 Raises:
1691 ValueError: When the object is not a module and does not have a parent.
1692 """
1693 return self.final_target.module
1695 @property
1696 def package(self) -> Module:
1697 """The absolute top module (the package) of this object.
1699 See also: [`module`][griffe.Alias.module].
1700 """
1701 return self.final_target.package
1703 @property
1704 def filepath(self) -> Path | list[Path]:
1705 """The file path (or directory list for namespace packages) where this object was defined.
1707 See also: [`relative_filepath`][griffe.Alias.relative_filepath],
1708 [`relative_package_filepath`][griffe.Alias.relative_package_filepath].
1709 """
1710 return self.final_target.filepath
1712 @property
1713 def relative_filepath(self) -> Path:
1714 """The file path where this object was defined, relative to the current working directory.
1716 If this object's file path is not relative to the current working directory, return its absolute path.
1718 See also: [`filepath`][griffe.Alias.filepath],
1719 [`relative_package_filepath`][griffe.Alias.relative_package_filepath].
1721 Raises:
1722 ValueError: When the relative path could not be computed.
1723 """
1724 return self.final_target.relative_filepath
1726 @property
1727 def relative_package_filepath(self) -> Path:
1728 """The file path where this object was defined, relative to the top module path.
1730 See also: [`filepath`][griffe.Alias.filepath],
1731 [`relative_filepath`][griffe.Alias.relative_filepath].
1733 Raises:
1734 ValueError: When the relative path could not be computed.
1735 """
1736 return self.final_target.relative_package_filepath
1738 @property
1739 def canonical_path(self) -> str:
1740 """The full dotted path of this object.
1742 The canonical path is the path where the object was defined (not imported).
1744 See also: [`path`][griffe.Alias.path].
1745 """
1746 return self.final_target.canonical_path
1748 @property
1749 def lines_collection(self) -> LinesCollection:
1750 """The lines collection attached to this object or its parents.
1752 See also: [`lines`][griffe.Alias.lines],
1753 [`source`][griffe.Alias.source].
1755 Raises:
1756 ValueError: When no modules collection can be found in the object or its parents.
1757 """
1758 return self.final_target.lines_collection
1760 @property
1761 def lines(self) -> list[str]:
1762 """The lines containing the source of this object.
1764 See also: [`source`][griffe.Alias.source],
1765 [`lines_collection`][griffe.Alias.lines_collection].
1766 """
1767 return self.final_target.lines
1769 @property
1770 def source(self) -> str:
1771 """The source code of this object.
1773 See also: [`lines`][griffe.Alias.lines],
1774 [`lines_collection`][griffe.Alias.lines_collection].
1775 """
1776 return self.final_target.source
1778 def resolve(self, name: str) -> str:
1779 """Resolve a name within this object's and parents' scope.
1781 Parameters:
1782 name: The name to resolve.
1784 Raises:
1785 NameResolutionError: When the name could not be resolved.
1787 Returns:
1788 The resolved name.
1789 """
1790 return self.final_target.resolve(name)
1792 # SPECIFIC MODULE/CLASS/FUNCTION/ATTRIBUTE/TYPE ALIAS PROXIES ---------------
1793 # These methods and properties exist on targets of specific kind.
1794 # We first try to reach the final target, triggering alias resolution errors
1795 # and cyclic aliases errors early. We avoid recursing in the alias chain.
1797 @property
1798 def _filepath(self) -> Path | list[Path] | None:
1799 return cast("Module", self.final_target)._filepath
1801 @property
1802 def bases(self) -> list[Expr | str]:
1803 """The class bases.
1805 See also: [`Class`][griffe.Class],
1806 [`resolved_bases`][griffe.Alias.resolved_bases],
1807 [`mro`][griffe.Alias.mro].
1808 """
1809 return cast("Class", self.final_target).bases
1811 @property
1812 def decorators(self) -> list[Decorator]:
1813 """The class/function decorators.
1815 See also: [`Function`][griffe.Function],
1816 [`Class`][griffe.Class].
1817 """
1818 return cast("Union[Class, Function]", self.target).decorators
1820 @property
1821 def imports_future_annotations(self) -> bool:
1822 """Whether this module import future annotations."""
1823 return cast("Module", self.final_target).imports_future_annotations
1825 @property
1826 def is_init_module(self) -> bool:
1827 """Whether this module is an `__init__.py` module.
1829 See also: [`is_module`][griffe.Alias.is_module].
1830 """
1831 return cast("Module", self.final_target).is_init_module
1833 @property
1834 def is_package(self) -> bool:
1835 """Whether this module is a package (top module).
1837 See also: [`is_subpackage`][griffe.Alias.is_subpackage].
1838 """
1839 return cast("Module", self.final_target).is_package
1841 @property
1842 def is_subpackage(self) -> bool:
1843 """Whether this module is a subpackage.
1845 See also: [`is_package`][griffe.Alias.is_package].
1846 """
1847 return cast("Module", self.final_target).is_subpackage
1849 @property
1850 def is_namespace_package(self) -> bool:
1851 """Whether this module is a namespace package (top folder, no `__init__.py`).
1853 See also: [`is_namespace_subpackage`][griffe.Alias.is_namespace_subpackage].
1854 """
1855 return cast("Module", self.final_target).is_namespace_package
1857 @property
1858 def is_namespace_subpackage(self) -> bool:
1859 """Whether this module is a namespace subpackage.
1861 See also: [`is_namespace_package`][griffe.Alias.is_namespace_package].
1862 """
1863 return cast("Module", self.final_target).is_namespace_subpackage
1865 @property
1866 def overloads(self) -> dict[str, list[Function]] | list[Function] | None:
1867 """The overloaded signatures declared in this class/module or for this function."""
1868 return cast("Union[Module, Class, Function]", self.final_target).overloads
1870 @overloads.setter
1871 def overloads(self, overloads: list[Function] | None) -> None:
1872 cast("Union[Module, Class, Function]", self.final_target).overloads = overloads
1874 @property
1875 def parameters(self) -> Parameters:
1876 """The parameters of the current function or `__init__` method for classes.
1878 This property can fetch inherited members,
1879 and therefore is part of the consumer API:
1880 do not use when producing Griffe trees!
1881 """
1882 return cast("Union[Class, Function]", self.final_target).parameters
1884 @property
1885 def returns(self) -> str | Expr | None:
1886 """The function return type annotation."""
1887 return cast("Function", self.final_target).returns
1889 @returns.setter
1890 def returns(self, returns: str | Expr | None) -> None:
1891 cast("Function", self.final_target).returns = returns
1893 @property
1894 def setter(self) -> Function | None:
1895 """The setter linked to this function (property)."""
1896 return cast("Attribute", self.final_target).setter
1898 @property
1899 def deleter(self) -> Function | None:
1900 """The deleter linked to this function (property)."""
1901 return cast("Attribute", self.final_target).deleter
1903 @property
1904 def value(self) -> str | Expr | None:
1905 """The attribute or type alias value."""
1906 return cast("Union[Attribute, TypeAlias]", self.final_target).value
1908 @value.setter
1909 def value(self, value: str | Expr | None) -> None:
1910 cast("Attribute", self.final_target).value = value
1912 @property
1913 def annotation(self) -> str | Expr | None:
1914 """The attribute type annotation."""
1915 return cast("Attribute", self.final_target).annotation
1917 @annotation.setter
1918 def annotation(self, annotation: str | Expr | None) -> None:
1919 cast("Attribute", self.final_target).annotation = annotation
1921 @property
1922 def resolved_bases(self) -> list[Object]:
1923 """Resolved class bases.
1925 This method is part of the consumer API:
1926 do not use when producing Griffe trees!
1927 """
1928 return cast("Class", self.final_target).resolved_bases
1930 def mro(self) -> list[Class]:
1931 """Return a list of classes in order corresponding to Python's MRO."""
1932 return cast("Class", self.final_target).mro()
1934 def signature(self, *, return_type: bool = False, name: str | None = None) -> str:
1935 """Construct the class/function signature.
1937 Parameters:
1938 return_type: Whether to include the return type in the signature.
1939 name: The name of the class/function to use in the signature.
1941 Returns:
1942 A string representation of the class/function signature.
1943 """
1944 return cast("Union[Class, Function]", self.final_target).signature(return_type=return_type, name=name)
1946 # SPECIFIC ALIAS METHOD AND PROPERTIES -----------------
1947 # These methods and properties do not exist on targets,
1948 # they are specific to aliases.
1950 @property
1951 def target(self) -> Object | Alias:
1952 """The resolved target (actual object), if possible.
1954 Upon accessing this property, if the target is not already resolved,
1955 a lookup is done using the modules collection to find the target.
1957 See also: [`final_target`][griffe.Alias.final_target],
1958 [`resolve_target`][griffe.Alias.resolve_target],
1959 [`resolved`][griffe.Alias.resolved].
1960 """
1961 if not self.resolved:
1962 self.resolve_target()
1963 return self._target # type: ignore[return-value]
1965 @target.setter
1966 def target(self, value: Object | Alias) -> None:
1967 if value is self or value.path == self.path:
1968 raise CyclicAliasError([self.target_path])
1969 self._target = value
1970 self.target_path = value.path
1971 if self.parent is not None:
1972 self._target.aliases[self.path] = self
1974 @property
1975 def final_target(self) -> Object:
1976 """The final, resolved target, if possible.
1978 This will iterate through the targets until a non-alias object is found.
1980 See also: [`target`][griffe.Alias.target],
1981 [`resolve_target`][griffe.Alias.resolve_target],
1982 [`resolved`][griffe.Alias.resolved].
1983 """
1984 # Here we quickly iterate on the alias chain,
1985 # remembering which path we've seen already to detect cycles.
1987 # The cycle detection is needed because alias chains can be created
1988 # as already resolved, and can contain cycles.
1990 # Using a dict as an ordered set.
1991 paths_seen: dict[str, None] = {}
1992 target = self
1993 while target.is_alias:
1994 if target.path in paths_seen: 1994 ↛ 1995line 1994 didn't jump to line 1995 because the condition on line 1994 was never true
1995 raise CyclicAliasError([*paths_seen, target.path])
1996 paths_seen[target.path] = None
1997 target = target.target # type: ignore[assignment]
1998 return target # type: ignore[return-value]
2000 def resolve_target(self) -> None:
2001 """Resolve the target.
2003 See also: [`target`][griffe.Alias.target],
2004 [`final_target`][griffe.Alias.final_target],
2005 [`resolved`][griffe.Alias.resolved].
2007 Raises:
2008 AliasResolutionError: When the target cannot be resolved.
2009 It happens when the target does not exist,
2010 or could not be loaded (unhandled dynamic object?),
2011 or when the target is from a module that was not loaded
2012 and added to the collection.
2013 CyclicAliasError: When the resolved target is the alias itself.
2014 """
2015 # Here we try to resolve the whole alias chain recursively.
2016 # We detect cycles by setting a "passed through" state variable
2017 # on each alias as we pass through it. Passing a second time
2018 # through an alias will raise a CyclicAliasError.
2020 # If a single link of the chain cannot be resolved,
2021 # the whole chain stays unresolved. This prevents
2022 # bad surprises later, in code that checks if
2023 # an alias is resolved by checking only
2024 # the first link of the chain.
2025 if self._passed_through: 2025 ↛ 2026line 2025 didn't jump to line 2026 because the condition on line 2025 was never true
2026 raise CyclicAliasError([self.target_path])
2027 self._passed_through = True
2028 try:
2029 self._resolve_target()
2030 finally:
2031 self._passed_through = False
2033 def _resolve_target(self) -> None:
2034 try:
2035 resolved = self.modules_collection.get_member(self.target_path)
2036 except KeyError as error:
2037 raise AliasResolutionError(self) from error
2038 if resolved is self: 2038 ↛ 2039line 2038 didn't jump to line 2039 because the condition on line 2038 was never true
2039 raise CyclicAliasError([self.target_path])
2040 if resolved.is_alias and not resolved.resolved:
2041 try:
2042 resolved.resolve_target()
2043 except CyclicAliasError as error:
2044 raise CyclicAliasError([self.target_path, *error.chain]) from error
2045 self._target = resolved
2046 if self.parent is not None: 2046 ↛ exitline 2046 didn't return from function '_resolve_target' because the condition on line 2046 was always true
2047 self._target.aliases[self.path] = self
2049 def _update_target_aliases(self) -> None:
2050 with suppress(AttributeError, AliasResolutionError, CyclicAliasError):
2051 self._target.aliases[self.path] = self # type: ignore[union-attr]
2053 @property
2054 def resolved(self) -> bool:
2055 """Whether this alias' target is resolved."""
2056 return self._target is not None
2058 @property
2059 def wildcard(self) -> str | None:
2060 """The module on which the wildcard import is performed (if any).
2062 See also: [`GriffeLoader.expand_wildcards`][griffe.GriffeLoader.expand_wildcards].
2063 """
2064 if self.name.endswith("/*"):
2065 return self.target_path
2066 return None
2068 def as_dict(self, *, full: bool = False, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002
2069 """Return this alias' data as a dictionary.
2071 See also: [`as_json`][griffe.Alias.as_json].
2073 Parameters:
2074 full: Whether to return full info, or just base info.
2075 **kwargs: Additional serialization options.
2077 Returns:
2078 A dictionary.
2079 """
2080 base: dict[str, Any] = {
2081 "kind": Kind.ALIAS,
2082 "name": self.name,
2083 "target_path": self.target_path,
2084 "runtime": self.runtime,
2085 "inherited": self.inherited,
2086 }
2088 if self.public is not None: 2088 ↛ 2089line 2088 didn't jump to line 2089 because the condition on line 2088 was never true
2089 base["public"] = self.public
2090 if self.deprecated is not None: 2090 ↛ 2091line 2090 didn't jump to line 2091 because the condition on line 2090 was never true
2091 base["deprecated"] = self.deprecated
2092 if self.alias_lineno: 2092 ↛ 2094line 2092 didn't jump to line 2094 because the condition on line 2092 was always true
2093 base["lineno"] = self.alias_lineno
2094 if self.alias_endlineno: 2094 ↛ 2097line 2094 didn't jump to line 2097 because the condition on line 2094 was always true
2095 base["endlineno"] = self.alias_endlineno
2097 if full:
2098 base.update(
2099 {
2100 "path": self.path,
2101 "is_public": self.is_public,
2102 "is_deprecated": self.is_deprecated,
2103 "is_private": self.is_private,
2104 "is_class_private": self.is_class_private,
2105 "is_special": self.is_special,
2106 "is_imported": self.is_imported,
2107 "is_exported": self.is_exported,
2108 "is_wildcard_exposed": self.is_wildcard_exposed,
2109 },
2110 )
2112 return base
2115class Module(Object):
2116 """The class representing a Python module."""
2118 kind = Kind.MODULE
2120 def __init__(self, *args: Any, filepath: Path | list[Path] | None = None, **kwargs: Any) -> None:
2121 """Initialize the module.
2123 Parameters:
2124 *args: See [`griffe.Object`][].
2125 filepath: The module file path (directory for namespace [sub]packages, none for builtin modules).
2126 **kwargs: See [`griffe.Object`][].
2127 """
2128 super().__init__(*args, **kwargs)
2129 self._filepath: Path | list[Path] | None = filepath
2130 self.overloads: dict[str, list[Function]] = defaultdict(list)
2131 """The overloaded signatures declared in this module."""
2133 def __repr__(self) -> str:
2134 try:
2135 return f"Module({self.filepath!r})"
2136 except BuiltinModuleError:
2137 return f"Module({self.name!r})"
2139 @property
2140 def filepath(self) -> Path | list[Path]:
2141 """The file path of this module.
2143 Raises:
2144 BuiltinModuleError: When the instance filepath is None.
2145 """
2146 if self._filepath is None:
2147 raise BuiltinModuleError(self.name)
2148 return self._filepath
2150 @property
2151 def imports_future_annotations(self) -> bool:
2152 """Whether this module import future annotations."""
2153 return (
2154 "annotations" in self.members
2155 and self.members["annotations"].is_alias
2156 and self.members["annotations"].target_path == "__future__.annotations" # type: ignore[union-attr]
2157 )
2159 @property
2160 def is_init_module(self) -> bool:
2161 """Whether this module is an `__init__.py` module.
2163 See also: [`is_module`][griffe.Module.is_module].
2164 """
2165 if isinstance(self.filepath, list): 2165 ↛ 2166line 2165 didn't jump to line 2166 because the condition on line 2165 was never true
2166 return False
2167 try:
2168 return self.filepath.name.split(".", 1)[0] == "__init__"
2169 except BuiltinModuleError:
2170 return False
2172 @property
2173 def is_package(self) -> bool:
2174 """Whether this module is a package (top module).
2176 See also: [`is_subpackage`][griffe.Module.is_subpackage].
2177 """
2178 return not bool(self.parent) and self.is_init_module
2180 @property
2181 def is_subpackage(self) -> bool:
2182 """Whether this module is a subpackage.
2184 See also: [`is_package`][griffe.Module.is_package].
2185 """
2186 return bool(self.parent) and self.is_init_module
2188 @property
2189 def is_namespace_package(self) -> bool:
2190 """Whether this module is a namespace package (top folder, no `__init__.py`).
2192 See also: [`is_namespace_subpackage`][griffe.Module.is_namespace_subpackage].
2193 """
2194 try:
2195 return self.parent is None and isinstance(self.filepath, list)
2196 except BuiltinModuleError:
2197 return False
2199 @property
2200 def is_namespace_subpackage(self) -> bool:
2201 """Whether this module is a namespace subpackage.
2203 See also: [`is_namespace_package`][griffe.Module.is_namespace_package].
2204 """
2205 try:
2206 return (
2207 self.parent is not None
2208 and isinstance(self.filepath, list)
2209 and (
2210 cast("Module", self.parent).is_namespace_package
2211 or cast("Module", self.parent).is_namespace_subpackage
2212 )
2213 )
2214 except BuiltinModuleError:
2215 return False
2217 def as_dict(self, **kwargs: Any) -> dict[str, Any]:
2218 """Return this module's data as a dictionary.
2220 See also: [`as_json`][griffe.Module.as_json].
2222 Parameters:
2223 **kwargs: Additional serialization options.
2225 Returns:
2226 A dictionary.
2227 """
2228 base = super().as_dict(**kwargs)
2229 if isinstance(self._filepath, list): 2229 ↛ 2230line 2229 didn't jump to line 2230 because the condition on line 2229 was never true
2230 base["filepath"] = [str(path) for path in self._filepath]
2231 elif self._filepath: 2231 ↛ 2234line 2231 didn't jump to line 2234 because the condition on line 2231 was always true
2232 base["filepath"] = str(self._filepath)
2233 else:
2234 base["filepath"] = None
2235 return base
2238class Class(Object):
2239 """The class representing a Python class."""
2241 kind = Kind.CLASS
2243 def __init__(
2244 self,
2245 *args: Any,
2246 bases: Sequence[Expr | str] | None = None,
2247 decorators: list[Decorator] | None = None,
2248 **kwargs: Any,
2249 ) -> None:
2250 """Initialize the class.
2252 Parameters:
2253 *args: See [`griffe.Object`][].
2254 bases: The list of base classes, if any.
2255 decorators: The class decorators, if any.
2256 **kwargs: See [`griffe.Object`][].
2257 """
2258 super().__init__(*args, **kwargs)
2260 self.bases: list[Expr | str] = list(bases) if bases else []
2261 """The class bases.
2263 See also: [`resolved_bases`][griffe.Class.resolved_bases],
2264 [`mro`][griffe.Class.mro].
2265 """
2267 self.decorators: list[Decorator] = decorators or []
2268 """The class decorators."""
2270 self.overloads: dict[str, list[Function]] = defaultdict(list)
2271 """The overloaded signatures declared in this class."""
2273 @property
2274 def parameters(self) -> Parameters:
2275 """The parameters of this class' `__init__` method, if any.
2277 This property fetches inherited members,
2278 and therefore is part of the consumer API:
2279 do not use when producing Griffe trees!
2280 """
2281 try:
2282 return self.all_members["__init__"].parameters # type: ignore[union-attr]
2283 except KeyError:
2284 return Parameters()
2286 def signature(self, *, return_type: bool = False, name: str | None = None) -> str:
2287 """Construct the class signature.
2289 Parameters:
2290 return_type: Whether to include the return type in the signature.
2291 name: The name of the class to use in the signature.
2293 Returns:
2294 A string representation of the class signature.
2295 """
2296 all_members = self.all_members
2297 if "__init__" in all_members: 2297 ↛ 2301line 2297 didn't jump to line 2301 because the condition on line 2297 was always true
2298 init = all_members["__init__"]
2299 if isinstance(init, Function): 2299 ↛ 2301line 2299 didn't jump to line 2301 because the condition on line 2299 was always true
2300 return init.signature(return_type=return_type, name=name or self.name)
2301 return ""
2303 @property
2304 def resolved_bases(self) -> list[Object]:
2305 """Resolved class bases.
2307 This method is part of the consumer API:
2308 do not use when producing Griffe trees!
2310 See also: [`bases`][griffe.Class.bases],
2311 [`mro`][griffe.Class.mro].
2312 """
2313 resolved_bases = []
2314 for base in self.bases:
2315 base_path = base if isinstance(base, str) else base.canonical_path
2316 try:
2317 resolved_base = self.modules_collection.get_member(base_path)
2318 if resolved_base.is_alias:
2319 resolved_base = resolved_base.final_target
2320 except (AliasResolutionError, CyclicAliasError, KeyError):
2321 logger.debug("Base class %s is not loaded, or not static, it cannot be resolved", base_path)
2322 else:
2323 resolved_bases.append(resolved_base)
2324 return resolved_bases
2326 def _mro(self, seen: tuple[str, ...] = ()) -> list[Class]:
2327 seen = (*seen, self.path)
2328 bases: list[Class] = [base for base in self.resolved_bases if base.is_class] # type: ignore[misc]
2329 if not bases:
2330 return [self]
2331 for base in bases:
2332 if base.path in seen:
2333 cycle = " -> ".join(seen) + f" -> {base.path}"
2334 raise ValueError(f"Cannot compute C3 linearization, inheritance cycle detected: {cycle}")
2335 return [self, *c3linear_merge(*[base._mro(seen) for base in bases], bases)]
2337 def mro(self) -> list[Class]:
2338 """Return a list of classes in order corresponding to Python's MRO.
2340 See also: [`bases`][griffe.Class.bases],
2341 [`resolved_bases`][griffe.Class.resolved_bases].
2342 """
2343 return self._mro()[1:] # Remove self.
2345 def as_dict(self, **kwargs: Any) -> dict[str, Any]:
2346 """Return this class' data as a dictionary.
2348 See also: [`as_json`][griffe.Class.as_json].
2350 Parameters:
2351 **kwargs: Additional serialization options.
2353 Returns:
2354 A dictionary.
2355 """
2356 base = super().as_dict(**kwargs)
2357 base["bases"] = self.bases
2358 base["decorators"] = [dec.as_dict(**kwargs) for dec in self.decorators]
2359 return base
2362class Function(Object):
2363 """The class representing a Python function."""
2365 kind = Kind.FUNCTION
2367 def __init__(
2368 self,
2369 *args: Any,
2370 parameters: Parameters | None = None,
2371 returns: str | Expr | None = None,
2372 decorators: list[Decorator] | None = None,
2373 **kwargs: Any,
2374 ) -> None:
2375 """Initialize the function.
2377 Parameters:
2378 *args: See [`griffe.Object`][].
2379 parameters: The function parameters.
2380 returns: The function return annotation.
2381 decorators: The function decorators, if any.
2382 **kwargs: See [`griffe.Object`][].
2383 """
2384 super().__init__(*args, **kwargs)
2385 self.parameters: Parameters = parameters or Parameters()
2386 """The function parameters."""
2387 self.returns: str | Expr | None = returns
2388 """The function return type annotation."""
2389 self.decorators: list[Decorator] = decorators or []
2390 """The function decorators."""
2391 self.overloads: list[Function] | None = None
2392 """The overloaded signatures of this function."""
2394 for parameter in self.parameters:
2395 parameter.function = self
2397 @property
2398 def annotation(self) -> str | Expr | None:
2399 """The type annotation of the returned value."""
2400 return self.returns
2402 def resolve(self, name: str) -> str:
2403 """Resolve a name within this object's and parents' scope.
2405 Parameters:
2406 name: The name to resolve.
2408 Raises:
2409 NameResolutionError: When the name could not be resolved.
2411 Returns:
2412 The resolved name.
2413 """
2414 # We're in an `__init__` method...
2415 if self.parent and self.name == "__init__":
2416 # ...and name is a parameter name: resolve to the parameter.
2417 if name in self.parameters:
2418 return f"{self.parent.path}({name})"
2420 # Kind of a special case: we avoid resolving to instance-attributes from a function scope.
2421 # See issue https://github.com/mkdocstrings/griffe/issues/367.
2422 resolved = super().resolve(name)
2423 try:
2424 obj = self.modules_collection.get_member(resolved)
2425 except KeyError:
2426 return resolved
2427 try:
2428 if obj.is_attribute and "instance-attribute" in obj.labels: 2428 ↛ 2432line 2428 didn't jump to line 2432 because the condition on line 2428 was always true
2429 raise NameResolutionError(name)
2430 except AliasResolutionError:
2431 pass
2432 return resolved
2433 return super().resolve(name)
2435 def as_dict(self, **kwargs: Any) -> dict[str, Any]:
2436 """Return this function's data as a dictionary.
2438 See also: [`as_json`][griffe.Function.as_json].
2440 Parameters:
2441 **kwargs: Additional serialization options.
2443 Returns:
2444 A dictionary.
2445 """
2446 base = super().as_dict(**kwargs)
2447 base["decorators"] = [dec.as_dict(**kwargs) for dec in self.decorators]
2448 base["parameters"] = [param.as_dict(**kwargs) for param in self.parameters]
2449 base["returns"] = self.returns
2450 return base
2452 def signature(self, *, return_type: bool = True, name: str | None = None) -> str:
2453 """Construct the function signature.
2455 Parameters:
2456 return_type: Whether to include the return type in the signature.
2457 name: The name of the function to use in the signature.
2459 Returns:
2460 A string representation of the function signature.
2461 """
2462 signature = f"{name or self.name}("
2464 has_pos_only = any(p.kind == ParameterKind.positional_only for p in self.parameters)
2465 render_pos_only_separator = True
2466 render_kw_only_separator = True
2468 param_strs = []
2470 for index, param in enumerate(self.parameters):
2471 # Skip 'self' or 'cls' for class methods if it's the first parameter.
2472 if index == 0 and param.name in ("self", "cls") and self.parent and self.parent.is_class: 2472 ↛ 2473line 2472 didn't jump to line 2473 because the condition on line 2472 was never true
2473 continue
2475 param_str = ""
2477 # Handle parameter kind and separators.
2478 if param.kind != ParameterKind.positional_only:
2479 if has_pos_only and render_pos_only_separator:
2480 render_pos_only_separator = False
2481 param_strs.append("/")
2483 if param.kind == ParameterKind.keyword_only and render_kw_only_separator: 2483 ↛ 2484line 2483 didn't jump to line 2484 because the condition on line 2483 was never true
2484 render_kw_only_separator = False
2485 param_strs.append("*")
2487 # Handle variadic parameters.
2488 if param.kind == ParameterKind.var_positional:
2489 param_str = "*"
2490 render_kw_only_separator = False
2491 elif param.kind == ParameterKind.var_keyword:
2492 param_str = "**"
2494 # Add parameter name.
2495 param_str += param.name
2497 # Handle type annotation
2498 if param.annotation is not None:
2499 param_str += f": {param.annotation}"
2500 equal = " = " # Space around equal when annotation is present.
2501 else:
2502 equal = "=" # No space when no annotation.
2504 # Handle default value.
2505 if param.default is not None and param.kind not in {
2506 ParameterKind.var_positional,
2507 ParameterKind.var_keyword,
2508 }:
2509 param_str += f"{equal}{param.default}"
2511 param_strs.append(param_str)
2513 # If we have positional-only parameters but no '/' was added yet
2514 if has_pos_only and render_pos_only_separator: 2514 ↛ 2515line 2514 didn't jump to line 2515 because the condition on line 2514 was never true
2515 param_strs.append("/")
2517 signature += ", ".join(param_strs)
2518 signature += ")"
2520 # Add return type if present.
2521 if return_type and self.annotation:
2522 signature += f" -> {self.annotation}"
2524 return signature
2527class Attribute(Object):
2528 """The class representing a Python module/class/instance attribute."""
2530 kind = Kind.ATTRIBUTE
2532 def __init__(
2533 self,
2534 *args: Any,
2535 value: str | Expr | None = None,
2536 annotation: str | Expr | None = None,
2537 **kwargs: Any,
2538 ) -> None:
2539 """Initialize the function.
2541 Parameters:
2542 *args: See [`griffe.Object`][].
2543 value: The attribute value, if any.
2544 annotation: The attribute annotation, if any.
2545 **kwargs: See [`griffe.Object`][].
2546 """
2547 super().__init__(*args, **kwargs)
2548 self.value: str | Expr | None = value
2549 """The attribute value."""
2550 self.annotation: str | Expr | None = annotation
2551 """The attribute type annotation."""
2552 self.setter: Function | None = None
2553 """The setter linked to this property."""
2554 self.deleter: Function | None = None
2555 """The deleter linked to this property."""
2557 def as_dict(self, **kwargs: Any) -> dict[str, Any]:
2558 """Return this attribute's data as a dictionary.
2560 See also: [`as_json`][griffe.Attribute.as_json].
2562 Parameters:
2563 **kwargs: Additional serialization options.
2565 Returns:
2566 A dictionary.
2567 """
2568 base = super().as_dict(**kwargs)
2569 if self.value is not None:
2570 base["value"] = self.value
2571 if self.annotation is not None:
2572 base["annotation"] = self.annotation
2573 return base
2576class TypeAlias(Object):
2577 """The class representing a Python type alias."""
2579 kind = Kind.TYPE_ALIAS
2581 def __init__(
2582 self,
2583 *args: Any,
2584 value: str | Expr | None = None,
2585 **kwargs: Any,
2586 ) -> None:
2587 """Initialize the function.
2589 Parameters:
2590 *args: See [`griffe.Object`][].
2591 value: The type alias value.
2592 **kwargs: See [`griffe.Object`][].
2593 """
2594 super().__init__(*args, **kwargs, runtime=False)
2595 self.value: str | Expr | None = value
2596 """The type alias value."""
2598 def as_dict(self, **kwargs: Any) -> dict[str, Any]:
2599 """Return this type alias's data as a dictionary.
2601 Parameters:
2602 **kwargs: Additional serialization options.
2604 Returns:
2605 A dictionary.
2606 """
2607 base = super().as_dict(**kwargs)
2608 base["value"] = self.value
2609 return base