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