Edit on GitHub

pdoc.doc

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

There are four main types of documentation objects:

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

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

A base class for all documentation objects.

Doc(modulename: str, qualname: str, obj: ~T, taken_from: tuple[str, str])
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

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
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
fullname: str
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(".")

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

name: str
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]

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

docstring: str
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)

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
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)

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

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

source_lines: tuple[int, int] | None
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

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

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

is_inherited: bool
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

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]):
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)

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

own_members: list[Doc]
242    @cached_property
243    @abstractmethod
244    def own_members(self) -> list[Doc]:
245        """A list of all own (i.e. non-inherited) `members`."""

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

members: dict[str, Doc]
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

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]]
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        }

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

flattened_own_members: list[Doc]
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

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

@cache
def get(self, identifier: str) -> Doc | None:
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)

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

class Module(pdoc.doc.Namespace[module]):
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)]

Representation of a module's documentation.

Module(module: module)
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__, ""))

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:
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))

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

is_package: bool
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

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]
454    @cached_property
455    def own_members(self) -> list[Doc]:
456        return list(self.members.values())

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

submodules: list[Module]
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

A list of all (direct) submodules.

variables: list[Variable]
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)]

A list of all documented module level variables.

classes: list[Class]
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)]

A list of all documented module level classes.

functions: list[Function]
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)]

A list of all documented module level functions.

class Class(pdoc.doc.Namespace[type]):
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        ]

Representation of a class's documentation.

kind = 'class'

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

docstring: str
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

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]
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

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

bases: list[tuple[str, str, str]]
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

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]
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

A list of all decorators the class is decorated with.

class_variables: list[Variable]
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        ]

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]
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        ]

A list of all instance variables in the class.

classmethods: list[Function]
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        ]

A list of all documented @classmethods.

staticmethods: list[Function]
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        ]

A list of all documented @staticmethods.

methods: list[Function]
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        ]

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

class Function(pdoc.doc.Doc[function]):
 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        )

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])
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

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

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
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)

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

is_staticmethod: bool
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)

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

decorators: list[str]
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

A list of all decorators the function is decorated with.

funcdef: str
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"

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

signature: inspect.Signature
 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

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
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        )

Like signature, but without the first argument.

This is useful to display constructors.

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

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
1083    @cached_property
1084    def is_classvar(self) -> bool:
1085        """`True` if the variable is a class variable, `False` otherwise."""
1086        if get_origin(self.annotation) is ClassVar:
1087            return True
1088        else:
1089            return False

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

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

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

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

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

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

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

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

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

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

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