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

A base class for all documentation objects.

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

type: str
122    @property
123    def type(self) -> str:  # pragma: no cover
124        warnings.warn(
125            "pdoc.doc.Doc.type is deprecated. Use pdoc.doc.Doc.kind instead.",
126            DeprecationWarning,
127        )
128        return self.kind
fullname: str
144    @cached_property
145    def fullname(self) -> str:
146        """The full qualified name of this doc object, for example `pdoc.doc.Doc`."""
147        # qualname is empty for modules
148        return f"{self.modulename}.{self.qualname}".rstrip(".")

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

name: str
150    @cached_property
151    def name(self) -> str:
152        """The name of this object. For top-level functions and classes, this is equal to the qualname attribute."""
153        return self.fullname.split(".")[-1]

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

docstring: str
155    @cached_property
156    def docstring(self) -> str:
157        """
158        The docstring for this object. It has already been cleaned by `inspect.cleandoc`.
159
160        If no docstring can be found, an empty string is returned.
161        """
162        return _safe_getdoc(self.obj)

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

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

source: str
164    @cached_property
165    def source(self) -> str:
166        """
167        The source code of the Python object as a `str`.
168
169        If the source cannot be obtained (for example, because we are dealing with a native C object),
170        an empty string is returned.
171        """
172        return doc_ast.get_source(self.obj)

The source code of the Python object as a str.

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

source_file: pathlib.Path | None
174    @cached_property
175    def source_file(self) -> Path | None:
176        """The name of the Python source file in which this object was defined. `None` for built-in objects."""
177        try:
178            return Path(inspect.getsourcefile(self.obj) or inspect.getfile(self.obj))  # type: ignore
179        except TypeError:
180            return 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
182    @cached_property
183    def source_lines(self) -> tuple[int, int] | None:
184        """
185        Return a `(start, end)` line number tuple for this object.
186
187        If no source file can be found, `None` is returned.
188        """
189        try:
190            lines, start = inspect.getsourcelines(self.obj)  # type: ignore
191            return start, start + len(lines) - 1
192        except Exception:
193            return None

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

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

is_inherited: bool
195    @cached_property
196    def is_inherited(self) -> bool:
197        """
198        If True, the doc object is inherited from another location.
199        This most commonly refers to methods inherited by a subclass,
200        but can also apply to variables that are assigned a class defined
201        in a different module.
202        """
203        return (self.modulename, self.qualname) != self.taken_from

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

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

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

