Edit on GitHub

pdoc.doc

This module defines pdoc's documentation objects. A documentation object corresponds to something in your Python code that has a docstring or type annotation. Typically, this only includes modules, classes, functions and methods. However, pdoc adds support for extracting documentation from the abstract syntax tree, which means that variables (module, class or instance) are supported too.

There are four main types of documentation objects:

All documentation types make heavy use of @functools.cached_property decorators. This means they have a large set of attributes that are lazily computed on first access. By convention, all attributes are read-only, although this is not enforced at runtime.

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

A base class for all documentation objects.

Doc(modulename: str, qualname: str, obj: ~T, taken_from: tuple[str, str])
128    def __init__(
129        self, modulename: str, qualname: str, obj: T, taken_from: tuple[str, str]
130    ):
131        """
132        Initializes a documentation object, where
133        `modulename` is the name this module is defined in,
134        `qualname` contains a dotted path leading to the object from the module top-level, and
135        `obj` is the object to document.
136        """
137        self.modulename = modulename
138        self.qualname = qualname
139        self.obj = obj
140        self.taken_from = taken_from

Initializes a documentation object, where modulename is the name this module is defined in, qualname contains a dotted path leading to the object from the module top-level, and obj is the object to document.

modulename: str

The module that this object is in, for example pdoc.doc.

qualname: str

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.

obj: ~T

The underlying Python object.

taken_from: tuple[str, str]

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

kind: ClassVar[str]

The type of the doc object, either "module", "class", "function", or "variable".

fullname: str

The full qualified name of this doc object, for example pdoc.doc.Doc.

name: str

The name of this object. For top-level functions and classes, this is equal to the qualname attribute.

docstring: str

The docstring for this object. It has already been cleaned by inspect.cleandoc.

If no docstring can be found, an empty string is returned.

source: str

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.

source_file: pathlib.Path | None

The name of the Python source file in which this object was defined. None for built-in objects.

source_lines: tuple[int, int] | None

Return a (start, end) line number tuple for this object.

If no source file can be found, None is returned.

is_inherited: bool

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.

class Namespace(pdoc.doc.Doc[~T]):
210class Namespace(Doc[T], metaclass=ABCMeta):
211    """
212    A documentation object that can have children. In other words, either a module or a class.
213    """
214
215    @cached_property
216    @abstractmethod
217    def _member_objects(self) -> dict[str, Any]:
218        """
219        A mapping from *all* public and private member names to their Python objects.
220        """
221
222    @cached_property
223    @abstractmethod
224    def _var_docstrings(self) -> dict[str, str]:
225        """A mapping from some member variable names to their docstrings."""
226
227    @cached_property
228    @abstractmethod
229    def _func_docstrings(self) -> dict[str, str]:
230        """A mapping from some member function names to their raw (not processed by any @decorators) docstrings."""
231
232    @cached_property
233    @abstractmethod
234    def _var_annotations(self) -> dict[str, Any]:
235        """A mapping from some member variable names to their type annotations."""
236
237    @abstractmethod
238    def _taken_from(self, member_name: str, obj: Any) -> tuple[str, str]:
239        """The location this member was taken from. If unknown, (modulename, qualname) is returned."""
240
241    @cached_property
242    @abstractmethod
243    def own_members(self) -> list[Doc]:
244        """A list of all own (i.e. non-inherited) `members`."""
245
246    @cached_property
247    def members(self) -> dict[str, Doc]:
248        """A mapping from all members to their documentation objects.
249
250        This mapping includes private members; they are only filtered out as part of the template logic.
251        Constructors for enums, dicts, and abstract base classes are not picked up unless they have a custom docstring.
252        """
253        members: dict[str, Doc] = {}
254        for name, obj in self._member_objects.items():
255            qualname = f"{self.qualname}.{name}".lstrip(".")
256            taken_from = self._taken_from(name, obj)
257            doc: Doc[Any]
258
259            is_classmethod = isinstance(obj, classmethod)
260            is_property = (
261                isinstance(obj, (property, cached_property))
262                or
263                # Python 3.9 - 3.10: @classmethod @property is allowed.
264                is_classmethod
265                and isinstance(obj.__func__, (property, cached_property))
266            )
267            if is_property:
268                func = obj
269                if is_classmethod:
270                    func = obj.__func__
271                if isinstance(func, property):
272                    func = func.fget
273                else:
274                    assert isinstance(func, cached_property)
275                    func = func.func
276
277                doc_f = Function(self.modulename, qualname, func, taken_from)
278                doc = Variable(
279                    self.modulename,
280                    qualname,
281                    docstring=doc_f.docstring,
282                    annotation=doc_f.signature.return_annotation,
283                    default_value=empty,
284                    taken_from=taken_from,
285                )
286            elif inspect.isroutine(obj):
287                doc = Function(self.modulename, qualname, obj, taken_from)  # type: ignore
288            elif (
289                inspect.isclass(obj)
290                and obj is not empty
291                and not isinstance(obj, GenericAlias)
292                and obj.__qualname__.rpartition(".")[2] == qualname.rpartition(".")[2]
293            ):
294                # `dict[str,str]` is a GenericAlias instance. We want to render type aliases as variables though.
295                doc = Class(self.modulename, qualname, obj, taken_from)
296            elif inspect.ismodule(obj):
297                if os.environ.get("PDOC_SUBMODULES"):  # pragma: no cover
298                    doc = Module.from_name(obj.__name__)
299                else:
300                    continue
301            elif inspect.isdatadescriptor(obj):
302                doc = Variable(
303                    self.modulename,
304                    qualname,
305                    docstring=getattr(obj, "__doc__", None) or "",
306                    annotation=self._var_annotations.get(name, empty),
307                    default_value=empty,
308                    taken_from=taken_from,
309                )
310            else:
311                doc = Variable(
312                    self.modulename,
313                    qualname,
314                    docstring="",
315                    annotation=self._var_annotations.get(name, empty),
316                    default_value=obj,
317                    taken_from=taken_from,
318                )
319            if self._var_docstrings.get(name):
320                doc.docstring = self._var_docstrings[name]
321            if self._func_docstrings.get(name) and not doc.docstring:
322                doc.docstring = self._func_docstrings[name]
323            members[doc.name] = doc
324
325        if isinstance(self, Module):
326            # quirk: doc_pyi expects .members to be set already
327            self.members = members  # type: ignore
328            doc_pyi.include_typeinfo_from_stub_files(self)
329
330        return members
331
332    @cached_property
333    def _members_by_origin(self) -> dict[tuple[str, str], list[Doc]]:
334        """A mapping from (modulename, qualname) locations to the attributes taken from that path"""
335        locations: dict[tuple[str, str], list[Doc]] = {}
336        for member in self.members.values():
337            mod, qualname = member.taken_from
338            parent_qualname = ".".join(qualname.rsplit(".", maxsplit=1)[:-1])
339            locations.setdefault((mod, parent_qualname), [])
340            locations[(mod, parent_qualname)].append(member)
341        return locations
342
343    @cached_property
344    def inherited_members(self) -> dict[tuple[str, str], list[Doc]]:
345        """A mapping from (modulename, qualname) locations to the attributes inherited from that path"""
346        return {
347            k: v
348            for k, v in self._members_by_origin.items()
349            if k not in (self.taken_from, (self.modulename, self.qualname))
350        }
351
352    @cached_property
353    def flattened_own_members(self) -> list[Doc]:
354        """
355        A list of all documented members and their child classes, recursively.
356        """
357        flattened = []
358        for x in self.own_members:
359            flattened.append(x)
360            if isinstance(x, Class):
361                flattened.extend(
362                    [cls for cls in x.flattened_own_members if isinstance(cls, Class)]
363                )
364        return flattened
365
366    @cache
367    def get(self, identifier: str) -> Doc | None:
368        """Returns the documentation object for a particular identifier, or `None` if the identifier cannot be found."""
369        head, _, tail = identifier.partition(".")
370        if tail:
371            h = self.members.get(head, None)
372            if isinstance(h, Class):
373                return h.get(tail)
374            return None
375        else:
376            return self.members.get(identifier, None)

