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

1# This module contains our models definitions, 

2# to represent Python objects (and other aspects of Python APIs)... in Python. 

3 

4from __future__ import annotations 

5 

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 

13 

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 

21 

22if TYPE_CHECKING: 

23 from collections.abc import Callable, Sequence 

24 

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 

29 

30 

31from functools import cached_property 

32 

33 

34class Decorator: 

35 """This class represents decorators.""" 

36 

37 def __init__(self, value: str | Expr, *, lineno: int | None, endlineno: int | None) -> None: 

38 """Initialize the decorator. 

39 

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.""" 

51 

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 

57 

58 def as_dict(self, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002 

59 """Return this decorator's data as a dictionary. 

60 

61 Parameters: 

62 **kwargs: Additional serialization options. 

63 

64 Returns: 

65 A dictionary. 

66 """ 

67 return { 

68 "value": self.value, 

69 "lineno": self.lineno, 

70 "endlineno": self.endlineno, 

71 } 

72 

73 

74class Docstring: 

75 """This class represents docstrings.""" 

76 

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. 

88 

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`. 

99 

100 See also: [`source`][griffe.Docstring.source]. 

101 """ 

102 

103 self.lineno: int | None = lineno 

104 """The starting line number of the docstring. 

105 

106 See also: [`endlineno`][griffe.Docstring.endlineno].""" 

107 

108 self.endlineno: int | None = endlineno 

109 """The ending line number of the docstring. 

110 

111 See also: [`lineno`][griffe.Docstring.lineno].""" 

112 

113 self.parent: Object | None = parent 

114 """The object this docstring is attached to.""" 

115 

116 self.parser: DocstringStyle | Parser | None = parser 

117 """The selected docstring parser. 

118 

119 See also: [`parser_options`][griffe.Docstring.parser_options], 

120 [`parse`][griffe.Docstring.parse]. 

121 """ 

122 

123 self.parser_options: DocstringOptions = parser_options or {} 

124 """The configured parsing options. 

125 

126 See also: [`parser`][griffe.Docstring.parser], 

127 [`parse`][griffe.Docstring.parse]. 

128 """ 

129 

130 @property 

131 def lines(self) -> list[str]: 

132 """The lines of the docstring. 

133 

134 See also: [`source`][griffe.Docstring.source]. 

135 """ 

136 return self.value.split("\n") 

137 

138 @property 

139 def source(self) -> str: 

140 """The original, uncleaned value of the docstring as written in the source. 

141 

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. 

145 

146 Raises: 

147 ValueError: If the original docstring cannot be retrieved 

148 (no parent, no line numbers, or attached to namespace package). 

149 

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]) 

159 

160 @cached_property 

161 def parsed(self) -> list[DocstringSection]: 

162 """The docstring sections, parsed into structured data.""" 

163 return self.parse() 

164 

165 def parse( 

166 self, 

167 parser: DocstringStyle | Parser | None = None, 

168 **options: Any, 

169 ) -> list[DocstringSection]: 

170 """Parse the docstring into structured data. 

171 

172 See also: [`parser`][griffe.Docstring.parser], 

173 [`parser_options`][griffe.Docstring.parser_options]. 

174 

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. 

179 

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)) 

184 

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. 

192 

193 Parameters: 

194 full: Whether to return full info, or just base info. 

195 **kwargs: Additional serialization options. 

196 

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 

208 

209 

210class Parameter: # noqa: PLW1641 

211 """This class represent a function parameter. 

212 

213 See also: [`Parameters`][griffe.Parameters]. 

214 """ 

215 

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. 

226 

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.""" 

248 

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 

254 

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})" 

257 

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 ) 

268 

269 @property 

270 def required(self) -> bool: 

271 """Whether this parameter is required.""" 

272 return self.default is None 

273 

274 def as_dict(self, *, full: bool = False, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002 

275 """Return this parameter's data as a dictionary. 

276 

277 Parameters: 

278 **kwargs: Additional serialization options. 

279 

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 

292 

293 

294class Parameters: 

295 """This class is a container for parameters. 

296 

297 It allows to get parameters using their position (index) or their name: 

298 

299 ```pycon 

300 >>> parameters = Parameters(Parameter("hello")) 

301 >>> parameters[0] is parameters["hello"] 

302 True 

303 ``` 

304 

305 See also: [`Parameter`][griffe.Parameter]. 

306 """ 

307 

308 def __init__(self, *parameters: Parameter) -> None: 

309 """Initialize the parameters container. 

310 

311 Parameters: 

312 *parameters: The initial parameters to add to the container. 

313 """ 

314 self._params: list[Parameter] = list(parameters) 

315 

316 def __repr__(self) -> str: 

317 return f"Parameters({', '.join(repr(param) for param in self._params)})" 

318 

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 

328 

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 

341 

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] 

353 

354 def __len__(self): 

355 """The number of parameters.""" 

356 return len(self._params) 

357 

358 def __iter__(self): 

359 """Iterate over the parameters, in order.""" 

360 return iter(self._params) 

361 

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 

369 

370 def add(self, parameter: Parameter) -> None: 

371 """Add a parameter to the container. 

372 

373 Parameters: 

374 parameter: The function parameter to add. 

375 

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) 

382 

383 

384class TypeParameter: 

385 """This class represents a type parameter.""" 

386 

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. 

397 

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. 

406 

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") 

412 

413 self.name: str = name 

414 """The type parameter name.""" 

415 

416 self.kind: TypeParameterKind = kind 

417 """The type parameter kind.""" 

418 

419 self.annotation: str | Expr | None 

420 """The type parameter bound or constraints.""" 

421 

422 if constraints: 

423 self.constraints = constraints 

424 else: 

425 self.bound = bound 

426 

427 self.default: str | Expr | None = default 

428 """The type parameter default value.""" 

429 

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})" 

432 

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 

439 

440 @bound.setter 

441 def bound(self, bound: str | Expr | None) -> None: 

442 self.annotation = bound 

443 

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 

450 

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 

457 

458 def as_dict(self, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002 

459 """Return this type parameter's data as a dictionary. 

460 

461 Parameters: 

462 **kwargs: Additional serialization options. 

463 

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 

474 

475 

476class TypeParameters: 

477 """This class is a container for type parameters. 

478 

479 It allows to get type parameters using their position (index) or their name: 

480 

481 ```pycon 

482 >>> type_parameters = TypeParameters(TypeParameter("hello"), kind=TypeParameterKind.type_var) 

483 >>> type_parameters[0] is type_parameters["hello"] 

484 True 

485 ``` 

486 """ 

487 

488 def __init__(self, *type_parameters: TypeParameter) -> None: 

489 """Initialize the type parameters container. 

490 

491 Parameters: 

492 *type_parameters: The initial type parameters to add to the container. 

493 """ 

494 self._type_params: list[TypeParameter] = list(type_parameters) 

495 

496 def __repr__(self) -> str: 

497 return f"TypeParameters({', '.join(repr(type_param) for type_param in self._type_params)})" 

498 

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 

508 

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 

521 

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] 

533 

534 def __len__(self): 

535 """The number of type parameters.""" 

536 return len(self._type_params) 

537 

538 def __iter__(self): 

539 """Iterate over the type parameters, in order.""" 

540 return iter(self._type_params) 

541 

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 

549 

550 def add(self, type_parameter: TypeParameter) -> None: 

551 """Add a type parameter to the container. 

552 

553 Parameters: 

554 type_parameter: The function parameter to add. 

555 

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) 

562 

563 

564class Object(ObjectAliasMixin): 

565 """An abstract class representing a Python object.""" 

566 

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. 

575 

576 Only aliases can be marked as inherited. 

577 """ 

578 

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. 

595 

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.""" 

612 

613 self.lineno: int | None = lineno 

614 """The starting line number of the object. 

615 

616 See also: [`endlineno`][griffe.Object.endlineno]. 

617 """ 

618 

619 self.endlineno: int | None = endlineno 

620 """The ending line number of the object. 

621 

622 See also: [`lineno`][griffe.Object.lineno]. 

623 """ 

624 

625 self.docstring: Docstring | None = docstring 

626 """The object docstring. 

627 

628 See also: [`has_docstring`][griffe.Object.has_docstring], 

629 [`has_docstrings`][griffe.Object.has_docstrings]. 

630 """ 

631 

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.""" 

637 

638 self.parent: Module | Class | None = parent 

639 """The parent of the object (none if top module).""" 

640 

641 self.members: dict[str, Object | Alias] = {} 

642 """The object members (modules, classes, functions, attributes, type aliases). 

643 

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 """ 

649 

650 self.labels: set[str] = set() 

651 """The object labels (`property`, `dataclass`, etc.). 

652 

653 See also: [`has_labels`][griffe.Object.has_labels].""" 

654 

655 self.imports: dict[str, str] = {} 

656 """The other objects imported by this object. 

657 

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 """ 

661 

662 self.exports: list[str | ExprName] | None = None 

663 """The names of the objects exported by this (module) object through the `__all__` variable. 

664 

665 Exports can contain string (object names) or resolvable names, 

666 like other lists of exports coming from submodules: 

667 

668 ```python 

669 from .submodule import __all__ as submodule_all 

670 

671 __all__ = ["hello", *submodule_all] 

672 ``` 

673 

674 Exports get expanded by the loader before it expands wildcards and resolves aliases. 

675 

676 See also: [`GriffeLoader.expand_exports`][griffe.GriffeLoader.expand_exports]. 

677 """ 

678 

679 self.aliases: dict[str, Alias] = {} 

680 """The aliases pointing to this object.""" 

681 

682 self.runtime: bool = runtime 

683 """Whether this object is available at runtime. 

684 

685 Typically, type-guarded objects (under an `if TYPE_CHECKING` condition) 

686 are not available at runtime. 

687 """ 

688 

689 self.extra: dict[str, dict[str, Any]] = defaultdict(dict) 

690 """Namespaced dictionaries storing extra metadata for this object, used by extensions.""" 

691 

692 self.public: bool | None = None 

693 """Whether this object is public.""" 

694 

695 self.deprecated: bool | str | None = None 

696 """Whether this object is deprecated (boolean or deprecation message).""" 

697 

698 self.analysis: Literal["static", "dynamic"] | None = analysis 

699 """The type of analysis used to load this object. 

700 

701 None means the object was created manually. 

702 """ 

703 

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 

708 

709 # Attach the docstring to this object. 

710 if docstring: 

711 docstring.parent = self 

712 

713 def __repr__(self) -> str: 

714 return f"{self.__class__.__name__}({self.name!r}, {self.lineno!r}, {self.endlineno!r})" 

715 

716 # Prevent using `__len__`. 

717 def __bool__(self) -> bool: 

718 """An object is always true-ish.""" 

719 return True 

720 

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()) 

724 

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 

731 

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 

736 

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 

750 

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 

755 

756 @property 

757 def has_docstring(self) -> bool: 

758 """Whether this object has a docstring (empty or not). 

759 

760 See also: [`docstring`][griffe.Object.docstring], 

761 [`has_docstrings`][griffe.Object.has_docstrings]. 

762 """ 

763 return bool(self.docstring) 

764 

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). 

776 

777 Inherited members are not considered. Imported members are not considered, 

778 unless they are also public. 

779 

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 

792 

793 def is_kind(self, kind: str | Kind | set[str | Kind]) -> bool: 

794 """Tell if this object is of the given kind. 

795 

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]. 

802 

803 Parameters: 

804 kind: An instance or set of kinds (strings or enumerations). 

805 

806 Raises: 

807 ValueError: When an empty set is given as argument. 

808 

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 

819 

820 @property 

821 def inherited_members(self) -> dict[str, Alias]: 

822 """Members that are inherited from base classes. 

823 

824 This method is part of the consumer API: 

825 do not use when producing Griffe trees! 

826 

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 

842 

843 @property 

844 def is_module(self) -> bool: 

845 """Whether this object is a module. 

846 

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 

856 

857 @property 

858 def is_class(self) -> bool: 

859 """Whether this object is a class. 

860 

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 

869 

870 @property 

871 def is_function(self) -> bool: 

872 """Whether this object is a function. 

873 

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 

882 

883 @property 

884 def is_attribute(self) -> bool: 

885 """Whether this object is an attribute. 

886 

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 

895 

896 @property 

897 def is_type_alias(self) -> bool: 

898 """Whether this object is a type alias. 

899 

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 

908 

909 @property 

910 def is_init_method(self) -> bool: 

911 """Whether this function is an `__init__` method.""" 

912 return False 

913 

914 @property 

915 def is_init_module(self) -> bool: 

916 """Whether this object is an `__init__.py` module. 

917 

918 See also: [`is_module`][griffe.Object.is_module]. 

919 """ 

920 return False 

921 

922 @property 

923 def is_package(self) -> bool: 

924 """Whether this object is a package (top module). 

925 

926 See also: [`is_subpackage`][griffe.Object.is_subpackage]. 

927 """ 

928 return False 

929 

930 @property 

931 def is_subpackage(self) -> bool: 

932 """Whether this object is a subpackage. 

933 

934 See also: [`is_package`][griffe.Object.is_package]. 

935 """ 

936 return False 

937 

938 @property 

939 def is_namespace_package(self) -> bool: 

940 """Whether this object is a namespace package (top folder, no `__init__.py`). 

941 

942 See also: [`is_namespace_subpackage`][griffe.Object.is_namespace_subpackage]. 

943 """ 

944 return False 

945 

946 @property 

947 def is_namespace_subpackage(self) -> bool: 

948 """Whether this object is a namespace subpackage. 

949 

950 See also: [`is_namespace_package`][griffe.Object.is_namespace_package]. 

951 """ 

952 return False 

953 

954 def has_labels(self, *labels: str) -> bool: 

955 """Tell if this object has all the given labels. 

956 

957 See also: [`labels`][griffe.Object.labels]. 

958 

959 Parameters: 

960 *labels: Labels that must be present. 

961 

962 Returns: 

963 True or False. 

964 """ 

965 return set(labels).issubset(self.labels) 

966 

967 def filter_members(self, *predicates: Callable[[Object | Alias], bool]) -> dict[str, Object | Alias]: 

968 """Filter and return members based on predicates. 

969 

970 See also: [`members`][griffe.Object.members]. 

971 

972 Parameters: 

973 *predicates: A list of predicates, i.e. callables accepting a member as argument and returning a boolean. 

974 

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 

984 

985 @property 

986 def module(self) -> Module: 

987 """The parent module of this object. 

988 

989 See also: [`package`][griffe.Object.package]. 

990 

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')) 

999 

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") 

1008 

1009 @property 

1010 def package(self) -> Module: 

1011 """The absolute top module (the package) of this object. 

1012 

1013 See also: [`module`][griffe.Object.module]. 

1014 

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] 

1025 

1026 @property 

1027 def filepath(self) -> Path | list[Path]: 

1028 """The file path (or directory list for namespace packages) where this object was defined. 

1029 

1030 See also: [`relative_filepath`][griffe.Object.relative_filepath], 

1031 [`relative_package_filepath`][griffe.Object.relative_package_filepath]. 

1032 

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 

1040 

1041 @property 

1042 def relative_package_filepath(self) -> Path: 

1043 """The file path where this object was defined, relative to the top module path. 

1044 

1045 See also: [`filepath`][griffe.Object.filepath], 

1046 [`relative_filepath`][griffe.Object.relative_filepath]. 

1047 

1048 Raises: 

1049 ValueError: When the relative path could not be computed. 

1050 """ 

1051 package_path = self.package.filepath 

1052 

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) 

1061 

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 

1070 

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 

1078 

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) 

1084 

1085 @property 

1086 def relative_filepath(self) -> Path: 

1087 """The file path where this object was defined, relative to the current working directory. 

1088 

1089 If this object's file path is not relative to the current working directory, return its absolute path. 

1090 

1091 See also: [`filepath`][griffe.Object.filepath], 

1092 [`relative_package_filepath`][griffe.Object.relative_package_filepath]. 

1093 

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 

1107 

1108 @property 

1109 def path(self) -> str: 

1110 """The dotted path of this object. 

1111 

1112 On regular objects (not aliases), the path is the canonical path. 

1113 

1114 See also: [`canonical_path`][griffe.Object.canonical_path]. 

1115 

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 

1123 

1124 @property 

1125 def canonical_path(self) -> str: 

1126 """The full dotted path of this object. 

1127 

1128 The canonical path is the path where the object was defined (not imported). 

1129 

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}" 

1135 

1136 @property 

1137 def modules_collection(self) -> ModulesCollection: 

1138 """The modules collection attached to this object or its parents. 

1139 

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 

1148 

1149 @property 

1150 def lines_collection(self) -> LinesCollection: 

1151 """The lines collection attached to this object or its parents. 

1152 

1153 See also: [`lines`][griffe.Object.lines], 

1154 [`source`][griffe.Object.source]. 

1155 

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 

1164 

1165 @property 

1166 def lines(self) -> list[str]: 

1167 """The lines containing the source of this object. 

1168 

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] 

1187 

1188 @property 

1189 def source(self) -> str: 

1190 """The source code of this object. 

1191 

1192 See also: [`lines`][griffe.Object.lines], 

1193 [`lines_collection`][griffe.Object.lines_collection]. 

1194 """ 

1195 return dedent("\n".join(self.lines)) 

1196 

1197 def resolve(self, name: str) -> str: 

1198 """Resolve a name within this object's and parents' scope. 

1199 

1200 Parameters: 

1201 name: The name to resolve. 

1202 

1203 Raises: 

1204 NameResolutionError: When the name could not be resolved. 

1205 

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? 

1211 

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}]" 

1222 

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 

1228 

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}") 

1232 

1233 # Name is parent, non-module object. 

1234 if name == self.parent.name and not self.parent.is_module: 

1235 return self.parent.path 

1236 

1237 # Recurse in parent. 

1238 return self.parent.resolve(name) 

1239 

1240 def as_dict(self, *, full: bool = False, **kwargs: Any) -> dict[str, Any]: 

1241 """Return this object's data as a dictionary. 

1242 

1243 See also: [`as_json`][griffe.Object.as_json]. 

1244 

1245 Parameters: 

1246 full: Whether to return full info, or just base info. 

1247 **kwargs: Additional serialization options. 

1248 

1249 Returns: 

1250 A dictionary. 

1251 """ 

1252 base: dict[str, Any] = { 

1253 "kind": self.kind, 

1254 "name": self.name, 

1255 "runtime": self.runtime, 

1256 } 

1257 

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`? 

1285 

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 ) 

