Edit on GitHub

pdoc.doc

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

There are four main types of documentation objects:

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

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

A base class for all documentation objects.

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

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

modulename: str

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

qualname: str

The qualified identifier name for this object. For example, if we have the following code:

class Foo:
    def bar(self):
        pass

The qualname of Foo's bar method is Foo.bar. The qualname of the Foo class is just Foo.

See https://www.python.org/dev/peps/pep-3155/ for details.

obj: ~T

The underlying Python object.

taken_from: tuple[str, str]

(modulename, qualname) of this doc object's original location. In the context of a module, this points to the location it was imported from, in the context of classes, this points to the class an attribute is inherited from.

kind: ClassVar[str]

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

fullname: str

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

name: str

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

docstring: str

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

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

source: str

The source code of the Python object as a str.

If the source cannot be obtained (for example, because we are dealing with a native C object), an empty string is returned.

source_file: pathlib.Path | None

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

source_lines: tuple[int, int] | None

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

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

is_inherited: bool

If True, the doc object is inherited from another location. This most commonly refers to methods inherited by a subclass, but can also apply to variables that are assigned a class defined in a different module.

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

Representation of a module's documentation.

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

Creates a documentation object given the actual Python module object.

kind = 'module'

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

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

Representation of a class's documentation.

kind = 'class'

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

docstring: str

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

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

own_members: list[pdoc.doc.Doc]

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

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

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

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

decorators: list[str]

A list of all decorators the class is decorated with.

class_variables: list[pdoc.doc.Variable]

A list of all documented class variables in the class.

Class variables are variables that are explicitly annotated with typing.ClassVar. All other variables are treated as instance variables.

instance_variables: list[pdoc.doc.Variable]

A list of all instance variables in the class.

classmethods: list[pdoc.doc.Function]

A list of all documented @classmethods.

staticmethods: list[pdoc.doc.Function]

A list of all documented @staticmethods.

methods: list[pdoc.doc.Function]

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

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

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])
847    def __init__(
848        self,
849        modulename: str,
850        qualname: str,
851        func: WrappedFunction,
852        taken_from: tuple[str, str],
853    ):
854        """Initialize a function's documentation object."""
855        unwrapped: types.FunctionType
856        if isinstance(func, (classmethod, staticmethod)):
857            unwrapped = func.__func__  # type: ignore
858        elif isinstance(func, singledispatchmethod):
859            unwrapped = func.func  # type: ignore
860        else:
861            unwrapped = func
862        super().__init__(modulename, qualname, unwrapped, taken_from)
863        self.wrapped = func

Initialize a function's documentation object.

kind = 'function'

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

wrapped: function | staticmethod | classmethod

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

obj: function

The unwrapped "real" function.

docstring: str

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

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

is_classmethod: bool

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

is_staticmethod: bool

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

decorators: list[str]

A list of all decorators the function is decorated with.

funcdef: str

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

signature: inspect.Signature

The function's signature.

This usually returns an instance of _PrettySignature, a subclass of inspect.Signature that contains pdoc-specific optimizations. For example, long argument lists are split over multiple lines in repr(). Additionally, all types are already resolved.

If the signature cannot be determined, a placeholder Signature object is returned.

signature_without_self: inspect.Signature

Like signature, but without the first argument.

This is useful to display constructors.

class Variable(pdoc.doc.Doc[NoneType]):
 992class Variable(Doc[None]):
 993    """
 994    Representation of a variable's documentation. This includes module, class and instance variables.
 995    """
 996
 997    kind = "variable"
 998
 999    default_value: Any | empty  # technically Any includes empty, but this conveys intent.