A documentation object that can have children. In other words, either a module or a class.

own_members: list[pdoc.doc.Doc]

A list of all own (i.e. non-inherited) members.

members: dict[str, pdoc.doc.Doc]

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.

inherited_members: dict[tuple[str, str], list[pdoc.doc.Doc]]

A mapping from (modulename, qualname) locations to the attributes inherited from that path

flattened_own_members: list[pdoc.doc.Doc]

A list of all documented members and their child classes, recursively.

@cache
def get(self, identifier: str) -> pdoc.doc.Doc | None:
366    @cache
367    def get(self, identifier: str) -> Doc | None:
368        """Returns the documentation object for a particular identifier, or `None` if the identifier cannot be found."""
369        head, _, tail = identifier.partition(".")
370        if tail:
371            h = self.members.get(head, None)
372            if isinstance(h, Class):
373                return h.get(tail)
374            return None
375        else:
376            return self.members.get(identifier, None)

Returns the documentation object for a particular identifier, or None if the identifier cannot be found.

class Module(pdoc.doc.Namespace[module]):
379class Module(Namespace[types.ModuleType]):
380    """
381    Representation of a module's documentation.
382    """
383
384    def __init__(
385        self,
386        module: types.ModuleType,
387    ):
388        """
389        Creates a documentation object given the actual
390        Python module object.
391        """
392        super().__init__(module.__name__, "", module, (module.__name__, ""))
393
394    kind = "module"
395
396    @classmethod
397    @cache
398    def from_name(cls, name: str) -> Module:
399        """Create a `Module` object by supplying the module's (full) name."""
400        return cls(extract.load_module(name))
401
402    @cache
403    @_include_fullname_in_traceback
404    def __repr__(self):
405        return f"<module {self.fullname}{_docstr(self)}{_children(self)}>"
406
407    @cached_property
408    def is_package(self) -> bool:
409        """
410        `True` if the module is a package, `False` otherwise.
411
412        Packages are a special kind of module that may have submodules.
413        Typically, this means that this file is in a directory named like the
414        module with the name `__init__.py`.
415        """
416        return _safe_getattr(self.obj, "__path__", None) is not None
417
418    @cached_property
419    def _var_docstrings(self) -> dict[str, str]:
420        return doc_ast.walk_tree(self.obj).var_docstrings
421
422    @cached_property
423    def _func_docstrings(self) -> dict[str, str]:
424        return doc_ast.walk_tree(self.obj).func_docstrings
425
426    @cached_property
427    def _var_annotations(self) -> dict[str, Any]:
428        annotations = doc_ast.walk_tree(self.obj).annotations.copy()
429        for k, v in _safe_getattr(self.obj, "__annotations__", {}).items():
430            annotations[k] = v
431
432        return resolve_annotations(annotations, self.obj, None, self.fullname)
433
434    def _taken_from(self, member_name: str, obj: Any) -> tuple[str, str]:
435        if obj is empty:
436            return self.modulename, f"{self.qualname}.{member_name}".lstrip(".")
437        if isinstance(obj, types.ModuleType):
438            return obj.__name__, ""
439
440        mod = _safe_getattr(obj, "__module__", None)
441        qual = _safe_getattr(obj, "__qualname__", None)
442        if mod and qual and "<locals>" not in qual:
443            return mod, qual
444        else:
445            # This might be wrong, but it's the best guess we have.
446            return (mod or self.modulename), f"{self.qualname}.{member_name}".lstrip(
447                "."
448            )
449
450    @cached_property
451    def own_members(self) -> list[Doc]:
452        return list(self.members.values())
453
454    @cached_property
455    def submodules(self) -> list[Module]:
456        """A list of all (direct) submodules."""
457        if not self.is_package:
458            return []
459
460        include: Callable[[str], bool]
461        mod_all = _safe_getattr(self.obj, "__all__", False)
462        if mod_all is not False:
463            mod_all_pos = {name: i for i, name in enumerate(mod_all)}
464            include = mod_all_pos.__contains__
465        else:
466
467            def include(name: str) -> bool:
468                # optimization: we don't even try to load modules starting with an underscore as they would not be
469                # visible by default. The downside of this is that someone who overrides `is_public` will miss those
470                # entries, the upsides are 1) better performance and 2) less warnings because of import failures
471                # (think of OS-specific modules, e.g. _linux.py failing to import on Windows).
472                return not name.startswith("_")
473
474        submodules = []
475        for mod in pkgutil.iter_modules(self.obj.__path__, f"{self.fullname}."):
476            _, _, mod_name = mod.name.rpartition(".")
477            if not include(mod_name):
478                continue
479            try:
480                module = Module.from_name(mod.name)
481            except RuntimeError:
482                warnings.warn(f"Couldn't import {mod.name}:\n{traceback.format_exc()}")
483                continue
484            submodules.append(module)
485
486        if mod_all:
487            submodules = sorted(submodules, key=lambda m: mod_all_pos[m.name])
488
489        return submodules
490
491    @cached_property
492    def _documented_members(self) -> set[str]:
493        return (
494            self._var_docstrings.keys()
495            | self._func_docstrings.keys()
496            | self._var_annotations.keys()
497        )
498
499    @cached_property
500    def _member_objects(self) -> dict[str, Any]:
501        members = {}
502
503        all_: list[str] | None = _safe_getattr(self.obj, "__all__", None)
504        if all_ is not None:
505            for name in all_:
506                if not isinstance(name, str):
507                    # Gracefully handle the case where objects are directly specified in __all__.
508                    name = _safe_getattr(name, "__name__", str(name))
509                if name in self.obj.__dict__:
510                    val = self.obj.__dict__[name]
511                elif name in self._var_annotations:
512                    val = empty
513                else:
514                    # this may be an unimported submodule, try importing.
515                    # (https://docs.python.org/3/tutorial/modules.html#importing-from-a-package)
516                    try:
517                        val = extract.load_module(f"{self.modulename}.{name}")
518                    except RuntimeError as e:
519                        warnings.warn(
520                            f"Found {name!r} in {self.modulename}.__all__, but it does not resolve: {e}"
521                        )
522                        val = empty
523                members[name] = val
524
525        else:
526            # Starting with Python 3.10, __annotations__ is created on demand,
527            # so we make a copy here as obj.__dict__ is changed while we iterate over it.
528            # Additionally, accessing self._documented_members may lead to the execution of TYPE_CHECKING blocks,
529            # which may also modify obj.__dict__. (https://github.com/mitmproxy/pdoc/issues/351)
530            for name, obj in list(self.obj.__dict__.items()):
531                # We already exclude everything here that is imported, only a TypeVar,
532                # or a variable without annotation and docstring.
533                # If one needs to document one of these things, __all__ is the correct way.
534                obj_module = inspect.getmodule(obj)
535                declared_in_this_module = self.obj.__name__ == _safe_getattr(
536                    obj_module, "__name__", None
537                )
538                include_in_docs = name in self._documented_members or (
539                    declared_in_this_module and not isinstance(obj, TypeVar)
540                )
541                if include_in_docs:
542                    members[name] = obj
543            for name in self._var_docstrings:
544                members.setdefault(name, empty)
545            for name in self._func_docstrings:
546                members.setdefault(name, empty)
547
548            members, notfound = doc_ast.sort_by_source(self.obj, {}, members)
549            members.update(notfound)
550
551        return members
552
553    @cached_property
554    def variables(self) -> list[Variable]:
555        """
556        A list of all documented module level variables.
557        """
558        return [x for x in self.members.values() if isinstance(x, Variable)]
559
560    @cached_property
561    def classes(self) -> list[Class]:
562        """
563        A list of all documented module level classes.
564        """
565        return [x for x in self.members.values() if isinstance(x, Class)]
566
567    @cached_property
568    def functions(self) -> list[Function]:
569        """
570        A list of all documented module level functions.
571        """
572        return [x for x in self.members.values() if isinstance(x, Function)]

