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        is_dataclass_with_default_docstring = (
 591            dataclasses.is_dataclass(self.obj)
 592            # from https://github.com/python/cpython/blob/3.10/Lib/dataclasses.py
 593            and doc
 594            == self.obj.__name__
 595            + str(inspect.signature(self.obj)).replace(" -> None", "")
 596        )
 597        if is_dataclass_with_default_docstring:
 598            return ""
 599        return doc
 600
 601    @cached_property
 602    def _var_docstrings(self) -> dict[str, str]:
 603        docstrings: dict[str, str] = {}
 604        for cls in self._bases:
 605            for name, docstr in doc_ast.walk_tree(cls).var_docstrings.items():
 606                docstrings.setdefault(name, docstr)
 607        return docstrings
 608
 609    @cached_property
 610    def _func_docstrings(self) -> dict[str, str]:
 611        docstrings: dict[str, str] = {}
 612        for cls in self._bases:
 613            for name, docstr in doc_ast.walk_tree(cls).func_docstrings.items():
 614                docstrings.setdefault(name, docstr)
 615        return docstrings
 616
 617    @cached_property
 618    def _var_annotations(self) -> dict[str, type]:
 619        # this is a bit tricky: __annotations__ also includes annotations from parent classes,
 620        # but we need to execute them in the namespace of the parent class.
 621        # Our workaround for this is to walk the MRO backwards, and only update/evaluate only if the annotation changes.
 622        annotations: dict[
 623            str, tuple[Any, type]
 624        ] = {}  # attribute -> (annotation_unresolved, annotation_resolved)
 625        for cls in reversed(self._bases):
 626            cls_annotations = doc_ast.walk_tree(cls).annotations.copy()
 627            dynamic_annotations = _safe_getattr(cls, "__annotations__", None)
 628            if isinstance(dynamic_annotations, dict):
 629                for attr, unresolved_annotation in dynamic_annotations.items():
 630                    cls_annotations[attr] = unresolved_annotation
 631            cls_fullname = (
 632                _safe_getattr(cls, "__module__", "") + "." + cls.__qualname__
 633            ).lstrip(".")
 634
 635            new_annotations = {
 636                attr: unresolved_annotation
 637                for attr, unresolved_annotation in cls_annotations.items()
 638                if attr not in annotations
 639                or annotations[attr][0] is not unresolved_annotation
 640            }
 641            localns = _safe_getattr(cls, "__dict__", None)
 642            for attr, t in resolve_annotations(
 643                new_annotations, inspect.getmodule(cls), localns, cls_fullname
 644            ).items():
 645                annotations[attr] = (new_annotations[attr], t)
 646
 647        return {k: v[1] for k, v in annotations.items()}
 648
 649    @cached_property
 650    def _bases(self) -> tuple[type, ...]:
 651        orig_bases = _safe_getattr(self.obj, "__orig_bases__", ())
 652        old_python_typeddict_workaround = (
 653            sys.version_info < (3, 12)
 654            and orig_bases
 655            and _safe_getattr(orig_bases[-1], "__name__", None) == "TypedDict"
 656        )
 657        if old_python_typeddict_workaround:  # pragma: no cover
 658            # TypedDicts on Python <3.12 have a botched __mro__. We need to fix it.
 659            return (self.obj, *orig_bases[:-1])
 660
 661        # __mro__ and __orig_bases__ differ between Python versions and special cases like TypedDict/NamedTuple.
 662        # This here is a pragmatic approximation of what we want.
 663        return (
 664            *(base for base in orig_bases if isinstance(base, type)),
 665            *self.obj.__mro__,
 666        )
 667
 668    @cached_property
 669    def _declarations(self) -> dict[str, tuple[str, str]]:
 670        decls: dict[str, tuple[str, str]] = {}
 671        for cls in self._bases:
 672            treeinfo = doc_ast.walk_tree(cls)
 673            for name in (
 674                treeinfo.var_docstrings.keys()
 675                | treeinfo.func_docstrings.keys()
 676                | treeinfo.annotations.keys()
 677            ):
 678                decls.setdefault(name, (cls.__module__, f"{cls.__qualname__}.{name}"))
 679            for name in cls.__dict__:
 680                decls.setdefault(name, (cls.__module__, f"{cls.__qualname__}.{name}"))
 681        if decls.get("__init__", None) == ("builtins", "object.__init__"):
 682            decls["__init__"] = (
 683                self.obj.__module__,
 684                f"{self.obj.__qualname__}.__init__",
 685            )
 686        return decls
 687
 688    def _taken_from(self, member_name: str, obj: Any) -> tuple[str, str]:
 689        try:
 690            return self._declarations[member_name]
 691        except KeyError:  # pragma: no cover
 692            # TypedDict botches __mro__ on Python <3.12 and may need special casing here.
 693            # One workaround is to also specify TypedDict as a base class, see pdoc.doc.Class._bases.
 694            warnings.warn(
 695                f"Cannot determine where {self.fullname}.{member_name} is taken from, assuming current file."
 696            )
 697            return self.modulename, f"{self.qualname}.{member_name}"
 698
 699    @cached_property
 700    def own_members(self) -> list[Doc]:
 701        members = self._members_by_origin.get((self.modulename, self.qualname), [])
 702        if self.taken_from != (self.modulename, self.qualname):
 703            # .taken_from may be != (self.modulename, self.qualname), for example when
 704            # a module re-exports a class from a private submodule.
 705            members += self._members_by_origin.get(self.taken_from, [])
 706        return members
 707
 708    @cached_property
 709    def _member_objects(self) -> dict[str, Any]:
 710        unsorted: dict[str, Any] = {}
 711        for cls in self._bases:
 712            for name, obj in cls.__dict__.items():
 713                unsorted.setdefault(name, obj)
 714        for name in self._var_docstrings:
 715            unsorted.setdefault(name, empty)
 716        for name in self._var_annotations:
 717            unsorted.setdefault(name, empty)
 718
 719        init_has_no_doc = unsorted.get("__init__", object.__init__).__doc__ in (
 720            None,
 721            object.__init__.__doc__,
 722        )
 723        if init_has_no_doc:
 724            if inspect.isabstract(self.obj):
 725                # Special case: We don't want to show constructors for abstract base classes unless
 726                # they have a custom docstring.
 727                del unsorted["__init__"]
 728            elif issubclass(self.obj, enum.Enum):
 729                # Special case: Do not show a constructor for enums. They are typically not constructed by users.
 730                # The alternative would be showing __new__, as __call__ is too verbose.
 731                del unsorted["__init__"]
 732            elif issubclass(self.obj, dict):
 733                # Special case: Do not show a constructor for dict subclasses.
 734                unsorted.pop(
 735                    "__init__", None
 736                )  # TypedDict subclasses may not have __init__.
 737            else:
 738                # Check if there's a helpful Metaclass.__call__ or Class.__new__. This dance is very similar to
 739                # https://github.com/python/cpython/blob/9feae41c4f04ca27fd2c865807a5caeb50bf4fc4/Lib/inspect.py#L2359-L2376
 740                call = _safe_getattr(type(self.obj), "__call__", None)
 741                custom_call_with_custom_docstring = (
 742                    call is not None
 743                    and not isinstance(call, NonUserDefinedCallables)
 744                    and call.__doc__ not in (None, object.__call__.__doc__)
 745                )
 746                if custom_call_with_custom_docstring:
 747                    unsorted["__init__"] = call
 748                else:
 749                    # Does our class define a custom __new__ method?
 750                    new = _safe_getattr(self.obj, "__new__", None)
 751                    custom_new_with_custom_docstring = (
 752                        new is not None
 753                        and not isinstance(new, NonUserDefinedCallables)
 754                        and new.__doc__ not in (None, object.__new__.__doc__)
 755                    )
 756                    if custom_new_with_custom_docstring:
 757                        unsorted["__init__"] = new
 758
 759        sorted: dict[str, Any] = {}
 760        for cls in self._bases:
 761            sorted, unsorted = doc_ast.sort_by_source(cls, sorted, unsorted)
 762        sorted.update(unsorted)
 763        return sorted
 764
 765    @cached_property
 766    def bases(self) -> list[tuple[str, str, str]]:
 767        """
 768        A list of all base classes, i.e. all immediate parent classes.
 769
 770        Each parent class is represented as a `(modulename, qualname, display_text)` tuple.
 771        """
 772        bases = []
 773        for x in _safe_getattr(self.obj, "__orig_bases__", self.obj.__bases__):
 774            if x is object:
 775                continue
 776            o = get_origin(x)
 777            if o:
 778                bases.append((o.__module__, o.__qualname__, str(x)))
 779            elif x.__module__ == self.modulename:
 780                bases.append((x.__module__, x.__qualname__, x.__qualname__))
 781            else:
 782                bases.append(
 783                    (x.__module__, x.__qualname__, f"{x.__module__}.{x.__qualname__}")
 784                )
 785        return bases
 786
 787    @cached_property
 788    def decorators(self) -> list[str]:
 789        """A list of all decorators the class is decorated with."""
 790        decorators = []
 791        for t in doc_ast.parse(self.obj).decorator_list:
 792            decorators.append(f"@{doc_ast.unparse(t)}")
 793        return decorators
 794
 795    @cached_property
 796    def class_variables(self) -> list[Variable]:
 797        """
 798        A list of all documented class variables in the class.
 799
 800        Class variables are variables that are explicitly annotated with `typing.ClassVar`.
 801        All other variables are treated as instance variables.
 802        """
 803        return [
 804            x
 805            for x in self.members.values()
 806            if isinstance(x, Variable) and x.is_classvar
 807        ]
 808
 809    @cached_property
 810    def instance_variables(self) -> list[Variable]:
 811        """
 812        A list of all instance variables in the class.
 813        """
 814        return [
 815            x
 816            for x in self.members.values()
 817            if isinstance(x, Variable) and not x.is_classvar
 818        ]
 819
 820    @cached_property
 821    def classmethods(self) -> list[Function]:
 822        """
 823        A list of all documented `@classmethod`s.
 824        """
 825        return [
 826            x
 827            for x in self.members.values()
 828            if isinstance(x, Function) and x.is_classmethod
 829        ]
 830
 831    @cached_property
 832    def staticmethods(self) -> list[Function]:
 833        """
 834        A list of all documented `@staticmethod`s.
 835        """
 836        return [
 837            x
 838            for x in self.members.values()
 839            if isinstance(x, Function) and x.is_staticmethod
 840        ]
 841
 842    @cached_property
 843    def methods(self) -> list[Function]:
 844        """
 845        A list of all documented methods in the class that are neither static- nor classmethods.
 846        """
 847        return [
 848            x
 849            for x in self.members.values()
 850            if isinstance(x, Function)
 851            and not x.is_staticmethod
 852            and not x.is_classmethod
 853        ]
 854
 855
 856if sys.version_info >= (3, 10):
 857    WrappedFunction = types.FunctionType | staticmethod | classmethod
 858else:  # pragma: no cover
 859    WrappedFunction = Union[types.FunctionType, staticmethod, classmethod]
 860
 861
 862class Function(Doc[types.FunctionType]):
 863    """
 864    Representation of a function's documentation.
 865
 866    This class covers all "flavors" of functions, for example it also
 867    supports `@classmethod`s or `@staticmethod`s.
 868    """
 869
 870    kind = "function"
 871
 872    wrapped: WrappedFunction
 873    """The original wrapped function (e.g., `staticmethod(func)`)"""
 874
 875    obj: types.FunctionType
 876    """The unwrapped "real" function."""
 877
 878    def __init__(
 879        self,
 880        modulename: str,
 881        qualname: str,
 882        func: WrappedFunction,
 883        taken_from: tuple[str, str],
 884    ):
 885        """Initialize a function's documentation object."""
 886        unwrapped: types.FunctionType
 887        if isinstance(func, (classmethod, staticmethod)):
 888            unwrapped = func.__func__  # type: ignore
 889        elif isinstance(func, singledispatchmethod):
 890            unwrapped = func.func  # type: ignore
 891        elif hasattr(func, "__wrapped__"):
 892            unwrapped = func.__wrapped__
 893        else:
 894            unwrapped = func
 895        super().__init__(modulename, qualname, unwrapped, taken_from)
 896        self.wrapped = func
 897
 898    @cache
 899    @_include_fullname_in_traceback
 900    def __repr__(self):
 901        if self.is_classmethod:
 902            t = "class"
 903        elif self.is_staticmethod:
 904            t = "static"
 905        elif self.qualname != _safe_getattr(self.obj, "__name__", None):
 906            t = "method"
 907        else:
 908            t = "function"
 909        return f"<{_decorators(self)}{t} {self.funcdef} {self.name}{self.signature}: ...{_docstr(self)}>"
 910
 911    @cached_property
 912    def docstring(self) -> str:
 913        doc = Doc.docstring.__get__(self)  # type: ignore
 914        if not doc:
 915            # inspect.getdoc fails for inherited @classmethods and unbound @property descriptors.
 916            # We now do an ugly dance to obtain the bound object instead,
 917            # that somewhat resembles what inspect._findclass is doing.
 918            cls = sys.modules.get(_safe_getattr(self.obj, "__module__", None), None)
 919            for name in _safe_getattr(self.obj, "__qualname__", "").split(".")[:-1]:
 920                cls = _safe_getattr(cls, name, None)
 921
 922            unbound = _safe_getattr(cls, "__dict__", {}).get(self.name)
 923            is_classmethod_property = isinstance(unbound, classmethod) and isinstance(
 924                unbound.__func__, (property, cached_property)
 925            )
 926            if not is_classmethod_property:
 927                # We choke on @classmethod @property, but that's okay because it's been deprecated with Python 3.11.
 928                # Directly accessing them would give us the return value, which has the wrong docstring.
 929                doc = _safe_getdoc(_safe_getattr(cls, self.name, None))
 930
 931        if doc == object.__init__.__doc__:
 932            # inspect.getdoc(Foo.__init__) returns the docstring, for object.__init__ if left undefined...
 933            return ""
 934        else:
 935            return doc
 936
 937    @cached_property
 938    def is_classmethod(self) -> bool:
 939        """
 940        `True` if this function is a `@classmethod`, `False` otherwise.
 941        """
 942        return isinstance(self.wrapped, classmethod)
 943
 944    @cached_property
 945    def is_staticmethod(self) -> bool:
 946        """
 947        `True` if this function is a `@staticmethod`, `False` otherwise.
 948        """
 949        return isinstance(self.wrapped, staticmethod)
 950
 951    @cached_property
 952    def decorators(self) -> list[str]:
 953        """A list of all decorators the function is decorated with."""
 954        decorators = []
 955        obj: types.FunctionType = self.obj  # type: ignore
 956        for t in doc_ast.parse(obj).decorator_list:
 957            decorators.append(f"@{doc_ast.unparse(t)}")
 958        return decorators
 959
 960    @cached_property
 961    def funcdef(self) -> str:
 962        """
 963        The string of keywords used to define the function, i.e. `"def"` or `"async def"`.
 964        """
 965        if inspect.iscoroutinefunction(self.obj) or inspect.isasyncgenfunction(
 966            self.obj
 967        ):
 968            return "async def"
 969        else:
 970            return "def"
 971
 972    @cached_property
 973    def signature(self) -> inspect.Signature:
 974        """
 975        The function's signature.
 976
 977        This usually returns an instance of `_PrettySignature`, a subclass of `inspect.Signature`
 978        that contains pdoc-specific optimizations. For example, long argument lists are split over multiple lines
 979        in repr(). Additionally, all types are already resolved.
 980
 981        If the signature cannot be determined, a placeholder Signature object is returned.
 982        """
 983        if self.obj is object.__init__:
 984            # there is a weird edge case were inspect.signature returns a confusing (self, /, *args, **kwargs)
 985            # signature for the default __init__ method.
 986            return inspect.Signature()
 987        try:
 988            sig = _PrettySignature.from_callable(self.obj)
 989        except Exception:
 990            return inspect.Signature(
 991                [inspect.Parameter("unknown", inspect.Parameter.POSITIONAL_OR_KEYWORD)]
 992            )
 993        mod = inspect.getmodule(self.obj)
 994        globalns = _safe_getattr(mod, "__dict__", {})
 995        localns = globalns
 996        for parent_cls_name in self.qualname.split(".")[:-1]:
 997            parent_cls = localns.get(parent_cls_name, object)
 998            localns = _safe_getattr(parent_cls, "__dict__", None)
 999            if localns is None:
1000                break  # pragma: no cover
1001
1002        if self.name == "__init__":
1003            sig = sig.replace(return_annotation=empty)
1004        else:
1005            sig = sig.replace(
1006                return_annotation=safe_eval_type(
1007                    sig.return_annotation, globalns, localns, mod, self.fullname
1008                )
1009            )
1010        for p in sig.parameters.values():
1011            p._annotation = safe_eval_type(  # type: ignore
1012                p.annotation, globalns, localns, mod, self.fullname
1013            )
1014        return sig
1015
1016    @cached_property
1017    def signature_without_self(self) -> inspect.Signature:
1018        """Like `signature`, but without the first argument.
1019
1020        This is useful to display constructors.
1021        """
1022        return self.signature.replace(
1023            parameters=list(self.signature.parameters.values())[1:]
1024        )
1025
1026
1027class Variable(Doc[None]):
1028    """
1029    Representation of a variable's documentation. This includes module, class and instance variables.
1030    """
1031
1032    kind = "variable"
1033
1034    default_value: (
1035        Any | empty
1036    )  # technically Any includes empty, but this conveys intent.
1037    """
1038    The variable's default value.
1039    
1040    In some cases, no default value is known. This may either be because a variable is only defined in the constructor,
1041    or it is only declared with a type annotation without assignment (`foo: int`).
1042    To distinguish this case from a default value of `None`, `pdoc.doc_types.empty` is used as a placeholder.
1043    """
1044
1045    annotation: type | empty
1046    """
1047    The variable's type annotation.
1048    
1049    If there is no type annotation, `pdoc.doc_types.empty` is used as a placeholder.
1050    """
1051
1052    def __init__(
1053        self,
1054        modulename: str,
1055        qualname: str,
1056        *,
1057        taken_from: tuple[str, str],
1058        docstring: str,
1059        annotation: type | empty = empty,
1060        default_value: Any | empty = empty,
1061    ):
1062        """
1063        Construct a variable doc object.
1064
1065        While classes and functions can introspect themselves to see their docstring,
1066        variables can't do that as we don't have a "variable object" we could query.
1067        As such, docstring, declaration location, type annotation, and the default value
1068        must be passed manually in the constructor.
1069        """
1070        super().__init__(modulename, qualname, None, taken_from)
1071        # noinspection PyPropertyAccess
1072        self.docstring = inspect.cleandoc(docstring)
1073        self.annotation = annotation
1074        self.default_value = default_value
1075
1076    @cache
1077    @_include_fullname_in_traceback
1078    def __repr__(self):
1079        if self.default_value_str:
1080            default = f" = {self.default_value_str}"
1081        else:
1082            default = ""
1083        return f'<var {self.qualname.rsplit(".")[-1]}{self.annotation_str}{default}{_docstr(self)}>'
1084
1085    @cached_property
1086    def is_classvar(self) -> bool:
1087        """`True` if the variable is a class variable, `False` otherwise."""
1088        if get_origin(self.annotation) is ClassVar:
1089            return True
1090        else:
1091            return False
1092
1093    @cached_property
1094    def is_typevar(self) -> bool:
1095        """`True` if the variable is a `typing.TypeVar`, `False` otherwise."""
1096        if isinstance(self.default_value, TypeVar):
1097            return True
1098        else:
1099            return False
1100
1101    @cached_property
1102    def is_type_alias_type(self) -> bool:
1103        """`True` if the variable is a `typing.TypeAliasType`, `False` otherwise."""
1104        return isinstance(self.default_value, TypeAliasType)
1105
1106    @cached_property
1107    def is_enum_member(self) -> bool:
1108        """`True` if the variable is an enum member, `False` otherwise."""
1109        if isinstance(self.default_value, enum.Enum):
1110            return True
1111        else:
1112            return False
1113
1114    @cached_property
1115    def default_value_str(self) -> str:
1116        """The variable's default value as a pretty-printed str."""
1117        if self.default_value is empty:
1118            return ""
1119        if isinstance(self.default_value, TypeAliasType):
1120            return formatannotation(self.default_value.__value__)
1121        elif self.annotation == TypeAlias:
1122            return formatannotation(self.default_value)
1123
1124        # This is not perfect, but a solid attempt at preventing accidental leakage of secrets.
1125        # If you have input on how to improve the heuristic, please send a pull request!
1126        value_taken_from_env_var = (
1127            isinstance(self.default_value, str)
1128            and len(self.default_value) >= 8
1129            and self.default_value in _environ_lookup()
1130        )
1131        if value_taken_from_env_var and not os.environ.get("PDOC_DISPLAY_ENV_VARS", ""):
1132            env_var = "$" + _environ_lookup()[self.default_value]
1133            warnings.warn(
1134                f"The default value of {self.fullname} matches the {env_var} environment variable. "
1135                f"To prevent accidental leakage of secrets, the default value is not displayed. "
1136                f"Disable this behavior by setting PDOC_DISPLAY_ENV_VARS=1 as an environment variable.",
1137                RuntimeWarning,
1138            )
1139            return env_var
1140
1141        try:
1142            pretty = repr(self.default_value)
1143        except Exception as e:
1144            warnings.warn(f"repr({self.fullname}) raised an exception ({e!r})")
1145            return ""
1146
1147        pretty = _remove_memory_addresses(pretty)
1148        return pretty
1149
1150    @cached_property
1151    def annotation_str(self) -> str:
1152        """The variable's type annotation as a pretty-printed str."""
1153        if self.annotation is not empty:
1154            return f": {formatannotation(self.annotation)}"
1155        else:
1156            return ""
1157
1158
1159@cache
1160def _environ_lookup():
1161    """
1162    A reverse lookup of os.environ. This is a cached function so that it is evaluated lazily.
1163    """
1164    return {value: key for key, value in os.environ.items()}
1165
1166
1167class _PrettySignature(inspect.Signature):
1168    """
1169    A subclass of `inspect.Signature` that pads __str__ over several lines
1170    for complex signatures.
1171    """
1172
1173    MULTILINE_CUTOFF = 70
1174
1175    def _params(self) -> list[str]:
1176        # redeclared here to keep code snipped below as-is.
1177        _POSITIONAL_ONLY = inspect.Parameter.POSITIONAL_ONLY
1178        _VAR_POSITIONAL = inspect.Parameter.VAR_POSITIONAL
1179        _KEYWORD_ONLY = inspect.Parameter.KEYWORD_ONLY
1180
1181        # https://github.com/python/cpython/blob/799f8489d418b7f9207d333eac38214931bd7dcc/Lib/inspect.py#L3083-L3117
1182        # Change: added re.sub() to formatted = ....
1183        # ✂ start ✂
1184        result = []
1185        render_pos_only_separator = False
1186        render_kw_only_separator = True
1187        for param in self.parameters.values():
1188            formatted = str(param)
1189            formatted = _remove_memory_addresses(formatted)
1190
1191            kind = param.kind
1192
1193            if kind == _POSITIONAL_ONLY:
1194                render_pos_only_separator = True
1195            elif render_pos_only_separator:
1196                # It's not a positional-only parameter, and the flag
1197                # is set to 'True' (there were pos-only params before.)
1198                result.append("/")
1199                render_pos_only_separator = False
1200
1201            if kind == _VAR_POSITIONAL:
1202                # OK, we have an '*args'-like parameter, so we won't need
1203                # a '*' to separate keyword-only arguments
1204                render_kw_only_separator = False
1205            elif kind == _KEYWORD_ONLY and render_kw_only_separator:
1206                # We have a keyword-only parameter to render and we haven't
1207                # rendered an '*args'-like parameter before, so add a '*'
1208                # separator to the parameters list ("foo(arg1, *, arg2)" case)
1209                result.append("*")
1210                # This condition should be only triggered once, so
1211                # reset the flag
1212                render_kw_only_separator = False
1213
1214            result.append(formatted)
1215
1216        if render_pos_only_separator:
1217            # There were only positional-only parameters, hence the
1218            # flag was not reset to 'False'
1219            result.append("/")
1220        # ✂ end ✂
1221
1222        return result
1223
1224    def _return_annotation_str(self) -> str:
1225        if self.return_annotation is not empty:
1226            return formatannotation(self.return_annotation)
1227        else:
1228            return ""
1229
1230    def __str__(self):
1231        result = self._params()
1232        return_annot = self._return_annotation_str()
1233
1234        total_len = sum(len(x) + 2 for x in result) + len(return_annot)
1235
1236        if total_len > self.MULTILINE_CUTOFF:
1237            rendered = "(\n    " + ",\n    ".join(result) + "\n)"
1238        else:
1239            rendered = "({})".format(", ".join(result))
1240        if return_annot:
1241            rendered += f" -> {return_annot}"
1242
1243        return rendered
1244
1245
1246def _cut(x: str) -> str:
1247    """helper function for Doc.__repr__()"""
1248    if len(x) < 20:
1249        return x
1250    else:
1251        return x[:20] + "…"
1252
1253
1254def _docstr(doc: Doc) -> str:
1255    """helper function for Doc.__repr__()"""
1256    docstr = []
1257    if doc.is_inherited:
1258        docstr.append(f"inherited from {'.'.join(doc.taken_from).rstrip('.')}")
1259    if doc.docstring:
1260        docstr.append(_cut(doc.docstring))
1261    if docstr:
1262        return f"  # {', '.join(docstr)}"
1263    else:
1264        return ""
1265
1266
1267def _decorators(doc: Class | Function) -> str:
1268    """helper function for Doc.__repr__()"""
1269    if doc.decorators:
1270        return " ".join(doc.decorators) + " "
1271    else:
1272        return ""
1273
1274
1275def _children(doc: Namespace) -> str:
1276    children = "\n".join(
1277        repr(x)
1278        for x in doc.members.values()
1279        if not x.name.startswith("_") or x.name == "__init__"
1280    )
1281    if children:
1282        children += "\n"
1283        children = f"\n{textwrap.indent(children, '    ')}"
1284    return children
1285
1286
1287def _safe_getattr(obj, attr, default):
1288    """Like `getattr()`, but never raises."""
1289    try:
1290        return getattr(obj, attr, default)
1291    except Exception as e:
1292        warnings.warn(
1293            f"getattr({obj!r}, {attr!r}, {default!r}) raised an exception: {e!r}"
1294        )
1295        return default
1296
1297
1298def _safe_getdoc(obj: Any) -> str:
1299    """Like `inspect.getdoc()`, but never raises. Always returns a stripped string."""
1300    try:
1301        doc = inspect.getdoc(obj) or ""
1302    except Exception as e:
1303        warnings.warn(f"inspect.getdoc({obj!r}) raised an exception: {e!r}")
1304        return ""
1305    else:
1306        return doc.strip()
1307
1308
1309def _remove_memory_addresses(x: str) -> str:
1310    """Remove memory addresses from repr() output"""
1311    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        is_dataclass_with_default_docstring = (
592            dataclasses.is_dataclass(self.obj)
593            # from https://github.com/python/cpython/blob/3.10/Lib/dataclasses.py
594            and doc
595            == self.obj.__name__
596            + str(inspect.signature(self.obj)).replace(" -> None", "")
597        )
598        if is_dataclass_with_default_docstring:
599            return ""
600        return doc
601
602    @cached_property
603    def _var_docstrings(self) -> dict[str, str]:
604        docstrings: dict[str, str] = {}
605        for cls in self._bases:
606            for name, docstr in doc_ast.walk_tree(cls).var_docstrings.items():
607                docstrings.setdefault(name, docstr)
608        return docstrings
609
610    @cached_property
611    def _func_docstrings(self) -> dict[str, str]:
612        docstrings: dict[str, str] = {}
613        for cls in self._bases:
614            for name, docstr in doc_ast.walk_tree(cls).func_docstrings.items():
615                docstrings.setdefault(name, docstr)
616        return docstrings
617
618    @cached_property
619    def _var_annotations(self) -> dict[str, type]:
620        # this is a bit tricky: __annotations__ also includes annotations from parent classes,
621        # but we need to execute them in the namespace of the parent class.
622        # Our workaround for this is to walk the MRO backwards, and only update/evaluate only if the annotation changes.
623        annotations: dict[
624            str, tuple[Any, type]
625        ] = {}  # attribute -> (annotation_unresolved, annotation_resolved)
626        for cls in reversed(self._bases):
627            cls_annotations = doc_ast.walk_tree(cls).annotations.copy()
628            dynamic_annotations = _safe_getattr(cls, "__annotations__", None)
629            if isinstance(dynamic_annotations, dict):
630                for attr, unresolved_annotation in dynamic_annotations.items():
631                    cls_annotations[attr] = unresolved_annotation
632            cls_fullname = (
633                _safe_getattr(cls, "__module__", "") + "." + cls.__qualname__
634            ).lstrip(".")
635
636            new_annotations = {
637                attr: unresolved_annotation
638                for attr, unresolved_annotation in cls_annotations.items()
639                if attr not in annotations
640                or annotations[attr][0] is not unresolved_annotation
641            }
642            localns = _safe_getattr(cls, "__dict__", None)
643            for attr, t in resolve_annotations(
644                new_annotations, inspect.getmodule(cls), localns, cls_fullname
645            ).items():
646                annotations[attr] = (new_annotations[attr], t)
647
648        return {k: v[1] for k, v in annotations.items()}
649
650    @cached_property
651    def _bases(self) -> tuple[type, ...]:
652        orig_bases = _safe_getattr(self.obj, "__orig_bases__", ())
653        old_python_typeddict_workaround = (
654            sys.version_info < (3, 12)
655            and orig_bases
656            and _safe_getattr(orig_bases[-1], "__name__", None) == "TypedDict"
657        )
658        if old_python_typeddict_workaround:  # pragma: no cover
659            # TypedDicts on Python <3.12 have a botched __mro__. We need to fix it.
660            return (self.obj, *orig_bases[:-1])
661
662        # __mro__ and __orig_bases__ differ between Python versions and special cases like TypedDict/NamedTuple.
663        # This here is a pragmatic approximation of what we want.
664        return (
665            *(base for base in orig_bases if isinstance(base, type)),
666            *self.obj.__mro__,
667        )
668
669    @cached_property
670    def _declarations(self) -> dict[str, tuple[str, str]]:
671        decls: dict[str, tuple[str, str]] = {}
672        for cls in self._bases:
673            treeinfo = doc_ast.walk_tree(cls)
674            for name in (
675                treeinfo.var_docstrings.keys()
676                | treeinfo.func_docstrings.keys()
677                | treeinfo.annotations.keys()
678            ):
679                decls.setdefault(name, (cls.__module__, f"{cls.__qualname__}.{name}"))
680            for name in cls.__dict__:
681                decls.setdefault(name, (cls.__module__, f"{cls.__qualname__}.{name}"))
682        if decls.get("__init__", None) == ("builtins", "object.__init__"):
683            decls["__init__"] = (
684                self.obj.__module__,
685                f"{self.obj.__qualname__}.__init__",
686            )
687        return decls
688
689    def _taken_from(self, member_name: str, obj: Any) -> tuple[str, str]:
690        try:
691            return self._declarations[member_name]
692        except KeyError:  # pragma: no cover
693            # TypedDict botches __mro__ on Python <3.12 and may need special casing here.
694            # One workaround is to also specify TypedDict as a base class, see pdoc.doc.Class._bases.
695            warnings.warn(
696                f"Cannot determine where {self.fullname}.{member_name} is taken from, assuming current file."
697            )
698            return self.modulename, f"{self.qualname}.{member_name}"
699
700    @cached_property
701    def own_members(self) -> list[Doc]:
702        members = self._members_by_origin.get((self.modulename, self.qualname), [])
703        if self.taken_from != (self.modulename, self.qualname):
704            # .taken_from may be != (self.modulename, self.qualname), for example when
705            # a module re-exports a class from a private submodule.
706            members += self._members_by_origin.get(self.taken_from, [])
707        return members
708
709    @cached_property
710    def _member_objects(self) -> dict[str, Any]:
711        unsorted: dict[str, Any] = {}
712        for cls in self._bases:
713            for name, obj in cls.__dict__.items():
714                unsorted.setdefault(name, obj)
715        for name in self._var_docstrings:
716            unsorted.setdefault(name, empty)
717        for name in self._var_annotations:
718            unsorted.setdefault(name, empty)
719
720        init_has_no_doc = unsorted.get("__init__", object.__init__).__doc__ in (
721            None,
722            object.__init__.__doc__,
723        )
724        if init_has_no_doc:
725            if inspect.isabstract(self.obj):
726                # Special case: We don't want to show constructors for abstract base classes unless
727                # they have a custom docstring.
728                del unsorted["__init__"]
729            elif issubclass(self.obj, enum.Enum):
730                # Special case: Do not show a constructor for enums. They are typically not constructed by users.
731                # The alternative would be showing __new__, as __call__ is too verbose.
732                del unsorted["__init__"]
733            elif issubclass(self.obj, dict):
734                # Special case: Do not show a constructor for dict subclasses.
735                unsorted.pop(
736                    "__init__", None
737                )  # TypedDict subclasses may not have __init__.
738            else:
739                # Check if there's a helpful Metaclass.__call__ or Class.__new__. This dance is very similar to
740                # https://github.com/python/cpython/blob/9feae41c4f04ca27fd2c865807a5caeb50bf4fc4/Lib/inspect.py#L2359-L2376
741                call = _safe_getattr(type(self.obj), "__call__", None)
742                custom_call_with_custom_docstring = (
743                    call is not None
744                    and not isinstance(call, NonUserDefinedCallables)
745                    and call.__doc__ not in (None, object.__call__.__doc__)
746                )
747                if custom_call_with_custom_docstring:
748                    unsorted["__init__"] = call
749                else:
750                    # Does our class define a custom __new__ method?
751                    new = _safe_getattr(self.obj, "__new__", None)
752                    custom_new_with_custom_docstring = (
753                        new is not None
754                        and not isinstance(new, NonUserDefinedCallables)
755                        and new.__doc__ not in (None, object.__new__.__doc__)
756                    )
757                    if custom_new_with_custom_docstring:
758                        unsorted["__init__"] = new
759
760        sorted: dict[str, Any] = {}
761        for cls in self._bases:
762            sorted, unsorted = doc_ast.sort_by_source(cls, sorted, unsorted)
763        sorted.update(unsorted)
764        return sorted
765
766    @cached_property
767    def bases(self) -> list[tuple[str, str, str]]:
768        """
769        A list of all base classes, i.e. all immediate parent classes.
770
771        Each parent class is represented as a `(modulename, qualname, display_text)` tuple.
772        """
773        bases = []
774        for x in _safe_getattr(self.obj, "__orig_bases__", self.obj.__bases__):
775            if x is object:
776                continue
777            o = get_origin(x)
778            if o:
779                bases.append((o.__module__, o.__qualname__, str(x)))
780            elif x.__module__ == self.modulename:
781                bases.append((x.__module__, x.__qualname__, x.__qualname__))
782            else:
783                bases.append(
784                    (x.__module__, x.__qualname__, f"{x.__module__}.{x.__qualname__}")
785                )
786        return bases
787
788    @cached_property
789    def decorators(self) -> list[str]:
790        """A list of all decorators the class is decorated with."""
791        decorators = []
792        for t in doc_ast.parse(self.obj).decorator_list:
793            decorators.append(f"@{doc_ast.unparse(t)}")
794        return decorators
795
796    @cached_property
797    def class_variables(self) -> list[Variable]:
798        """
799        A list of all documented class variables in the class.
800
801        Class variables are variables that are explicitly annotated with `typing.ClassVar`.
802        All other variables are treated as instance variables.
803        """
804        return [
805            x
806            for x in self.members.values()
807            if isinstance(x, Variable) and x.is_classvar
808        ]
809
810    @cached_property
811    def instance_variables(self) -> list[Variable]:
812        """
813        A list of all instance variables in the class.
814        """
815        return [
816            x
817            for x in self.members.values()
818            if isinstance(x, Variable) and not x.is_classvar
819        ]
820
821    @cached_property
822    def classmethods(self) -> list[Function]:
823        """
824        A list of all documented `@classmethod`s.
825        """
826        return [
827            x
828            for x in self.members.values()
829            if isinstance(x, Function) and x.is_classmethod
830        ]
831
832    @cached_property
833    def staticmethods(self) -> list[Function]:
834        """
835        A list of all documented `@staticmethod`s.
836        """
837        return [
838            x
839            for x in self.members.values()
840            if isinstance(x, Function) and x.is_staticmethod
841        ]
842
843    @cached_property
844    def methods(self) -> list[Function]:
845        """
846        A list of all documented methods in the class that are neither static- nor classmethods.
847        """
848        return [
849            x
850            for x in self.members.values()
851            if isinstance(x, Function)
852            and not x.is_staticmethod
853            and not x.is_classmethod
854        ]

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        is_dataclass_with_default_docstring = (
592            dataclasses.is_dataclass(self.obj)
593            # from https://github.com/python/cpython/blob/3.10/Lib/dataclasses.py
594            and doc
595            == self.obj.__name__
596            + str(inspect.signature(self.obj)).replace(" -> None", "")
597        )
598        if is_dataclass_with_default_docstring:
599            return ""
600        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]
700    @cached_property
701    def own_members(self) -> list[Doc]:
702        members = self._members_by_origin.get((self.modulename, self.qualname), [])
703        if self.taken_from != (self.modulename, self.qualname):
704            # .taken_from may be != (self.modulename, self.qualname), for example when
705            # a module re-exports a class from a private submodule.
706            members += self._members_by_origin.get(self.taken_from, [])
707        return members

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