1317 

1318 with suppress(ValueError): 

1319 base["relative_filepath"] = self.relative_filepath 

1320 

1321 if "source_link" not in base and (source_link := self.source_link) is not None: 

1322 base["source_link"] = source_link 

1323 

1324 return base 

1325 

1326 

1327class Alias(ObjectAliasMixin): 

1328 """This class represents an alias, or indirection, to an object declared in another module. 

1329 

1330 Aliases represent objects that are in the scope of a module or class, 

1331 but were imported from another module. 

1332 

1333 They behave almost exactly like regular objects, to a few exceptions: 

1334 

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 """ 

1341 

1342 is_alias: bool = True 

1343 """Always true for aliases.""" 

1344 is_collection: bool = False 

1345 """Always false for aliases. 

1346 

1347 See also: [`ModulesCollection`][griffe.ModulesCollection]. 

1348 """ 

1349 

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. 

1364 

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.""" 

1380 

1381 self.alias_lineno: int | None = lineno 

1382 """The starting line number of the alias.""" 

1383 

1384 self.alias_endlineno: int | None = endlineno 

1385 """The ending line number of the alias.""" 

1386 

1387 self.runtime: bool = runtime 

1388 """Whether this alias is available at runtime.""" 

1389 

1390 self.inherited: bool = inherited 

1391 """Whether this alias represents an inherited member.""" 

1392 

1393 self.wildcard_imported: bool = wildcard_imported 

1394 """Whether this alias was created using a wildcard import.""" 

1395 

1396 self.public: bool | None = None 

1397 """Whether this alias is public.""" 

1398 

1399 self.deprecated: str | bool | None = None 

1400 """Whether this alias is deprecated (boolean or deprecation message).""" 

1401 

1402 self.analysis: Literal["static", "dynamic"] | None = analysis 

1403 """The type of analysis used to load this alias. 