members: dict[str, Doc]
248    @cached_property
249    def members(self) -> dict[str, Doc]:
250        """A mapping from all members to their documentation objects.
251
252        This mapping includes private members; they are only filtered out as part of the template logic.
253        Constructors for enums, dicts, and abstract base classes are not picked up unless they have a custom docstring.
254        """
255        members: dict[str, Doc] = {}
256        for name, obj in self._member_objects.items():
257            qualname = f"{self.qualname}.{name}".lstrip(".")
258            taken_from = self._taken_from(name, obj)
259            doc: Doc[Any]
260
261            is_classmethod = isinstance(obj, classmethod)
262            is_property = (
263                isinstance(obj, (property, cached_property))
264                or
265                # Python 3.9 - 3.10: @classmethod @property is allowed.
266                is_classmethod
267                and isinstance(obj.__func__, (property, cached_property))
268            )
269            if is_property:
270                func = obj
271                if is_classmethod:
272                    func = obj.__func__
273                if isinstance(func, property):
274                    func = func.fget
275                else:
276                    assert isinstance(func, cached_property)
277                    func = func.func
278
279                doc_f = Function(self.modulename, qualname, func, taken_from)
280                doc = Variable(
281                    self.modulename,
282                    qualname,
283                    docstring=doc_f.docstring,
284                    annotation=doc_f.signature.return_annotation,
285                    default_value=empty,
286                    taken_from=taken_from,
287                )
288                doc.source = doc_f.source
289                doc.source_file = doc_f.source_file
290                doc.source_lines = doc_f.source_lines
291            elif inspect.isroutine(obj):
292                doc = Function(self.modulename, qualname, obj, taken_from)  # type: ignore
293            elif (
294                inspect.isclass(obj)
295                and obj is not empty
296                and not isinstance(obj, GenericAlias)
297                and obj.__qualname__.rpartition(".")[2] == qualname.rpartition(".")[2]
298            ):
299                # `dict[str,str]` is a GenericAlias instance. We want to render type aliases as variables though.
300                doc = Class(self.modulename, qualname, obj, taken_from)
301            elif inspect.ismodule(obj):
302                if os.environ.get("PDOC_SUBMODULES"):  # pragma: no cover
303                    doc = Module.from_name(obj.__name__)
304                else:
305                    continue
306            elif inspect.isdatadescriptor(obj):
307                doc = Variable(
308                    self.modulename,
309                    qualname,
310                    docstring=getattr(obj, "__doc__", None) or "",
311                    annotation=self._var_annotations.get(name, empty),
312                    default_value=empty,
313                    taken_from=taken_from,
314                )
315            else:
316                doc = Variable(
317                    self.modulename,
318                    qualname,
319                    docstring="",
320                    annotation=self._var_annotations.get(name, empty),
321                    default_value=obj,
322                    taken_from=taken_from,
323                )
324            if self._var_docstrings.get(name):
325                doc.docstring = self._var_docstrings[name]
326            if self._func_docstrings.get(name) and not doc.docstring:
327                doc.docstring = self._func_docstrings[name]
328            members[doc.name] = doc
329
330        if isinstance(self, Module):
331            # quirk: doc_pyi expects .members to be set already
332            self.members = members  # type: ignore
333            doc_pyi.include_typeinfo_from_stub_files(self)
334
335        return members

A mapping from all members to their documentation objects.

This mapping includes private members; they are only filtered out as part of the template logic. Constructors for enums, dicts, and abstract base classes are not picked up unless they have a custom docstring.

inherited_members: dict[tuple[str, str], list[Doc]]
348    @cached_property
349    def inherited_members(self) -> dict[tuple[str, str], list[Doc]]:
350        """A mapping from (modulename, qualname) locations to the attributes inherited from that path"""
351        return {
352            k: v
353            for k, v in self._members_by_origin.items()
354            if k not in (self.taken_from, (self.modulename, self.qualname))
355        }

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

flattened_own_members: list[Doc]
357    @cached_property
358    def flattened_own_members(self) -> list[Doc]:
359        """
360        A list of all documented members and their child classes, recursively.
361        """
362        flattened = []
363        for x in self.own_members:
364            flattened.append(x)
365            if isinstance(x, Class):
366                flattened.extend(
367                    [cls for cls in x.flattened_own_members if isinstance(cls, Class)]
368                )
369        return flattened

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

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

Representation of a module's documentation.

Module(module: module)
389    def __init__(
390        self,
391        module: types.ModuleType,
392    ):
393        """
394        Creates a documentation object given the actual
395        Python module object.
396        """
397        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) -> Module:
401    @classmethod
402    @cache
403    def from_name(cls, name: str) -> Module:
404        """Create a `Module` object by supplying the module's (full) name."""
405        return cls(extract.load_module(name))

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

is_package: bool
412    @cached_property
413    def is_package(self) -> bool:
414        """
415        `True` if the module is a package, `False` otherwise.
416
417        Packages are a special kind of module that may have submodules.
418        Typically, this means that this file is in a directory named like the
419        module with the name `__init__.py`.
420        """
421        return _safe_getattr(self.obj, "__path__", None) is not None

True if the module is a package, False otherwise.

Packages are a special kind of module that may have submodules. Typically, this means that this file is in a directory named like the module with the name __init__.py.

own_members: list[Doc]
455    @cached_property
456    def own_members(self) -> list[Doc]:
457        return list(self.members.values())

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

