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

A base class for all documentation objects.

Doc(modulename: str, qualname: str, obj: ~T, taken_from: tuple[str, str])
113    def __init__(
114        self, modulename: str, qualname: str, obj: T, taken_from: tuple[str, str]
115    ):
116        """
117        Initializes a documentation object, where
118        `modulename` is the name this module is defined in,
119        `qualname` contains a dotted path leading to the object from the module top-level, and
120        `obj` is the object to document.
121        """
122        self.modulename = modulename
123        self.qualname = qualname
124        self.obj = obj
125        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.

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.

type: str

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

class Namespace(pdoc.doc.Doc[~T]):
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 _var_annotations(self) -> dict[str, Any]:
229        """A mapping from some member variable names to their type annotations."""
230
231    @abstractmethod
232    def _taken_from(self, member_name: str, obj: Any) -> tuple[str, str]:
233        """The location this member was taken from. If unknown, (modulename, qualname) is returned."""
234
235    @cached_property
236    @abstractmethod
237    def own_members(self) -> list[Doc]:
238        """A list of all own (i.e. non-inherited) `members`."""
239
240    @cached_property
241    def members(self) -> dict[str, Doc]:
242        """A mapping from all members to their documentation objects.
243
244        This mapping includes private members; they are only filtered out as part of the template logic.
245        Constructors for enums, dicts, and abstract base classes are not picked up unless they have a custom docstring.
246        """
247        members: dict[str, Doc] = {}
248        for name, obj in self._member_objects.items():
249            qualname = f"{self.qualname}.{name}".lstrip(".")
250            taken_from = self._taken_from(name, obj)
251            doc: Doc[Any]
252
253            is_classmethod = isinstance(obj, classmethod)
254            is_property = (
255                isinstance(obj, (property, cached_property))
256                or
257                # Python 3.9 - 3.10: @classmethod @property is allowed.
258                is_classmethod
259                and isinstance(obj.__func__, (property, cached_property))
260            )
261            if is_property:
262                func = obj
263                if is_classmethod:
264                    func = obj.__func__
265                if isinstance(func, property):
266                    func = func.fget
267                else:
268                    assert isinstance(func, cached_property)
269                    func = func.func
270
271                doc_f = Function(self.modulename, qualname, func, taken_from)
272                doc = Variable(
273                    self.modulename,
274                    qualname,
275                    docstring=doc_f.docstring,
276                    annotation=doc_f.signature.return_annotation,
277                    default_value=empty,
278                    taken_from=taken_from,
279                )
280            elif inspect.isroutine(obj):
281                doc = Function(self.modulename, qualname, obj, taken_from)  # type: ignore
282            elif (
283                inspect.isclass(obj)
284                and obj is not empty
285                and not isinstance(obj, GenericAlias)
286            ):
287                # `dict[str,str]` is a GenericAlias instance. We want to render type aliases as variables though.
288                doc = Class(self.modulename, qualname, obj, taken_from)
289            elif inspect.ismodule(obj):
290                if os.environ.get("PDOC_SUBMODULES"):  # pragma: no cover
291                    doc = Module.from_name(obj.__name__)
292                else:
293                    continue
294            elif inspect.isdatadescriptor(obj):
295                doc = Variable(
296                    self.modulename,
297                    qualname,
298                    docstring=getattr(obj, "__doc__", None) or "",
299                    annotation=self._var_annotations.get(name, empty),
300                    default_value=empty,
301                    taken_from=taken_from,
302                )
303            else:
304                doc = Variable(
305                    self.modulename,
306                    qualname,
307                    docstring="",
308                    annotation=self._var_annotations.get(name, empty),
309                    default_value=obj,
310                    taken_from=taken_from,
311                )
312            if self._var_docstrings.get(name):
313                doc.docstring = self._var_docstrings[name]
314            members[doc.name] = doc
315
316        if isinstance(self, Module):
317            # quirk: doc_pyi expects .members to be set already
318            self.members = members  # type: ignore
319            doc_pyi.include_typeinfo_from_stub_files(self)
320
321        return members
322
323    @cached_property
324    def _members_by_origin(self) -> dict[tuple[str, str], list[Doc]]:
325        """A mapping from (modulename, qualname) locations to the attributes taken from that path"""
326        locations: dict[tuple[str, str], list[Doc]] = {}
327        for member in self.members.values():
328            mod, qualname = member.taken_from
329            parent_qualname = ".".join(qualname.rsplit(".", maxsplit=1)[:-1])
330            locations.setdefault((mod, parent_qualname), [])
331            locations[(mod, parent_qualname)].append(member)
332        return locations
333
334    @cached_property
335    def inherited_members(self) -> dict[tuple[str, str], list[Doc]]:
336        """A mapping from (modulename, qualname) locations to the attributes inherited from that path"""
337        return {
338            k: v
339            for k, v in self._members_by_origin.items()
340            if k not in (self.taken_from, (self.modulename, self.qualname))
341        }
342
343    @cached_property
344    def flattened_own_members(self) -> list[Doc]:
345        """
346        A list of all documented members and their child classes, recursively.
347        """
348        flattened = []
349        for x in self.own_members:
350            flattened.append(x)
351            if isinstance(x, Class):
352                flattened.extend(
353                    [cls for cls in x.flattened_own_members if isinstance(cls, Class)]
354                )
355        return flattened
356
357    @cache
358    def get(self, identifier: str) -> Doc | None:
359        """Returns the documentation object for a particular identifier, or `None` if the identifier cannot be found."""
360        head, _, tail = identifier.partition(".")
361        if tail:
362            h = self.members.get(head, None)
363            if isinstance(h, Class):
364                return h.get(tail)
365            return None
366        else:
367            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:
357    @cache
358    def get(self, identifier: str) -> Doc | None:
359        """Returns the documentation object for a particular identifier, or `None` if the identifier cannot be found."""
360        head, _, tail = identifier.partition(".")
361        if tail:
362            h = self.members.get(head, None)
363            if isinstance(h, Class):
364                return h.get(tail)
365            return None
366        else:
367            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]):
370class Module(Namespace[types.ModuleType]):
371    """
372    Representation of a module's documentation.
373    """
374
375    def __init__(
376        self,
377        module: types.ModuleType,
378    ):
379        """
380        Creates a documentation object given the actual
381        Python module object.
382        """
383        super().__init__(module.__name__, "", module, (module.__name__, ""))
384
385    @classmethod
386    @cache
387    def from_name(cls, name: str) -> Module:
388        """Create a `Module` object by supplying the module's (full) name."""
389        return cls(extract.load_module(name))
390
391    @cache
392    @_include_fullname_in_traceback
393    def __repr__(self):
394        return f"<module {self.fullname}{_docstr(self)}{_children(self)}>"
395
396    @cached_property
397    def is_package(self) -> bool:
398        """
399        `True` if the module is a package, `False` otherwise.
400
401        Packages are a special kind of module that may have submodules.
402        Typically, this means that this file is in a directory named like the
403        module with the name `__init__.py`.
404        """
405        return _safe_getattr(self.obj, "__path__", None) is not None
406
407    @cached_property
408    def _var_docstrings(self) -> dict[str, str]:
409        return doc_ast.walk_tree(self.obj).docstrings
410
411    @cached_property
412    def _var_annotations(self) -> dict[str, Any]:
413        annotations = doc_ast.walk_tree(self.obj).annotations.copy()
414        for k, v in _safe_getattr(self.obj, "__annotations__", {}).items():
415            annotations[k] = v
416
417        return resolve_annotations(annotations, self.obj, self.fullname)
418
419    def _taken_from(self, member_name: str, obj: Any) -> tuple[str, str]:
420        if obj is empty:
421            return self.modulename, f"{self.qualname}.{member_name}".lstrip(".")
422        if isinstance(obj, types.ModuleType):
423            return obj.__name__, ""
424
425        mod = _safe_getattr(obj, "__module__", None)
426        qual = _safe_getattr(obj, "__qualname__", None)
427        if mod and qual and "<locals>" not in qual:
428            return mod, qual
429        else:
430            # This might be wrong, but it's the best guess we have.
431            return (mod or self.modulename), f"{self.qualname}.{member_name}".lstrip(
432                "."
433            )
434
435    @cached_property
436    def own_members(self) -> list[Doc]:
437        return list(self.members.values())
438
439    @cached_property
440    def submodules(self) -> list[Module]:
441        """A list of all (direct) submodules."""
442        if not self.is_package:
443            return []
444
445        include: Callable[[str], bool]
446        mod_all = _safe_getattr(self.obj, "__all__", False)
447        if mod_all is not False:
448            mod_all_pos = {name: i for i, name in enumerate(mod_all)}
449            include = mod_all_pos.__contains__
450        else:
451
452            def include(name: str) -> bool:
453                # optimization: we don't even try to load modules starting with an underscore as they would not be
454                # visible by default. The downside of this is that someone who overrides `is_public` will miss those
455                # entries, the upsides are 1) better performance and 2) less warnings because of import failures
456                # (think of OS-specific modules, e.g. _linux.py failing to import on Windows).
457                return not name.startswith("_")
458
459        submodules = []
460        for mod in pkgutil.iter_modules(self.obj.__path__, f"{self.fullname}."):
461            _, _, mod_name = mod.name.rpartition(".")
462            if not include(mod_name):
463                continue
464            try:
465                module = Module.from_name(mod.name)
466            except RuntimeError:
467                warnings.warn(f"Couldn't import {mod.name}:\n{traceback.format_exc()}")
468                continue
469            submodules.append(module)
470
471        if mod_all:
472            submodules = sorted(submodules, key=lambda m: mod_all_pos[m.name])
473
474        return submodules
475
476    @cached_property
477    def _documented_members(self) -> set[str]:
478        return self._var_docstrings.keys() | self._var_annotations.keys()
479
480    @cached_property
481    def _member_objects(self) -> dict[str, Any]:
482        members = {}
483
484        all_: list[str] | None = _safe_getattr(self.obj, "__all__", None)
485        if all_ is not None:
486            for name in all_:
487                if not isinstance(name, str):
488                    # Gracefully handle the case where objects are directly specified in __all__.
489                    name = _safe_getattr(name, "__name__", str(name))
490                if name in self.obj.__dict__:
491                    val = self.obj.__dict__[name]
492                elif name in self._var_annotations:
493                    val = empty
494                else:
495                    # this may be an unimported submodule, try importing.
496                    # (https://docs.python.org/3/tutorial/modules.html#importing-from-a-package)
497                    try:
498                        val = extract.load_module(f"{self.modulename}.{name}")
499                    except RuntimeError as e:
500                        warnings.warn(
501                            f"Found {name!r} in {self.modulename}.__all__, but it does not resolve: {e}"
502                        )
503                        val = empty
504                members[name] = val
505
506        else:
507            # Starting with Python 3.10, __annotations__ is created on demand,
508            # so we make a copy here as obj.__dict__ is changed while we iterate over it.
509            # Additionally, accessing self._documented_members may lead to the execution of TYPE_CHECKING blocks,
510            # which may also modify obj.__dict__. (https://github.com/mitmproxy/pdoc/issues/351)
511            for name, obj in list(self.obj.__dict__.items()):
512                # We already exclude everything here that is imported, only a TypeVar,
513                # or a variable without annotation and docstring.
514                # If one needs to document one of these things, __all__ is the correct way.
515                obj_module = inspect.getmodule(obj)
516                declared_in_this_module = self.obj.__name__ == _safe_getattr(
517                    obj_module, "__name__", None
518                )
519                include_in_docs = name in self._documented_members or (
520                    declared_in_this_module and not isinstance(obj, TypeVar)
521                )
522                if include_in_docs:
523                    members[name] = obj
524            for name in self._var_docstrings:
525                members.setdefault(name, empty)
526
527            members, notfound = doc_ast.sort_by_source(self.obj, {}, members)
528            members.update(notfound)
529
530        return members
531
532    @cached_property
533    def variables(self) -> list[Variable]:
534        """
535        A list of all documented module level variables.
536        """
537        return [x for x in self.members.values() if isinstance(x, Variable)]
538
539    @cached_property
540    def classes(self) -> list[Class]:
541        """
542        A list of all documented module level classes.
543        """
544        return [x for x in self.members.values() if isinstance(x, Class)]
545
546    @cached_property
547    def functions(self) -> list[Function]:
548        """
549        A list of all documented module level functions.
550        """
551        return [x for x in self.members.values() if isinstance(x, Function)]