1404 

1405 None means the alias was created manually. 

1406 """ 

1407 

1408 self._parent: Module | Class | Alias | None = parent 

1409 self._passed_through: bool = False 

1410 

1411 self.target_path: str 

1412 """The path of this alias' target.""" 

1413 

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() 

1421 

1422 def __repr__(self) -> str: 

1423 return f"Alias({self.name!r}, {self.target_path!r})" 

1424 

1425 # Prevent using `__len__`. 

1426 def __bool__(self) -> bool: 

1427 """An alias is always true-ish.""" 

1428 return True 

1429 

1430 def __len__(self) -> int: 

1431 """The length of an alias is always 1.""" 

1432 return 1 

1433 

1434 # SPECIAL PROXIES ------------------------------- 

1435 # The following methods and properties exist on the target(s), 

1436 # but we must handle them in a special way. 

1437 

1438 @property 

1439 def kind(self) -> Kind: 

1440 """The target's kind, or `Kind.ALIAS` if the target cannot be resolved. 

1441 

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 

1449 

1450 @property 

1451 def has_docstring(self) -> bool: 

1452 """Whether this alias' target has a non-empty docstring. 

1453 

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 

1461 

1462 @property 

1463 def has_docstrings(self) -> bool: 

1464 """Whether this alias' target or any of its members has a non-empty docstring. 

1465 

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 

1473 

1474 @property 

1475 def parent(self) -> Module | Class | Alias | None: 

1476 """The parent of this alias.""" 

1477 return self._parent 

1478 

1479 @parent.setter 

1480 def parent(self, value: Module | Class | Alias) -> None: 

1481 self._parent = value 

1482 self._update_target_aliases() 

1483 

1484 @property 

1485 def path(self) -> str: 

1486 """The dotted path / import path of this object. 

