Edit on GitHub

pdoc.doc

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

There are four main types of documentation objects:

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

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

A base class for all documentation objects.

Doc(modulename: str, qualname: str, obj: ~T, taken_from: tuple[str, str])
106    def __init__(
107        self, modulename: str, qualname: str, obj: T, taken_from: tuple[str, str]
108    ):
109        """
110        Initializes a documentation object, where
111        `modulename` is the name this module is defined in,
112        `qualname` contains a dotted path leading to the object from the module top-level, and
113        `obj` is the object to document.
114        """
115        self.modulename = modulename
116        self.qualname = qualname
117        self.obj = obj
118        self.taken_from = taken_from

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

modulename: str

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

qualname: str

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

class Foo:
    def bar(self):
        pass

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

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

obj: ~T

The underlying Python object.

taken_from: tuple[str, str]

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

fullname: str

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

name: str

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

docstring: str

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

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

source: str

The source code of the Python object as a str.

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

source_file: pathlib.Path | None

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

source_lines: tuple[int, int] | None

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

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

is_inherited: bool

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

type: str

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

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

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

own_members: list[pdoc.doc.Doc]

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

members: dict[str, pdoc.doc.Doc]

A mapping from all members to their documentation objects.

This mapping includes private members; they are only filtered out as part of the template logic.

inherited_members: dict[tuple[str, str], list[pdoc.doc.Doc]]

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

flattened_own_members: list[pdoc.doc.Doc]

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

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

Representation of a module's documentation.

Module(module: module)
368    def __init__(
369        self,
370        module: types.ModuleType,
371    ):
372        """
373        Creates a documentation object given the actual
374        Python module object.
375        """
376        super().__init__(module.__name__, "", module, (module.__name__, ""))

Creates a documentation object given the actual Python module object.

@classmethod
@cache
def from_name(cls, name: str) -> pdoc.doc.Module:
378    @classmethod
379    @cache
380    def from_name(cls, name: str) -> Module:
381        """Create a `Module` object by supplying the module's (full) name."""
382        return cls(extract.load_module(name))

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

is_package: bool

True if the module is a package, False otherwise.

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

own_members: list[pdoc.doc.Doc]
submodules: list[pdoc.doc.Module]

A list of all (direct) submodules.

variables: list[pdoc.doc.Variable]

A list of all documented module level variables.

classes: list[pdoc.doc.Class]

A list of all documented module level classes.

functions: list[pdoc.doc.Function]

A list of all documented module level functions.

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

Representation of a class's documentation.

docstring: str
own_members: list[pdoc.doc.Doc]
bases: list[tuple[str, str, str]]

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

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

decorators: list[str]

A list of all decorators the class is decorated with.

class_variables: list[pdoc.doc.Variable]

A list of all documented class variables in the class.

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

instance_variables: list[pdoc.doc.Variable]

A list of all instance variables in the class.

classmethods: list[pdoc.doc.Function]

A list of all documented @classmethods.

staticmethods: list[pdoc.doc.Function]

A list of all documented @staticmethods.

methods: list[pdoc.doc.Function]

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

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

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])
801    def __init__(
802        self,
803        modulename: str,
804        qualname: str,
805        func: WrappedFunction,
806        taken_from: tuple[str, str],
807    ):
808        """Initialize a function's documentation object."""
809        unwrapped: types.FunctionType
810        if isinstance(func, (classmethod, staticmethod)):
811            unwrapped = func.__func__  # type: ignore
812        else:
813            unwrapped = func
814        super().__init__(modulename, qualname, unwrapped, taken_from)
815        self.wrapped = func

Initialize a function's documentation object.

wrapped: function | staticmethod | classmethod

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

obj: function

The unwrapped "real" function.

docstring: str
is_classmethod: bool

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

is_staticmethod: bool

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

decorators: list[str]

A list of all decorators the function is decorated with.

funcdef: str

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

signature: inspect.Signature

The function's signature.

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

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

signature_without_self: inspect.Signature

Like signature, but without the first argument.