submodules: list[Module]
459    @cached_property
460    def submodules(self) -> list[Module]:
461        """A list of all (direct) submodules."""
462        include: Callable[[str], bool]
463        mod_all = _safe_getattr(self.obj, "__all__", False)
464        if mod_all is not False:
465            mod_all_pos = {name: i for i, name in enumerate(mod_all)}
466            include = mod_all_pos.__contains__
467        else:
468
469            def include(name: str) -> bool:
470                # optimization: we don't even try to load modules starting with an underscore as they would not be
471                # visible by default. The downside of this is that someone who overrides `is_public` will miss those
472                # entries, the upsides are 1) better performance and 2) less warnings because of import failures
473                # (think of OS-specific modules, e.g. _linux.py failing to import on Windows).
474                return not name.startswith("_")
475
476        submodules: list[Module] = []
477        for mod_name, mod in extract.iter_modules2(self.obj).items():
478            if not include(mod_name):
479                continue
480            try:
481                module = Module.from_name(mod.name)
482            except RuntimeError:
483                warnings.warn(f"Couldn't import {mod.name}:\n{traceback.format_exc()}")
484                continue
485            submodules.append(module)
486
487        if mod_all:
488            submodules = sorted(submodules, key=lambda m: mod_all_pos[m.name])
489
490        return submodules

A list of all (direct) submodules.

variables: list[Variable]
551    @cached_property
552    def variables(self) -> list[Variable]:
553        """
554        A list of all documented module level variables.
555        """
556        return [x for x in self.members.values() if isinstance(x, Variable)]

A list of all documented module level variables.

classes: list[Class]
558    @cached_property
559    def classes(self) -> list[Class]:
560        """
561        A list of all documented module level classes.
562        """
563        return [x for x in self.members.values() if isinstance(x, Class)]

A list of all documented module level classes.

functions: list[Function]
565    @cached_property
566    def functions(self) -> list[Function]:
567        """
568        A list of all documented module level functions.
569        """
570        return [x for x in self.members.values() if isinstance(x, Function)]

A list of all documented module level functions.

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

Representation of a class's documentation.

kind = 'class'

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

docstring: str
585    @cached_property
586    def docstring(self) -> str:
587        doc = Doc.docstring.__get__(self)  # type: ignore
588        if doc == dict.__doc__:
589            # Don't display default docstring for dict subclasses (primarily TypedDict).
590            return ""
591        if doc in _Enum_default_docstrings:
592            # Don't display default docstring for enum subclasses.
593            return ""
594        if dataclasses.is_dataclass(self.obj) and doc.startswith(self.obj.__name__):
595            try:
596                sig = inspect.signature(self.obj)
597            except Exception:
598                pass
599            else:
600                # from https://github.com/python/cpython/blob/3.10/Lib/dataclasses.py
601                is_dataclass_with_default_docstring = doc == self.obj.__name__ + str(
602                    sig
603                ).replace(" -> None", "")
604                if is_dataclass_with_default_docstring:
605                    return ""
606        return doc

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

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

own_members: list[Doc]
706    @cached_property
707    def own_members(self) -> list[Doc]:
708        members = self._members_by_origin.get((self.modulename, self.qualname), [])
709        if self.taken_from != (self.modulename, self.qualname):
710            # .taken_from may be != (self.modulename, self.qualname), for example when
711            # a module re-exports a class from a private submodule.
712            members += self._members_by_origin.get(self.taken_from, [])
713        return members

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

bases: list[tuple[str, str, str]]
772    @cached_property
773    def bases(self) -> list[tuple[str, str, str]]:
774        """
775        A list of all base classes, i.e. all immediate parent classes.
776
777        Each parent class is represented as a `(modulename, qualname, display_text)` tuple.
778        """
779        bases = []
780        for x in _safe_getattr(self.obj, "__orig_bases__", self.obj.__bases__):
781            if x is object:
782                continue
783            o = get_origin(x)
784            if o:
785                bases.append((o.__module__, o.__qualname__, str(x)))
786            elif x.__module__ == self.modulename:
787                bases.append((x.__module__, x.__qualname__, x.__qualname__))
788            else:
789                bases.append(
790                    (x.__module__, x.__qualname__, f"{x.__module__}.{x.__qualname__}")
791                )
792        return bases

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

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