1487 

1488 See also: [`canonical_path`][griffe.Alias.canonical_path]. 

1489 """ 

1490 return f"{self.parent.path}.{self.name}" # ty:ignore[possibly-missing-attribute] 

1491 

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] 

1497 

1498 @property 

1499 def members(self) -> dict[str, Object | Alias]: 

1500 """The target's members (modules, classes, functions, attributes, type aliases). 

1501 

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 

1508 

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 } 

1517 

1518 @property 

1519 def inherited_members(self) -> dict[str, Alias]: 

1520 """Members that are inherited from base classes. 

1521 

1522 Each inherited member of the target will be wrapped in an alias, 

1523 to preserve correct object access paths. 

1524 

1525 This method is part of the consumer API: 

1526 do not use when producing Griffe trees! 

1527 

1528 See also: [`members`][griffe.Alias.members]. 

1529 """ 

1530 final_target = self.final_target 

1531 

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 } 

1540 

1541 def as_json(self, *, full: bool = False, **kwargs: Any) -> str: 

1542 """Return this target's data as a JSON string. 

1543 

1544 See also: [`as_dict`][griffe.Alias.as_dict]. 

1545 

1546 Parameters: 

1547 full: Whether to return full info, or just base info. 

1548 **kwargs: Additional serialization options passed to encoder. 

1549 

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) 

1557 

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. 

1562 

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 

1567 

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 

1572 

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 

1577 

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 

1582 

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 

1587 

1588 @property 

1589 def lineno(self) -> int | None: 

1590 """The starting line number of the target object. 