Representation of a module's documentation.

Module(module: module)
375    def __init__(
376        self,
377        module: types.ModuleType,
378    ):
379        """
380        Creates a documentation object given the actual
381        Python module object.
382        """
383        super().__init__(module.__name__, "", module, (module.__name__, ""))

Creates a documentation object given the actual Python module object.

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

Representation of a class's documentation.

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]):
807class Function(Doc[types.FunctionType]):
808    """
809    Representation of a function's documentation.
810
811    This class covers all "flavors" of functions, for example it also
812    supports `@classmethod`s or `@staticmethod`s.
813    """
814
815    wrapped: WrappedFunction
816    """The original wrapped function (e.g., `staticmethod(func)`)"""
817
818    obj: types.FunctionType
819    """The unwrapped "real" function."""
820
821    def __init__(
822        self,
823        modulename: str,
824        qualname: str,
825        func: WrappedFunction,
826        taken_from: tuple[str, str],
827    ):
828        """Initialize a function's documentation object."""
829        unwrapped: types.FunctionType
830        if isinstance(func, (classmethod, staticmethod)):
831            unwrapped = func.__func__  # type: ignore
832        elif isinstance(func, singledispatchmethod):
833            unwrapped = func.func  # type: ignore
834        else:
835            unwrapped = func
836        super().__init__(modulename, qualname, unwrapped, taken_from)
837        self.wrapped = func
838
839    @cache
840    @_include_fullname_in_traceback
841    def __repr__(self):
842        if self.is_classmethod:
843            t = "class"
844        elif self.is_staticmethod:
845            t = "static"
846        elif self.qualname != _safe_getattr(self.obj, "__name__", None):
847            t = "method"
848        else:
849            t = "function"
850        return f"<{_decorators(self)}{t} {self.funcdef} {self.name}{self.signature}: ...{_docstr(self)}>"
851
852    @cached_property
853    def docstring(self) -> str:
854        doc = Doc.docstring.__get__(self)  # type: ignore
855        if not doc:
856            # inspect.getdoc fails for inherited @classmethods and unbound @property descriptors.
857            # We now do an ugly dance to obtain the bound object instead,
858            # that somewhat resembles what inspect._findclass is doing.
859            cls = sys.modules.get(_safe_getattr(self.obj, "__module__", None), None)
860            for name in _safe_getattr(self.obj, "__qualname__", "").split(".")[:-1]:
861                cls = _safe_getattr(cls, name, None)
862
863            unbound = _safe_getattr(cls, "__dict__", {}).get(self.name)
864            is_classmethod_property = isinstance(unbound, classmethod) and isinstance(
865                unbound.__func__, (property, cached_property)
866            )
867            if not is_classmethod_property:
868                # We choke on @classmethod @property, but that's okay because it's been deprecated with Python 3.11.
869                # Directly accessing them would give us the return value, which has the wrong docstring.
870                doc = _safe_getdoc(_safe_getattr(cls, self.name, None))
871
872        if doc == object.__init__.__doc__:
873            # inspect.getdoc(Foo.__init__) returns the docstring, for object.__init__ if left undefined...
874            return ""
875        else:
876            return doc
877
878    @cached_property
879    def is_classmethod(self) -> bool:
880        """
881        `True` if this function is a `@classmethod`, `False` otherwise.
882        """
883        return isinstance(self.wrapped, classmethod)
884
885    @cached_property
886    def is_staticmethod(self) -> bool:
887        """
888        `True` if this function is a `@staticmethod`, `False` otherwise.
889        """
890        return isinstance(self.wrapped, staticmethod)
891
892    @cached_property
893    def decorators(self) -> list[str]:
894        """A list of all decorators the function is decorated with."""
895        decorators = []
896        obj: types.FunctionType = self.obj  # type: ignore
897        for t in doc_ast.parse(obj).decorator_list:
898            decorators.append(f"@{doc_ast.unparse(t)}")
899        return decorators
900
901    @cached_property
902    def funcdef(self) -> str:
903        """
904        The string of keywords used to define the function, i.e. `"def"` or `"async def"`.
905        """
906        if inspect.iscoroutinefunction(self.obj) or inspect.isasyncgenfunction(
907            self.obj
908        ):
909            return "async def"
910        else:
911            return "def"
912
913    @cached_property
914    def signature(self) -> inspect.Signature:
915        """
916        The function's signature.
917
918        This usually returns an instance of `_PrettySignature`, a subclass of `inspect.Signature`
919        that contains pdoc-specific optimizations. For example, long argument lists are split over multiple lines
920        in repr(). Additionally, all types are already resolved.
921
922        If the signature cannot be determined, a placeholder Signature object is returned.
923        """
924        if self.obj is object.__init__:
925            # there is a weird edge case were inspect.signature returns a confusing (self, /, *args, **kwargs)
926            # signature for the default __init__ method.
927            return inspect.Signature()
928        try:
929            sig = _PrettySignature.from_callable(self.obj)
930        except Exception:
931            return inspect.Signature(
932                [inspect.Parameter("unknown", inspect.Parameter.POSITIONAL_OR_KEYWORD)]
933            )
934        mod = inspect.getmodule(self.obj)
935        globalns = _safe_getattr(mod, "__dict__", {})
936
937        if self.name == "__init__":
938            sig = sig.replace(return_annotation=empty)
939        else:
940            sig = sig.replace(
941                return_annotation=safe_eval_type(
942                    sig.return_annotation, globalns, mod, self.fullname
943                )
944            )
945        for p in sig.parameters.values():
946            p._annotation = safe_eval_type(p.annotation, globalns, mod, self.fullname)  # type: ignore
947        return sig
948
949    @cached_property
950    def signature_without_self(self) -> inspect.Signature:
951        """Like `signature`, but without the first argument.
952
953        This is useful to display constructors.
954        """
955        return self.signature.replace(
956            parameters=list(self.signature.parameters.values())[1:]
957        )

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])
821    def __init__(
822        self,
823        modulename: str,
824        qualname: str,
825        func: WrappedFunction,
826        taken_from: tuple[str, str],
827    ):
828        """Initialize a function's documentation object."""
829        unwrapped: types.FunctionType
830        if isinstance(func, (classmethod, staticmethod)):
831            unwrapped = func.__func__  # type: ignore
832        elif isinstance(func, singledispatchmethod):
833            unwrapped = func.func  # type: ignore
834        else:
835            unwrapped = func
836        super().__init__(modulename, qualname, unwrapped, taken_from)
837        self.wrapped = func