1000    """
1001    The variable's default value.
1002    
1003    In some cases, no default value is known. This may either be because a variable is only defined in the constructor,
1004    or it is only declared with a type annotation without assignment (`foo: int`).
1005    To distinguish this case from a default value of `None`, `pdoc.doc_types.empty` is used as a placeholder.
1006    """
1007
1008    annotation: type | empty
1009    """
1010    The variable's type annotation.
1011    
1012    If there is no type annotation, `pdoc.doc_types.empty` is used as a placeholder.
1013    """
1014
1015    def __init__(
1016        self,
1017        modulename: str,
1018        qualname: str,
1019        *,
1020        taken_from: tuple[str, str],
1021        docstring: str,
1022        annotation: type | empty = empty,
1023        default_value: Any | empty = empty,
1024    ):
1025        """
1026        Construct a variable doc object.
1027
1028        While classes and functions can introspect themselves to see their docstring,
1029        variables can't do that as we don't have a "variable object" we could query.
1030        As such, docstring, declaration location, type annotation, and the default value
1031        must be passed manually in the constructor.
1032        """
1033        super().__init__(modulename, qualname, None, taken_from)
1034        # noinspection PyPropertyAccess
1035        self.docstring = inspect.cleandoc(docstring)
1036        self.annotation = annotation
1037        self.default_value = default_value
1038
1039    @cache
1040    @_include_fullname_in_traceback
1041    def __repr__(self):
1042        return f'<var {self.qualname.rsplit(".")[-1]}{self.annotation_str}{self.default_value_str}{_docstr(self)}>'
1043
1044    @cached_property
1045    def is_classvar(self) -> bool:
1046        """`True` if the variable is a class variable, `False` otherwise."""
1047        if get_origin(self.annotation) is ClassVar:
1048            return True
1049        else:
1050            return False
1051
1052    @cached_property
1053    def is_enum_member(self) -> bool:
1054        """`True` if the variable is an enum member, `False` otherwise."""
1055        if isinstance(self.default_value, enum.Enum):
1056            return True
1057        else:
1058            return False
1059
1060    @cached_property
1061    def default_value_str(self) -> str:
1062        """The variable's default value as a pretty-printed str."""
1063        if self.default_value is empty:
1064            return ""
1065        else:
1066            try:
1067                return re.sub(
1068                    r" at 0x[0-9a-fA-F]+(?=>)",
1069                    "",
1070                    f" = {repr(self.default_value)}",
1071                )
1072            except Exception:
1073                return " = <unable to get value representation>"
1074
1075    @cached_property
1076    def annotation_str(self) -> str:
1077        """The variable's type annotation as a pretty-printed str."""
1078        if self.annotation is not empty:
1079            return f": {formatannotation(self.annotation)}"
1080        else:
1081            return ""

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

Variable( modulename: str, qualname: str, *, taken_from: tuple[str, str], docstring: str, annotation: type | inspect._empty, default_value: typing.Any | inspect._empty)
1015    def __init__(
1016        self,
1017        modulename: str,
1018        qualname: str,
1019        *,
1020        taken_from: tuple[str, str],
1021        docstring: str,
1022        annotation: type | empty = empty,
1023        default_value: Any | empty = empty,
1024    ):
1025        """
1026        Construct a variable doc object.
1027
1028        While classes and functions can introspect themselves to see their docstring,
1029        variables can't do that as we don't have a "variable object" we could query.
1030        As such, docstring, declaration location, type annotation, and the default value
1031        must be passed manually in the constructor.
1032        """
1033        super().__init__(modulename, qualname, None, taken_from)
1034        # noinspection PyPropertyAccess
1035        self.docstring = inspect.cleandoc(docstring)
1036        self.annotation = annotation
1037        self.default_value = default_value

Construct a variable doc object.

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

kind = 'variable'

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

default_value: typing.Any | inspect._empty

The variable's default value.

In some cases, no default value is known. This may either be because a variable is only defined in the constructor, or it is only declared with a type annotation without assignment (foo: int). To distinguish this case from a default value of None, pdoc.doc_types.empty is used as a placeholder.

annotation: type | inspect._empty

The variable's type annotation.

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

docstring: str

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

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

is_classvar: bool

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

is_enum_member: bool

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

default_value_str: str

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

annotation_str: str

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