1591 

1592 See also: [`endlineno`][griffe.Alias.endlineno]. 

1593 """ 

1594 return self.final_target.lineno 

1595 

1596 @lineno.setter 

1597 def lineno(self, lineno: int | None) -> None: 

1598 self.final_target.lineno = lineno 

1599 

1600 @property 

1601 def endlineno(self) -> int | None: 

1602 """The ending line number of the target object. 

1603 

1604 See also: [`lineno`][griffe.Alias.lineno]. 

1605 """ 

1606 return self.final_target.endlineno 

1607 

1608 @endlineno.setter 

1609 def endlineno(self, endlineno: int | None) -> None: 

1610 self.final_target.endlineno = endlineno 

1611 

1612 @property 

1613 def docstring(self) -> Docstring | None: 

1614 """The target docstring. 

1615 

1616 See also: [`has_docstring`][griffe.Alias.has_docstring], 

1617 [`has_docstrings`][griffe.Alias.has_docstrings]. 

1618 """ 

1619 return self.final_target.docstring 

1620 

1621 @docstring.setter 

1622 def docstring(self, docstring: Docstring | None) -> None: 

1623 self.final_target.docstring = docstring 

1624 

1625 @property 

1626 def type_parameters(self) -> TypeParameters: 

1627 """The target type parameters.""" 

1628 return self.final_target.type_parameters 

1629 

1630 @property 

1631 def labels(self) -> set[str]: 

1632 """The target labels (`property`, `dataclass`, etc.). 

1633 

1634 See also: [`has_labels`][griffe.Alias.has_labels]. 

1635 """ 

1636 return self.final_target.labels 

1637 

1638 @property 

1639 def imports(self) -> dict[str, str]: 

1640 """The other objects imported by this alias' target. 

1641 

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 ...`). 

1644 

1645 See also: [`is_imported`][griffe.Alias.is_imported]. 

1646 """ 

1647 return self.final_target.imports 

1648 

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. 

1652 

1653 Exports can contain string (object names) or resolvable names, 

1654 like other lists of exports coming from submodules: 

1655 

1656 ```python 

1657 from .submodule import __all__ as submodule_all 

1658 

1659 __all__ = ["hello", *submodule_all] 