decorators: list[str]
794    @cached_property
795    def decorators(self) -> list[str]:
796        """A list of all decorators the class is decorated with."""
797        decorators = []
798        for t in doc_ast.parse(self.obj).decorator_list:
799            decorators.append(f"@{doc_ast.unparse(t)}")
800        return decorators

A list of all decorators the class is decorated with.

class_variables: list[Variable]
802    @cached_property
803    def class_variables(self) -> list[Variable]:
804        """
805        A list of all documented class variables in the class.
806
807        Class variables are variables that are explicitly annotated with `typing.ClassVar`.
808        All other variables are treated as instance variables.
809        """
810        return [
811            x
812            for x in self.members.values()
813            if isinstance(x, Variable) and x.is_classvar
814        ]

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[Variable]
816    @cached_property
817    def instance_variables(self) -> list[Variable]:
818        """
819        A list of all instance variables in the class.
820        """
821        return [
822            x
823            for x in self.members.values()
824            if isinstance(x, Variable) and not x.is_classvar
825        ]

A list of all instance variables in the class.

classmethods: list[Function]
827    @cached_property
828    def classmethods(self) -> list[Function]:
829        """
830        A list of all documented `@classmethod`s.
831        """
832        return [
833            x
834            for x in self.members.values()
835            if isinstance(x, Function) and x.is_classmethod
836        ]

A list of all documented @classmethods.

staticmethods: list[Function]
838    @cached_property
839    def staticmethods(self) -> list[Function]:
840        """
841        A list of all documented `@staticmethod`s.
842        """
843        return [
844            x
845            for x in self.members.values()
846            if isinstance(x, Function) and x.is_staticmethod
847        ]

A list of all documented @staticmethods.

methods: list[Function]
849    @cached_property
850    def methods(self) -> list[Function]:
851        """
852        A list of all documented methods in the class that are neither static- nor classmethods.
853        """
854        return [
855            x
856            for x in self.members.values()
857            if isinstance(x, Function)
858            and not x.is_staticmethod
859            and not x.is_classmethod
860        ]

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

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

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])
885    def __init__(
886        self,
887        modulename: str,
888        qualname: str,
889        func: WrappedFunction,
890        taken_from: tuple[str, str],
891    ):
892        """Initialize a function's documentation object."""
893        unwrapped: types.FunctionType
894        if isinstance(func, (classmethod, staticmethod)):
895            unwrapped = func.__func__  # type: ignore
896        elif isinstance(func, singledispatchmethod):
897            unwrapped = func.func  # type: ignore
898        elif hasattr(func, "__wrapped__"):
899            unwrapped = func.__wrapped__
900        else:
901            unwrapped = func
902        super().__init__(modulename, qualname, unwrapped, taken_from)
903        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
918    @cached_property
919    def docstring(self) -> str:
920        doc = Doc.docstring.__get__(self)  # type: ignore
921        if not doc:
922            # inspect.getdoc fails for inherited @classmethods and unbound @property descriptors.
923            # We now do an ugly dance to obtain the bound object instead,
924            # that somewhat resembles what inspect._findclass is doing.
925            cls = sys.modules.get(_safe_getattr(self.obj, "__module__", None), None)
926            for name in _safe_getattr(self.obj, "__qualname__", "").split(".")[:-1]:
927                cls = _safe_getattr(cls, name, None)
928
929            unbound = _safe_getattr(cls, "__dict__", {}).get(self.name)
930            is_classmethod_property = isinstance(unbound, classmethod) and isinstance(
931                unbound.__func__, (property, cached_property)
932            )
933            if not is_classmethod_property:
934                # We choke on @classmethod @property, but that's okay because it's been deprecated with Python 3.11.
935                # Directly accessing them would give us the return value, which has the wrong docstring.
936                doc = _safe_getdoc(_safe_getattr(cls, self.name, None))
937
938        if doc == object.__init__.__doc__:
939            # inspect.getdoc(Foo.__init__) returns the docstring, for object.__init__ if left undefined...
940            return ""
941        else:
942            return doc

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

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

is_classmethod: bool
944    @cached_property
945    def is_classmethod(self) -> bool:
946        """
947        `True` if this function is a `@classmethod`, `False` otherwise.
948        """
949        return isinstance(self.wrapped, classmethod)

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