Initialize a function's documentation object.

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]):
 960class Variable(Doc[None]):
 961    """
 962    Representation of a variable's documentation. This includes module, class and instance variables.
 963    """
 964
 965    default_value: Any | empty  # technically Any includes empty, but this conveys intent.
 966    """
 967    The variable's default value.
 968    
 969    In some cases, no default value is known. This may either be because a variable is only defined in the constructor,
 970    or it is only declared with a type annotation without assignment (`foo: int`).
 971    To distinguish this case from a default value of `None`, `pdoc.doc_types.empty` is used as a placeholder.
 972    """
 973
 974    annotation: type | empty
 975    """
 976    The variable's type annotation.
 977    
 978    If there is no type annotation, `pdoc.doc_types.empty` is used as a placeholder.
 979    """
 980
 981    def __init__(
 982        self,
 983        modulename: str,
 984        qualname: str,
 985        *,
 986        taken_from: tuple[str, str],
 987        docstring: str,
 988        annotation: type | empty = empty,
 989        default_value: Any | empty = empty,
 990    ):
 991        """
 992        Construct a variable doc object.
 993
 994        While classes and functions can introspect themselves to see their docstring,
 995        variables can't do that as we don't have a "variable object" we could query.
 996        As such, docstring, declaration location, type annotation, and the default value
 997        must be passed manually in the constructor.
 998        """
 999        super().__init__(modulename, qualname, None, taken_from)