Representation of a module's documentation.

Module(module: module)
384    def __init__(
385        self,
386        module: types.ModuleType,
387    ):
388        """
389        Creates a documentation object given the actual
390        Python module object.
391        """
392        super().__init__(module.__name__, "", module, (module.__name__, ""))

Creates a documentation object given the actual Python module object.

kind = 'module'

The type of the doc object, either "module", "class", "function", or "variable".

@classmethod
@cache
def from_name(cls, name: str) -> pdoc.doc.Module:
396    @classmethod
397    @cache
398    def from_name(cls, name: str) -> Module:
399        """Create a `Module` object by supplying the module's (full) name."""
400        return cls(extract.load_module(name))

Create a Module object by supplying the module's (full) name.

is_package: bool

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.

own_members: list[pdoc.doc.Doc]

A list of all own (i.e. non-inherited) members.

submodules: list[pdoc.doc.Module]

A list of all (direct) submodules.

variables: list[pdoc.doc.Variable]

A list of all documented module level variables.

classes: list[pdoc.doc.Class]

A list of all documented module level classes.

functions: list[pdoc.doc.Function]

A list of all documented module level functions.

class Class(pdoc.doc.Namespace[type]):
575class Class(Namespace[type]):
576    """
577    Representation of a class's documentation.
578    """
579
580    kind = "class"
581
582    @cache
583    @_include_fullname_in_traceback
584    def __repr__(self):
585        return f"<{_decorators(self)}class {self.modulename}.{self.qualname}{_docstr(self)}{_children(self)}>"
586
587    @cached_property
588    def docstring(self) -> str:
589        doc = Doc.docstring.__get__(self)  # type: ignore
590        if doc == dict.__doc__:
591            # Don't display default docstring for dict subclasses (primarily TypedDict).
592            return ""
593        is_dataclass_with_default_docstring = (
594            dataclasses.is_dataclass(self.obj)
595            # from https://github.com/python/cpython/blob/3.10/Lib/dataclasses.py
596            and doc
597            == self.obj.__name__
598            + str(inspect.signature(self.obj)).replace(" -> None", "")
599        )
600        if is_dataclass_with_default_docstring:
601            return ""
602        return doc
603
604    @cached_property
605    def _var_docstrings(self) -> dict[str, str]:
606        docstrings: dict[str, str] = {}
607        for cls in self._mro:
608            for name, docstr in doc_ast.walk_tree(cls).var_docstrings.items():
609                docstrings.setdefault(name, docstr)
610        return docstrings
611
612    @cached_property
613    def _func_docstrings(self) -> dict[str, str]:
614        docstrings: dict[str, str] = {}
615        for cls in self._mro:
616            for name, docstr in doc_ast.walk_tree(cls).func_docstrings.items():
617                docstrings.setdefault(name, docstr)
618        return docstrings
619
620    @cached_property
621    def _var_annotations(self) -> dict[str, type]:
622        # this is a bit tricky: __annotations__ also includes annotations from parent classes,
623        # but we need to execute them in the namespace of the parent class.
624        # Our workaround for this is to walk the MRO backwards, and only update/evaluate only if the annotation changes.
625        annotations: dict[
626            str, tuple[Any, type]
627        ] = {}  # attribute -> (annotation_unresolved, annotation_resolved)
628        for cls in reversed(self._mro):
629            cls_annotations = doc_ast.walk_tree(cls).annotations.copy()
630            dynamic_annotations = _safe_getattr(cls, "__annotations__", None)
631            if isinstance(dynamic_annotations, dict):
632                for attr, unresolved_annotation in dynamic_annotations.items():
633                    cls_annotations[attr] = unresolved_annotation
634            cls_fullname = (
635                _safe_getattr(cls, "__module__", "") + "." + cls.__qualname__
636            ).lstrip(".")
637
638            new_annotations = {
639                attr: unresolved_annotation
640                for attr, unresolved_annotation in cls_annotations.items()
641                if attr not in annotations
642                or annotations[attr][0] is not unresolved_annotation
643            }
644            localns = _safe_getattr(cls, "__dict__", None)
645            for attr, t in resolve_annotations(
646                new_annotations, inspect.getmodule(cls), localns, cls_fullname
647            ).items():
648                annotations[attr] = (new_annotations[attr], t)
649
650        return {k: v[1] for k, v in annotations.items()}
651
652    @cached_property
653    def _mro(self) -> tuple[type, ...]:
654        orig_bases = _safe_getattr(self.obj, "__orig_bases__", None)
655        if (
656            orig_bases
657            and _safe_getattr(orig_bases[-1], "__name__", None) == "TypedDict"
658        ):
659            # TypedDicts have a botched __mro__.
660            # However, if the subclasses also specify TypedDict as a base class, we can use __orig_bases__
661            # https://github.com/sphinx-doc/sphinx/pull/10806
662            return (self.obj, *orig_bases[:-1])
663        else:
664            return self.obj.__mro__
665
666    @cached_property
667    def _declarations(self) -> dict[str, tuple[str, str]]:
668        decls: dict[str, tuple[str, str]] = {}
669        for cls in self._mro:
670            treeinfo = doc_ast.walk_tree(cls)
671            for name in (
672                treeinfo.var_docstrings.keys()
673                | treeinfo.func_docstrings.keys()
674                | treeinfo.annotations.keys()
675            ):
676                decls.setdefault(name, (cls.__module__, f"{cls.__qualname__}.{name}"))
677            for name in cls.__dict__:
678                decls.setdefault(name, (cls.__module__, f"{cls.__qualname__}.{name}"))
679        if decls.get("__init__", None) == ("builtins", "object.__init__"):
680            decls["__init__"] = (
681                self.obj.__module__,
682                f"{self.obj.__qualname__}.__init__",
683            )
684        return decls
685
686    def _taken_from(self, member_name: str, obj: Any) -> tuple[str, str]:
687        try:
688            return self._declarations[member_name]
689        except KeyError:  # pragma: no cover
690            # TypedDict botches __mro__ and may need special casing here.
691            # One workaround is to also specify TypedDict as a base class, see pdoc.doc.Class._mro.
692            warnings.warn(
693                f"Cannot determine where {self.fullname}.{member_name} is taken from, assuming current file."
694            )
695            return self.modulename, f"{self.qualname}.{member_name}"
696
697    @cached_property
698    def own_members(self) -> list[Doc]:
699        members = self._members_by_origin.get((self.modulename, self.qualname), [])
700        if self.taken_from != (self.modulename, self.qualname):
701            # .taken_from may be != (self.modulename, self.qualname), for example when
702            # a module re-exports a class from a private submodule.
703            members += self._members_by_origin.get(self.taken_from, [])
704        return members
705
706    @cached_property
707    def _member_objects(self) -> dict[str, Any]:
708        unsorted: dict[str, Any] = {}
709        for cls in self._mro:
710            for name, obj in cls.__dict__.items():
711                unsorted.setdefault(name, obj)
712        for name in self._var_docstrings:
713            unsorted.setdefault(name, empty)
714        for name in self._var_annotations:
715            unsorted.setdefault(name, empty)
716
717        init_has_no_doc = unsorted.get("__init__", object.__init__).__doc__ in (
718            None,
719            object.__init__.__doc__,
720        )
721        if init_has_no_doc:
722            if inspect.isabstract(self.obj):
723                # Special case: We don't want to show constructors for abstract base classes unless
724                # they have a custom docstring.
725                del unsorted["__init__"]
726            elif issubclass(self.obj, enum.Enum):
727                # Special case: Do not show a constructor for enums. They are typically not constructed by users.
728                # The alternative would be showing __new__, as __call__ is too verbose.
729                del unsorted["__init__"]
730            elif issubclass(self.obj, dict):
731                # Special case: Do not show a constructor for dict subclasses.
732                unsorted.pop(
733                    "__init__", None
734                )  # TypedDict subclasses may not have __init__.
735            else:
736                # Check if there's a helpful Metaclass.__call__ or Class.__new__. This dance is very similar to
737                # https://github.com/python/cpython/blob/9feae41c4f04ca27fd2c865807a5caeb50bf4fc4/Lib/inspect.py#L2359-L2376
738                call = _safe_getattr(type(self.obj), "__call__", None)
739                custom_call_with_custom_docstring = (
740                    call is not None
741                    and not isinstance(call, NonUserDefinedCallables)
742                    and call.__doc__ not in (None, object.__call__.__doc__)
743                )
744                if custom_call_with_custom_docstring:
745                    unsorted["__init__"] = call
746                else:
747                    # Does our class define a custom __new__ method?
748                    new = _safe_getattr(self.obj, "__new__", None)
749                    custom_new_with_custom_docstring = (
750                        new is not None
751                        and not isinstance(new, NonUserDefinedCallables)
752                        and new.__doc__ not in (None, object.__new__.__doc__)
753                    )
754                    if custom_new_with_custom_docstring:
755                        unsorted["__init__"] = new
756
757        sorted: dict[str, Any] = {}
758        for cls in self._mro:
759            sorted, unsorted = doc_ast.sort_by_source(cls, sorted, unsorted)
760        sorted.update(unsorted)
761        return sorted
762
763    @cached_property
764    def bases(self) -> list[tuple[str, str, str]]:
765        """
766        A list of all base classes, i.e. all immediate parent classes.
767
768        Each parent class is represented as a `(modulename, qualname, display_text)` tuple.
769        """
770        bases = []
771        for x in _safe_getattr(self.obj, "__orig_bases__", self.obj.__bases__):
772            if x is object:
773                continue
774            o = get_origin(x)
775            if o:
776                bases.append((o.__module__, o.__qualname__, str(x)))
777            elif x.__module__ == self.modulename:
778                bases.append((x.__module__, x.__qualname__, x.__qualname__))
779            else:
780                bases.append(
781                    (x.__module__, x.__qualname__, f"{x.__module__}.{x.__qualname__}")
782                )
783        return bases
784
785    @cached_property
786    def decorators(self) -> list[str]:
787        """A list of all decorators the class is decorated with."""
788        decorators = []
789        for t in doc_ast.parse(self.obj).decorator_list:
790            decorators.append(f"@{doc_ast.unparse(t)}")
791        return decorators
792
793    @cached_property
794    def class_variables(self) -> list[Variable]:
795        """
796        A list of all documented class variables in the class.
797
798        Class variables are variables that are explicitly annotated with `typing.ClassVar`.
799        All other variables are treated as instance variables.
800        """
801        return [
802            x
803            for x in self.members.values()
804            if isinstance(x, Variable) and x.is_classvar
805        ]
806
807    @cached_property
808    def instance_variables(self) -> list[Variable]:
809        """
810        A list of all instance variables in the class.
811        """
812        return [
813            x
814            for x in self.members.values()
815            if isinstance(x, Variable) and not x.is_classvar
816        ]
817
818    @cached_property
819    def classmethods(self) -> list[Function]:
820        """
821        A list of all documented `@classmethod`s.
822        """
823        return [
824            x
825            for x in self.members.values()
826            if isinstance(x, Function) and x.is_classmethod
827        ]
828
829    @cached_property
830    def staticmethods(self) -> list[Function]:
831        """
832        A list of all documented `@staticmethod`s.
833        """
834        return [
835            x
836            for x in self.members.values()
837            if isinstance(x, Function) and x.is_staticmethod
838        ]
839
840    @cached_property
841    def methods(self) -> list[Function]:
842        """
843        A list of all documented methods in the class that are neither static- nor classmethods.
844        """
845        return [
846            x
847            for x in self.members.values()
848            if isinstance(x, Function)
849            and not x.is_staticmethod
850            and not x.is_classmethod
851        ]