is_staticmethod: bool
951    @cached_property
952    def is_staticmethod(self) -> bool:
953        """
954        `True` if this function is a `@staticmethod`, `False` otherwise.
955        """
956        return isinstance(self.wrapped, staticmethod)

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

decorators: list[str]
958    @cached_property
959    def decorators(self) -> list[str]:
960        """A list of all decorators the function is decorated with."""
961        decorators = []
962        obj: types.FunctionType = self.obj  # type: ignore
963        for t in doc_ast.parse(obj).decorator_list:
964            decorators.append(f"@{doc_ast.unparse(t)}")
965        return decorators

A list of all decorators the function is decorated with.

funcdef: str
967    @cached_property
968    def funcdef(self) -> str:
969        """
970        The string of keywords used to define the function, i.e. `"def"` or `"async def"`.
971        """
972        if inspect.iscoroutinefunction(self.obj) or inspect.isasyncgenfunction(
973            self.obj
974        ):
975            return "async def"
976        else:
977            return "def"

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

signature: inspect.Signature
 979    @cached_property
 980    def signature(self) -> inspect.Signature:
 981        """
 982        The function's signature.
 983
 984        This usually returns an instance of `_PrettySignature`, a subclass of `inspect.Signature`
 985        that contains pdoc-specific optimizations. For example, long argument lists are split over multiple lines
 986        in repr(). Additionally, all types are already resolved.
 987
 988        If the signature cannot be determined, a placeholder Signature object is returned.
 989        """
 990        if self.obj is object.__init__:
 991            # there is a weird edge case were inspect.signature returns a confusing (self, /, *args, **kwargs)
 992            # signature for the default __init__ method.
 993            return inspect.Signature()
 994        try:
 995            sig = _PrettySignature.from_callable(self.obj)
 996        except Exception:
 997            return inspect.Signature(
 998                [inspect.Parameter("unknown", inspect.Parameter.POSITIONAL_OR_KEYWORD)]
 999            )
1000        mod = inspect.getmodule(self.obj)
1001        globalns = _safe_getattr(mod, "__dict__", {})
1002        localns = globalns
1003        for parent_cls_name in self.qualname.split(".")[:-1]:
1004            parent_cls = localns.get(parent_cls_name, object)
1005            localns = _safe_getattr(parent_cls, "__dict__", None)
1006            if localns is None:
1007                break  # pragma: no cover
1008
1009        if self.name == "__init__":
1010            sig = sig.replace(return_annotation=empty)
1011        else:
1012            sig = sig.replace(
1013                return_annotation=safe_eval_type(
1014                    sig.return_annotation, globalns, localns, mod, self.fullname
1015                )
1016            )
1017        for p in sig.parameters.values():
1018            p._annotation = safe_eval_type(  # type: ignore
1019                p.annotation, globalns, localns, mod, self.fullname
1020            )
1021        return sig

The function's signature.

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

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

signature_without_self: inspect.Signature
1023    @cached_property
1024    def signature_without_self(self) -> inspect.Signature:
1025        """Like `signature`, but without the first argument.
1026
1027        This is useful to display constructors.
1028        """
1029        return self.signature.replace(
1030            parameters=list(self.signature.parameters.values())[1:]
1031        )

Like signature, but without the first argument.

This is useful to display constructors.