1000        # noinspection PyPropertyAccess
1001        self.docstring = inspect.cleandoc(docstring)
1002        self.annotation = annotation
1003        self.default_value = default_value
1004
1005    @cache
1006    @_include_fullname_in_traceback
1007    def __repr__(self):
1008        return f'<var {self.qualname.rsplit(".")[-1]}{self.annotation_str}{self.default_value_str}{_docstr(self)}>'
1009
1010    @cached_property
1011    def is_classvar(self) -> bool:
1012        """`True` if the variable is a class variable, `False` otherwise."""
1013        if get_origin(self.annotation) is ClassVar:
1014            return True
1015        else:
1016            return False
1017
1018    @cached_property
1019    def default_value_str(self) -> str:
1020        """The variable's default value as a pretty-printed str."""
1021        if self.default_value is empty:
1022            return ""
1023        else:
1024            try:
1025                return re.sub(
1026                    r" at 0x[0-9a-fA-F]+(?=>)",
1027                    "",
1028                    f" = {repr(self.default_value)}",
1029                )
1030            except Exception:
1031                return " = <unable to get value representation>"
1032
1033    @cached_property
1034    def annotation_str(self) -> str:
1035        """The variable's type annotation as a pretty-printed str."""
1036        if self.annotation is not empty:
1037            return f": {formatannotation(self.annotation)}"
1038        else:
1039            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: Union[Any, inspect._empty])
 981    def __init__(
 982        self,
 983        modulename: str,
 984        qualname: str,
 985        *,
 986        taken_from: tuple[str, str],
 987        docstring: str,
 988        annotation: type | empty = empty,
 989        default_value: Any | empty = empty,
 990    ):
 991        """
 992        Construct a variable doc object.
 993
 994        While classes and functions can introspect themselves to see their docstring,
 995        variables can't do that as we don't have a "variable object" we could query.
 996        As such, docstring, declaration location, type annotation, and the default value
 997        must be passed manually in the constructor.
 998        """
 999        super().__init__(modulename, qualname, None, taken_from)
1000        # noinspection PyPropertyAccess
1001        self.docstring = inspect.cleandoc(docstring)
1002        self.annotation = annotation
1003        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.

default_value: Union[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.

is_classvar: bool

True if the variable is a class variable, 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.