Representation of a class's documentation.

kind = 'class'

The type of the doc object, either "module", "class", "function", or "variable".

docstring: str

The docstring for this object. It has already been cleaned by inspect.cleandoc.

If no docstring can be found, an empty string is returned.

own_members: list[pdoc.doc.Doc]

A list of all own (i.e. non-inherited) members.

bases: list[tuple[str, str, str]]

A list of all base classes, i.e. all immediate parent classes.

Each parent class is represented as a (modulename, qualname, display_text) tuple.

decorators: list[str]

A list of all decorators the class is decorated with.

class_variables: list[pdoc.doc.Variable]

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.

instance_variables: list[pdoc.doc.Variable]

A list of all instance variables in the class.

classmethods: list[pdoc.doc.Function]

A list of all documented @classmethods.

staticmethods: list[pdoc.doc.Function]

A list of all documented @staticmethods.

methods: list[pdoc.doc.Function]

A list of all documented methods in the class that are neither static- nor classmethods.

class Function(pdoc.doc.Doc[function]):
 860class Function(Doc[types.FunctionType]):
 861    """
 862    Representation of a function's documentation.
 863
 864    This class covers all "flavors" of functions, for example it also
 865    supports `@classmethod`s or `@staticmethod`s.
 866    """
 867
 868    kind = "function"
 869
 870    wrapped: WrappedFunction
 871    """The original wrapped function (e.g., `staticmethod(func)`)"""
 872
 873    obj: types.FunctionType
 874    """The unwrapped "real" function."""
 875
 876    def __init__(
 877        self,
 878        modulename: str,
 879        qualname: str,
 880        func: WrappedFunction,
 881        taken_from: tuple[str, str],
 882    ):
 883        """Initialize a function's documentation object."""
 884        unwrapped: types.FunctionType
 885        if isinstance(func, (classmethod, staticmethod)):
 886            unwrapped = func.__func__  # type: ignore
 887        elif isinstance(func, singledispatchmethod):
 888            unwrapped = func.func  # type: ignore
 889        else:
 890            unwrapped = func
 891        super().__init__(modulename, qualname, unwrapped, taken_from)
 892        self.wrapped = func
 893
 894    @cache
 895    @_include_fullname_in_traceback
 896    def __repr__(self):
 897        if self.is_classmethod:
 898            t = "class"
 899        elif self.is_staticmethod:
 900            t = "static"
 901        elif self.qualname != _safe_getattr(self.obj, "__name__", None):
 902            t = "method"
 903        else:
 904            t = "function"
 905        return f"<{_decorators(self)}{t} {self.funcdef} {self.name}{self.signature}: ...{_docstr(self)}>"
 906
 907    @cached_property
 908    def docstring(self) -> str:
 909        doc = Doc.docstring.__get__(self)  # type: ignore
 910        if not doc:
 911            # inspect.getdoc fails for inherited @classmethods and unbound @property descriptors.
 912            # We now do an ugly dance to obtain the bound object instead,
 913            # that somewhat resembles what inspect._findclass is doing.
 914            cls = sys.modules.get(_safe_getattr(self.obj, "__module__", None), None)
 915            for name in _safe_getattr(self.obj, "__qualname__", "").split(".")[:-1]:
 916                cls = _safe_getattr(cls, name, None)
 917
 918            unbound = _safe_getattr(cls, "__dict__", {}).get(self.name)
 919            is_classmethod_property = isinstance(unbound, classmethod) and isinstance(
 920                unbound.__func__, (property, cached_property)
 921            )
 922            if not is_classmethod_property:
 923                # We choke on @classmethod @property, but that's okay because it's been deprecated with Python 3.11.
 924                # Directly accessing them would give us the return value, which has the wrong docstring.
 925                doc = _safe_getdoc(_safe_getattr(cls, self.name, None))
 926
 927        if doc == object.__init__.__doc__:
 928            # inspect.getdoc(Foo.__init__) returns the docstring, for object.__init__ if left undefined...
 929            return ""
 930        else:
 931            return doc
 932
 933    @cached_property
 934    def is_classmethod(self) -> bool:
 935        """
 936        `True` if this function is a `@classmethod`, `False` otherwise.
 937        """
 938        return isinstance(self.wrapped, classmethod)
 939
 940    @cached_property
 941    def is_staticmethod(self) -> bool:
 942        """
 943        `True` if this function is a `@staticmethod`, `False` otherwise.
 944        """
 945        return isinstance(self.wrapped, staticmethod)
 946
 947    @cached_property
 948    def decorators(self) -> list[str]:
 949        """A list of all decorators the function is decorated with."""
 950        decorators = []
 951        obj: types.FunctionType = self.obj  # type: ignore
 952        for t in doc_ast.parse(obj).decorator_list:
 953            decorators.append(f"@{doc_ast.unparse(t)}")
 954        return decorators
 955
 956    @cached_property
 957    def funcdef(self) -> str:
 958        """
 959        The string of keywords used to define the function, i.e. `"def"` or `"async def"`.
 960        """
 961        if inspect.iscoroutinefunction(self.obj) or inspect.isasyncgenfunction(
 962            self.obj
 963        ):
 964            return "async def"
 965        else:
 966            return "def"
 967
 968    @cached_property
 969    def signature(self) -> inspect.Signature:
 970        """
 971        The function's signature.
 972
 973        This usually returns an instance of `_PrettySignature`, a subclass of `inspect.Signature`
 974        that contains pdoc-specific optimizations. For example, long argument lists are split over multiple lines
 975        in repr(). Additionally, all types are already resolved.
 976
 977        If the signature cannot be determined, a placeholder Signature object is returned.
 978        """
 979        if self.obj is object.__init__:
 980            # there is a weird edge case were inspect.signature returns a confusing (self, /, *args, **kwargs)
 981            # signature for the default __init__ method.
 982            return inspect.Signature()
 983        try:
 984            sig = _PrettySignature.from_callable(self.obj)
 985        except Exception:
 986            return inspect.Signature(
 987                [inspect.Parameter("unknown", inspect.Parameter.POSITIONAL_OR_KEYWORD)]
 988            )
 989        mod = inspect.getmodule(self.obj)
 990        globalns = _safe_getattr(mod, "__dict__", {})
 991        localns = globalns
 992        for parent_cls_name in self.qualname.split(".")[:-1]:
 993            parent_cls = localns.get(parent_cls_name, object)
 994            localns = _safe_getattr(parent_cls, "__dict__", None)
 995            if localns is None:
 996                break  # pragma: no cover
 997
 998        if self.name == "__init__":
 999            sig = sig.replace(return_annotation=empty)