class Variable(pdoc.doc.Doc[NoneType]):
1034class Variable(Doc[None]):
1035    """
1036    Representation of a variable's documentation. This includes module, class and instance variables.
1037    """
1038
1039    kind = "variable"
1040
1041    default_value: (
1042        Any | empty
1043    )  # technically Any includes empty, but this conveys intent.
1044    """
1045    The variable's default value.
1046    
1047    In some cases, no default value is known. This may either be because a variable is only defined in the constructor,
1048    or it is only declared with a type annotation without assignment (`foo: int`).
1049    To distinguish this case from a default value of `None`, `pdoc.doc_types.empty` is used as a placeholder.
1050    """
1051
1052    annotation: type | empty
1053    """
1054    The variable's type annotation.
1055    
1056    If there is no type annotation, `pdoc.doc_types.empty` is used as a placeholder.
1057    """
1058
1059    def __init__(
1060        self,
1061        modulename: str,
1062        qualname: str,
1063        *,
1064        taken_from: tuple[str, str],
1065        docstring: str,
1066        annotation: type | empty = empty,
1067        default_value: Any | empty = empty,
1068    ):
1069        """
1070        Construct a variable doc object.
1071
1072        While classes and functions can introspect themselves to see their docstring,
1073        variables can't do that as we don't have a "variable object" we could query.
1074        As such, docstring, declaration location, type annotation, and the default value
1075        must be passed manually in the constructor.
1076        """
1077        super().__init__(modulename, qualname, None, taken_from)
1078        # noinspection PyPropertyAccess
1079        self.docstring = inspect.cleandoc(docstring)
1080        self.annotation = annotation
1081        self.default_value = default_value
1082
1083    @cache
1084    @_include_fullname_in_traceback
1085    def __repr__(self):
1086        if self.default_value_str:
1087            default = f" = {self.default_value_str}"
1088        else:
1089            default = ""
1090        return f'<var {self.qualname.rsplit(".")[-1]}{self.annotation_str}{default}{_docstr(self)}>'
1091
1092    @cached_property
1093    def is_classvar(self) -> bool:
1094        """`True` if the variable is a class variable, `False` otherwise."""
1095        if get_origin(self.annotation) is ClassVar:
1096            return True
1097        else:
1098            return False
1099
1100    @cached_property
1101    def is_typevar(self) -> bool:
1102        """`True` if the variable is a `typing.TypeVar`, `False` otherwise."""
1103        if isinstance(self.default_value, TypeVar):
1104            return True
1105        else:
1106            return False
1107
1108    @cached_property
1109    def is_type_alias_type(self) -> bool:
1110        """`True` if the variable is a `typing.TypeAliasType`, `False` otherwise."""
1111        return isinstance(self.default_value, TypeAliasType)
1112
1113    @cached_property
1114    def is_enum_member(self) -> bool:
1115        """`True` if the variable is an enum member, `False` otherwise."""
1116        if isinstance(self.default_value, enum.Enum):
1117            return True
1118        else:
1119            return False
1120
1121    @cached_property
1122    def default_value_str(self) -> str:
1123        """The variable's default value as a pretty-printed str."""
1124        if self.default_value is empty:
1125            return ""
1126        if isinstance(self.default_value, TypeAliasType):
1127            return formatannotation(self.default_value.__value__)
1128        elif self.annotation == TypeAlias:
1129            return formatannotation(self.default_value)
1130
1131        # This is not perfect, but a solid attempt at preventing accidental leakage of secrets.
1132        # If you have input on how to improve the heuristic, please send a pull request!
1133        value_taken_from_env_var = (
1134            isinstance(self.default_value, str)
1135            and len(self.default_value) >= 8
1136            and self.default_value in _environ_lookup()
1137        )
1138        if value_taken_from_env_var and not os.environ.get("PDOC_DISPLAY_ENV_VARS", ""):
1139            env_var = "$" + _environ_lookup()[self.default_value]
1140            warnings.warn(
1141                f"The default value of {self.fullname} matches the {env_var} environment variable. "
1142                f"To prevent accidental leakage of secrets, the default value is not displayed. "
1143                f"Disable this behavior by setting PDOC_DISPLAY_ENV_VARS=1 as an environment variable.",
1144                RuntimeWarning,
1145            )
1146            return env_var
1147
1148        try:
1149            pretty = repr(self.default_value)
1150        except Exception as e:
1151            warnings.warn(f"repr({self.fullname}) raised an exception ({e!r})")
1152            return ""
1153
1154        pretty = _remove_memory_addresses(pretty)
1155        return pretty
1156
1157    @cached_property
1158    def annotation_str(self) -> str:
1159        """The variable's type annotation as a pretty-printed str."""
1160        if self.annotation is not empty:
1161            return f": {formatannotation(self.annotation)}"
1162        else:
1163            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)
1059    def __init__(
1060        self,
1061        modulename: str,
1062        qualname: str,
1063        *,
1064        taken_from: tuple[str, str],
1065        docstring: str,
1066        annotation: type | empty = empty,
1067        default_value: Any | empty = empty,
1068    ):
1069        """
1070        Construct a variable doc object.
1071
1072        While classes and functions can introspect themselves to see their docstring,
1073        variables can't do that as we don't have a "variable object" we could query.
1074        As such, docstring, declaration location, type annotation, and the default value
1075        must be passed manually in the constructor.
1076        """
1077        super().__init__(modulename, qualname, None, taken_from)
1078        # noinspection PyPropertyAccess
1079        self.docstring = inspect.cleandoc(docstring)
1080        self.annotation = annotation
1081        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
155    @cached_property
156    def docstring(self) -> str:
157        """
158        The docstring for this object. It has already been cleaned by `inspect.cleandoc`.
159
160        If no docstring can be found, an empty string is returned.
161        """
162        return _safe_getdoc(self.obj)

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

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