bases: list[tuple[str, str, str]]
766    @cached_property
767    def bases(self) -> list[tuple[str, str, str]]:
768        """
769        A list of all base classes, i.e. all immediate parent classes.
770
771        Each parent class is represented as a `(modulename, qualname, display_text)` tuple.
772        """
773        bases = []
774        for x in _safe_getattr(self.obj, "__orig_bases__", self.obj.__bases__):
775            if x is object:
776                continue
777            o = get_origin(x)
778            if o:
779                bases.append((o.__module__, o.__qualname__, str(x)))
780            elif x.__module__ == self.modulename:
781                bases.append((x.__module__, x.__qualname__, x.__qualname__))
782            else:
783                bases.append(
784                    (x.__module__, x.__qualname__, f"{x.__module__}.{x.__qualname__}")
785                )
786        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]
788    @cached_property
789    def decorators(self) -> list[str]:
790        """A list of all decorators the class is decorated with."""
791        decorators = []
792        for t in doc_ast.parse(self.obj).decorator_list:
793            decorators.append(f"@{doc_ast.unparse(t)}")
794        return decorators

A list of all decorators the class is decorated with.

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

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]
810    @cached_property
811    def instance_variables(self) -> list[Variable]:
812        """
813        A list of all instance variables in the class.
814        """
815        return [
816            x
817            for x in self.members.values()
818            if isinstance(x, Variable) and not x.is_classvar
819        ]