1660 ``` 

1661 

1662 Exports get expanded by the loader before it expands wildcards and resolves aliases. 

1663 

1664 See also: [`GriffeLoader.expand_exports`][griffe.GriffeLoader.expand_exports]. 

1665 """ 

1666 return self.final_target.exports 

1667 

1668 @property 

1669 def aliases(self) -> dict[str, Alias]: 

1670 """The aliases pointing to this object.""" 

1671 return self.final_target.aliases 

1672 

1673 def is_kind(self, kind: str | Kind | set[str | Kind]) -> bool: 

1674 """Tell if this object is of the given kind. 

1675 

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]. 

1682 

1683 Parameters: 

1684 kind: An instance or set of kinds (strings or enumerations). 

1685 

1686 Raises: 

1687 ValueError: When an empty set is given as argument. 

1688 

1689 Returns: 

1690 True or False. 

1691 """ 

1692 return self.final_target.is_kind(kind) 

1693 

1694 @property 

1695 def is_module(self) -> bool: 

1696 """Whether this object is a module. 

1697 

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 

1707 

1708 @property 

1709 def is_class(self) -> bool: 

1710 """Whether this object is a class. 

1711 

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 

1720 

1721 @property 

1722 def is_function(self) -> bool: 

1723 """Whether this object is a function. 

1724 

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 

1733 

1734 @property 

1735 def is_attribute(self) -> bool: 

1736 """Whether this object is an attribute. 

1737 

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 

1746 

1747 @property 

1748 def is_type_alias(self) -> bool: 

1749 """Whether this object is a type alias. 

1750 

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 

1759 

1760 def has_labels(self, *labels: str) -> bool: 

1761 """Tell if this object has all the given labels. 

1762 

1763 See also: [`labels`][griffe.Alias.labels]. 

1764 

1765 Parameters: 

1766 *labels: Labels that must be present. 

1767 

1768 Returns: 

1769 True or False. 

1770 """ 

1771 return self.final_target.has_labels(*labels) 

1772 

1773 def filter_members(self, *predicates: Callable[[Object | Alias], bool]) -> dict[str, Object | Alias]: 

1774 """Filter and return members based on predicates. 

1775 

1776 See also: [`members`][griffe.Alias.members], 

1777 [`get_member`][griffe.Alias.get_member], 

1778 [`set_member`][griffe.Alias.set_member]. 

1779 

1780 Parameters: 

1781 *predicates: A list of predicates, i.e. callables accepting a member as argument and returning a boolean. 

1782 

1783 Returns: 

1784 A dictionary of members. 

1785 """ 

1786 return self.final_target.filter_members(*predicates) 

1787 

1788 @property 

1789 def module(self) -> Module: 

1790 """The parent module of this object. 

1791 

1792 See also: [`package`][griffe.Alias.package]. 

1793 

1794 Raises: 

1795 ValueError: When the object is not a module and does not have a parent. 

1796 """ 

1797 return self.final_target.module 

1798 

1799 @property 

1800 def package(self) -> Module: 

1801 """The absolute top module (the package) of this object. 

1802 

1803 See also: [`module`][griffe.Alias.module]. 

1804 """ 

1805 return self.final_target.package 

1806 

1807 @property 

1808 def filepath(self) -> Path | list[Path]: 

1809 """The file path (or directory list for namespace packages) where this object was defined. 

1810 

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 

1815 

1816 @property 

1817 def relative_filepath(self) -> Path: 

1818 """The file path where this object was defined, relative to the current working directory. 

1819 

1820 If this object's file path is not relative to the current working directory, return its absolute path. 

1821 

1822 See also: [`filepath`][griffe.Alias.filepath], 

1823 [`relative_package_filepath`][griffe.Alias.relative_package_filepath]. 

1824 

1825 Raises: 

1826 ValueError: When the relative path could not be computed. 

1827 """ 

1828 return self.final_target.relative_filepath 

1829 

1830 @property 

1831 def relative_package_filepath(self) -> Path: 

1832 """The file path where this object was defined, relative to the top module path. 

1833 

1834 See also: [`filepath`][griffe.Alias.filepath], 

1835 [`relative_filepath`][griffe.Alias.relative_filepath]. 

1836 

1837 Raises: 

1838 ValueError: When the relative path could not be computed. 

1839 """ 

1840 return self.final_target.relative_package_filepath 

1841 

1842 @property 

1843 def canonical_path(self) -> str: 

1844 """The full dotted path of this object. 

1845 

1846 The canonical path is the path where the object was defined (not imported). 

1847 

1848 See also: [`path`][griffe.Alias.path]. 

1849 """ 

1850 return self.final_target.canonical_path 

1851 

1852 @property 

1853 def lines_collection(self) -> LinesCollection: 

1854 """The lines collection attached to this object or its parents. 

1855 

1856 See also: [`lines`][griffe.Alias.lines], 

1857 [`source`][griffe.Alias.source]. 

1858 

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 

1863 

1864 @property 

1865 def lines(self) -> list[str]: 

1866 """The lines containing the source of this object. 

1867 

1868 See also: [`source`][griffe.Alias.source], 

1869 [`lines_collection`][griffe.Alias.lines_collection]. 

1870 """ 

1871 return self.final_target.lines 

1872 

1873 @property 

1874 def source(self) -> str: 

1875 """The source code of this object. 

1876 

1877 See also: [`lines`][griffe.Alias.lines], 

1878 [`lines_collection`][griffe.Alias.lines_collection]. 

1879 """ 

1880 return self.final_target.source 

1881 

1882 def resolve(self, name: str) -> str: 

1883 """Resolve a name within this object's and parents' scope. 

1884 

1885 Parameters: 

1886 name: The name to resolve. 

1887 

1888 Raises: 

1889 NameResolutionError: When the name could not be resolved. 

1890 

1891 Returns: 

1892 The resolved name. 

1893 """ 

1894 return self.final_target.resolve(name) 

1895 

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. 

1900 

1901 @property 

1902 def _filepath(self) -> Path | list[Path] | None: 

1903 return cast("Module", self.final_target)._filepath 

1904 

1905 @property 

1906 def bases(self) -> list[Expr | str]: 

1907 """The class bases. 

1908 

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 

1914 

1915 @property 

1916 def keywords(self) -> dict[str, Expr | str]: 

1917 """The class keywords.""" 

1918 return cast("Class", self.final_target).keywords 

1919 

1920 @property 

1921 def decorators(self) -> list[Decorator]: 

1922 """The class/function decorators. 

1923 

1924 See also: [`Function`][griffe.Function], 

1925 [`Class`][griffe.Class]. 

1926 """ 

1927 return cast("Class | Function", self.target).decorators 

1928 

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 

1933 

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 

1938 

1939 @property 

1940 def is_init_module(self) -> bool: 

1941 """Whether this module is an `__init__.py` module. 

1942 

1943 See also: [`is_module`][griffe.Alias.is_module]. 

1944 """ 

1945 return cast("Module", self.final_target).is_init_module 

1946 

1947 @property 

1948 def is_package(self) -> bool: 

1949 """Whether this module is a package (top module). 

1950 

1951 See also: [`is_subpackage`][griffe.Alias.is_subpackage]. 

1952 """ 

1953 return cast("Module", self.final_target).is_package 

1954 

1955 @property 

1956 def is_subpackage(self) -> bool: 

1957 """Whether this module is a subpackage. 

1958 

1959 See also: [`is_package`][griffe.Alias.is_package]. 

1960 """ 

1961 return cast("Module", self.final_target).is_subpackage 

1962 

1963 @property 

1964 def is_namespace_package(self) -> bool: 

1965 """Whether this module is a namespace package (top folder, no `__init__.py`). 

1966 

1967 See also: [`is_namespace_subpackage`][griffe.Alias.is_namespace_subpackage]. 

1968 """ 

1969 return cast("Module", self.final_target).is_namespace_package 

1970 

1971 @property 

1972 def is_namespace_subpackage(self) -> bool: 

1973 """Whether this module is a namespace subpackage. 

1974 

1975 See also: [`is_namespace_package`][griffe.Alias.is_namespace_package]. 

1976 """ 

1977 return cast("Module", self.final_target).is_namespace_subpackage 

1978 

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 

1983 

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] 

1987 

1988 @property 

1989 def parameters(self) -> Parameters: 

1990 """The parameters of the current function or `__init__` method for classes. 

1991 

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 

1997 

1998 @property 

1999 def returns(self) -> str | Expr | None: 

2000 """The function return type annotation.""" 

2001 return cast("Function", self.final_target).returns 

2002 

2003 @returns.setter 

2004 def returns(self, returns: str | Expr | None) -> None: 

2005 cast("Function", self.final_target).returns = returns 

2006 

2007 @property 

2008 def setter(self) -> Function | None: 

2009 """The setter linked to this function (property).""" 

2010 return cast("Attribute", self.final_target).setter 

2011 

2012 @property 

2013 def deleter(self) -> Function | None: 

2014 """The deleter linked to this function (property).""" 

2015 return cast("Attribute", self.final_target).deleter 

2016 

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 

2021 

2022 @value.setter 

2023 def value(self, value: str | Expr | None) -> None: 

2024 cast("Attribute", self.final_target).value = value 

2025 

2026 @property 

2027 def annotation(self) -> str | Expr | None: 

2028 """The attribute type annotation.""" 

2029 return cast("Attribute", self.final_target).annotation 

2030 

2031 @annotation.setter 

2032 def annotation(self, annotation: str | Expr | None) -> None: 

2033 cast("Attribute", self.final_target).annotation = annotation 

2034 

2035 @property 

2036 def resolved_bases(self) -> list[Object]: 

2037 """Resolved class bases. 

2038 

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 

2043 

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() 

2047 

2048 def signature(self, *, return_type: bool = False, name: str | None = None) -> str: 

2049 """Construct the class/function signature. 

2050 

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. 

2054 

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) 

2059 

2060 # SPECIFIC ALIAS METHOD AND PROPERTIES ----------------- 

2061 # These methods and properties do not exist on targets, 

2062 # they are specific to aliases. 

2063 

2064 @property 

2065 def target(self) -> Object | Alias: 

2066 """The resolved target (actual object), if possible. 

2067 

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. 

2070 

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] 

2078 

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 

2087 

2088 @property 

2089 def final_target(self) -> Object: 

2090 """The final, resolved target, if possible. 

2091 

2092 This will iterate through the targets until a non-alias object is found. 

2093 

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. 

2100 

2101 # The cycle detection is needed because alias chains can be created 

2102 # as already resolved, and can contain cycles. 

2103 

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] 

2113 

2114 def resolve_target(self) -> None: 

2115 """Resolve the target. 

2116 

2117 See also: [`target`][griffe.Alias.target], 

2118 [`final_target`][griffe.Alias.final_target], 

2119 [`resolved`][griffe.Alias.resolved]. 

2120 

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. 

2133 

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 

2146 

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 

2162 

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] 

2166 

2167 @property 

2168 def resolved(self) -> bool: 

2169 """Whether this alias' target is resolved.""" 

2170 return self._target is not None 

2171 

2172 @property 

2173 def wildcard(self) -> str | None: 

2174 """The module on which the wildcard import is performed (if any). 

2175 

2176 See also: [`GriffeLoader.expand_wildcards`][griffe.GriffeLoader.expand_wildcards]. 

2177 """ 

2178 if self.name.endswith("/*"): 

2179 return self.target_path 

2180 return None 

2181 

2182 def as_dict(self, *, full: bool = False, **kwargs: Any) -> dict[str, Any]: # noqa: ARG002 

2183 """Return this alias' data as a dictionary. 

2184 

2185 See also: [`as_json`][griffe.Alias.as_json]. 

2186 

2187 Parameters: 

2188 full: Whether to return full info, or just base info. 

2189 **kwargs: Additional serialization options. 

2190 

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 } 

2201 

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 

2212 

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 ) 

2227 

2228 return base 

2229 

2230 

2231class Module(Object): 

2232 """The class representing a Python module.""" 

2233 

2234 kind = Kind.MODULE 

2235 

2236 def __init__(self, *args: Any, filepath: Path | list[Path] | None = None, **kwargs: Any) -> None: 

2237 """Initialize the module. 

2238 

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.""" 

2248 

2249 def __repr__(self) -> str: 

2250 try: 

2251 return f"Module({self.filepath!r})" 

2252 except BuiltinModuleError: 

2253 return f"Module({self.name!r})" 

2254 

2255 @property 

2256 def filepath(self) -> Path | list[Path]: 

2257 """The file path of this module. 

2258 

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 

2265 

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 ) 

2274 

2275 @property 

2276 def is_init_module(self) -> bool: 

2277 """Whether this module is an `__init__.py` module. 

2278 

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 

2287 

2288 @property 

2289 def is_package(self) -> bool: 

2290 """Whether this module is a package (top module). 

2291 

2292 See also: [`is_subpackage`][griffe.Module.is_subpackage]. 

2293 """ 

2294 return not bool(self.parent) and self.is_init_module 

2295 

2296 @property 

2297 def is_subpackage(self) -> bool: 

2298 """Whether this module is a subpackage. 

2299 

2300 See also: [`is_package`][griffe.Module.is_package]. 

2301 """ 

2302 return bool(self.parent) and self.is_init_module 

2303 

2304 @property 

2305 def is_namespace_package(self) -> bool: 

2306 """Whether this module is a namespace package (top folder, no `__init__.py`). 

2307 

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 

2314 

2315 @property 

2316 def is_namespace_subpackage(self) -> bool: 

2317 """Whether this module is a namespace subpackage. 

2318 

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 

2332 

2333 def as_dict(self, **kwargs: Any) -> dict[str, Any]: 

2334 """Return this module's data as a dictionary. 

2335 

2336 See also: [`as_json`][griffe.Module.as_json]. 

2337 

2338 Parameters: 

2339 **kwargs: Additional serialization options. 

2340 

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 

2352 

2353 

2354class Class(Object): 

2355 """The class representing a Python class.""" 

2356 

2357 kind = Kind.CLASS 

2358 

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. 

2368 

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) 

2377 

2378 self.bases: list[Expr | str] = list(bases) if bases else [] 

2379 """The class bases. 

2380 

2381 See also: [`resolved_bases`][griffe.Class.resolved_bases], 

2382 [`mro`][griffe.Class.mro]. 

2383 """ 

2384 

2385 self.decorators: list[Decorator] = decorators or [] 

2386 """The class decorators.""" 

2387 

2388 self.keywords: dict[str, Any] = keywords or {} 

2389 """The class keywords arguments.""" 

2390 

2391 self.overloads: dict[str, list[Function]] = defaultdict(list) 

2392 """The overloaded signatures declared in this class.""" 

2393 

2394 @property 

2395 def parameters(self) -> Parameters: 

2396 """The parameters of this class' `__init__` method, if any. 

2397 

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() 

2406 

2407 def signature(self, *, return_type: bool = False, name: str | None = None) -> str: 

2408 """Construct the class signature. 

2409 

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. 

2413 

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 "" 

2423 

2424 @property 

2425 def resolved_bases(self) -> list[Object]: 

2426 """Resolved class bases. 

2427 

2428 This method is part of the consumer API: 

2429 do not use when producing Griffe trees! 

2430 

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 

2446 

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)] 

2457 

2458 def mro(self) -> list[Class]: 

2459 """Return a list of classes in order corresponding to Python's MRO. 

2460 

2461 See also: [`bases`][griffe.Class.bases], 

2462 [`resolved_bases`][griffe.Class.resolved_bases]. 

2463 """ 

2464 return self._mro()[1:] # Remove self. 

2465 

2466 def as_dict(self, **kwargs: Any) -> dict[str, Any]: 

2467 """Return this class' data as a dictionary. 

2468 

2469 See also: [`as_json`][griffe.Class.as_json]. 

2470 

2471 Parameters: 

2472 **kwargs: Additional serialization options. 

2473 

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 

2481 

2482 

2483class Function(Object): 

2484 """The class representing a Python function.""" 

2485 

2486 kind = Kind.FUNCTION 

2487 

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. 

2497 

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.""" 

2514 

2515 for parameter in self.parameters: 

2516 parameter.function = self 

2517 

2518 @property 

2519 def annotation(self) -> str | Expr | None: 

2520 """The type annotation of the returned value.""" 

2521 return self.returns 

2522 

2523 def resolve(self, name: str) -> str: 

2524 """Resolve a name within this object's and parents' scope. 

2525 

2526 Parameters: 

2527 name: The name to resolve. 

2528 

2529 Raises: 

2530 NameResolutionError: When the name could not be resolved. 

2531 

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})" 

2540 

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) 

2555 

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__") 

2560 

2561 def as_dict(self, **kwargs: Any) -> dict[str, Any]: 

2562 """Return this function's data as a dictionary. 

2563 

2564 See also: [`as_json`][griffe.Function.as_json]. 

2565 

2566 Parameters: 

2567 **kwargs: Additional serialization options. 

2568 

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 

2577 

2578 def signature(self, *, return_type: bool = True, name: str | None = None) -> str: 

2579 """Construct the function signature. 

2580 

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. 

2584 

2585 Returns: 

2586 A string representation of the function signature. 

2587 """ 

2588 signature = f"{name or self.name}(" 

2589 

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 

2593 

2594 param_strs = [] 

2595 

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 

2600 

2601 param_str = "" 

2602 

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("/") 

2608 

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("*") 

2612 

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 = "**" 

2619 

2620 # Add parameter name. 

2621 param_str += param.name 

2622 

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. 

2629 

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}" 

2636 

2637 param_strs.append(param_str) 

2638 

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("/") 

2642 

2643 signature += ", ".join(param_strs) 

2644 signature += ")" 

2645 

2646 # Add return type if present. 

2647 if return_type and self.annotation: 

2648 signature += f" -> {self.annotation}" 

2649 

2650 return signature 

2651 

2652 

2653class Attribute(Object): 

2654 """The class representing a Python module/class/instance attribute.""" 

2655 

2656 kind = Kind.ATTRIBUTE 

2657 

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. 

2666 

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.""" 

2682 

2683 def as_dict(self, **kwargs: Any) -> dict[str, Any]: 

2684 """Return this attribute's data as a dictionary. 

2685 

2686 See also: [`as_json`][griffe.Attribute.as_json]. 

2687 

2688 Parameters: 

2689 **kwargs: Additional serialization options. 

2690 

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 

2700 

2701 

2702class TypeAlias(Object): 

2703 """The class representing a Python type alias.""" 

2704 

2705 kind = Kind.TYPE_ALIAS 

2706 

2707 def __init__( 

2708 self, 

2709 *args: Any, 

2710 value: str | Expr | None = None, 

2711 **kwargs: Any, 

2712 ) -> None: 

2713 """Initialize the function. 

2714 

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.""" 

2723 

2724 def as_dict(self, **kwargs: Any) -> dict[str, Any]: 

2725 """Return this type alias's data as a dictionary. 

2726 

2727 Parameters: 

2728 **kwargs: Additional serialization options. 

2729 

2730 Returns: 

2731 A dictionary. 

2732 """ 

2733 base = super().as_dict(**kwargs) 

2734 base["value"] = self.value 

2735 return base