is_classvar: bool
1092    @cached_property
1093    def is_classvar(self) -> bool:
1094        """`True` if the variable is a class variable, `False` otherwise."""
1095        if get_origin(self.annotation) is ClassVar:
1096            return True
1097        else:
1098            return False

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

is_typevar: bool
1100    @cached_property
1101    def is_typevar(self) -> bool:
1102        """`True` if the variable is a `typing.TypeVar`, `False` otherwise."""
1103        if isinstance(self.default_value, TypeVar):
1104            return True
1105        else:
1106            return False

True if the variable is a typing.TypeVar, False otherwise.

is_type_alias_type: bool
1108    @cached_property
1109    def is_type_alias_type(self) -> bool:
1110        """`True` if the variable is a `typing.TypeAliasType`, `False` otherwise."""
1111        return isinstance(self.default_value, TypeAliasType)

True if the variable is a typing.TypeAliasType, False otherwise.

is_enum_member: bool
1113    @cached_property
1114    def is_enum_member(self) -> bool:
1115        """`True` if the variable is an enum member, `False` otherwise."""
1116        if isinstance(self.default_value, enum.Enum):
1117            return True
1118        else:
1119            return False

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

default_value_str: str
1121    @cached_property
1122    def default_value_str(self) -> str:
1123        """The variable's default value as a pretty-printed str."""
1124        if self.default_value is empty:
1125            return ""
1126        if isinstance(self.default_value, TypeAliasType):
1127            return formatannotation(self.default_value.__value__)
1128        elif self.annotation == TypeAlias:
1129            return formatannotation(self.default_value)
1130
1131        # This is not perfect, but a solid attempt at preventing accidental leakage of secrets.
1132        # If you have input on how to improve the heuristic, please send a pull request!
1133        value_taken_from_env_var = (
1134            isinstance(self.default_value, str)
1135            and len(self.default_value) >= 8
1136            and self.default_value in _environ_lookup()
1137        )
1138        if value_taken_from_env_var and not os.environ.get("PDOC_DISPLAY_ENV_VARS", ""):
1139            env_var = "$" + _environ_lookup()[self.default_value]
1140            warnings.warn(
1141                f"The default value of {self.fullname} matches the {env_var} environment variable. "
1142                f"To prevent accidental leakage of secrets, the default value is not displayed. "
1143                f"Disable this behavior by setting PDOC_DISPLAY_ENV_VARS=1 as an environment variable.",
1144                RuntimeWarning,
1145            )
1146            return env_var
1147
1148        try:
1149            pretty = repr(self.default_value)
1150        except Exception as e:
1151            warnings.warn(f"repr({self.fullname}) raised an exception ({e!r})")
1152            return ""
1153
1154        pretty = _remove_memory_addresses(pretty)
1155        return pretty

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

annotation_str: str
1157    @cached_property
1158    def annotation_str(self) -> str:
1159        """The variable's type annotation as a pretty-printed str."""
1160        if self.annotation is not empty:
1161            return f": {formatannotation(self.annotation)}"
1162        else:
1163            return ""

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