A list of all instance variables in the class.

classmethods: list[Function]
821    @cached_property
822    def classmethods(self) -> list[Function]:
823        """
824        A list of all documented `@classmethod`s.
825        """
826        return [
827            x
828            for x in self.members.values()
829            if isinstance(x, Function) and x.is_classmethod
830        ]

A list of all documented @classmethods.

staticmethods: list[Function]
832    @cached_property
833    def staticmethods(self) -> list[Function]:
834        """
835        A list of all documented `@staticmethod`s.
836        """
837        return [
838            x
839            for x in self.members.values()
840            if isinstance(x, Function) and x.is_staticmethod
841        ]

A list of all documented @staticmethods.

methods: list[Function]
843    @cached_property
844    def methods(self) -> list[Function]:
845        """
846        A list of all documented methods in the class that are neither static- nor classmethods.
847        """
848        return [
849            x
850            for x in self.members.values()
851            if isinstance(x, Function)
852            and not x.is_staticmethod
853            and not x.is_classmethod
854        ]

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

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

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

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

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

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

decorators: list[str]
952    @cached_property
953    def decorators(self) -> list[str]:
954        """A list of all decorators the function is decorated with."""
955        decorators = []
956        obj: types.FunctionType = self.obj  # type: ignore
957        for t in doc_ast.parse(obj).decorator_list:
958            decorators.append(f"@{doc_ast.unparse(t)}")
959        return decorators

