pdoc.doc
This module defines pdoc's documentation objects. A documentation object corresponds to something
in your Python code that has a docstring or type annotation. Typically, this only includes
modules, classes, functions and methods. However, pdoc
adds support for extracting documentation
from the abstract syntax tree, which means that variables (module, class or instance) are supported too.
There are four main types of documentation objects:
All documentation types make heavy use of @functools.cached_property
decorators.
This means they have a large set of attributes that are lazily computed on first access.
By convention, all attributes are read-only, although this is not enforced at runtime.
1""" 2This module defines pdoc's documentation objects. A documentation object corresponds to *something* 3in your Python code that has a docstring or type annotation. Typically, this only includes 4modules, classes, functions and methods. However, `pdoc` adds support for extracting documentation 5from the abstract syntax tree, which means that variables (module, class or instance) are supported too. 6 7There are four main types of documentation objects: 8 9- `Module` 10- `Class` 11- `Function` 12- `Variable` 13 14All documentation types make heavy use of `@functools.cached_property` decorators. 15This means they have a large set of attributes that are lazily computed on first access. 16By convention, all attributes are read-only, although this is not enforced at runtime. 17""" 18from __future__ import annotations 19 20import dataclasses 21import enum 22import inspect 23import os 24import pkgutil 25import re 26import sys 27import textwrap 28import traceback 29import types 30import warnings 31from abc import ABCMeta 32from abc import abstractmethod 33from collections.abc import Callable 34from functools import wraps 35from pathlib import Path 36from typing import Any 37from typing import ClassVar 38from typing import Generic 39from typing import TypeVar 40from typing import Union 41 42from ._compat import cache 43from ._compat import cached_property 44from ._compat import formatannotation 45from ._compat import get_origin 46from ._compat import singledispatchmethod 47from pdoc import doc_ast 48from pdoc import doc_pyi 49from pdoc import extract 50from pdoc.doc_types import empty 51from pdoc.doc_types import GenericAlias 52from pdoc.doc_types import NonUserDefinedCallables 53from pdoc.doc_types import resolve_annotations 54from pdoc.doc_types import safe_eval_type 55 56 57def _include_fullname_in_traceback(f): 58 """ 59 Doc.__repr__ should not raise, but it may raise if we screwed up. 60 Debugging this is a bit tricky, because, well, we can't repr() in the traceback either then. 61 This decorator adds location information to the traceback, which helps tracking down bugs. 62 """ 63 64 @wraps(f) 65 def wrapper(self): 66 try: 67 return f(self) 68 except Exception as e: 69 raise RuntimeError(f"Error in {self.fullname}'s repr!") from e 70 71 return wrapper 72 73 74T = TypeVar("T") 75 76 77class Doc(Generic[T]): 78 """ 79 A base class for all documentation objects. 80 """ 81 82 modulename: str 83 """ 84 The module that this object is in, for example `pdoc.doc`. 85 """ 86 87 qualname: str 88 """ 89 The qualified identifier name for this object. For example, if we have the following code: 90 91 ```python 92 class Foo: 93 def bar(self): 94 pass 95 ``` 96 97 The qualname of `Foo`'s `bar` method is `Foo.bar`. The qualname of the `Foo` class is just `Foo`. 98 99 See <https://www.python.org/dev/peps/pep-3155/> for details. 100 """ 101 102 obj: T 103 """ 104 The underlying Python object. 105 """ 106 107 taken_from: tuple[str, str] 108 """ 109 `(modulename, qualname)` of this doc object's original location. 110 In the context of a module, this points to the location it was imported from, 111 in the context of classes, this points to the class an attribute is inherited from. 112 """ 113 114 kind: ClassVar[str] 115 """ 116 The type of the doc object, either `"module"`, `"class"`, `"function"`, or `"variable"`. 117 """ 118 119 @property 120 def type(self) -> str: # pragma: no cover 121 warnings.warn( 122 "pdoc.doc.Doc.type is deprecated. Use pdoc.doc.Doc.kind instead.", 123 DeprecationWarning, 124 ) 125 return self.kind 126 127 def __init__( 128 self, modulename: str, qualname: str, obj: T, taken_from: tuple[str, str] 129 ): 130 """ 131 Initializes a documentation object, where 132 `modulename` is the name this module is defined in, 133 `qualname` contains a dotted path leading to the object from the module top-level, and 134 `obj` is the object to document. 135 """ 136 self.modulename = modulename 137 self.qualname = qualname 138 self.obj = obj 139 self.taken_from = taken_from 140 141 @cached_property 142 def fullname(self) -> str: 143 """The full qualified name of this doc object, for example `pdoc.doc.Doc`.""" 144 # qualname is empty for modules 145 return f"{self.modulename}.{self.qualname}".rstrip(".") 146 147 @cached_property 148 def name(self) -> str: 149 """The name of this object. For top-level functions and classes, this is equal to the qualname attribute.""" 150 return self.fullname.split(".")[-1] 151 152 @cached_property 153 def docstring(self) -> str: 154 """ 155 The docstring for this object. It has already been cleaned by `inspect.cleandoc`. 156 157 If no docstring can be found, an empty string is returned. 158 """ 159 return _safe_getdoc(self.obj) 160 161 @cached_property 162 def source(self) -> str: 163 """ 164 The source code of the Python object as a `str`. 165 166 If the source cannot be obtained (for example, because we are dealing with a native C object), 167 an empty string is returned. 168 """ 169 return doc_ast.get_source(self.obj) 170 171 @cached_property 172 def source_file(self) -> Path | None: 173 """The name of the Python source file in which this object was defined. `None` for built-in objects.""" 174 try: 175 return Path(inspect.getsourcefile(self.obj) or inspect.getfile(self.obj)) # type: ignore 176 except TypeError: 177 return None 178 179 @cached_property 180 def source_lines(self) -> tuple[int, int] | None: 181 """ 182 Return a `(start, end)` line number tuple for this object. 183 184 If no source file can be found, `None` is returned. 185 """ 186 try: 187 lines, start = inspect.getsourcelines(self.obj) # type: ignore 188 return start, start + len(lines) - 1 189 except Exception: 190 return None 191 192 @cached_property 193 def is_inherited(self) -> bool: 194 """ 195 If True, the doc object is inherited from another location. 196 This most commonly refers to methods inherited by a subclass, 197 but can also apply to variables that are assigned a class defined 198 in a different module. 199 """ 200 return (self.modulename, self.qualname) != self.taken_from 201 202 def __lt__(self, other): 203 assert isinstance(other, Doc) 204 return self.fullname.replace("__init__", "").__lt__( 205 other.fullname.replace("__init__", "") 206 ) 207 208 209class Namespace(Doc[T], metaclass=ABCMeta): 210 """ 211 A documentation object that can have children. In other words, either a module or a class. 212 """ 213 214 @cached_property 215 @abstractmethod 216 def _member_objects(self) -> dict[str, Any]: 217 """ 218 A mapping from *all* public and private member names to their Python objects. 219 """ 220 221 @cached_property 222 @abstractmethod 223 def _var_docstrings(self) -> dict[str, str]: 224 """A mapping from some member variable names to their docstrings.""" 225 226 @cached_property 227 @abstractmethod 228 def _func_docstrings(self) -> dict[str, str]: 229 """A mapping from some member function names to their raw (not processed by any @decorators) docstrings.""" 230 231 @cached_property 232 @abstractmethod 233 def _var_annotations(self) -> dict[str, Any]: 234 """A mapping from some member variable names to their type annotations.""" 235 236 @abstractmethod 237 def _taken_from(self, member_name: str, obj: Any) -> tuple[str, str]: 238 """The location this member was taken from. If unknown, (modulename, qualname) is returned.""" 239 240 @cached_property 241 @abstractmethod 242 def own_members(self) -> list[Doc]: 243 """A list of all own (i.e. non-inherited) `members`.""" 244 245 @cached_property 246 def members(self) -> dict[str, Doc]: 247 """A mapping from all members to their documentation objects. 248 249 This mapping includes private members; they are only filtered out as part of the template logic. 250 Constructors for enums, dicts, and abstract base classes are not picked up unless they have a custom docstring. 251 """ 252 members: dict[str, Doc] = {} 253 for name, obj in self._member_objects.items(): 254 qualname = f"{self.qualname}.{name}".lstrip(".") 255 taken_from = self._taken_from(name, obj) 256 doc: Doc[Any] 257 258 is_classmethod = isinstance(obj, classmethod) 259 is_property = ( 260 isinstance(obj, (property, cached_property)) 261 or 262 # Python 3.9 - 3.10: @classmethod @property is allowed. 263 is_classmethod 264 and isinstance(obj.__func__, (property, cached_property)) 265 ) 266 if is_property: 267 func = obj 268 if is_classmethod: 269 func = obj.__func__ 270 if isinstance(func, property): 271 func = func.fget 272 else: 273 assert isinstance(func, cached_property) 274 func = func.func 275 276 doc_f = Function(self.modulename, qualname, func, taken_from) 277 doc = Variable( 278 self.modulename, 279 qualname, 280 docstring=doc_f.docstring, 281 annotation=doc_f.signature.return_annotation, 282 default_value=empty, 283 taken_from=taken_from, 284 ) 285 elif inspect.isroutine(obj): 286 doc = Function(self.modulename, qualname, obj, taken_from) # type: ignore 287 elif ( 288 inspect.isclass(obj) 289 and obj is not empty 290 and not isinstance(obj, GenericAlias) 291 and obj.__qualname__.rpartition(".")[2] == qualname.rpartition(".")[2] 292 ): 293 # `dict[str,str]` is a GenericAlias instance. We want to render type aliases as variables though. 294 doc = Class(self.modulename, qualname, obj, taken_from) 295 elif inspect.ismodule(obj): 296 if os.environ.get("PDOC_SUBMODULES"): # pragma: no cover 297 doc = Module.from_name(obj.__name__) 298 else: 299 continue 300 elif inspect.isdatadescriptor(obj): 301 doc = Variable( 302 self.modulename, 303 qualname, 304 docstring=getattr(obj, "__doc__", None) or "", 305 annotation=self._var_annotations.get(name, empty), 306 default_value=empty, 307 taken_from=taken_from, 308 ) 309 else: 310 doc = Variable( 311 self.modulename, 312 qualname, 313 docstring="", 314 annotation=self._var_annotations.get(name, empty), 315 default_value=obj, 316 taken_from=taken_from, 317 ) 318 if self._var_docstrings.get(name): 319 doc.docstring = self._var_docstrings[name] 320 if self._func_docstrings.get(name) and not doc.docstring: 321 doc.docstring = self._func_docstrings[name] 322 members[doc.name] = doc 323 324 if isinstance(self, Module): 325 # quirk: doc_pyi expects .members to be set already 326 self.members = members # type: ignore 327 doc_pyi.include_typeinfo_from_stub_files(self) 328 329 return members 330 331 @cached_property 332 def _members_by_origin(self) -> dict[tuple[str, str], list[Doc]]: 333 """A mapping from (modulename, qualname) locations to the attributes taken from that path""" 334 locations: dict[tuple[str, str], list[Doc]] = {} 335 for member in self.members.values(): 336 mod, qualname = member.taken_from 337 parent_qualname = ".".join(qualname.rsplit(".", maxsplit=1)[:-1]) 338 locations.setdefault((mod, parent_qualname), []) 339 locations[(mod, parent_qualname)].append(member) 340 return locations 341 342 @cached_property 343 def inherited_members(self) -> dict[tuple[str, str], list[Doc]]: 344 """A mapping from (modulename, qualname) locations to the attributes inherited from that path""" 345 return { 346 k: v 347 for k, v in self._members_by_origin.items() 348 if k not in (self.taken_from, (self.modulename, self.qualname)) 349 } 350 351 @cached_property 352 def flattened_own_members(self) -> list[Doc]: 353 """ 354 A list of all documented members and their child classes, recursively. 355 """ 356 flattened = [] 357 for x in self.own_members: 358 flattened.append(x) 359 if isinstance(x, Class): 360 flattened.extend( 361 [cls for cls in x.flattened_own_members if isinstance(cls, Class)] 362 ) 363 return flattened 364 365 @cache 366 def get(self, identifier: str) -> Doc | None: 367 """Returns the documentation object for a particular identifier, or `None` if the identifier cannot be found.""" 368 head, _, tail = identifier.partition(".") 369 if tail: 370 h = self.members.get(head, None) 371 if isinstance(h, Class): 372 return h.get(tail) 373 return None 374 else: 375 return self.members.get(identifier, None) 376 377 378class Module(Namespace[types.ModuleType]): 379 """ 380 Representation of a module's documentation. 381 """ 382 383 def __init__( 384 self, 385 module: types.ModuleType, 386 ): 387 """ 388 Creates a documentation object given the actual 389 Python module object. 390 """ 391 super().__init__(module.__name__, "", module, (module.__name__, "")) 392 393 kind = "module" 394 395 @classmethod 396 @cache 397 def from_name(cls, name: str) -> Module: 398 """Create a `Module` object by supplying the module's (full) name.""" 399 return cls(extract.load_module(name)) 400 401 @cache 402 @_include_fullname_in_traceback 403 def __repr__(self): 404 return f"<module {self.fullname}{_docstr(self)}{_children(self)}>" 405 406 @cached_property 407 def is_package(self) -> bool: 408 """ 409 `True` if the module is a package, `False` otherwise. 410 411 Packages are a special kind of module that may have submodules. 412 Typically, this means that this file is in a directory named like the 413 module with the name `__init__.py`. 414 """ 415 return _safe_getattr(self.obj, "__path__", None) is not None 416 417 @cached_property 418 def _var_docstrings(self) -> dict[str, str]: 419 return doc_ast.walk_tree(self.obj).var_docstrings 420 421 @cached_property 422 def _func_docstrings(self) -> dict[str, str]: 423 return doc_ast.walk_tree(self.obj).func_docstrings 424 425 @cached_property 426 def _var_annotations(self) -> dict[str, Any]: 427 annotations = doc_ast.walk_tree(self.obj).annotations.copy() 428 for k, v in _safe_getattr(self.obj, "__annotations__", {}).items(): 429 annotations[k] = v 430 431 return resolve_annotations(annotations, self.obj, None, self.fullname) 432 433 def _taken_from(self, member_name: str, obj: Any) -> tuple[str, str]: 434 if obj is empty: 435 return self.modulename, f"{self.qualname}.{member_name}".lstrip(".") 436 if isinstance(obj, types.ModuleType): 437 return obj.__name__, "" 438 439 mod = _safe_getattr(obj, "__module__", None) 440 qual = _safe_getattr(obj, "__qualname__", None) 441 if mod and qual and "<locals>" not in qual: 442 return mod, qual 443 else: 444 # This might be wrong, but it's the best guess we have. 445 return (mod or self.modulename), f"{self.qualname}.{member_name}".lstrip( 446 "." 447 ) 448 449 @cached_property 450 def own_members(self) -> list[Doc]: 451 return list(self.members.values()) 452 453 @cached_property 454 def submodules(self) -> list[Module]: 455 """A list of all (direct) submodules.""" 456 if not self.is_package: 457 return [] 458 459 include: Callable[[str], bool] 460 mod_all = _safe_getattr(self.obj, "__all__", False) 461 if mod_all is not False: 462 mod_all_pos = {name: i for i, name in enumerate(mod_all)} 463 include = mod_all_pos.__contains__ 464 else: 465 466 def include(name: str) -> bool: 467 # optimization: we don't even try to load modules starting with an underscore as they would not be 468 # visible by default. The downside of this is that someone who overrides `is_public` will miss those 469 # entries, the upsides are 1) better performance and 2) less warnings because of import failures 470 # (think of OS-specific modules, e.g. _linux.py failing to import on Windows). 471 return not name.startswith("_") 472 473 submodules = [] 474 for mod in pkgutil.iter_modules(self.obj.__path__, f"{self.fullname}."): 475 _, _, mod_name = mod.name.rpartition(".") 476 if not include(mod_name): 477 continue 478 try: 479 module = Module.from_name(mod.name) 480 except RuntimeError: 481 warnings.warn(f"Couldn't import {mod.name}:\n{traceback.format_exc()}") 482 continue 483 submodules.append(module) 484 485 if mod_all: 486 submodules = sorted(submodules, key=lambda m: mod_all_pos[m.name]) 487 488 return submodules 489 490 @cached_property 491 def _documented_members(self) -> set[str]: 492 return ( 493 self._var_docstrings.keys() 494 | self._func_docstrings.keys() 495 | self._var_annotations.keys() 496 ) 497 498 @cached_property 499 def _member_objects(self) -> dict[str, Any]: 500 members = {} 501 502 all_: list[str] | None = _safe_getattr(self.obj, "__all__", None) 503 if all_ is not None: 504 for name in all_: 505 if not isinstance(name, str): 506 # Gracefully handle the case where objects are directly specified in __all__. 507 name = _safe_getattr(name, "__name__", str(name)) 508 if name in self.obj.__dict__: 509 val = self.obj.__dict__[name] 510 elif name in self._var_annotations: 511 val = empty 512 else: 513 # this may be an unimported submodule, try importing. 514 # (https://docs.python.org/3/tutorial/modules.html#importing-from-a-package) 515 try: 516 val = extract.load_module(f"{self.modulename}.{name}") 517 except RuntimeError as e: 518 warnings.warn( 519 f"Found {name!r} in {self.modulename}.__all__, but it does not resolve: {e}" 520 ) 521 val = empty 522 members[name] = val 523 524 else: 525 # Starting with Python 3.10, __annotations__ is created on demand, 526 # so we make a copy here as obj.__dict__ is changed while we iterate over it. 527 # Additionally, accessing self._documented_members may lead to the execution of TYPE_CHECKING blocks, 528 # which may also modify obj.__dict__. (https://github.com/mitmproxy/pdoc/issues/351) 529 for name, obj in list(self.obj.__dict__.items()): 530 # We already exclude everything here that is imported, only a TypeVar, 531 # or a variable without annotation and docstring. 532 # If one needs to document one of these things, __all__ is the correct way. 533 obj_module = inspect.getmodule(obj) 534 declared_in_this_module = self.obj.__name__ == _safe_getattr( 535 obj_module, "__name__", None 536 ) 537 include_in_docs = name in self._documented_members or ( 538 declared_in_this_module and not isinstance(obj, TypeVar) 539 ) 540 if include_in_docs: 541 members[name] = obj 542 for name in self._var_docstrings: 543 members.setdefault(name, empty) 544 for name in self._func_docstrings: 545 members.setdefault(name, empty) 546 547 members, notfound = doc_ast.sort_by_source(self.obj, {}, members) 548 members.update(notfound) 549 550 return members 551 552 @cached_property 553 def variables(self) -> list[Variable]: 554 """ 555 A list of all documented module level variables. 556 """ 557 return [x for x in self.members.values() if isinstance(x, Variable)] 558 559 @cached_property 560 def classes(self) -> list[Class]: 561 """ 562 A list of all documented module level classes. 563 """ 564 return [x for x in self.members.values() if isinstance(x, Class)] 565 566 @cached_property 567 def functions(self) -> list[Function]: 568 """ 569 A list of all documented module level functions. 570 """ 571 return [x for x in self.members.values() if isinstance(x, Function)] 572 573 574class Class(Namespace[type]): 575 """ 576 Representation of a class's documentation. 577 """ 578 579 kind = "class" 580 581 @cache 582 @_include_fullname_in_traceback 583 def __repr__(self): 584 return f"<{_decorators(self)}class {self.modulename}.{self.qualname}{_docstr(self)}{_children(self)}>" 585 586 @cached_property 587 def docstring(self) -> str: 588 doc = Doc.docstring.__get__(self) # type: ignore 589 if doc == dict.__doc__: 590 # Don't display default docstring for dict subclasses (primarily TypedDict). 591 return "" 592 is_dataclass_with_default_docstring = ( 593 dataclasses.is_dataclass(self.obj) 594 # from https://github.com/python/cpython/blob/3.10/Lib/dataclasses.py 595 and doc 596 == self.obj.__name__ 597 + str(inspect.signature(self.obj)).replace(" -> None", "") 598 ) 599 if is_dataclass_with_default_docstring: 600 return "" 601 return doc 602 603 @cached_property 604 def _var_docstrings(self) -> dict[str, str]: 605 docstrings: dict[str, str] = {} 606 for cls in self._mro: 607 for name, docstr in doc_ast.walk_tree(cls).var_docstrings.items(): 608 docstrings.setdefault(name, docstr) 609 return docstrings 610 611 @cached_property 612 def _func_docstrings(self) -> dict[str, str]: 613 docstrings: dict[str, str] = {} 614 for cls in self._mro: 615 for name, docstr in doc_ast.walk_tree(cls).func_docstrings.items(): 616 docstrings.setdefault(name, docstr) 617 return docstrings 618 619 @cached_property 620 def _var_annotations(self) -> dict[str, type]: 621 # this is a bit tricky: __annotations__ also includes annotations from parent classes, 622 # but we need to execute them in the namespace of the parent class. 623 # Our workaround for this is to walk the MRO backwards, and only update/evaluate only if the annotation changes. 624 annotations: dict[ 625 str, tuple[Any, type] 626 ] = {} # attribute -> (annotation_unresolved, annotation_resolved) 627 for cls in reversed(self._mro): 628 cls_annotations = doc_ast.walk_tree(cls).annotations.copy() 629 dynamic_annotations = _safe_getattr(cls, "__annotations__", None) 630 if isinstance(dynamic_annotations, dict): 631 for attr, unresolved_annotation in dynamic_annotations.items(): 632 cls_annotations[attr] = unresolved_annotation 633 cls_fullname = ( 634 _safe_getattr(cls, "__module__", "") + "." + cls.__qualname__ 635 ).lstrip(".") 636 637 new_annotations = { 638 attr: unresolved_annotation 639 for attr, unresolved_annotation in cls_annotations.items() 640 if attr not in annotations 641 or annotations[attr][0] is not unresolved_annotation 642 } 643 localns = _safe_getattr(cls, "__dict__", None) 644 for attr, t in resolve_annotations( 645 new_annotations, inspect.getmodule(cls), localns, cls_fullname 646 ).items(): 647 annotations[attr] = (new_annotations[attr], t) 648 649 return {k: v[1] for k, v in annotations.items()} 650 651 @cached_property 652 def _mro(self) -> tuple[type, ...]: 653 orig_bases = _safe_getattr(self.obj, "__orig_bases__", None) 654 if ( 655 orig_bases 656 and _safe_getattr(orig_bases[-1], "__name__", None) == "TypedDict" 657 ): 658 # TypedDicts have a botched __mro__. 659 # However, if the subclasses also specify TypedDict as a base class, we can use __orig_bases__ 660 # https://github.com/sphinx-doc/sphinx/pull/10806 661 return (self.obj, *orig_bases[:-1]) 662 else: 663 return self.obj.__mro__ 664 665 @cached_property 666 def _declarations(self) -> dict[str, tuple[str, str]]: 667 decls: dict[str, tuple[str, str]] = {} 668 for cls in self._mro: 669 treeinfo = doc_ast.walk_tree(cls) 670 for name in ( 671 treeinfo.var_docstrings.keys() 672 | treeinfo.func_docstrings.keys() 673 | treeinfo.annotations.keys() 674 ): 675 decls.setdefault(name, (cls.__module__, f"{cls.__qualname__}.{name}")) 676 for name in cls.__dict__: 677 decls.setdefault(name, (cls.__module__, f"{cls.__qualname__}.{name}")) 678 if decls.get("__init__", None) == ("builtins", "object.__init__"): 679 decls["__init__"] = ( 680 self.obj.__module__, 681 f"{self.obj.__qualname__}.__init__", 682 ) 683 return decls 684 685 def _taken_from(self, member_name: str, obj: Any) -> tuple[str, str]: 686 try: 687 return self._declarations[member_name] 688 except KeyError: # pragma: no cover 689 # TypedDict botches __mro__ and may need special casing here. 690 # One workaround is to also specify TypedDict as a base class, see pdoc.doc.Class._mro. 691 warnings.warn( 692 f"Cannot determine where {self.fullname}.{member_name} is taken from, assuming current file." 693 ) 694 return self.modulename, f"{self.qualname}.{member_name}" 695 696 @cached_property 697 def own_members(self) -> list[Doc]: 698 members = self._members_by_origin.get((self.modulename, self.qualname), []) 699 if self.taken_from != (self.modulename, self.qualname): 700 # .taken_from may be != (self.modulename, self.qualname), for example when 701 # a module re-exports a class from a private submodule. 702 members += self._members_by_origin.get(self.taken_from, []) 703 return members 704 705 @cached_property 706 def _member_objects(self) -> dict[str, Any]: 707 unsorted: dict[str, Any] = {} 708 for cls in self._mro: 709 for name, obj in cls.__dict__.items(): 710 unsorted.setdefault(name, obj) 711 for name in self._var_docstrings: 712 unsorted.setdefault(name, empty) 713 for name in self._var_annotations: 714 unsorted.setdefault(name, empty) 715 716 init_has_no_doc = unsorted.get("__init__", object.__init__).__doc__ in ( 717 None, 718 object.__init__.__doc__, 719 ) 720 if init_has_no_doc: 721 if inspect.isabstract(self.obj): 722 # Special case: We don't want to show constructors for abstract base classes unless 723 # they have a custom docstring. 724 del unsorted["__init__"] 725 elif issubclass(self.obj, enum.Enum): 726 # Special case: Do not show a constructor for enums. They are typically not constructed by users. 727 # The alternative would be showing __new__, as __call__ is too verbose. 728 del unsorted["__init__"] 729 elif issubclass(self.obj, dict): 730 # Special case: Do not show a constructor for dict subclasses. 731 unsorted.pop( 732 "__init__", None 733 ) # TypedDict subclasses may not have __init__. 734 else: 735 # Check if there's a helpful Metaclass.__call__ or Class.__new__. This dance is very similar to 736 # https://github.com/python/cpython/blob/9feae41c4f04ca27fd2c865807a5caeb50bf4fc4/Lib/inspect.py#L2359-L2376 737 call = _safe_getattr(type(self.obj), "__call__", None) 738 custom_call_with_custom_docstring = ( 739 call is not None 740 and not isinstance(call, NonUserDefinedCallables) 741 and call.__doc__ not in (None, object.__call__.__doc__) 742 ) 743 if custom_call_with_custom_docstring: 744 unsorted["__init__"] = call 745 else: 746 # Does our class define a custom __new__ method? 747 new = _safe_getattr(self.obj, "__new__", None) 748 custom_new_with_custom_docstring = ( 749 new is not None 750 and not isinstance(new, NonUserDefinedCallables) 751 and new.__doc__ not in (None, object.__new__.__doc__) 752 ) 753 if custom_new_with_custom_docstring: 754 unsorted["__init__"] = new 755 756 sorted: dict[str, Any] = {} 757 for cls in self._mro: 758 sorted, unsorted = doc_ast.sort_by_source(cls, sorted, unsorted) 759 sorted.update(unsorted) 760 return sorted 761 762 @cached_property 763 def bases(self) -> list[tuple[str, str, str]]: 764 """ 765 A list of all base classes, i.e. all immediate parent classes. 766 767 Each parent class is represented as a `(modulename, qualname, display_text)` tuple. 768 """ 769 bases = [] 770 for x in _safe_getattr(self.obj, "__orig_bases__", self.obj.__bases__): 771 if x is object: 772 continue 773 o = get_origin(x) 774 if o: 775 bases.append((o.__module__, o.__qualname__, str(x))) 776 elif x.__module__ == self.modulename: 777 bases.append((x.__module__, x.__qualname__, x.__qualname__)) 778 else: 779 bases.append( 780 (x.__module__, x.__qualname__, f"{x.__module__}.{x.__qualname__}") 781 ) 782 return bases 783 784 @cached_property 785 def decorators(self) -> list[str]: 786 """A list of all decorators the class is decorated with.""" 787 decorators = [] 788 for t in doc_ast.parse(self.obj).decorator_list: 789 decorators.append(f"@{doc_ast.unparse(t)}") 790 return decorators 791 792 @cached_property 793 def class_variables(self) -> list[Variable]: 794 """ 795 A list of all documented class variables in the class. 796 797 Class variables are variables that are explicitly annotated with `typing.ClassVar`. 798 All other variables are treated as instance variables. 799 """ 800 return [ 801 x 802 for x in self.members.values() 803 if isinstance(x, Variable) and x.is_classvar 804 ] 805 806 @cached_property 807 def instance_variables(self) -> list[Variable]: 808 """ 809 A list of all instance variables in the class. 810 """ 811 return [ 812 x 813 for x in self.members.values() 814 if isinstance(x, Variable) and not x.is_classvar 815 ] 816 817 @cached_property 818 def classmethods(self) -> list[Function]: 819 """ 820 A list of all documented `@classmethod`s. 821 """ 822 return [ 823 x 824 for x in self.members.values() 825 if isinstance(x, Function) and x.is_classmethod 826 ] 827 828 @cached_property 829 def staticmethods(self) -> list[Function]: 830 """ 831 A list of all documented `@staticmethod`s. 832 """ 833 return [ 834 x 835 for x in self.members.values() 836 if isinstance(x, Function) and x.is_staticmethod 837 ] 838 839 @cached_property 840 def methods(self) -> list[Function]: 841 """ 842 A list of all documented methods in the class that are neither static- nor classmethods. 843 """ 844 return [ 845 x 846 for x in self.members.values() 847 if isinstance(x, Function) 848 and not x.is_staticmethod 849 and not x.is_classmethod 850 ] 851 852 853if sys.version_info >= (3, 10): 854 WrappedFunction = types.FunctionType | staticmethod | classmethod 855else: # pragma: no cover 856 WrappedFunction = Union[types.FunctionType, staticmethod, classmethod] 857 858 859class Function(Doc[types.FunctionType]): 860 """ 861 Representation of a function's documentation. 862 863 This class covers all "flavors" of functions, for example it also 864 supports `@classmethod`s or `@staticmethod`s. 865 """ 866 867 kind = "function" 868 869 wrapped: WrappedFunction 870 """The original wrapped function (e.g., `staticmethod(func)`)""" 871 872 obj: types.FunctionType 873 """The unwrapped "real" function.""" 874 875 def __init__( 876 self, 877 modulename: str, 878 qualname: str, 879 func: WrappedFunction, 880 taken_from: tuple[str, str], 881 ): 882 """Initialize a function's documentation object.""" 883 unwrapped: types.FunctionType 884 if isinstance(func, (classmethod, staticmethod)): 885 unwrapped = func.__func__ # type: ignore 886 elif isinstance(func, singledispatchmethod): 887 unwrapped = func.func # type: ignore 888 else: 889 unwrapped = func 890 super().__init__(modulename, qualname, unwrapped, taken_from) 891 self.wrapped = func 892 893 @cache 894 @_include_fullname_in_traceback 895 def __repr__(self): 896 if self.is_classmethod: 897 t = "class" 898 elif self.is_staticmethod: 899 t = "static" 900 elif self.qualname != _safe_getattr(self.obj, "__name__", None): 901 t = "method" 902 else: 903 t = "function" 904 return f"<{_decorators(self)}{t} {self.funcdef} {self.name}{self.signature}: ...{_docstr(self)}>" 905 906 @cached_property 907 def docstring(self) -> str: 908 doc = Doc.docstring.__get__(self) # type: ignore 909 if not doc: 910 # inspect.getdoc fails for inherited @classmethods and unbound @property descriptors. 911 # We now do an ugly dance to obtain the bound object instead, 912 # that somewhat resembles what inspect._findclass is doing. 913 cls = sys.modules.get(_safe_getattr(self.obj, "__module__", None), None) 914 for name in _safe_getattr(self.obj, "__qualname__", "").split(".")[:-1]: 915 cls = _safe_getattr(cls, name, None) 916 917 unbound = _safe_getattr(cls, "__dict__", {}).get(self.name) 918 is_classmethod_property = isinstance(unbound, classmethod) and isinstance( 919 unbound.__func__, (property, cached_property) 920 ) 921 if not is_classmethod_property: 922 # We choke on @classmethod @property, but that's okay because it's been deprecated with Python 3.11. 923 # Directly accessing them would give us the return value, which has the wrong docstring. 924 doc = _safe_getdoc(_safe_getattr(cls, self.name, None)) 925 926 if doc == object.__init__.__doc__: 927 # inspect.getdoc(Foo.__init__) returns the docstring, for object.__init__ if left undefined... 928 return "" 929 else: 930 return doc 931 932 @cached_property 933 def is_classmethod(self) -> bool: 934 """ 935 `True` if this function is a `@classmethod`, `False` otherwise. 936 """ 937 return isinstance(self.wrapped, classmethod) 938 939 @cached_property 940 def is_staticmethod(self) -> bool: 941 """ 942 `True` if this function is a `@staticmethod`, `False` otherwise. 943 """ 944 return isinstance(self.wrapped, staticmethod) 945 946 @cached_property 947 def decorators(self) -> list[str]: 948 """A list of all decorators the function is decorated with.""" 949 decorators = [] 950 obj: types.FunctionType = self.obj # type: ignore 951 for t in doc_ast.parse(obj).decorator_list: 952 decorators.append(f"@{doc_ast.unparse(t)}") 953 return decorators 954 955 @cached_property 956 def funcdef(self) -> str: 957 """ 958 The string of keywords used to define the function, i.e. `"def"` or `"async def"`. 959 """ 960 if inspect.iscoroutinefunction(self.obj) or inspect.isasyncgenfunction( 961 self.obj 962 ): 963 return "async def" 964 else: 965 return "def" 966 967 @cached_property 968 def signature(self) -> inspect.Signature: 969 """ 970 The function's signature. 971 972 This usually returns an instance of `_PrettySignature`, a subclass of `inspect.Signature` 973 that contains pdoc-specific optimizations. For example, long argument lists are split over multiple lines 974 in repr(). Additionally, all types are already resolved. 975 976 If the signature cannot be determined, a placeholder Signature object is returned. 977 """ 978 if self.obj is object.__init__: 979 # there is a weird edge case were inspect.signature returns a confusing (self, /, *args, **kwargs) 980 # signature for the default __init__ method. 981 return inspect.Signature() 982 try: 983 sig = _PrettySignature.from_callable(self.obj) 984 except Exception: 985 return inspect.Signature( 986 [inspect.Parameter("unknown", inspect.Parameter.POSITIONAL_OR_KEYWORD)] 987 ) 988 mod = inspect.getmodule(self.obj) 989 globalns = _safe_getattr(mod, "__dict__", {}) 990 localns = globalns 991 for parent_cls_name in self.qualname.split(".")[:-1]: 992 parent_cls = localns.get(parent_cls_name, object) 993 localns = _safe_getattr(parent_cls, "__dict__", None) 994 if localns is None: 995 break # pragma: no cover 996 997 if self.name == "__init__": 998 sig = sig.replace(return_annotation=empty) 999 else: 1000 sig = sig.replace( 1001 return_annotation=safe_eval_type( 1002 sig.return_annotation, globalns, localns, mod, self.fullname 1003 ) 1004 ) 1005 for p in sig.parameters.values(): 1006 p._annotation = safe_eval_type(p.annotation, globalns, localns, mod, self.fullname) # type: ignore 1007 return sig 1008 1009 @cached_property 1010 def signature_without_self(self) -> inspect.Signature: 1011 """Like `signature`, but without the first argument. 1012 1013 This is useful to display constructors. 1014 """ 1015 return self.signature.replace( 1016 parameters=list(self.signature.parameters.values())[1:] 1017 ) 1018 1019 1020class Variable(Doc[None]): 1021 """ 1022 Representation of a variable's documentation. This includes module, class and instance variables. 1023 """ 1024 1025 kind = "variable" 1026 1027 default_value: Any | empty # technically Any includes empty, but this conveys intent. 1028 """ 1029 The variable's default value. 1030 1031 In some cases, no default value is known. This may either be because a variable is only defined in the constructor, 1032 or it is only declared with a type annotation without assignment (`foo: int`). 1033 To distinguish this case from a default value of `None`, `pdoc.doc_types.empty` is used as a placeholder. 1034 """ 1035 1036 annotation: type | empty 1037 """ 1038 The variable's type annotation. 1039 1040 If there is no type annotation, `pdoc.doc_types.empty` is used as a placeholder. 1041 """ 1042 1043 def __init__( 1044 self, 1045 modulename: str, 1046 qualname: str, 1047 *, 1048 taken_from: tuple[str, str], 1049 docstring: str, 1050 annotation: type | empty = empty, 1051 default_value: Any | empty = empty, 1052 ): 1053 """ 1054 Construct a variable doc object. 1055 1056 While classes and functions can introspect themselves to see their docstring, 1057 variables can't do that as we don't have a "variable object" we could query. 1058 As such, docstring, declaration location, type annotation, and the default value 1059 must be passed manually in the constructor. 1060 """ 1061 super().__init__(modulename, qualname, None, taken_from) 1062 # noinspection PyPropertyAccess 1063 self.docstring = inspect.cleandoc(docstring) 1064 self.annotation = annotation 1065 self.default_value = default_value 1066 1067 @cache 1068 @_include_fullname_in_traceback 1069 def __repr__(self): 1070 if self.default_value_str: 1071 default = f" = {self.default_value_str}" 1072 else: 1073 default = "" 1074 return f'<var {self.qualname.rsplit(".")[-1]}{self.annotation_str}{default}{_docstr(self)}>' 1075 1076 @cached_property 1077 def is_classvar(self) -> bool: 1078 """`True` if the variable is a class variable, `False` otherwise.""" 1079 if get_origin(self.annotation) is ClassVar: 1080 return True 1081 else: 1082 return False 1083 1084 @cached_property 1085 def is_enum_member(self) -> bool: 1086 """`True` if the variable is an enum member, `False` otherwise.""" 1087 if isinstance(self.default_value, enum.Enum): 1088 return True 1089 else: 1090 return False 1091 1092 @cached_property 1093 def default_value_str(self) -> str: 1094 """The variable's default value as a pretty-printed str.""" 1095 if self.default_value is empty: 1096 return "" 1097 1098 try: 1099 pretty = repr(self.default_value) 1100 except Exception as e: 1101 warnings.warn(f"repr({self.fullname}) raised an exception ({e!r})") 1102 return "" 1103 1104 pretty = re.sub(r" at 0x[0-9a-fA-F]+(?=>)", "", pretty) 1105 return pretty 1106 1107 @cached_property 1108 def annotation_str(self) -> str: 1109 """The variable's type annotation as a pretty-printed str.""" 1110 if self.annotation is not empty: 1111 return f": {formatannotation(self.annotation)}" 1112 else: 1113 return "" 1114 1115 1116class _PrettySignature(inspect.Signature): 1117 """ 1118 A subclass of `inspect.Signature` that pads __str__ over several lines 1119 for complex signatures. 1120 """ 1121 1122 MULTILINE_CUTOFF = 70 1123 1124 def _params(self) -> list[str]: 1125 # redeclared here to keep code snipped below as-is. 1126 _POSITIONAL_ONLY = inspect.Parameter.POSITIONAL_ONLY 1127 _VAR_POSITIONAL = inspect.Parameter.VAR_POSITIONAL 1128 _KEYWORD_ONLY = inspect.Parameter.KEYWORD_ONLY 1129 1130 # https://github.com/python/cpython/blob/799f8489d418b7f9207d333eac38214931bd7dcc/Lib/inspect.py#L3083-L3117 1131 # Change: added re.sub() to formatted = .... 1132 # ✂ start ✂ 1133 result = [] 1134 render_pos_only_separator = False 1135 render_kw_only_separator = True 1136 for param in self.parameters.values(): 1137 formatted = re.sub(r" at 0x[0-9a-fA-F]+(?=>$)", "", str(param)) 1138 1139 kind = param.kind 1140 1141 if kind == _POSITIONAL_ONLY: 1142 render_pos_only_separator = True 1143 elif render_pos_only_separator: 1144 # It's not a positional-only parameter, and the flag 1145 # is set to 'True' (there were pos-only params before.) 1146 result.append("/") 1147 render_pos_only_separator = False 1148 1149 if kind == _VAR_POSITIONAL: 1150 # OK, we have an '*args'-like parameter, so we won't need 1151 # a '*' to separate keyword-only arguments 1152 render_kw_only_separator = False 1153 elif kind == _KEYWORD_ONLY and render_kw_only_separator: 1154 # We have a keyword-only parameter to render and we haven't 1155 # rendered an '*args'-like parameter before, so add a '*' 1156 # separator to the parameters list ("foo(arg1, *, arg2)" case) 1157 result.append("*") 1158 # This condition should be only triggered once, so 1159 # reset the flag 1160 render_kw_only_separator = False 1161 1162 result.append(formatted) 1163 1164 if render_pos_only_separator: 1165 # There were only positional-only parameters, hence the 1166 # flag was not reset to 'False' 1167 result.append("/") 1168 # ✂ end ✂ 1169 1170 return result 1171 1172 def _return_annotation_str(self) -> str: 1173 if self.return_annotation is not empty: 1174 return formatannotation(self.return_annotation) 1175 else: 1176 return "" 1177 1178 def __str__(self): 1179 result = self._params() 1180 return_annot = self._return_annotation_str() 1181 1182 total_len = sum(len(x) + 2 for x in result) + len(return_annot) 1183 1184 if total_len > self.MULTILINE_CUTOFF: 1185 rendered = "(\n " + ",\n ".join(result) + "\n)" 1186 else: 1187 rendered = "({})".format(", ".join(result)) 1188 if return_annot: 1189 rendered += f" -> {return_annot}" 1190 1191 return rendered 1192 1193 1194def _cut(x: str) -> str: 1195 """helper function for Doc.__repr__()""" 1196 if len(x) < 20: 1197 return x 1198 else: 1199 return x[:20] + "…" 1200 1201 1202def _docstr(doc: Doc) -> str: 1203 """helper function for Doc.__repr__()""" 1204 docstr = [] 1205 if doc.is_inherited: 1206 docstr.append(f"inherited from {'.'.join(doc.taken_from).rstrip('.')}") 1207 if doc.docstring: 1208 docstr.append(_cut(doc.docstring)) 1209 if docstr: 1210 return f" # {', '.join(docstr)}" 1211 else: 1212 return "" 1213 1214 1215def _decorators(doc: Class | Function) -> str: 1216 """helper function for Doc.__repr__()""" 1217 if doc.decorators: 1218 return " ".join(doc.decorators) + " " 1219 else: 1220 return "" 1221 1222 1223def _children(doc: Namespace) -> str: 1224 children = "\n".join( 1225 repr(x) 1226 for x in doc.members.values() 1227 if not x.name.startswith("_") or x.name == "__init__" 1228 ) 1229 if children: 1230 children += "\n" 1231 children = f"\n{textwrap.indent(children, ' ')}" 1232 return children 1233 1234 1235def _safe_getattr(obj, attr, default): 1236 """Like `getattr()`, but never raises.""" 1237 try: 1238 return getattr(obj, attr, default) 1239 except Exception as e: 1240 warnings.warn( 1241 f"getattr({obj!r}, {attr!r}, {default!r}) raised an exception: {e!r}" 1242 ) 1243 return default 1244 1245 1246def _safe_getdoc(obj: Any) -> str: 1247 """Like `inspect.getdoc()`, but never raises. Always returns a stripped string.""" 1248 try: 1249 doc = inspect.getdoc(obj) or "" 1250 except Exception as e: 1251 warnings.warn(f"inspect.getdoc({obj!r}) raised an exception: {e!r}") 1252 return "" 1253 else: 1254 return doc.strip()
78class Doc(Generic[T]): 79 """ 80 A base class for all documentation objects. 81 """ 82 83 modulename: str 84 """ 85 The module that this object is in, for example `pdoc.doc`. 86 """ 87 88 qualname: str 89 """ 90 The qualified identifier name for this object. For example, if we have the following code: 91 92 ```python 93 class Foo: 94 def bar(self): 95 pass 96 ``` 97 98 The qualname of `Foo`'s `bar` method is `Foo.bar`. The qualname of the `Foo` class is just `Foo`. 99 100 See <https://www.python.org/dev/peps/pep-3155/> for details. 101 """ 102 103 obj: T 104 """ 105 The underlying Python object. 106 """ 107 108 taken_from: tuple[str, str] 109 """ 110 `(modulename, qualname)` of this doc object's original location. 111 In the context of a module, this points to the location it was imported from, 112 in the context of classes, this points to the class an attribute is inherited from. 113 """ 114 115 kind: ClassVar[str] 116 """ 117 The type of the doc object, either `"module"`, `"class"`, `"function"`, or `"variable"`. 118 """ 119 120 @property 121 def type(self) -> str: # pragma: no cover 122 warnings.warn( 123 "pdoc.doc.Doc.type is deprecated. Use pdoc.doc.Doc.kind instead.", 124 DeprecationWarning, 125 ) 126 return self.kind 127 128 def __init__( 129 self, modulename: str, qualname: str, obj: T, taken_from: tuple[str, str] 130 ): 131 """ 132 Initializes a documentation object, where 133 `modulename` is the name this module is defined in, 134 `qualname` contains a dotted path leading to the object from the module top-level, and 135 `obj` is the object to document. 136 """ 137 self.modulename = modulename 138 self.qualname = qualname 139 self.obj = obj 140 self.taken_from = taken_from 141 142 @cached_property 143 def fullname(self) -> str: 144 """The full qualified name of this doc object, for example `pdoc.doc.Doc`.""" 145 # qualname is empty for modules 146 return f"{self.modulename}.{self.qualname}".rstrip(".") 147 148 @cached_property 149 def name(self) -> str: 150 """The name of this object. For top-level functions and classes, this is equal to the qualname attribute.""" 151 return self.fullname.split(".")[-1] 152 153 @cached_property 154 def docstring(self) -> str: 155 """ 156 The docstring for this object. It has already been cleaned by `inspect.cleandoc`. 157 158 If no docstring can be found, an empty string is returned. 159 """ 160 return _safe_getdoc(self.obj) 161 162 @cached_property 163 def source(self) -> str: 164 """ 165 The source code of the Python object as a `str`. 166 167 If the source cannot be obtained (for example, because we are dealing with a native C object), 168 an empty string is returned. 169 """ 170 return doc_ast.get_source(self.obj) 171 172 @cached_property 173 def source_file(self) -> Path | None: 174 """The name of the Python source file in which this object was defined. `None` for built-in objects.""" 175 try: 176 return Path(inspect.getsourcefile(self.obj) or inspect.getfile(self.obj)) # type: ignore 177 except TypeError: 178 return None 179 180 @cached_property 181 def source_lines(self) -> tuple[int, int] | None: 182 """ 183 Return a `(start, end)` line number tuple for this object. 184 185 If no source file can be found, `None` is returned. 186 """ 187 try: 188 lines, start = inspect.getsourcelines(self.obj) # type: ignore 189 return start, start + len(lines) - 1 190 except Exception: 191 return None 192 193 @cached_property 194 def is_inherited(self) -> bool: 195 """ 196 If True, the doc object is inherited from another location. 197 This most commonly refers to methods inherited by a subclass, 198 but can also apply to variables that are assigned a class defined 199 in a different module. 200 """ 201 return (self.modulename, self.qualname) != self.taken_from 202 203 def __lt__(self, other): 204 assert isinstance(other, Doc) 205 return self.fullname.replace("__init__", "").__lt__( 206 other.fullname.replace("__init__", "") 207 )
A base class for all documentation objects.
128 def __init__( 129 self, modulename: str, qualname: str, obj: T, taken_from: tuple[str, str] 130 ): 131 """ 132 Initializes a documentation object, where 133 `modulename` is the name this module is defined in, 134 `qualname` contains a dotted path leading to the object from the module top-level, and 135 `obj` is the object to document. 136 """ 137 self.modulename = modulename 138 self.qualname = qualname 139 self.obj = obj 140 self.taken_from = taken_from
Initializes a documentation object, where
modulename
is the name this module is defined in,
qualname
contains a dotted path leading to the object from the module top-level, and
obj
is the object to document.
The qualified identifier name for this object. For example, if we have the following code:
class Foo:
def bar(self):
pass
The qualname of Foo
's bar
method is Foo.bar
. The qualname of the Foo
class is just Foo
.
See https://www.python.org/dev/peps/pep-3155/ for details.
(modulename, qualname)
of this doc object's original location.
In the context of a module, this points to the location it was imported from,
in the context of classes, this points to the class an attribute is inherited from.
The type of the doc object, either "module"
, "class"
, "function"
, or "variable"
.
The name of this object. For top-level functions and classes, this is equal to the qualname attribute.
The docstring for this object. It has already been cleaned by inspect.cleandoc
.
If no docstring can be found, an empty string is returned.
The source code of the Python object as a str
.
If the source cannot be obtained (for example, because we are dealing with a native C object), an empty string is returned.
The name of the Python source file in which this object was defined. None
for built-in objects.
210class Namespace(Doc[T], metaclass=ABCMeta): 211 """ 212 A documentation object that can have children. In other words, either a module or a class. 213 """ 214 215 @cached_property 216 @abstractmethod 217 def _member_objects(self) -> dict[str, Any]: 218 """ 219 A mapping from *all* public and private member names to their Python objects. 220 """ 221 222 @cached_property 223 @abstractmethod 224 def _var_docstrings(self) -> dict[str, str]: 225 """A mapping from some member variable names to their docstrings.""" 226 227 @cached_property 228 @abstractmethod 229 def _func_docstrings(self) -> dict[str, str]: 230 """A mapping from some member function names to their raw (not processed by any @decorators) docstrings.""" 231 232 @cached_property 233 @abstractmethod 234 def _var_annotations(self) -> dict[str, Any]: 235 """A mapping from some member variable names to their type annotations.""" 236 237 @abstractmethod 238 def _taken_from(self, member_name: str, obj: Any) -> tuple[str, str]: 239 """The location this member was taken from. If unknown, (modulename, qualname) is returned.""" 240 241 @cached_property 242 @abstractmethod 243 def own_members(self) -> list[Doc]: 244 """A list of all own (i.e. non-inherited) `members`.""" 245 246 @cached_property 247 def members(self) -> dict[str, Doc]: 248 """A mapping from all members to their documentation objects. 249 250 This mapping includes private members; they are only filtered out as part of the template logic. 251 Constructors for enums, dicts, and abstract base classes are not picked up unless they have a custom docstring. 252 """ 253 members: dict[str, Doc] = {} 254 for name, obj in self._member_objects.items(): 255 qualname = f"{self.qualname}.{name}".lstrip(".") 256 taken_from = self._taken_from(name, obj) 257 doc: Doc[Any] 258 259 is_classmethod = isinstance(obj, classmethod) 260 is_property = ( 261 isinstance(obj, (property, cached_property)) 262 or 263 # Python 3.9 - 3.10: @classmethod @property is allowed. 264 is_classmethod 265 and isinstance(obj.__func__, (property, cached_property)) 266 ) 267 if is_property: 268 func = obj 269 if is_classmethod: 270 func = obj.__func__ 271 if isinstance(func, property): 272 func = func.fget 273 else: 274 assert isinstance(func, cached_property) 275 func = func.func 276 277 doc_f = Function(self.modulename, qualname, func, taken_from) 278 doc = Variable( 279 self.modulename, 280 qualname, 281 docstring=doc_f.docstring, 282 annotation=doc_f.signature.return_annotation, 283 default_value=empty, 284 taken_from=taken_from, 285 ) 286 elif inspect.isroutine(obj): 287 doc = Function(self.modulename, qualname, obj, taken_from) # type: ignore 288 elif ( 289 inspect.isclass(obj) 290 and obj is not empty 291 and not isinstance(obj, GenericAlias) 292 and obj.__qualname__.rpartition(".")[2] == qualname.rpartition(".")[2] 293 ): 294 # `dict[str,str]` is a GenericAlias instance. We want to render type aliases as variables though. 295 doc = Class(self.modulename, qualname, obj, taken_from) 296 elif inspect.ismodule(obj): 297 if os.environ.get("PDOC_SUBMODULES"): # pragma: no cover 298 doc = Module.from_name(obj.__name__) 299 else: 300 continue 301 elif inspect.isdatadescriptor(obj): 302 doc = Variable( 303 self.modulename, 304 qualname, 305 docstring=getattr(obj, "__doc__", None) or "", 306 annotation=self._var_annotations.get(name, empty), 307 default_value=empty, 308 taken_from=taken_from, 309 ) 310 else: 311 doc = Variable( 312 self.modulename, 313 qualname, 314 docstring="", 315 annotation=self._var_annotations.get(name, empty), 316 default_value=obj, 317 taken_from=taken_from, 318 ) 319 if self._var_docstrings.get(name): 320 doc.docstring = self._var_docstrings[name] 321 if self._func_docstrings.get(name) and not doc.docstring: 322 doc.docstring = self._func_docstrings[name] 323 members[doc.name] = doc 324 325 if isinstance(self, Module): 326 # quirk: doc_pyi expects .members to be set already 327 self.members = members # type: ignore 328 doc_pyi.include_typeinfo_from_stub_files(self) 329 330 return members 331 332 @cached_property 333 def _members_by_origin(self) -> dict[tuple[str, str], list[Doc]]: 334 """A mapping from (modulename, qualname) locations to the attributes taken from that path""" 335 locations: dict[tuple[str, str], list[Doc]] = {} 336 for member in self.members.values(): 337 mod, qualname = member.taken_from 338 parent_qualname = ".".join(qualname.rsplit(".", maxsplit=1)[:-1]) 339 locations.setdefault((mod, parent_qualname), []) 340 locations[(mod, parent_qualname)].append(member) 341 return locations 342 343 @cached_property 344 def inherited_members(self) -> dict[tuple[str, str], list[Doc]]: 345 """A mapping from (modulename, qualname) locations to the attributes inherited from that path""" 346 return { 347 k: v 348 for k, v in self._members_by_origin.items() 349 if k not in (self.taken_from, (self.modulename, self.qualname)) 350 } 351 352 @cached_property 353 def flattened_own_members(self) -> list[Doc]: 354 """ 355 A list of all documented members and their child classes, recursively. 356 """ 357 flattened = [] 358 for x in self.own_members: 359 flattened.append(x) 360 if isinstance(x, Class): 361 flattened.extend( 362 [cls for cls in x.flattened_own_members if isinstance(cls, Class)] 363 ) 364 return flattened 365 366 @cache 367 def get(self, identifier: str) -> Doc | None: 368 """Returns the documentation object for a particular identifier, or `None` if the identifier cannot be found.""" 369 head, _, tail = identifier.partition(".") 370 if tail: 371 h = self.members.get(head, None) 372 if isinstance(h, Class): 373 return h.get(tail) 374 return None 375 else: 376 return self.members.get(identifier, None)
A documentation object that can have children. In other words, either a module or a class.
A mapping from all members to their documentation objects.
This mapping includes private members; they are only filtered out as part of the template logic. Constructors for enums, dicts, and abstract base classes are not picked up unless they have a custom docstring.
A mapping from (modulename, qualname) locations to the attributes inherited from that path
A list of all documented members and their child classes, recursively.
366 @cache 367 def get(self, identifier: str) -> Doc | None: 368 """Returns the documentation object for a particular identifier, or `None` if the identifier cannot be found.""" 369 head, _, tail = identifier.partition(".") 370 if tail: 371 h = self.members.get(head, None) 372 if isinstance(h, Class): 373 return h.get(tail) 374 return None 375 else: 376 return self.members.get(identifier, None)
Returns the documentation object for a particular identifier, or None
if the identifier cannot be found.
Inherited Members
379class Module(Namespace[types.ModuleType]): 380 """ 381 Representation of a module's documentation. 382 """ 383 384 def __init__( 385 self, 386 module: types.ModuleType, 387 ): 388 """ 389 Creates a documentation object given the actual 390 Python module object. 391 """ 392 super().__init__(module.__name__, "", module, (module.__name__, "")) 393 394 kind = "module" 395 396 @classmethod 397 @cache 398 def from_name(cls, name: str) -> Module: 399 """Create a `Module` object by supplying the module's (full) name.""" 400 return cls(extract.load_module(name)) 401 402 @cache 403 @_include_fullname_in_traceback 404 def __repr__(self): 405 return f"<module {self.fullname}{_docstr(self)}{_children(self)}>" 406 407 @cached_property 408 def is_package(self) -> bool: 409 """ 410 `True` if the module is a package, `False` otherwise. 411 412 Packages are a special kind of module that may have submodules. 413 Typically, this means that this file is in a directory named like the 414 module with the name `__init__.py`. 415 """ 416 return _safe_getattr(self.obj, "__path__", None) is not None 417 418 @cached_property 419 def _var_docstrings(self) -> dict[str, str]: 420 return doc_ast.walk_tree(self.obj).var_docstrings 421 422 @cached_property 423 def _func_docstrings(self) -> dict[str, str]: 424 return doc_ast.walk_tree(self.obj).func_docstrings 425 426 @cached_property 427 def _var_annotations(self) -> dict[str, Any]: 428 annotations = doc_ast.walk_tree(self.obj).annotations.copy() 429 for k, v in _safe_getattr(self.obj, "__annotations__", {}).items(): 430 annotations[k] = v 431 432 return resolve_annotations(annotations, self.obj, None, self.fullname) 433 434 def _taken_from(self, member_name: str, obj: Any) -> tuple[str, str]: 435 if obj is empty: 436 return self.modulename, f"{self.qualname}.{member_name}".lstrip(".") 437 if isinstance(obj, types.ModuleType): 438 return obj.__name__, "" 439 440 mod = _safe_getattr(obj, "__module__", None) 441 qual = _safe_getattr(obj, "__qualname__", None) 442 if mod and qual and "<locals>" not in qual: 443 return mod, qual 444 else: 445 # This might be wrong, but it's the best guess we have. 446 return (mod or self.modulename), f"{self.qualname}.{member_name}".lstrip( 447 "." 448 ) 449 450 @cached_property 451 def own_members(self) -> list[Doc]: 452 return list(self.members.values()) 453 454 @cached_property 455 def submodules(self) -> list[Module]: 456 """A list of all (direct) submodules.""" 457 if not self.is_package: 458 return [] 459 460 include: Callable[[str], bool] 461 mod_all = _safe_getattr(self.obj, "__all__", False) 462 if mod_all is not False: 463 mod_all_pos = {name: i for i, name in enumerate(mod_all)} 464 include = mod_all_pos.__contains__ 465 else: 466 467 def include(name: str) -> bool: 468 # optimization: we don't even try to load modules starting with an underscore as they would not be 469 # visible by default. The downside of this is that someone who overrides `is_public` will miss those 470 # entries, the upsides are 1) better performance and 2) less warnings because of import failures 471 # (think of OS-specific modules, e.g. _linux.py failing to import on Windows). 472 return not name.startswith("_") 473 474 submodules = [] 475 for mod in pkgutil.iter_modules(self.obj.__path__, f"{self.fullname}."): 476 _, _, mod_name = mod.name.rpartition(".") 477 if not include(mod_name): 478 continue 479 try: 480 module = Module.from_name(mod.name) 481 except RuntimeError: 482 warnings.warn(f"Couldn't import {mod.name}:\n{traceback.format_exc()}") 483 continue 484 submodules.append(module) 485 486 if mod_all: 487 submodules = sorted(submodules, key=lambda m: mod_all_pos[m.name]) 488 489 return submodules 490 491 @cached_property 492 def _documented_members(self) -> set[str]: 493 return ( 494 self._var_docstrings.keys() 495 | self._func_docstrings.keys() 496 | self._var_annotations.keys() 497 ) 498 499 @cached_property 500 def _member_objects(self) -> dict[str, Any]: 501 members = {} 502 503 all_: list[str] | None = _safe_getattr(self.obj, "__all__", None) 504 if all_ is not None: 505 for name in all_: 506 if not isinstance(name, str): 507 # Gracefully handle the case where objects are directly specified in __all__. 508 name = _safe_getattr(name, "__name__", str(name)) 509 if name in self.obj.__dict__: 510 val = self.obj.__dict__[name] 511 elif name in self._var_annotations: 512 val = empty 513 else: 514 # this may be an unimported submodule, try importing. 515 # (https://docs.python.org/3/tutorial/modules.html#importing-from-a-package) 516 try: 517 val = extract.load_module(f"{self.modulename}.{name}") 518 except RuntimeError as e: 519 warnings.warn( 520 f"Found {name!r} in {self.modulename}.__all__, but it does not resolve: {e}" 521 ) 522 val = empty 523 members[name] = val 524 525 else: 526 # Starting with Python 3.10, __annotations__ is created on demand, 527 # so we make a copy here as obj.__dict__ is changed while we iterate over it. 528 # Additionally, accessing self._documented_members may lead to the execution of TYPE_CHECKING blocks, 529 # which may also modify obj.__dict__. (https://github.com/mitmproxy/pdoc/issues/351) 530 for name, obj in list(self.obj.__dict__.items()): 531 # We already exclude everything here that is imported, only a TypeVar, 532 # or a variable without annotation and docstring. 533 # If one needs to document one of these things, __all__ is the correct way. 534 obj_module = inspect.getmodule(obj) 535 declared_in_this_module = self.obj.__name__ == _safe_getattr( 536 obj_module, "__name__", None 537 ) 538 include_in_docs = name in self._documented_members or ( 539 declared_in_this_module and not isinstance(obj, TypeVar) 540 ) 541 if include_in_docs: 542 members[name] = obj 543 for name in self._var_docstrings: 544 members.setdefault(name, empty) 545 for name in self._func_docstrings: 546 members.setdefault(name, empty) 547 548 members, notfound = doc_ast.sort_by_source(self.obj, {}, members) 549 members.update(notfound) 550 551 return members 552 553 @cached_property 554 def variables(self) -> list[Variable]: 555 """ 556 A list of all documented module level variables. 557 """ 558 return [x for x in self.members.values() if isinstance(x, Variable)] 559 560 @cached_property 561 def classes(self) -> list[Class]: 562 """ 563 A list of all documented module level classes. 564 """ 565 return [x for x in self.members.values() if isinstance(x, Class)] 566 567 @cached_property 568 def functions(self) -> list[Function]: 569 """ 570 A list of all documented module level functions. 571 """ 572 return [x for x in self.members.values() if isinstance(x, Function)]
Representation of a module's documentation.
384 def __init__( 385 self, 386 module: types.ModuleType, 387 ): 388 """ 389 Creates a documentation object given the actual 390 Python module object. 391 """ 392 super().__init__(module.__name__, "", module, (module.__name__, ""))
Creates a documentation object given the actual Python module object.
396 @classmethod 397 @cache 398 def from_name(cls, name: str) -> Module: 399 """Create a `Module` object by supplying the module's (full) name.""" 400 return cls(extract.load_module(name))
Create a Module
object by supplying the module's (full) name.
575class Class(Namespace[type]): 576 """ 577 Representation of a class's documentation. 578 """ 579 580 kind = "class" 581 582 @cache 583 @_include_fullname_in_traceback 584 def __repr__(self): 585 return f"<{_decorators(self)}class {self.modulename}.{self.qualname}{_docstr(self)}{_children(self)}>" 586 587 @cached_property 588 def docstring(self) -> str: 589 doc = Doc.docstring.__get__(self) # type: ignore 590 if doc == dict.__doc__: 591 # Don't display default docstring for dict subclasses (primarily TypedDict). 592 return "" 593 is_dataclass_with_default_docstring = ( 594 dataclasses.is_dataclass(self.obj) 595 # from https://github.com/python/cpython/blob/3.10/Lib/dataclasses.py 596 and doc 597 == self.obj.__name__ 598 + str(inspect.signature(self.obj)).replace(" -> None", "") 599 ) 600 if is_dataclass_with_default_docstring: 601 return "" 602 return doc 603 604 @cached_property 605 def _var_docstrings(self) -> dict[str, str]: 606 docstrings: dict[str, str] = {} 607 for cls in self._mro: 608 for name, docstr in doc_ast.walk_tree(cls).var_docstrings.items(): 609 docstrings.setdefault(name, docstr) 610 return docstrings 611 612 @cached_property 613 def _func_docstrings(self) -> dict[str, str]: 614 docstrings: dict[str, str] = {} 615 for cls in self._mro: 616 for name, docstr in doc_ast.walk_tree(cls).func_docstrings.items(): 617 docstrings.setdefault(name, docstr) 618 return docstrings 619 620 @cached_property 621 def _var_annotations(self) -> dict[str, type]: 622 # this is a bit tricky: __annotations__ also includes annotations from parent classes, 623 # but we need to execute them in the namespace of the parent class. 624 # Our workaround for this is to walk the MRO backwards, and only update/evaluate only if the annotation changes. 625 annotations: dict[ 626 str, tuple[Any, type] 627 ] = {} # attribute -> (annotation_unresolved, annotation_resolved) 628 for cls in reversed(self._mro): 629 cls_annotations = doc_ast.walk_tree(cls).annotations.copy() 630 dynamic_annotations = _safe_getattr(cls, "__annotations__", None) 631 if isinstance(dynamic_annotations, dict): 632 for attr, unresolved_annotation in dynamic_annotations.items(): 633 cls_annotations[attr] = unresolved_annotation 634 cls_fullname = ( 635 _safe_getattr(cls, "__module__", "") + "." + cls.__qualname__ 636 ).lstrip(".") 637 638 new_annotations = { 639 attr: unresolved_annotation 640 for attr, unresolved_annotation in cls_annotations.items() 641 if attr not in annotations 642 or annotations[attr][0] is not unresolved_annotation 643 } 644 localns = _safe_getattr(cls, "__dict__", None) 645 for attr, t in resolve_annotations( 646 new_annotations, inspect.getmodule(cls), localns, cls_fullname 647 ).items(): 648 annotations[attr] = (new_annotations[attr], t) 649 650 return {k: v[1] for k, v in annotations.items()} 651 652 @cached_property 653 def _mro(self) -> tuple[type, ...]: 654 orig_bases = _safe_getattr(self.obj, "__orig_bases__", None) 655 if ( 656 orig_bases 657 and _safe_getattr(orig_bases[-1], "__name__", None) == "TypedDict" 658 ): 659 # TypedDicts have a botched __mro__. 660 # However, if the subclasses also specify TypedDict as a base class, we can use __orig_bases__ 661 # https://github.com/sphinx-doc/sphinx/pull/10806 662 return (self.obj, *orig_bases[:-1]) 663 else: 664 return self.obj.__mro__ 665 666 @cached_property 667 def _declarations(self) -> dict[str, tuple[str, str]]: 668 decls: dict[str, tuple[str, str]] = {} 669 for cls in self._mro: 670 treeinfo = doc_ast.walk_tree(cls) 671 for name in ( 672 treeinfo.var_docstrings.keys() 673 | treeinfo.func_docstrings.keys() 674 | treeinfo.annotations.keys() 675 ): 676 decls.setdefault(name, (cls.__module__, f"{cls.__qualname__}.{name}")) 677 for name in cls.__dict__: 678 decls.setdefault(name, (cls.__module__, f"{cls.__qualname__}.{name}")) 679 if decls.get("__init__", None) == ("builtins", "object.__init__"): 680 decls["__init__"] = ( 681 self.obj.__module__, 682 f"{self.obj.__qualname__}.__init__", 683 ) 684 return decls 685 686 def _taken_from(self, member_name: str, obj: Any) -> tuple[str, str]: 687 try: 688 return self._declarations[member_name] 689 except KeyError: # pragma: no cover 690 # TypedDict botches __mro__ and may need special casing here. 691 # One workaround is to also specify TypedDict as a base class, see pdoc.doc.Class._mro. 692 warnings.warn( 693 f"Cannot determine where {self.fullname}.{member_name} is taken from, assuming current file." 694 ) 695 return self.modulename, f"{self.qualname}.{member_name}" 696 697 @cached_property 698 def own_members(self) -> list[Doc]: 699 members = self._members_by_origin.get((self.modulename, self.qualname), []) 700 if self.taken_from != (self.modulename, self.qualname): 701 # .taken_from may be != (self.modulename, self.qualname), for example when 702 # a module re-exports a class from a private submodule. 703 members += self._members_by_origin.get(self.taken_from, []) 704 return members 705 706 @cached_property 707 def _member_objects(self) -> dict[str, Any]: 708 unsorted: dict[str, Any] = {} 709 for cls in self._mro: 710 for name, obj in cls.__dict__.items(): 711 unsorted.setdefault(name, obj) 712 for name in self._var_docstrings: 713 unsorted.setdefault(name, empty) 714 for name in self._var_annotations: 715 unsorted.setdefault(name, empty) 716 717 init_has_no_doc = unsorted.get("__init__", object.__init__).__doc__ in ( 718 None, 719 object.__init__.__doc__, 720 ) 721 if init_has_no_doc: 722 if inspect.isabstract(self.obj): 723 # Special case: We don't want to show constructors for abstract base classes unless 724 # they have a custom docstring. 725 del unsorted["__init__"] 726 elif issubclass(self.obj, enum.Enum): 727 # Special case: Do not show a constructor for enums. They are typically not constructed by users. 728 # The alternative would be showing __new__, as __call__ is too verbose. 729 del unsorted["__init__"] 730 elif issubclass(self.obj, dict): 731 # Special case: Do not show a constructor for dict subclasses. 732 unsorted.pop( 733 "__init__", None 734 ) # TypedDict subclasses may not have __init__. 735 else: 736 # Check if there's a helpful Metaclass.__call__ or Class.__new__. This dance is very similar to 737 # https://github.com/python/cpython/blob/9feae41c4f04ca27fd2c865807a5caeb50bf4fc4/Lib/inspect.py#L2359-L2376 738 call = _safe_getattr(type(self.obj), "__call__", None) 739 custom_call_with_custom_docstring = ( 740 call is not None 741 and not isinstance(call, NonUserDefinedCallables) 742 and call.__doc__ not in (None, object.__call__.__doc__) 743 ) 744 if custom_call_with_custom_docstring: 745 unsorted["__init__"] = call 746 else: 747 # Does our class define a custom __new__ method? 748 new = _safe_getattr(self.obj, "__new__", None) 749 custom_new_with_custom_docstring = ( 750 new is not None 751 and not isinstance(new, NonUserDefinedCallables) 752 and new.__doc__ not in (None, object.__new__.__doc__) 753 ) 754 if custom_new_with_custom_docstring: 755 unsorted["__init__"] = new 756 757 sorted: dict[str, Any] = {} 758 for cls in self._mro: 759 sorted, unsorted = doc_ast.sort_by_source(cls, sorted, unsorted) 760 sorted.update(unsorted) 761 return sorted 762 763 @cached_property 764 def bases(self) -> list[tuple[str, str, str]]: 765 """ 766 A list of all base classes, i.e. all immediate parent classes. 767 768 Each parent class is represented as a `(modulename, qualname, display_text)` tuple. 769 """ 770 bases = [] 771 for x in _safe_getattr(self.obj, "__orig_bases__", self.obj.__bases__): 772 if x is object: 773 continue 774 o = get_origin(x) 775 if o: 776 bases.append((o.__module__, o.__qualname__, str(x))) 777 elif x.__module__ == self.modulename: 778 bases.append((x.__module__, x.__qualname__, x.__qualname__)) 779 else: 780 bases.append( 781 (x.__module__, x.__qualname__, f"{x.__module__}.{x.__qualname__}") 782 ) 783 return bases 784 785 @cached_property 786 def decorators(self) -> list[str]: 787 """A list of all decorators the class is decorated with.""" 788 decorators = [] 789 for t in doc_ast.parse(self.obj).decorator_list: 790 decorators.append(f"@{doc_ast.unparse(t)}") 791 return decorators 792 793 @cached_property 794 def class_variables(self) -> list[Variable]: 795 """ 796 A list of all documented class variables in the class. 797 798 Class variables are variables that are explicitly annotated with `typing.ClassVar`. 799 All other variables are treated as instance variables. 800 """ 801 return [ 802 x 803 for x in self.members.values() 804 if isinstance(x, Variable) and x.is_classvar 805 ] 806 807 @cached_property 808 def instance_variables(self) -> list[Variable]: 809 """ 810 A list of all instance variables in the class. 811 """ 812 return [ 813 x 814 for x in self.members.values() 815 if isinstance(x, Variable) and not x.is_classvar 816 ] 817 818 @cached_property 819 def classmethods(self) -> list[Function]: 820 """ 821 A list of all documented `@classmethod`s. 822 """ 823 return [ 824 x 825 for x in self.members.values() 826 if isinstance(x, Function) and x.is_classmethod 827 ] 828 829 @cached_property 830 def staticmethods(self) -> list[Function]: 831 """ 832 A list of all documented `@staticmethod`s. 833 """ 834 return [ 835 x 836 for x in self.members.values() 837 if isinstance(x, Function) and x.is_staticmethod 838 ] 839 840 @cached_property 841 def methods(self) -> list[Function]: 842 """ 843 A list of all documented methods in the class that are neither static- nor classmethods. 844 """ 845 return [ 846 x 847 for x in self.members.values() 848 if isinstance(x, Function) 849 and not x.is_staticmethod 850 and not x.is_classmethod 851 ]
Representation of a class's documentation.
The docstring for this object. It has already been cleaned by inspect.cleandoc
.
If no docstring can be found, an empty string is returned.
A list of all base classes, i.e. all immediate parent classes.
Each parent class is represented as a (modulename, qualname, display_text)
tuple.
A list of all documented class variables in the class.
Class variables are variables that are explicitly annotated with typing.ClassVar
.
All other variables are treated as instance variables.
A list of all documented methods in the class that are neither static- nor classmethods.
860class Function(Doc[types.FunctionType]): 861 """ 862 Representation of a function's documentation. 863 864 This class covers all "flavors" of functions, for example it also 865 supports `@classmethod`s or `@staticmethod`s. 866 """ 867 868 kind = "function" 869 870 wrapped: WrappedFunction 871 """The original wrapped function (e.g., `staticmethod(func)`)""" 872 873 obj: types.FunctionType 874 """The unwrapped "real" function.""" 875 876 def __init__( 877 self, 878 modulename: str, 879 qualname: str, 880 func: WrappedFunction, 881 taken_from: tuple[str, str], 882 ): 883 """Initialize a function's documentation object.""" 884 unwrapped: types.FunctionType 885 if isinstance(func, (classmethod, staticmethod)): 886 unwrapped = func.__func__ # type: ignore 887 elif isinstance(func, singledispatchmethod): 888 unwrapped = func.func # type: ignore 889 else: 890 unwrapped = func 891 super().__init__(modulename, qualname, unwrapped, taken_from) 892 self.wrapped = func 893 894 @cache 895 @_include_fullname_in_traceback 896 def __repr__(self): 897 if self.is_classmethod: 898 t = "class" 899 elif self.is_staticmethod: 900 t = "static" 901 elif self.qualname != _safe_getattr(self.obj, "__name__", None): 902 t = "method" 903 else: 904 t = "function" 905 return f"<{_decorators(self)}{t} {self.funcdef} {self.name}{self.signature}: ...{_docstr(self)}>" 906 907 @cached_property 908 def docstring(self) -> str: 909 doc = Doc.docstring.__get__(self) # type: ignore 910 if not doc: 911 # inspect.getdoc fails for inherited @classmethods and unbound @property descriptors. 912 # We now do an ugly dance to obtain the bound object instead, 913 # that somewhat resembles what inspect._findclass is doing. 914 cls = sys.modules.get(_safe_getattr(self.obj, "__module__", None), None) 915 for name in _safe_getattr(self.obj, "__qualname__", "").split(".")[:-1]: 916 cls = _safe_getattr(cls, name, None) 917 918 unbound = _safe_getattr(cls, "__dict__", {}).get(self.name) 919 is_classmethod_property = isinstance(unbound, classmethod) and isinstance( 920 unbound.__func__, (property, cached_property) 921 ) 922 if not is_classmethod_property: 923 # We choke on @classmethod @property, but that's okay because it's been deprecated with Python 3.11. 924 # Directly accessing them would give us the return value, which has the wrong docstring. 925 doc = _safe_getdoc(_safe_getattr(cls, self.name, None)) 926 927 if doc == object.__init__.__doc__: 928 # inspect.getdoc(Foo.__init__) returns the docstring, for object.__init__ if left undefined... 929 return "" 930 else: 931 return doc 932 933 @cached_property 934 def is_classmethod(self) -> bool: 935 """ 936 `True` if this function is a `@classmethod`, `False` otherwise. 937 """ 938 return isinstance(self.wrapped, classmethod) 939 940 @cached_property 941 def is_staticmethod(self) -> bool: 942 """ 943 `True` if this function is a `@staticmethod`, `False` otherwise. 944 """ 945 return isinstance(self.wrapped, staticmethod) 946 947 @cached_property 948 def decorators(self) -> list[str]: 949 """A list of all decorators the function is decorated with.""" 950 decorators = [] 951 obj: types.FunctionType = self.obj # type: ignore 952 for t in doc_ast.parse(obj).decorator_list: 953 decorators.append(f"@{doc_ast.unparse(t)}") 954 return decorators 955 956 @cached_property 957 def funcdef(self) -> str: 958 """ 959 The string of keywords used to define the function, i.e. `"def"` or `"async def"`. 960 """ 961 if inspect.iscoroutinefunction(self.obj) or inspect.isasyncgenfunction( 962 self.obj 963 ): 964 return "async def" 965 else: 966 return "def" 967 968 @cached_property 969 def signature(self) -> inspect.Signature: 970 """ 971 The function's signature. 972 973 This usually returns an instance of `_PrettySignature`, a subclass of `inspect.Signature` 974 that contains pdoc-specific optimizations. For example, long argument lists are split over multiple lines 975 in repr(). Additionally, all types are already resolved. 976 977 If the signature cannot be determined, a placeholder Signature object is returned. 978 """ 979 if self.obj is object.__init__: 980 # there is a weird edge case were inspect.signature returns a confusing (self, /, *args, **kwargs) 981 # signature for the default __init__ method. 982 return inspect.Signature() 983 try: 984 sig = _PrettySignature.from_callable(self.obj) 985 except Exception: 986 return inspect.Signature( 987 [inspect.Parameter("unknown", inspect.Parameter.POSITIONAL_OR_KEYWORD)] 988 ) 989 mod = inspect.getmodule(self.obj) 990 globalns = _safe_getattr(mod, "__dict__", {}) 991 localns = globalns 992 for parent_cls_name in self.qualname.split(".")[:-1]: 993 parent_cls = localns.get(parent_cls_name, object) 994 localns = _safe_getattr(parent_cls, "__dict__", None) 995 if localns is None: 996 break # pragma: no cover 997 998 if self.name == "__init__": 999 sig = sig.replace(return_annotation=empty) 1000 else: 1001 sig = sig.replace( 1002 return_annotation=safe_eval_type( 1003 sig.return_annotation, globalns, localns, mod, self.fullname 1004 ) 1005 ) 1006 for p in sig.parameters.values(): 1007 p._annotation = safe_eval_type(p.annotation, globalns, localns, mod, self.fullname) # type: ignore 1008 return sig 1009 1010 @cached_property 1011 def signature_without_self(self) -> inspect.Signature: 1012 """Like `signature`, but without the first argument. 1013 1014 This is useful to display constructors. 1015 """ 1016 return self.signature.replace( 1017 parameters=list(self.signature.parameters.values())[1:] 1018 )
Representation of a function's documentation.
This class covers all "flavors" of functions, for example it also
supports @classmethod
s or @staticmethod
s.
876 def __init__( 877 self, 878 modulename: str, 879 qualname: str, 880 func: WrappedFunction, 881 taken_from: tuple[str, str], 882 ): 883 """Initialize a function's documentation object.""" 884 unwrapped: types.FunctionType 885 if isinstance(func, (classmethod, staticmethod)): 886 unwrapped = func.__func__ # type: ignore 887 elif isinstance(func, singledispatchmethod): 888 unwrapped = func.func # type: ignore 889 else: 890 unwrapped = func 891 super().__init__(modulename, qualname, unwrapped, taken_from) 892 self.wrapped = func
Initialize a function's documentation object.
The original wrapped function (e.g., staticmethod(func)
)
The docstring for this object. It has already been cleaned by inspect.cleandoc
.
If no docstring can be found, an empty string is returned.
The function's signature.
This usually returns an instance of _PrettySignature
, a subclass of inspect.Signature
that contains pdoc-specific optimizations. For example, long argument lists are split over multiple lines
in repr(). Additionally, all types are already resolved.
If the signature cannot be determined, a placeholder Signature object is returned.
Like signature
, but without the first argument.
This is useful to display constructors.
Inherited Members
1021class Variable(Doc[None]): 1022 """ 1023 Representation of a variable's documentation. This includes module, class and instance variables. 1024 """ 1025 1026 kind = "variable" 1027 1028 default_value: Any | empty # technically Any includes empty, but this conveys intent. 1029 """ 1030 The variable's default value. 1031 1032 In some cases, no default value is known. This may either be because a variable is only defined in the constructor, 1033 or it is only declared with a type annotation without assignment (`foo: int`). 1034 To distinguish this case from a default value of `None`, `pdoc.doc_types.empty` is used as a placeholder. 1035 """ 1036 1037 annotation: type | empty 1038 """ 1039 The variable's type annotation. 1040 1041 If there is no type annotation, `pdoc.doc_types.empty` is used as a placeholder. 1042 """ 1043 1044 def __init__( 1045 self, 1046 modulename: str, 1047 qualname: str, 1048 *, 1049 taken_from: tuple[str, str], 1050 docstring: str, 1051 annotation: type | empty = empty, 1052 default_value: Any | empty = empty, 1053 ): 1054 """ 1055 Construct a variable doc object. 1056 1057 While classes and functions can introspect themselves to see their docstring, 1058 variables can't do that as we don't have a "variable object" we could query. 1059 As such, docstring, declaration location, type annotation, and the default value 1060 must be passed manually in the constructor. 1061 """ 1062 super().__init__(modulename, qualname, None, taken_from) 1063 # noinspection PyPropertyAccess 1064 self.docstring = inspect.cleandoc(docstring) 1065 self.annotation = annotation 1066 self.default_value = default_value 1067 1068 @cache 1069 @_include_fullname_in_traceback 1070 def __repr__(self): 1071 if self.default_value_str: 1072 default = f" = {self.default_value_str}" 1073 else: 1074 default = "" 1075 return f'<var {self.qualname.rsplit(".")[-1]}{self.annotation_str}{default}{_docstr(self)}>' 1076 1077 @cached_property 1078 def is_classvar(self) -> bool: 1079 """`True` if the variable is a class variable, `False` otherwise.""" 1080 if get_origin(self.annotation) is ClassVar: 1081 return True 1082 else: 1083 return False 1084 1085 @cached_property 1086 def is_enum_member(self) -> bool: 1087 """`True` if the variable is an enum member, `False` otherwise.""" 1088 if isinstance(self.default_value, enum.Enum): 1089 return True 1090 else: 1091 return False 1092 1093 @cached_property 1094 def default_value_str(self) -> str: 1095 """The variable's default value as a pretty-printed str.""" 1096 if self.default_value is empty: 1097 return "" 1098 1099 try: 1100 pretty = repr(self.default_value) 1101 except Exception as e: 1102 warnings.warn(f"repr({self.fullname}) raised an exception ({e!r})") 1103 return "" 1104 1105 pretty = re.sub(r" at 0x[0-9a-fA-F]+(?=>)", "", pretty) 1106 return pretty 1107 1108 @cached_property 1109 def annotation_str(self) -> str: 1110 """The variable's type annotation as a pretty-printed str.""" 1111 if self.annotation is not empty: 1112 return f": {formatannotation(self.annotation)}" 1113 else: 1114 return ""
Representation of a variable's documentation. This includes module, class and instance variables.
1044 def __init__( 1045 self, 1046 modulename: str, 1047 qualname: str, 1048 *, 1049 taken_from: tuple[str, str], 1050 docstring: str, 1051 annotation: type | empty = empty, 1052 default_value: Any | empty = empty, 1053 ): 1054 """ 1055 Construct a variable doc object. 1056 1057 While classes and functions can introspect themselves to see their docstring, 1058 variables can't do that as we don't have a "variable object" we could query. 1059 As such, docstring, declaration location, type annotation, and the default value 1060 must be passed manually in the constructor. 1061 """ 1062 super().__init__(modulename, qualname, None, taken_from) 1063 # noinspection PyPropertyAccess 1064 self.docstring = inspect.cleandoc(docstring) 1065 self.annotation = annotation 1066 self.default_value = default_value
Construct a variable doc object.
While classes and functions can introspect themselves to see their docstring, variables can't do that as we don't have a "variable object" we could query. As such, docstring, declaration location, type annotation, and the default value must be passed manually in the constructor.
The variable's default value.
In some cases, no default value is known. This may either be because a variable is only defined in the constructor,
or it is only declared with a type annotation without assignment (foo: int
).
To distinguish this case from a default value of None
, pdoc.doc_types.empty
is used as a placeholder.
The variable's type annotation.
If there is no type annotation, pdoc.doc_types.empty
is used as a placeholder.
The docstring for this object. It has already been cleaned by inspect.cleandoc
.
If no docstring can be found, an empty string is returned.