This is useful to display constructors.

class Variable(pdoc.doc.Doc[NoneType]):
 930class Variable(Doc[None]):
 931    """
 932    Representation of a variable's documentation. This includes module, class and instance variables.
 933    """
 934
 935    default_value: Any | empty  # technically Any includes empty, but this conveys intent.
 936    """
 937    The variable's default value.
 938    
 939    In some cases, no default value is known. This may either be because a variable is only defined in the constructor,
 940    or it is only declared with a type annotation without assignment (`foo: int`).
 941    To distinguish this case from a default value of `None`, `pdoc.doc_types.empty` is used as a placeholder.
 942    """
 943
 944    annotation: type | empty
 945    """
 946    The variable's type annotation.
 947    
 948    If there is no type annotation, `pdoc.doc_types.empty` is used as a placeholder.
 949    """
 950
 951    def __init__(
 952        self,
 953        modulename: str,
 954        qualname: str,
 955        *,
 956        taken_from: tuple[str, str],
 957        docstring: str,
 958        annotation: type | empty = empty,
 959        default_value: Any | empty = empty,
 960    ):
 961        """
 962        Construct a variable doc object.
 963
 964        While classes and functions can introspect themselves to see their docstring,
 965        variables can't do that as we don't have a "variable object" we could query.
 966        As such, docstring, declaration location, type annotation, and the default value
 967        must be passed manually in the constructor.
 968        """
 969        super().__init__(modulename, qualname, None, taken_from)
 970        # noinspection PyPropertyAccess
 971        self.docstring = inspect.cleandoc(docstring)
 972        self.annotation = annotation
 973        self.default_value = default_value
 974
 975    @cache
 976    @_include_fullname_in_traceback
 977    def __repr__(self):
 978        return f'<var {self.qualname.rsplit(".")[-1]}{self.annotation_str}{self.default_value_str}{_docstr(self)}>'
 979
 980    @cached_property
 981    def is_classvar(self) -> bool:
 982        """`True` if the variable is a class variable, `False` otherwise."""
 983        if get_origin(self.annotation) is ClassVar:
 984            return True
 985        else:
 986            return False
 987
 988    @cached_property
 989    def default_value_str(self) -> str:
 990        """The variable's default value as a pretty-printed str."""
 991        if self.default_value is empty:
 992            return ""
 993        else:
 994            try:
 995                return re.sub(
 996                    r" at 0x[0-9a-fA-F]+(?=>)",
 997                    "",
 998                    f" = {repr(self.default_value)}",
 999                )
1000            except Exception:
1001                return " = <unable to get value representation>"
1002
1003    @cached_property
1004    def annotation_str(self) -> str:
1005        """The variable's type annotation as a pretty-printed str."""
1006        if self.annotation is not empty:
1007            return f": {formatannotation(self.annotation)}"
1008        else:
1009            return ""

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

Variable( modulename: str, qualname: str, *, taken_from: tuple[str, str], docstring: str, annotation: type | inspect._empty, default_value: Union[Any, inspect._empty])
951    def __init__(
952        self,
953        modulename: str,
954        qualname: str,
955        *,
956        taken_from: tuple[str, str],
957        docstring: str,
958        annotation: type | empty = empty,
959        default_value: Any | empty = empty,
960    ):
961        """
962        Construct a variable doc object.
963
964        While classes and functions can introspect themselves to see their docstring,
965        variables can't do that as we don't have a "variable object" we could query.
966        As such, docstring, declaration location, type annotation, and the default value
967        must be passed manually in the constructor.
968        """
969        super().__init__(modulename, qualname, None, taken_from)
970        # noinspection PyPropertyAccess
971        self.docstring = inspect.cleandoc(docstring)
972        self.annotation = annotation
973        self.default_value = default_value

Construct a variable doc object.

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

default_value: Union[Any, inspect._empty]

The variable's default value.

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

annotation: type | inspect._empty

The variable's type annotation.

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

is_classvar: bool

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

default_value_str: str

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

annotation_str: str

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