A list of all decorators the function is decorated with.

funcdef: str
961    @cached_property
962    def funcdef(self) -> str:
963        """
964        The string of keywords used to define the function, i.e. `"def"` or `"async def"`.
965        """
966        if inspect.iscoroutinefunction(self.obj) or inspect.isasyncgenfunction(
967            self.obj
968        ):
969            return "async def"
970        else:
971            return "def"

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

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

Like signature, but without the first argument.

This is useful to display constructors.

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

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

is_typevar: bool
1094    @cached_property
1095    def is_typevar(self) -> bool:
1096        """`True` if the variable is a `typing.TypeVar`, `False` otherwise."""
1097        if isinstance(self.default_value, TypeVar):
1098            return True
1099        else:
1100            return False

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

is_type_alias_type: bool
1102    @cached_property
1103    def is_type_alias_type(self) -> bool:
1104        """`True` if the variable is a `typing.TypeAliasType`, `False` otherwise."""
1105        return isinstance(self.default_value, TypeAliasType)

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

is_enum_member: bool
1107    @cached_property
1108    def is_enum_member(self) -> bool:
1109        """`True` if the variable is an enum member, `False` otherwise."""
1110        if isinstance(self.default_value, enum.Enum):
1111            return True
1112        else:
1113            return False

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

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

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

annotation_str: str
1151    @cached_property
1152    def annotation_str(self) -> str:
1153        """The variable's type annotation as a pretty-printed str."""
1154        if self.annotation is not empty:
1155            return f": {formatannotation(self.annotation)}"
1156        else:
1157            return ""

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