1000        else:
1001            sig = sig.replace(
1002                return_annotation=safe_eval_type(
1003                    sig.return_annotation, globalns, localns, mod, self.fullname
1004                )
1005            )
1006        for p in sig.parameters.values():
1007            p._annotation = safe_eval_type(p.annotation, globalns, localns, mod, self.fullname)  # type: ignore
1008        return sig
1009
1010    @cached_property
1011    def signature_without_self(self) -> inspect.Signature:
1012        """Like `signature`, but without the first argument.
1013
1014        This is useful to display constructors.
1015        """
1016        return self.signature.replace(
1017            parameters=list(self.signature.parameters.values())[1:]
1018        )

Representation of a function's documentation.

This class covers all "flavors" of functions, for example it also supports @classmethods or @staticmethods.

Function( modulename: str, qualname: str, func: function | staticmethod | classmethod, taken_from: tuple[str, str])
876    def __init__(
877        self,
878        modulename: str,
879        qualname: str,
880        func: WrappedFunction,
881        taken_from: tuple[str, str],
882    ):
883        """Initialize a function's documentation object."""
884        unwrapped: types.FunctionType
885        if isinstance(func, (classmethod, staticmethod)):
886            unwrapped = func.__func__  # type: ignore
887        elif isinstance(func, singledispatchmethod):
888            unwrapped = func.func  # type: ignore
889        else:
890            unwrapped = func
891        super().__init__(modulename, qualname, unwrapped, taken_from)
892        self.wrapped = func

Initialize a function's documentation object.

kind = 'function'

The type of the doc object, either "module", "class", "function", or "variable".

wrapped: function | staticmethod | classmethod

The original wrapped function (e.g., staticmethod(func))

obj: function

The unwrapped "real" function.

docstring: str

The docstring for this object. It has already been cleaned by inspect.cleandoc.

If no docstring can be found, an empty string is returned.

is_classmethod: bool

True if this function is a @classmethod, False otherwise.

is_staticmethod: bool

True if this function is a @staticmethod, False otherwise.

decorators: list[str]

A list of all decorators the function is decorated with.

funcdef: str

The string of keywords used to define the function, i.e. "def" or "async def".

signature: inspect.Signature

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.

signature_without_self: inspect.Signature

Like signature, but without the first argument.

This is useful to display constructors.

class Variable(pdoc.doc.Doc[NoneType]):
1021class Variable(Doc[None]):
1022    """
1023    Representation of a variable's documentation. This includes module, class and instance variables.
1024    """
1025
1026    kind = "variable"
1027
1028    default_value: Any | empty  # technically Any includes empty, but this conveys intent.
1029    """
1030    The variable's default value.
1031    
1032    In some cases, no default value is known. This may either be because a variable is only defined in the constructor,
1033    or it is only declared with a type annotation without assignment (`foo: int`).
1034    To distinguish this case from a default value of `None`, `pdoc.doc_types.empty` is used as a placeholder.
1035    """
1036
1037    annotation: type | empty
1038    """
1039    The variable's type annotation.
1040    
1041    If there is no type annotation, `pdoc.doc_types.empty` is used as a placeholder.
1042    """
1043
1044    def __init__(
1045        self,
1046        modulename: str,
1047        qualname: str,
1048        *,
1049        taken_from: tuple[str, str],
1050        docstring: str,
1051        annotation: type | empty = empty,
1052        default_value: Any | empty = empty,
1053    ):
1054        """
1055        Construct a variable doc object.
1056
1057        While classes and functions can introspect themselves to see their docstring,
1058        variables can't do that as we don't have a "variable object" we could query.
1059        As such, docstring, declaration location, type annotation, and the default value
1060        must be passed manually in the constructor.
1061        """
1062        super().__init__(modulename, qualname, None, taken_from)
1063        # noinspection PyPropertyAccess
1064        self.docstring = inspect.cleandoc(docstring)
1065        self.annotation = annotation
1066        self.default_value = default_value
1067
1068    @cache
1069    @_include_fullname_in_traceback
1070    def __repr__(self):
1071        if self.default_value_str:
1072            default = f" = {self.default_value_str}"
1073        else:
1074            default = ""
1075        return f'<var {self.qualname.rsplit(".")[-1]}{self.annotation_str}{default}{_docstr(self)}>'
1076
1077    @cached_property
1078    def is_classvar(self) -> bool:
1079        """`True` if the variable is a class variable, `False` otherwise."""
1080        if get_origin(self.annotation) is ClassVar:
1081            return True
1082        else:
1083            return False
1084
1085    @cached_property
1086    def is_enum_member(self) -> bool:
1087        """`True` if the variable is an enum member, `False` otherwise."""
1088        if isinstance(self.default_value, enum.Enum):
1089            return True
1090        else:
1091            return False
1092
1093    @cached_property
1094    def default_value_str(self) -> str:
1095        """The variable's default value as a pretty-printed str."""
1096        if self.default_value is empty:
1097            return ""
1098
1099        try:
1100            pretty = repr(self.default_value)
1101        except Exception as e:
1102            warnings.warn(f"repr({self.fullname}) raised an exception ({e!r})")
1103            return ""
1104
1105        pretty = re.sub(r" at 0x[0-9a-fA-F]+(?=>)", "", pretty)
1106        return pretty
1107
1108    @cached_property
1109    def annotation_str(self) -> str:
1110        """The variable's type annotation as a pretty-printed str."""
1111        if self.annotation is not empty:
1112            return f": {formatannotation(self.annotation)}"
1113        else:
1114            return ""

Representation of a variable's documentation. This includes module, class and instance variables.

Variable( modulename: str, qualname: str, *, taken_from: tuple[str, str], docstring: str, annotation: type | inspect._empty, default_value: typing.Any | inspect._empty)
1044    def __init__(
1045        self,
1046        modulename: str,
1047        qualname: str,
1048        *,
1049        taken_from: tuple[str, str],
1050        docstring: str,
1051        annotation: type | empty = empty,
1052        default_value: Any | empty = empty,
1053    ):
1054        """
1055        Construct a variable doc object.
1056
1057        While classes and functions can introspect themselves to see their docstring,
1058        variables can't do that as we don't have a "variable object" we could query.
1059        As such, docstring, declaration location, type annotation, and the default value
1060        must be passed manually in the constructor.
1061        """
1062        super().__init__(modulename, qualname, None, taken_from)
1063        # noinspection PyPropertyAccess
1064        self.docstring = inspect.cleandoc(docstring)
1065        self.annotation = annotation
1066        self.default_value = default_value

Construct a variable doc object.

While classes and functions can introspect themselves to see their docstring, variables can't do that as we don't have a "variable object" we could query. As such, docstring, declaration location, type annotation, and the default value must be passed manually in the constructor.

kind = 'variable'

The type of the doc object, either "module", "class", "function", or "variable".

default_value: typing.Any | inspect._empty

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.

annotation: type | inspect._empty

The variable's type annotation.

If there is no type annotation, pdoc.doc_types.empty is used as a placeholder.

docstring: str

The docstring for this object. It has already been cleaned by inspect.cleandoc.

If no docstring can be found, an empty string is returned.

is_classvar: bool

True if the variable is a class variable, False otherwise.

is_enum_member: bool

True if the variable is an enum member, False otherwise.

default_value_str: str

The variable's default value as a pretty-printed str.

annotation_str: str

The variable's type annotation as a pretty-printed str.