Package pdoc
Python package pdoc
provides types, functions, and a command-line
interface for accessing public documentation of Python modules, and
for presenting it in a user-friendly, industry-standard open format.
It is best suited for small- to medium-sized projects with tidy,
hierarchical APIs.
pdoc
extracts documentation of:
- modules (including submodules),
- functions (including methods, properties, coroutines …),
- classes, and
- variables (including globals, class variables, and instance variables).
Documentation is extracted from live objects' docstrings
using Python's __doc__
attribute1. Documentation for
variables is found by examining objects' abstract syntax trees.
What objects are documented?
pdoc
only extracts public API documentation.2
Code objects (modules, variables, functions, classes, methods) are considered
public in the modules where they are defined (vs. imported from somewhere else)
as long as their identifiers don't begin with an underscore ( _ ).3
If a module defines __all__
, then only the identifiers contained
in this list are considered public, regardless of where they were defined.
This can be fine-tuned through __pdoc__
dict.
Where does pdoc
get documentation from?
In Python, objects like modules, functions, classes, and methods
have a special attribute __doc__
which contains that object's
documentation string (docstring).
For example, the following code defines a function with a docstring
and shows how to access its contents:
>>> def test():
... """This is a docstring."""
... pass
...
>>> test.__doc__
'This is a docstring.'
It's pretty much the same with classes and modules. See PEP-257 for Python docstring conventions.
These docstrings are set as descriptions for each module, class,
function, and method listed in the documentation produced by pdoc
.
pdoc
extends the standard use of docstrings in Python in two
important ways: by allowing methods to inherit docstrings, and
by introducing syntax for docstrings for variables.
Docstrings inheritance
pdoc
considers methods' docstrings inherited from superclass methods',
following the normal class inheritance patterns.
Consider the following code example:
>>> class A:
... def test(self):
... """Docstring for A."""
... pass
...
>>> class B(A):
... def test(self):
... pass
...
>>> A.test.__doc__
'Docstring for A.'
>>> B.test.__doc__
None
In Python, the docstring for B.test
doesn't exist, even though a
docstring was defined for A.test
.
Contrary, when pdoc
generates documentation for code such as above,
it will automatically attach the docstring for A.test
to
B.test
if the latter doesn't define its own.
In the default HTML template, such inherited docstrings are greyed out.
Docstrings for variables
Python by itself doesn't allow docstrings attached to variables.
However, pdoc
supports documenting module (or global)
variables, class variables, and object instance variables via
two different mechanisms: PEP-224 and #:
doc-comments.
For example:
module_variable = 1
"""PEP 224 docstring for module_variable."""
class C:
#: Documentation comment for class_variable
#: spanning over three lines.
class_variable = 2 #: Assignment line is included.
def __init__(self):
#: Instance variable's doc-comment
self.variable = 3
"""But note, PEP 224 docstrings take precedence."""
While the resulting variables have no __doc__
attribute,
pdoc
compensates by reading the source code (when available)
and parsing the syntax tree.
By convention, variables defined in a class' __init__
method
and attached to self
are considered and documented as
instance variables.
Class and instance variables can also inherit docstrings.
Overriding docstrings with __pdoc__
Docstrings for objects can be disabled, overridden, or whitelisted with a special
module-level dictionary __pdoc__
. The keys
should be string identifiers within the scope of the module or,
alternatively, fully-qualified reference names. E.g. for instance
variable self.variable
of class C
, its module-level identifier is
'C.variable'
, and some_package.module.C.variable
its refname.
If __pdoc__[key] = False
, then key
(and its members) will be
excluded from the documentation of the module.
Conversely, if __pdoc__[key] = True
, then key
(and its public members) will be
included in the documentation of the module. This can be used to
include documentation of private objects,
including special functions such as __call__
, which are ignored by default.
Alternatively, the values of __pdoc__
can be the overriding docstrings.
This feature is useful when there's no feasible way of
attaching a docstring to something. A good example is a
namedtuple:
__pdoc__ = {}
Table = namedtuple('Table', ['types', 'names', 'rows'])
__pdoc__['Table.types'] = 'Types for each column in the table.'
__pdoc__['Table.names'] = 'The names of each column in the table.'
__pdoc__['Table.rows'] = 'Lists corresponding to each row in the table.'
pdoc
will then show Table
as a class with documentation for the
types
, names
and rows
members.
Note
The assignments to __pdoc__
need to be placed where they'll be
executed when the module is imported. For example, at the top level
of a module or in the definition of a class.
Supported Docstring Formats
Currently, pure Markdown (with extensions), numpydoc, and Google-style docstrings formats are supported, along with some reST directives.
Additionally, if latex_math
template config option is enabled,
LaTeX math syntax is supported when placed between
recognized delimiters: \(...\)
for inline equations and
\[...\]
or $$...$$
for block equations. Note, you need to escape
your backslashes in Python docstrings (\\(
, \\frac{}{}
, …)
or, alternatively, use raw string literals.
Supported reST directives
The following reST directives should work:
- specific and generic admonitions (attention, caution, danger, error, hint, important, note, tip, warning, admonition),
.. image::
or.. figure::
(without options),.. include::
, with support for the options::start-line:
,:end-line:
,:start-after:
and:end-before:
... math::
.. versionadded::
.. versionchanged::
.. deprecated::
.. todo::
Linking To Other Identifiers
In your documentation, you may refer to other identifiers in
your modules. When exporting to HTML, linking is automatically
done whenever you surround an identifier with backticks ( ` ).
Unless within the current module,
the identifier name must be fully qualified, for example
`pdoc.Doc.docstring`
is correct (and will link to
Doc.docstring
) while `Doc.docstring`
only works within pdoc
module.
Command-line interface
pdoc
includes a feature-rich "binary" program for producing
HTML and plain text documentation of your modules.
For example, to produce HTML documentation of your whole package
in subdirectory 'build' of the current directory, using the default
HTML template, run:
$ pdoc --html --output-dir build my_package
If you want to omit the source code preview, run:
$ pdoc --html --config show_source_code=False my_package
Find additional template configuration tunables in custom templates section below.
To run a local HTTP server while developing your package or writing docstrings for it, run:
$ pdoc --http : my_package
To re-build documentation as part of your continuous integration (CI)
best practice, i.e. ensuring all reference links are correct and
up-to-date, make warnings error loudly by settings the environment
variable PYTHONWARNINGS
before running pdoc:
$ export PYTHONWARNINGS='error::UserWarning'
For brief usage instructions, type:
$ pdoc --help
Even more usage examples can be found in the FAQ.
Programmatic Usage
The main entry point is Module
which wraps a module object
and recursively imports and wraps any submodules and their members.
After all related modules are wrapped (related modules are those that
share the same Context
), you need to call
link_inheritance()
with the used Context
instance to
establish class inheritance links.
Afterwards, you can use Module.html()
and Module.text()
methods to output documentation in the desired format.
For example:
import pdoc
modules = ['a', 'b'] # Public submodules are auto-imported
context = pdoc.Context()
modules = [pdoc.Module(mod, context=context)
for mod in modules]
pdoc.link_inheritance(context)
def recursive_htmls(mod):
yield mod.name, mod.html()
for submod in mod.submodules():
yield from recursive_htmls(submod)
for mod in modules:
for module_name, html in recursive_htmls(mod):
... # Process
When documenting a single module, you might find
functions html()
and text()
handy.
For importing arbitrary modules/files, use import_module()
.
Alternatively, use the runnable script included with this package.
Custom Templates
To override the built-in HTML/CSS and plain text templates, copy
the relevant templates from pdoc/templates
directory into a directory
of your choosing and edit them. When you run pdoc command
afterwards, pass the directory path as a parameter to the
--template-dir
switch.
Tip
If you find you only need to apply minor alterations to the HTML template, see if you can do so by overriding just some of the following, placeholder sub-templates:
- config.mako: Basic template configuration, affects the way templates are rendered.
- head.mako: Included just before
</head>
. Best for adding resources and styles. - logo.mako: Included at the very top of the navigation sidebar. Empty by default.
- credits.mako: Included in the footer, right before pdoc version string.
See default template files for reference.
Tip
You can also alter individual config.mako preferences using the
--config
command-line switch.
If working with pdoc
programmatically, prepend the directory with
modified templates into the directories
list of the
tpl_lookup
object.
Compatibility
pdoc
requires Python 3.7+.
The last version to support Python 2.x is pdoc3 0.3.x.
Contributing
pdoc
is on GitHub. Bug reports and pull requests are welcome.
License
pdoc
is licensed under the terms of GNU AGPL-3.0 or later,
meaning you can use it for any reasonable purpose and remain in
complete ownership of all the documentation you produce,
but you are also encouraged to make sure any upgrades to pdoc
itself find their way back to the community.
-
Documented modules are executed in order to provide
__doc__
attributes. Any non-fenced global code in imported modules will affect the current runtime environment. ↩ -
Here, public API refers to the API that is made available to your project end-users, not the public API e.g. of a private class that can be reasonably extended elsewhere by your project developers. ↩
-
Prefixing private, implementation-specific objects with an underscore is a common convention. ↩
Sub-modules
pdoc.cli
-
pdoc's CLI interface and helper functions.
pdoc.html_helpers
-
Helper functions for HTML output.
pdoc.test
-
Unit tests for pdoc package.
Global variables
var tpl_lookup
-
A
mako.lookup.TemplateLookup
object that knows how to load templates from the file system. You may add additional paths by modifying the object'sdirectories
attribute.
Functions
def html(module_name, docfilter=None, reload=False, skip_errors=False, **kwargs) ‑> str
-
Expand source code Browse git
def html(module_name, docfilter=None, reload=False, skip_errors=False, **kwargs) -> str: """ Returns the documentation for the module `module_name` in HTML format. The module must be a module or an importable string. `docfilter` is an optional predicate that controls which documentation objects are shown in the output. It is a function that takes a single argument (a documentation object) and returns `True` or `False`. If `False`, that object will not be documented. """ mod = Module(import_module(module_name, reload=reload, skip_errors=False), docfilter=docfilter, skip_errors=skip_errors) link_inheritance() return mod.html(**kwargs)
Returns the documentation for the module
module_name
in HTML format. The module must be a module or an importable string.docfilter
is an optional predicate that controls which documentation objects are shown in the output. It is a function that takes a single argument (a documentation object) and returnsTrue
orFalse
. IfFalse
, that object will not be documented. def import_module(module: str | module, *, reload: bool = False, skip_errors: bool = False) ‑> module
-
Expand source code Browse git
def import_module( module: Union[str, ModuleType], *, reload: bool = False, skip_errors: bool = False, ) -> ModuleType: """ Return module object matching `module` specification (either a python module path or a filesystem path to file/directory). """ @contextmanager def _module_path(module): from os.path import abspath, dirname, isfile, isdir, split path = '_pdoc_dummy_nonexistent' module_name = inspect.getmodulename(module) if isdir(module): path, module = split(abspath(module)) elif isfile(module) and module_name: path, module = dirname(abspath(module)), module_name try: sys.path.insert(0, path) yield module finally: sys.path.remove(path) if isinstance(module, Module): module = module.obj if isinstance(module, str): with _module_path(module) as module_path: try: module = importlib.import_module(module_path) except Exception as e: msg = f'Error importing {module!r}: {e.__class__.__name__}: {e}' if not skip_errors: raise ImportError(msg) warn(msg, category=Module.ImportWarning, stacklevel=2) module = ModuleType(module_path) assert inspect.ismodule(module) # If this is pdoc itself, return without reloading. Otherwise later # `isinstance(..., pdoc.Doc)` calls won't work correctly. if reload and not module.__name__.startswith(__name__): module = importlib.reload(module) # We recursively reload all submodules, in case __all_ is used - cf. issue #264 for mod_key, mod in list(sys.modules.items()): if mod_key.startswith(module.__name__): importlib.reload(mod) return module
Return module object matching
module
specification (either a python module path or a filesystem path to file/directory). def link_inheritance(context: Context | None = None)
-
Expand source code Browse git
def link_inheritance(context: Optional[Context] = None): """ Link inheritance relationsships between `pdoc.Class` objects (and between their members) of all `pdoc.Module` objects that share the provided `context` (`pdoc.Context`). You need to call this if you expect `pdoc.Doc.inherits` and inherited `pdoc.Doc.docstring` to be set correctly. """ if context is None: context = _global_context graph = {cls: set(cls.mro(only_documented=True)) for cls in _filter_type(Class, context)} for cls in _toposort(graph): cls._fill_inheritance() for module in _filter_type(Module, context): module._link_inheritance()
Link inheritance relationsships between
Class
objects (and between their members) of allModule
objects that share the providedcontext
(Context
).You need to call this if you expect
Doc.inherits
and inheritedDoc.docstring
to be set correctly. def maybe_lru_cache(func)
-
Expand source code Browse git
def maybe_lru_cache(func): cached_func = lru_cache()(func) @wraps(func) def wrapper(*args): try: return cached_func(*args) except TypeError: return func(*args) return wrapper
def reset()
-
Expand source code Browse git
def reset(): """Resets the global `pdoc.Context` to the initial (empty) state.""" global _global_context _global_context.clear() # Clear LRU caches for func in (_get_type_hints, _is_blacklisted, _is_whitelisted): func.cache_clear() for cls in (Doc, Module, Class, Function, Variable, External): for _, method in inspect.getmembers(cls): if isinstance(method, property): method = method.fget if hasattr(method, 'cache_clear'): method.cache_clear()
Resets the global
Context
to the initial (empty) state. def text(module_name, docfilter=None, reload=False, skip_errors=False, **kwargs) ‑> str
-
Expand source code Browse git
def text(module_name, docfilter=None, reload=False, skip_errors=False, **kwargs) -> str: """ Returns the documentation for the module `module_name` in plain text format suitable for viewing on a terminal. The module must be a module or an importable string. `docfilter` is an optional predicate that controls which documentation objects are shown in the output. It is a function that takes a single argument (a documentation object) and returns `True` or `False`. If `False`, that object will not be documented. """ mod = Module(import_module(module_name, reload=reload, skip_errors=False), docfilter=docfilter, skip_errors=skip_errors) link_inheritance() return mod.text(**kwargs)
Returns the documentation for the module
module_name
in plain text format suitable for viewing on a terminal. The module must be a module or an importable string.docfilter
is an optional predicate that controls which documentation objects are shown in the output. It is a function that takes a single argument (a documentation object) and returnsTrue
orFalse
. IfFalse
, that object will not be documented.
Classes
class Class (name: str,
module: Module,
obj,
*,
docstring: str | None = None)-
Expand source code Browse git
class Class(Doc): """ Representation of a class' documentation. """ def __init__(self, name: str, module: Module, obj, *, docstring: Optional[str] = None): assert inspect.isclass(obj) if docstring is None: init_doc = inspect.getdoc(obj.__init__) or '' if init_doc == object.__init__.__doc__: init_doc = '' docstring = f'{inspect.getdoc(obj) or ""}\n\n{init_doc}'.strip() super().__init__(name, module, obj, docstring=docstring) self.doc: Dict[str, Union[Function, Variable]] = {} """A mapping from identifier name to a `pdoc.Doc` objects.""" # Annotations for filtering. # Use only own, non-inherited annotations (the rest will be inherited) annotations = getattr(self.obj, '__annotations__', {}) public_objs = [] for _name, obj in _getmembers_all(self.obj): # Filter only *own* members. The rest are inherited # in Class._fill_inheritance() if ((_name in self.obj.__dict__ or _name in annotations) and (_is_public(_name) or _is_whitelisted(_name, self))): if _is_blacklisted(_name, self): self.module._context.blacklisted.add(f'{self.refname}.{_name}') continue obj = inspect.unwrap(obj) public_objs.append((_name, obj)) def definition_order_index( name, _annot_index=list(annotations).index, _dict_index=list(self.obj.__dict__).index): try: return _dict_index(name) except ValueError: pass try: return _annot_index(name) - len(annotations) # sort annotated first except ValueError: return 9e9 public_objs.sort(key=lambda i: definition_order_index(i[0])) var_docstrings, instance_var_docstrings = _pep224_docstrings(self) # Convert the public Python objects to documentation objects. for name, obj in public_objs: if _is_function(obj): self.doc[name] = Function( name, self.module, obj, cls=self) else: self.doc[name] = Variable( name, self.module, docstring=( var_docstrings.get(name) or (inspect.isclass(obj) or _is_descriptor(obj)) and inspect.getdoc(obj)), cls=self, kind="prop" if isinstance(obj, property) else "var", obj=_is_descriptor(obj) and obj or None, instance_var=(_is_descriptor(obj) or name in getattr(self.obj, '__slots__', ()) or (is_dataclass(self.obj) and name in annotations))) for name, docstring in instance_var_docstrings.items(): self.doc[name] = Variable( name, self.module, docstring, cls=self, obj=getattr(self.obj, name, None), instance_var=True) @staticmethod def _method_type(cls: type, name: str): """ Returns `None` if the method `name` of class `cls` is a regular method. Otherwise, it returns `classmethod` or `staticmethod`, as appropriate. """ func = getattr(cls, name, None) if inspect.ismethod(func): # If the function is already bound, it's a classmethod. # Regular methods are not bound before initialization. return classmethod for c in inspect.getmro(cls): if name in c.__dict__: if isinstance(c.__dict__[name], staticmethod): return staticmethod return None raise RuntimeError(f"{cls}.{name} not found") @property def refname(self) -> str: return f'{self.module.name}.{self.qualname}' def mro(self, only_documented=False) -> List['Class']: """ Returns a list of ancestor (superclass) documentation objects in method resolution order. The list will contain objects of type `pdoc.Class` if the types are documented, and `pdoc.External` otherwise. """ classes = [cast(Class, self.module.find_class(c)) for c in inspect.getmro(self.obj) if c not in (self.obj, object)] if self in classes: # This can contain self in case of a class inheriting from # a class with (previously) the same name. E.g. # # class Loc(namedtuple('Loc', 'lat lon')): ... # # We remove it from ancestors so that toposort doesn't break. classes.remove(self) if only_documented: classes = _filter_type(Class, classes) return classes def subclasses(self) -> List['Class']: """ Returns a list of subclasses of this class that are visible to the Python interpreter (obtained from `type.__subclasses__()`). The objects in the list are of type `pdoc.Class` if available, and `pdoc.External` otherwise. """ return sorted(cast(Class, self.module.find_class(c)) for c in type.__subclasses__(self.obj)) def params(self, *, annotate=False, link=None) -> List[str]: """ Return a list of formatted parameters accepted by the class constructor (method `__init__`). See `pdoc.Function.params`. """ name = self.name + '.__init__' qualname = self.qualname + '.__init__' refname = self.refname + '.__init__' exclusions = self.module.__pdoc__ if name in exclusions or qualname in exclusions or refname in exclusions: return [] return Function._params(self, annotate=annotate, link=link, module=self.module) def _filter_doc_objs(self, type: Type[T], include_inherited=True, filter_func: Callable[[T], bool] = lambda x: True, sort=True) -> List[T]: result = [obj for obj in _filter_type(type, self.doc) if (include_inherited or not obj.inherits) and filter_func(obj)] return sorted(result) if sort else result def class_variables(self, include_inherited=True, sort=True) -> List['Variable']: """ Returns an optionally-sorted list of `pdoc.Variable` objects that represent this class' class variables. """ return self._filter_doc_objs( Variable, include_inherited, lambda dobj: not dobj.instance_var, sort) def instance_variables(self, include_inherited=True, sort=True) -> List['Variable']: """ Returns an optionally-sorted list of `pdoc.Variable` objects that represent this class' instance variables. Instance variables are those defined in a class's `__init__` as `self.variable = ...`. """ return self._filter_doc_objs( Variable, include_inherited, lambda dobj: dobj.instance_var, sort) def methods(self, include_inherited=True, sort=True) -> List['Function']: """ Returns an optionally-sorted list of `pdoc.Function` objects that represent this class' methods. """ return self._filter_doc_objs( Function, include_inherited, lambda dobj: dobj.is_method, sort) def functions(self, include_inherited=True, sort=True) -> List['Function']: """ Returns an optionally-sorted list of `pdoc.Function` objects that represent this class' static functions. """ return self._filter_doc_objs( Function, include_inherited, lambda dobj: not dobj.is_method, sort) def inherited_members(self) -> List[Tuple['Class', List[Doc]]]: """ Returns all inherited members as a list of tuples (ancestor class, list of ancestor class' members sorted by name), sorted by MRO. """ return sorted(((cast(Class, k), sorted(g)) for k, g in groupby((i.inherits for i in self.doc.values() if i.inherits), key=lambda i: i.cls)), # type: ignore key=lambda x, _mro_index=self.mro().index: _mro_index(x[0])) # type: ignore def _fill_inheritance(self): """ Traverses this class's ancestor list and attempts to fill in missing documentation objects from its ancestors. Afterwards, call to `pdoc.Class._link_inheritance()` to also set `pdoc.Doc.inherits` pointers. """ super_members = self._super_members = {} for cls in self.mro(only_documented=True): for name, dobj in cls.doc.items(): if name not in super_members and dobj.docstring: super_members[name] = dobj if name not in self.doc: dobj = copy(dobj) dobj.cls = self self.doc[name] = dobj self.module._context[dobj.refname] = dobj def _link_inheritance(self): """ Set `pdoc.Doc.inherits` pointers to inherited ancestors' members, as appropriate. This must be called after `pdoc.Class._fill_inheritance()`. The reason this is split in two parts is that in-between the `__pdoc__` overrides are applied. """ if not hasattr(self, '_super_members'): return for name, parent_dobj in self._super_members.items(): try: dobj = self.doc[name] except KeyError: # There is a key in some __pdoc__ dict blocking this member continue if (dobj.obj is parent_dobj.obj or (dobj.docstring or parent_dobj.docstring) == parent_dobj.docstring): dobj.inherits = parent_dobj dobj.docstring = parent_dobj.docstring del self._super_members
Representation of a class' documentation.
Initializes a documentation object, where
name
is the public identifier name,module
is aModule
object where raw Python objectobj
is defined, anddocstring
is its documentation string. Ifdocstring
is left empty, it will be read withinspect.getdoc()
.Ancestors
Instance variables
var doc
-
A mapping from identifier name to a
Doc
objects.
Methods
def class_variables(self, include_inherited=True, sort=True) ‑> List[Variable]
-
Expand source code Browse git
def class_variables(self, include_inherited=True, sort=True) -> List['Variable']: """ Returns an optionally-sorted list of `pdoc.Variable` objects that represent this class' class variables. """ return self._filter_doc_objs( Variable, include_inherited, lambda dobj: not dobj.instance_var, sort)
Returns an optionally-sorted list of
Variable
objects that represent this class' class variables. def functions(self, include_inherited=True, sort=True) ‑> List[Function]
-
Expand source code Browse git
def functions(self, include_inherited=True, sort=True) -> List['Function']: """ Returns an optionally-sorted list of `pdoc.Function` objects that represent this class' static functions. """ return self._filter_doc_objs( Function, include_inherited, lambda dobj: not dobj.is_method, sort)
Returns an optionally-sorted list of
Function
objects that represent this class' static functions. def inherited_members(self) ‑> List[Tuple[Class, List[Doc]]]
-
Expand source code Browse git
def inherited_members(self) -> List[Tuple['Class', List[Doc]]]: """ Returns all inherited members as a list of tuples (ancestor class, list of ancestor class' members sorted by name), sorted by MRO. """ return sorted(((cast(Class, k), sorted(g)) for k, g in groupby((i.inherits for i in self.doc.values() if i.inherits), key=lambda i: i.cls)), # type: ignore key=lambda x, _mro_index=self.mro().index: _mro_index(x[0])) # type: ignore
Returns all inherited members as a list of tuples (ancestor class, list of ancestor class' members sorted by name), sorted by MRO.
def instance_variables(self, include_inherited=True, sort=True) ‑> List[Variable]
-
Expand source code Browse git
def instance_variables(self, include_inherited=True, sort=True) -> List['Variable']: """ Returns an optionally-sorted list of `pdoc.Variable` objects that represent this class' instance variables. Instance variables are those defined in a class's `__init__` as `self.variable = ...`. """ return self._filter_doc_objs( Variable, include_inherited, lambda dobj: dobj.instance_var, sort)
Returns an optionally-sorted list of
Variable
objects that represent this class' instance variables. Instance variables are those defined in a class's__init__
asself.variable = ...
. def methods(self, include_inherited=True, sort=True) ‑> List[Function]
-
Expand source code Browse git
def methods(self, include_inherited=True, sort=True) -> List['Function']: """ Returns an optionally-sorted list of `pdoc.Function` objects that represent this class' methods. """ return self._filter_doc_objs( Function, include_inherited, lambda dobj: dobj.is_method, sort)
Returns an optionally-sorted list of
Function
objects that represent this class' methods. def mro(self, only_documented=False) ‑> List[Class]
-
Expand source code Browse git
def mro(self, only_documented=False) -> List['Class']: """ Returns a list of ancestor (superclass) documentation objects in method resolution order. The list will contain objects of type `pdoc.Class` if the types are documented, and `pdoc.External` otherwise. """ classes = [cast(Class, self.module.find_class(c)) for c in inspect.getmro(self.obj) if c not in (self.obj, object)] if self in classes: # This can contain self in case of a class inheriting from # a class with (previously) the same name. E.g. # # class Loc(namedtuple('Loc', 'lat lon')): ... # # We remove it from ancestors so that toposort doesn't break. classes.remove(self) if only_documented: classes = _filter_type(Class, classes) return classes
def params(self, *, annotate=False, link=None) ‑> List[str]
-
Expand source code Browse git
def params(self, *, annotate=False, link=None) -> List[str]: """ Return a list of formatted parameters accepted by the class constructor (method `__init__`). See `pdoc.Function.params`. """ name = self.name + '.__init__' qualname = self.qualname + '.__init__' refname = self.refname + '.__init__' exclusions = self.module.__pdoc__ if name in exclusions or qualname in exclusions or refname in exclusions: return [] return Function._params(self, annotate=annotate, link=link, module=self.module)
Return a list of formatted parameters accepted by the class constructor (method
__init__
). SeeFunction.params()
. def subclasses(self) ‑> List[Class]
-
Expand source code Browse git
def subclasses(self) -> List['Class']: """ Returns a list of subclasses of this class that are visible to the Python interpreter (obtained from `type.__subclasses__()`). The objects in the list are of type `pdoc.Class` if available, and `pdoc.External` otherwise. """ return sorted(cast(Class, self.module.find_class(c)) for c in type.__subclasses__(self.obj))
Inherited members
class Context
-
Expand source code Browse git
class Context(dict): """ The context object that maps all documented identifiers (`pdoc.Doc.refname`) to their respective `pdoc.Doc` objects. You can pass an instance of `pdoc.Context` to `pdoc.Module` constructor. All `pdoc.Module` objects that share the same `pdoc.Context` will see (and be able to link in HTML to) each other's identifiers. If you don't pass your own `Context` instance to `Module` constructor, a global context object will be used. """ __pdoc__['Context.__init__'] = False def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # A surrogate so that the check in Module._link_inheritance() # "__pdoc__-overriden key {!r} does not exist" can see the object # (and not warn). self.blacklisted = getattr(args[0], 'blacklisted', set()) if args else set()
The context object that maps all documented identifiers (
Doc.refname
) to their respectiveDoc
objects.You can pass an instance of
Context
toModule
constructor. AllModule
objects that share the sameContext
will see (and be able to link in HTML to) each other's identifiers.If you don't pass your own
Context
instance toModule
constructor, a global context object will be used.Ancestors
- builtins.dict
class Doc (name: str, module, obj, docstring: str = '')
-
Expand source code Browse git
class Doc: """ A base class for all documentation objects. A documentation object corresponds to *something* in a Python module that has a docstring associated with it. Typically, this includes modules, classes, functions, and methods. However, `pdoc` adds support for extracting some docstrings from abstract syntax trees, making (module, class or instance) variables supported too. A special type of documentation object `pdoc.External` is used to represent identifiers that are not part of the public interface of a module. (The name "External" is a bit of a misnomer, since it can also correspond to unexported members of the module, particularly in a class's ancestor list.) """ def __init__(self, name: str, module, obj, docstring: str = ''): """ Initializes a documentation object, where `name` is the public identifier name, `module` is a `pdoc.Module` object where raw Python object `obj` is defined, and `docstring` is its documentation string. If `docstring` is left empty, it will be read with `inspect.getdoc()`. """ self.module = module """ The module documentation object that this object is defined in. """ self.name = name """ The identifier name for this object. """ self.obj = obj """ The raw python object. """ docstring = (docstring or inspect.getdoc(obj) or '').strip() if '.. include::' in docstring: from pdoc.html_helpers import _ToMarkdown docstring = _ToMarkdown.admonitions(docstring, self.module, ('include',)) self.docstring = docstring """ The cleaned docstring for this object with any `.. include::` directives resolved (i.e. content included). """ self.inherits: Optional[Union[Class, Function, Variable]] = None """ The Doc object (Class, Function, or Variable) this object inherits from, if any. """ def __repr__(self): return f'<{self.__class__.__name__} {self.refname!r}>' @property @lru_cache() def source(self) -> str: """ Cleaned (dedented) source code of the Python object. If not available, an empty string. """ try: lines, _ = inspect.getsourcelines(_unwrap_descriptor(self)) except (ValueError, TypeError, OSError): return '' return inspect.cleandoc(''.join(['\n'] + lines)) @property def refname(self) -> str: """ Reference name of this documentation object, usually its fully qualified path (e.g. <code>pdoc.Doc.refname</code>). Every documentation object provides this property. """ # Ok for Module and External, the rest need it overriden return self.name @property def qualname(self) -> str: """ Module-relative "qualified" name of this documentation object, used for show (e.g. <code>Doc.qualname</code>). """ return getattr(self.obj, '__qualname__', self.name) @lru_cache() def url(self, relative_to: Optional['Module'] = None, *, link_prefix: str = '', top_ancestor: bool = False) -> str: """ Canonical relative URL (including page fragment) for this documentation object. Specify `relative_to` (a `pdoc.Module` object) to obtain a relative URL. For usage of `link_prefix` see `pdoc.html()`. If `top_ancestor` is `True`, the returned URL instead points to the top ancestor in the object's `pdoc.Doc.inherits` chain. """ if top_ancestor: self = self._inherits_top() if relative_to is None or link_prefix: return link_prefix + self._url() if self.module.name == relative_to.name: return f'#{self.refname}' # Otherwise, compute relative path from current module to link target url = os.path.relpath(self._url(), relative_to.url()).replace(path.sep, '/') # We have one set of '..' too many if url.startswith('../'): url = url[3:] return url def _url(self): return f'{self.module._url()}#{self.refname}' def _inherits_top(self): """ Follow the `pdoc.Doc.inherits` chain and return the top object. """ top = self while top.inherits: top = top.inherits return top def __lt__(self, other): return self.refname < other.refname
A base class for all documentation objects.
A documentation object corresponds to something in a Python module that has a docstring associated with it. Typically, this includes modules, classes, functions, and methods. However,
pdoc
adds support for extracting some docstrings from abstract syntax trees, making (module, class or instance) variables supported too.A special type of documentation object
External
is used to represent identifiers that are not part of the public interface of a module. (The name "External" is a bit of a misnomer, since it can also correspond to unexported members of the module, particularly in a class's ancestor list.)Initializes a documentation object, where
name
is the public identifier name,module
is aModule
object where raw Python objectobj
is defined, anddocstring
is its documentation string. Ifdocstring
is left empty, it will be read withinspect.getdoc()
.Subclasses
Instance variables
var docstring
-
The cleaned docstring for this object with any
.. include::
directives resolved (i.e. content included). var inherits
-
The Doc object (Class, Function, or Variable) this object inherits from, if any.
var module
-
The module documentation object that this object is defined in.
var name
-
The identifier name for this object.
var obj
-
The raw python object.
prop qualname : str
-
Expand source code Browse git
@property def qualname(self) -> str: """ Module-relative "qualified" name of this documentation object, used for show (e.g. <code>Doc.qualname</code>). """ return getattr(self.obj, '__qualname__', self.name)
Module-relative "qualified" name of this documentation object, used for show (e.g.
Doc.qualname
). prop refname : str
-
Expand source code Browse git
@property def refname(self) -> str: """ Reference name of this documentation object, usually its fully qualified path (e.g. <code>pdoc.Doc.refname</code>). Every documentation object provides this property. """ # Ok for Module and External, the rest need it overriden return self.name
Reference name of this documentation object, usually its fully qualified path (e.g.
pdoc.Doc.refname
). Every documentation object provides this property. prop source : str
-
Expand source code Browse git
@property @lru_cache() def source(self) -> str: """ Cleaned (dedented) source code of the Python object. If not available, an empty string. """ try: lines, _ = inspect.getsourcelines(_unwrap_descriptor(self)) except (ValueError, TypeError, OSError): return '' return inspect.cleandoc(''.join(['\n'] + lines))
Cleaned (dedented) source code of the Python object. If not available, an empty string.
Methods
def url(self,
relative_to: ForwardRef('Module') | None = None,
*,
link_prefix: str = '',
top_ancestor: bool = False) ‑> str-
Expand source code Browse git
@lru_cache() def url(self, relative_to: Optional['Module'] = None, *, link_prefix: str = '', top_ancestor: bool = False) -> str: """ Canonical relative URL (including page fragment) for this documentation object. Specify `relative_to` (a `pdoc.Module` object) to obtain a relative URL. For usage of `link_prefix` see `pdoc.html()`. If `top_ancestor` is `True`, the returned URL instead points to the top ancestor in the object's `pdoc.Doc.inherits` chain. """ if top_ancestor: self = self._inherits_top() if relative_to is None or link_prefix: return link_prefix + self._url() if self.module.name == relative_to.name: return f'#{self.refname}' # Otherwise, compute relative path from current module to link target url = os.path.relpath(self._url(), relative_to.url()).replace(path.sep, '/') # We have one set of '..' too many if url.startswith('../'): url = url[3:] return url
Canonical relative URL (including page fragment) for this documentation object.
Specify
relative_to
(aModule
object) to obtain a relative URL.For usage of
link_prefix
seehtml()
.If
top_ancestor
isTrue
, the returned URL instead points to the top ancestor in the object'sDoc.inherits
chain.
class External (name: str)
-
Expand source code Browse git
class External(Doc): """ A representation of an external identifier. The textual representation is the same as an internal identifier. External identifiers are also used to represent something that is not documented but appears somewhere in the public interface (like the ancestor list of a class). """ __pdoc__["External.docstring"] = """ An empty string. External identifiers do not have docstrings. """ __pdoc__["External.module"] = """ Always `None`. External identifiers have no associated `pdoc.Module`. """ __pdoc__["External.name"] = """ Always equivalent to `pdoc.External.refname` since external identifiers are always expressed in their fully qualified form. """ def __init__(self, name: str): """ Initializes an external identifier with `name`, where `name` should be a fully qualified name. """ super().__init__(name, None, None) def url(self, *args, **kwargs): """ `External` objects return absolute urls matching `/{name}.ext`. """ return f'/{self.name}.ext'
A representation of an external identifier. The textual representation is the same as an internal identifier.
External identifiers are also used to represent something that is not documented but appears somewhere in the public interface (like the ancestor list of a class).
Initializes an external identifier with
name
, wherename
should be a fully qualified name.Ancestors
Methods
def url(self, *args, **kwargs)
-
Expand source code Browse git
def url(self, *args, **kwargs): """ `External` objects return absolute urls matching `/{name}.ext`. """ return f'/{self.name}.ext'
External
objects return absolute urls matching/{name}.ext
.
Inherited members
class Function (name: str,
module: Module,
obj,
*,
cls: Class | None = None)-
Expand source code Browse git
class Function(Doc): """ Representation of documentation for a function or method. """ def __init__(self, name: str, module: Module, obj, *, cls: Optional[Class] = None): """ Same as `pdoc.Doc`, except `obj` must be a Python function object. The docstring is gathered automatically. `cls` should be set when this is a method or a static function beloing to a class. `cls` should be a `pdoc.Class` object. `method` should be `True` when the function is a method. In all other cases, it should be `False`. """ assert callable(obj), (name, module, obj) super().__init__(name, module, obj) self.cls = cls """ The `pdoc.Class` documentation object if the function is a method. If not, this is None. """ @property def is_method(self) -> bool: """ Whether this function is a normal bound method. In particular, static and class methods have this set to False. """ assert self.cls return not Class._method_type(self.cls.obj, self.name) @property def method(self): warn('`Function.method` is deprecated. Use: `Function.is_method`', DeprecationWarning, stacklevel=2) return self.is_method __pdoc__['Function.method'] = False def funcdef(self) -> str: """ Generates the string of keywords used to define the function, for example `def` or `async def`. """ return 'async def' if self._is_async else 'def' @property def _is_async(self): """ Returns whether is function is asynchronous, either as a coroutine or an async generator. """ try: # Both of these are required because coroutines aren't classified as async # generators and vice versa. obj = inspect.unwrap(self.obj) return (inspect.iscoroutinefunction(obj) or inspect.isasyncgenfunction(obj)) except AttributeError: return False def return_annotation(self, *, link=None) -> str: """Formatted function return type annotation or empty string if none.""" annot = '' for method in ( lambda: _get_type_hints(self.obj)['return'], # Mainly for non-property variables lambda: _get_type_hints(cast(Class, self.cls).obj)[self.name], # global variables lambda: _get_type_hints(not self.cls and self.module.obj)[self.name], # properties lambda: inspect.signature(_unwrap_descriptor(self)).return_annotation, # Use raw annotation strings in unmatched forward declarations lambda: cast(Class, self.cls).obj.__annotations__[self.name], # Extract annotation from the docstring for C builtin function lambda: Function._signature_from_string(self).return_annotation, ): try: annot = method() except Exception: continue else: break else: # Don't warn on variables. The annotation just isn't available. if not isinstance(self, Variable): warn(f"Error handling return annotation for {self!r}", stacklevel=3) if annot is inspect.Parameter.empty or not annot: return '' if isinstance(annot, str): s = annot else: s = _formatannotation(annot) s = re.sub(r'\bForwardRef\((?P<quot>[\"\'])(?P<str>.*?)(?P=quot)\)', r'\g<str>', s) s = s.replace(' ', '\N{NBSP}') # Better line breaks in html signatures if link: from pdoc.html_helpers import _linkify s = re.sub(r'[\w\.]+', partial(_linkify, link=link, module=self.module), s) return s def params(self, *, annotate: bool = False, link: Optional[Callable[[Doc], str]] = None) -> List[str]: """ Returns a list where each element is a nicely formatted parameter of this function. This includes argument lists, keyword arguments and default values, and it doesn't include any optional arguments whose names begin with an underscore. If `annotate` is True, the parameter strings include [PEP 484] type hint annotations. [PEP 484]: https://www.python.org/dev/peps/pep-0484/ """ return self._params(self, annotate=annotate, link=link, module=self.module) @staticmethod def _params(doc_obj, annotate=False, link=None, module=None): try: # We want __init__ to actually be implemented somewhere in the # MRO to still satisfy https://github.com/pdoc3/pdoc/issues/124 if ( inspect.isclass(doc_obj.obj) and doc_obj.obj.__init__ is not object.__init__ ): # Remove the first argument (self) from __init__ signature init_sig = inspect.signature(doc_obj.obj.__init__) init_params = list(init_sig.parameters.values()) signature = init_sig.replace(parameters=init_params[1:]) else: signature = inspect.signature(doc_obj.obj) except ValueError: signature = Function._signature_from_string(doc_obj) if not signature: return ['...'] def safe_default_value(p: inspect.Parameter): value = p.default if value is inspect.Parameter.empty: return p replacement = next((i for i in ('os.environ', 'sys.stdin', 'sys.stdout', 'sys.stderr',) if value is eval(i)), None) if not replacement: if isinstance(value, enum.Enum): replacement = str(value) elif inspect.isclass(value): replacement = f'{value.__module__ or _UNKNOWN_MODULE}.{value.__qualname__}' elif ' at 0x' in repr(value): replacement = re.sub(r' at 0x\w+', '', repr(value)) nonlocal link if link and ('<' in repr(value) or '>' in repr(value)): import html replacement = html.escape(replacement or repr(value)) if replacement: class mock: def __repr__(self): return replacement return p.replace(default=mock()) return p params = [] kw_only = False pos_only = False EMPTY = inspect.Parameter.empty if link: from pdoc.html_helpers import _linkify _linkify = partial(_linkify, link=link, module=module) for p in signature.parameters.values(): # type: inspect.Parameter if not _is_public(p.name) and p.default is not EMPTY: continue if p.kind == p.POSITIONAL_ONLY: pos_only = True elif pos_only: params.append("/") pos_only = False if p.kind == p.VAR_POSITIONAL: kw_only = True if p.kind == p.KEYWORD_ONLY and not kw_only: kw_only = True params.append('*') p = safe_default_value(p) if not annotate: p = p.replace(annotation=EMPTY) formatted = p.name if p.annotation is not EMPTY: annotation = _formatannotation(p.annotation).replace(' ', '\N{NBSP}') # "Eval" forward-declarations (typing string literals) if isinstance(p.annotation, str): annotation = annotation.strip("'") if link: annotation = re.sub(r'[\w\.]+', _linkify, annotation) formatted += f':\N{NBSP}{annotation}' if p.default is not EMPTY: if p.annotation is not EMPTY: formatted += f'\N{NBSP}=\N{NBSP}{repr(p.default)}' else: formatted += f'={repr(p.default)}' if p.kind == p.VAR_POSITIONAL: formatted = f'*{formatted}' elif p.kind == p.VAR_KEYWORD: formatted = f'**{formatted}' params.append(formatted) if pos_only: params.append("/") return params @staticmethod @lru_cache() def _signature_from_string(self): signature = None for expr, cleanup_docstring, filter in ( # Full proper typed signature, such as one from pybind11 (r'^{}\(.*\)(?: -> .*)?$', True, lambda s: s), # Human-readable, usage-like signature from some Python builtins # (e.g. `range` or `slice` or `itertools.repeat` or `numpy.arange`) (r'^{}\(.*\)(?= -|$)', False, lambda s: s.replace('[', '').replace(']', '')), ): strings = sorted(re.findall(expr.format(self.name), self.docstring, re.MULTILINE), key=len, reverse=True) if strings: string = filter(strings[0]) _locals, _globals = {}, {} _globals.update({'capsule': None}) # pybind11 capsule data type _globals.update(typing.__dict__) _globals.update(self.module.obj.__dict__) # Trim binding module basename from type annotations # See: https://github.com/pdoc3/pdoc/pull/148#discussion_r407114141 module_basename = self.module.name.rsplit('.', maxsplit=1)[-1] if module_basename in string and module_basename not in _globals: string = re.sub(fr'(?<!\.)\b{module_basename}\.\b', '', string) try: exec(f'def {string}: pass', _globals, _locals) except Exception: continue signature = inspect.signature(_locals[self.name]) if cleanup_docstring and len(strings) == 1: # Remove signature from docstring variable self.docstring = self.docstring.replace(strings[0], '') break return signature @property def refname(self) -> str: return f'{self.cls.refname if self.cls else self.module.refname}.{self.name}'
Representation of documentation for a function or method.
Same as
Doc
, exceptobj
must be a Python function object. The docstring is gathered automatically.cls
should be set when this is a method or a static function beloing to a class.cls
should be aClass
object.method
should beTrue
when the function is a method. In all other cases, it should beFalse
.Ancestors
Instance variables
var cls
-
The
Class
documentation object if the function is a method. If not, this is None. prop is_method : bool
-
Expand source code Browse git
@property def is_method(self) -> bool: """ Whether this function is a normal bound method. In particular, static and class methods have this set to False. """ assert self.cls return not Class._method_type(self.cls.obj, self.name)
Whether this function is a normal bound method.
In particular, static and class methods have this set to False.
Methods
def funcdef(self) ‑> str
-
Expand source code Browse git
def funcdef(self) -> str: """ Generates the string of keywords used to define the function, for example `def` or `async def`. """ return 'async def' if self._is_async else 'def'
Generates the string of keywords used to define the function, for example
def
orasync def
. def params(self,
*,
annotate: bool = False,
link: Callable[[Doc], str] | None = None) ‑> List[str]-
Expand source code Browse git
def params(self, *, annotate: bool = False, link: Optional[Callable[[Doc], str]] = None) -> List[str]: """ Returns a list where each element is a nicely formatted parameter of this function. This includes argument lists, keyword arguments and default values, and it doesn't include any optional arguments whose names begin with an underscore. If `annotate` is True, the parameter strings include [PEP 484] type hint annotations. [PEP 484]: https://www.python.org/dev/peps/pep-0484/ """ return self._params(self, annotate=annotate, link=link, module=self.module)
Returns a list where each element is a nicely formatted parameter of this function. This includes argument lists, keyword arguments and default values, and it doesn't include any optional arguments whose names begin with an underscore.
If
annotate
is True, the parameter strings include PEP 484 type hint annotations. def return_annotation(self, *, link=None) ‑> str
-
Expand source code Browse git
def return_annotation(self, *, link=None) -> str: """Formatted function return type annotation or empty string if none.""" annot = '' for method in ( lambda: _get_type_hints(self.obj)['return'], # Mainly for non-property variables lambda: _get_type_hints(cast(Class, self.cls).obj)[self.name], # global variables lambda: _get_type_hints(not self.cls and self.module.obj)[self.name], # properties lambda: inspect.signature(_unwrap_descriptor(self)).return_annotation, # Use raw annotation strings in unmatched forward declarations lambda: cast(Class, self.cls).obj.__annotations__[self.name], # Extract annotation from the docstring for C builtin function lambda: Function._signature_from_string(self).return_annotation, ): try: annot = method() except Exception: continue else: break else: # Don't warn on variables. The annotation just isn't available. if not isinstance(self, Variable): warn(f"Error handling return annotation for {self!r}", stacklevel=3) if annot is inspect.Parameter.empty or not annot: return '' if isinstance(annot, str): s = annot else: s = _formatannotation(annot) s = re.sub(r'\bForwardRef\((?P<quot>[\"\'])(?P<str>.*?)(?P=quot)\)', r'\g<str>', s) s = s.replace(' ', '\N{NBSP}') # Better line breaks in html signatures if link: from pdoc.html_helpers import _linkify s = re.sub(r'[\w\.]+', partial(_linkify, link=link, module=self.module), s) return s
Formatted function return type annotation or empty string if none.
Inherited members
class Module (module: str | module,
*,
docfilter: Callable[[Doc], bool] | None = None,
supermodule: ForwardRef('Module') | None = None,
context: Context | None = None,
skip_errors: bool = False)-
Expand source code Browse git
class Module(Doc): """ Representation of a module's documentation. """ __pdoc__["Module.name"] = """ The name of this module with respect to the context/path in which it was imported from. It is always an absolute import path. """ def __init__(self, module: Union[ModuleType, str], *, docfilter: Optional[Callable[[Doc], bool]] = None, supermodule: Optional['Module'] = None, context: Optional[Context] = None, skip_errors: bool = False): """ Creates a `Module` documentation object given the actual module Python object. `docfilter` is an optional predicate that controls which sub-objects are documentated (see also: `pdoc.html()`). `supermodule` is the parent `pdoc.Module` this module is a submodule of. `context` is an instance of `pdoc.Context`. If `None` a global context object will be used. If `skip_errors` is `True` and an unimportable, erroneous submodule is encountered, a warning will be issued instead of raising an exception. """ if isinstance(module, str): module = import_module(module, skip_errors=skip_errors) super().__init__(module.__name__, self, module) if self.name.endswith('.__init__') and not self.is_package: self.name = self.name[:-len('.__init__')] self._context = _global_context if context is None else context """ A lookup table for ALL doc objects of all modules that share this context, mainly used in `Module.find_ident()`. """ assert isinstance(self._context, Context), \ 'pdoc.Module(context=) should be a pdoc.Context instance' self.supermodule = supermodule """ The parent `pdoc.Module` this module is a submodule of, or `None`. """ self.doc: Dict[str, Union[Module, Class, Function, Variable]] = {} """A mapping from identifier name to a documentation object.""" self._is_inheritance_linked = False """Re-entry guard for `pdoc.Module._link_inheritance()`.""" self._skipped_submodules = set() var_docstrings, _ = _pep224_docstrings(self) # Populate self.doc with this module's public members public_objs = [] if hasattr(self.obj, '__all__'): for name in self.obj.__all__: try: obj = getattr(self.obj, name) except AttributeError: warn(f"Module {self.module!r} doesn't contain identifier `{name}` " "exported in `__all__`") else: if not _is_blacklisted(name, self): obj = inspect.unwrap(obj) public_objs.append((name, obj)) else: def is_from_this_module(obj): mod = inspect.getmodule(inspect.unwrap(obj)) return mod is None or mod.__name__ == self.obj.__name__ for name, obj in inspect.getmembers(self.obj): if ((_is_public(name) or _is_whitelisted(name, self)) and (_is_blacklisted(name, self) or # skips unwrapping that follows is_from_this_module(obj) or name in var_docstrings)): if _is_blacklisted(name, self): self._context.blacklisted.add(f'{self.refname}.{name}') continue obj = inspect.unwrap(obj) public_objs.append((name, obj)) index = list(self.obj.__dict__).index public_objs.sort(key=lambda i: index(i[0])) for name, obj in public_objs: if _is_function(obj): self.doc[name] = Function(name, self, obj) elif inspect.isclass(obj): self.doc[name] = Class(name, self, obj) elif name in var_docstrings: self.doc[name] = Variable(name, self, var_docstrings[name], obj=obj) # If the module is a package, scan the directory for submodules if self.is_package: def iter_modules(paths): """ Custom implementation of `pkgutil.iter_modules()` because that one doesn't play well with namespace packages. See: https://github.com/pypa/setuptools/issues/83 """ from os.path import isdir, join for pth in paths: if pth.startswith("__editable__."): # See https://github.com/pypa/pip/issues/11380 continue for file in os.listdir(pth): if file.startswith(('.', '__pycache__', '__init__.py')): continue module_name = inspect.getmodulename(file) if module_name: yield module_name if isdir(join(pth, file)) and '.' not in file: yield file for root in iter_modules(self.obj.__path__): # Ignore if this module was already doc'd. if root in self.doc: continue # Ignore if it isn't exported if not _is_public(root) and not _is_whitelisted(root, self): continue if _is_blacklisted(root, self): self._skipped_submodules.add(root) continue assert self.refname == self.name fullname = f"{self.name}.{root}" m = Module(import_module(fullname, skip_errors=skip_errors), docfilter=docfilter, supermodule=self, context=self._context, skip_errors=skip_errors) self.doc[root] = m # Skip empty namespace packages because they may # as well be other auxiliary directories if m.is_namespace and not m.doc: del self.doc[root] self._context.pop(m.refname) # Apply docfilter if docfilter: for name, dobj in self.doc.copy().items(): if not docfilter(dobj): self.doc.pop(name) self._context.pop(dobj.refname, None) # Build the reference name dictionary of the module self._context[self.refname] = self for docobj in self.doc.values(): self._context[docobj.refname] = docobj if isinstance(docobj, Class): self._context.update((obj.refname, obj) for obj in docobj.doc.values()) class ImportWarning(UserWarning): """ Our custom import warning because the builtin is ignored by default. https://docs.python.org/3/library/warnings.html#default-warning-filter """ __pdoc__['Module.ImportWarning'] = False @property def __pdoc__(self) -> dict: """This module's __pdoc__ dict, or an empty dict if none.""" return getattr(self.obj, '__pdoc__', {}) def _link_inheritance(self): # Inherited members are already in place since # `Class._fill_inheritance()` has been called from # `pdoc.fill_inheritance()`. # Now look for docstrings in the module's __pdoc__ override. if self._is_inheritance_linked: # Prevent re-linking inheritance for modules which have already # had done so. Otherwise, this would raise "does not exist" # errors if `pdoc.link_inheritance()` is called multiple times. return # Apply __pdoc__ overrides for name, docstring in self.__pdoc__.items(): # In case of whitelisting with "True", there's nothing to do if docstring is True: continue refname = f"{self.refname}.{name}" if docstring in (False, None): if docstring is None: warn('Setting `__pdoc__[key] = None` is deprecated; ' 'use `__pdoc__[key] = False` ' f'(key: {name!r}, module: {self.name!r}).') if name in self._skipped_submodules: continue if (not name.endswith('.__init__') and name not in self.doc and refname not in self._context and refname not in self._context.blacklisted): warn(f'__pdoc__-overriden key {name!r} does not exist ' f'in module {self.name!r}') obj = self.find_ident(name) cls = getattr(obj, 'cls', None) if cls: del cls.doc[obj.name] self.doc.pop(name, None) self._context.pop(refname, None) # Pop also all that startwith refname for key in list(self._context.keys()): if key.startswith(refname + '.'): del self._context[key] continue dobj = self.find_ident(refname) if isinstance(dobj, External): continue if not isinstance(docstring, str): raise ValueError('__pdoc__ dict values must be strings; ' f'__pdoc__[{name!r}] is of type {type(docstring)}') dobj.docstring = inspect.cleandoc(docstring) # Now after docstrings are set correctly, continue the # inheritance routine, marking members inherited or not for c in _filter_type(Class, self.doc): c._link_inheritance() self._is_inheritance_linked = True def text(self, **kwargs) -> str: """ Returns the documentation for this module as plain text. """ txt = _render_template('/text.mako', module=self, **kwargs) return re.sub("\n\n\n+", "\n\n", txt) def html(self, minify=True, **kwargs) -> str: """ Returns the documentation for this module as self-contained HTML. If `minify` is `True`, the resulting HTML is minified. For explanation of other arguments, see `pdoc.html()`. `kwargs` is passed to the `mako` render function. """ html = _render_template('/html.mako', module=self, **kwargs) if minify: from pdoc.html_helpers import minify_html html = minify_html(html) if not html.endswith('\n'): html = html + '\n' return html @property def is_package(self) -> bool: """ `True` if this module is a package. Works by checking whether the module has a `__path__` attribute. """ return hasattr(self.obj, "__path__") @property def is_namespace(self) -> bool: """ `True` if this module is a namespace package. """ try: return self.obj.__spec__.origin in (None, 'namespace') # None in Py3.7+ except AttributeError: return False def find_class(self, cls: type) -> Doc: """ Given a Python `cls` object, try to find it in this module or in any of the exported identifiers of the submodules. """ # XXX: Is this corrent? Does it always match # `Class.module.name + Class.qualname`?. Especially now? # If not, see what was here before. return self.find_ident(f'{cls.__module__ or _UNKNOWN_MODULE}.{cls.__qualname__}') def find_ident(self, name: str) -> Doc: """ Searches this module and **all** other public modules for an identifier with name `name` in its list of exported identifiers. The documentation object corresponding to the identifier is returned. If one cannot be found, then an instance of `External` is returned populated with the given identifier. """ _name = name.rstrip('()') # Function specified with parentheses if _name.endswith('.__init__'): # Ref to class' init is ref to class itself _name = _name[:-len('.__init__')] return (self.doc.get(_name) or self._context.get(_name) or self._context.get(f'{self.name}.{_name}') or External(name)) def _filter_doc_objs(self, type: Type[T], sort=True) -> List[T]: result = _filter_type(type, self.doc) return sorted(result) if sort else result def variables(self, sort=True) -> List['Variable']: """ Returns all documented module-level variables in the module, optionally sorted alphabetically, as a list of `pdoc.Variable`. """ return self._filter_doc_objs(Variable, sort) def classes(self, sort=True) -> List['Class']: """ Returns all documented module-level classes in the module, optionally sorted alphabetically, as a list of `pdoc.Class`. """ return self._filter_doc_objs(Class, sort) def functions(self, sort=True) -> List['Function']: """ Returns all documented module-level functions in the module, optionally sorted alphabetically, as a list of `pdoc.Function`. """ return self._filter_doc_objs(Function, sort) def submodules(self) -> List['Module']: """ Returns all documented sub-modules of the module sorted alphabetically as a list of `pdoc.Module`. """ return self._filter_doc_objs(Module) def _url(self): url = self.module.name.replace('.', '/') if self.is_package: return url + _URL_PACKAGE_SUFFIX elif url.endswith('/index'): return url + _URL_INDEX_MODULE_SUFFIX return url + _URL_MODULE_SUFFIX
Representation of a module's documentation.
Creates a
Module
documentation object given the actual module Python object.docfilter
is an optional predicate that controls which sub-objects are documentated (see also:html()
).supermodule
is the parentModule
this module is a submodule of.context
is an instance ofContext
. IfNone
a global context object will be used.If
skip_errors
isTrue
and an unimportable, erroneous submodule is encountered, a warning will be issued instead of raising an exception.Ancestors
Instance variables
var doc
-
A mapping from identifier name to a documentation object.
prop is_namespace : bool
-
Expand source code Browse git
@property def is_namespace(self) -> bool: """ `True` if this module is a namespace package. """ try: return self.obj.__spec__.origin in (None, 'namespace') # None in Py3.7+ except AttributeError: return False
True
if this module is a namespace package. prop is_package : bool
-
Expand source code Browse git
@property def is_package(self) -> bool: """ `True` if this module is a package. Works by checking whether the module has a `__path__` attribute. """ return hasattr(self.obj, "__path__")
True
if this module is a package.Works by checking whether the module has a
__path__
attribute. var supermodule
-
The parent
Module
this module is a submodule of, orNone
.
Methods
def classes(self, sort=True) ‑> List[Class]
-
Expand source code Browse git
def classes(self, sort=True) -> List['Class']: """ Returns all documented module-level classes in the module, optionally sorted alphabetically, as a list of `pdoc.Class`. """ return self._filter_doc_objs(Class, sort)
Returns all documented module-level classes in the module, optionally sorted alphabetically, as a list of
Class
. def find_class(self, cls: type) ‑> Doc
-
Expand source code Browse git
def find_class(self, cls: type) -> Doc: """ Given a Python `cls` object, try to find it in this module or in any of the exported identifiers of the submodules. """ # XXX: Is this corrent? Does it always match # `Class.module.name + Class.qualname`?. Especially now? # If not, see what was here before. return self.find_ident(f'{cls.__module__ or _UNKNOWN_MODULE}.{cls.__qualname__}')
Given a Python
cls
object, try to find it in this module or in any of the exported identifiers of the submodules. def find_ident(self, name: str) ‑> Doc
-
Expand source code Browse git
def find_ident(self, name: str) -> Doc: """ Searches this module and **all** other public modules for an identifier with name `name` in its list of exported identifiers. The documentation object corresponding to the identifier is returned. If one cannot be found, then an instance of `External` is returned populated with the given identifier. """ _name = name.rstrip('()') # Function specified with parentheses if _name.endswith('.__init__'): # Ref to class' init is ref to class itself _name = _name[:-len('.__init__')] return (self.doc.get(_name) or self._context.get(_name) or self._context.get(f'{self.name}.{_name}') or External(name))
Searches this module and all other public modules for an identifier with name
name
in its list of exported identifiers.The documentation object corresponding to the identifier is returned. If one cannot be found, then an instance of
External
is returned populated with the given identifier. def functions(self, sort=True) ‑> List[Function]
-
Expand source code Browse git
def functions(self, sort=True) -> List['Function']: """ Returns all documented module-level functions in the module, optionally sorted alphabetically, as a list of `pdoc.Function`. """ return self._filter_doc_objs(Function, sort)
Returns all documented module-level functions in the module, optionally sorted alphabetically, as a list of
Function
. def html(self, minify=True, **kwargs) ‑> str
-
Expand source code Browse git
def html(self, minify=True, **kwargs) -> str: """ Returns the documentation for this module as self-contained HTML. If `minify` is `True`, the resulting HTML is minified. For explanation of other arguments, see `pdoc.html()`. `kwargs` is passed to the `mako` render function. """ html = _render_template('/html.mako', module=self, **kwargs) if minify: from pdoc.html_helpers import minify_html html = minify_html(html) if not html.endswith('\n'): html = html + '\n' return html
Returns the documentation for this module as self-contained HTML.
If
minify
isTrue
, the resulting HTML is minified.For explanation of other arguments, see
html()
.kwargs
is passed to themako
render function. def submodules(self) ‑> List[Module]
-
Expand source code Browse git
def submodules(self) -> List['Module']: """ Returns all documented sub-modules of the module sorted alphabetically as a list of `pdoc.Module`. """ return self._filter_doc_objs(Module)
Returns all documented sub-modules of the module sorted alphabetically as a list of
Module
. def text(self, **kwargs) ‑> str
-
Expand source code Browse git
def text(self, **kwargs) -> str: """ Returns the documentation for this module as plain text. """ txt = _render_template('/text.mako', module=self, **kwargs) return re.sub("\n\n\n+", "\n\n", txt)
Returns the documentation for this module as plain text.
def variables(self, sort=True) ‑> List[Variable]
-
Expand source code Browse git
def variables(self, sort=True) -> List['Variable']: """ Returns all documented module-level variables in the module, optionally sorted alphabetically, as a list of `pdoc.Variable`. """ return self._filter_doc_objs(Variable, sort)
Returns all documented module-level variables in the module, optionally sorted alphabetically, as a list of
Variable
.
Inherited members
class Variable (name: str,
module: Module,
docstring,
*,
obj=None,
cls: Class | None = None,
instance_var: bool = False,
kind: Literal['prop', 'var'] = 'var')-
Expand source code Browse git
class Variable(Doc): """ Representation of a variable's documentation. This includes module, class, and instance variables. """ def __init__(self, name: str, module: Module, docstring, *, obj=None, cls: Optional[Class] = None, instance_var: bool = False, kind: Literal["prop", "var"] = 'var'): """ Same as `pdoc.Doc`, except `cls` should be provided as a `pdoc.Class` object when this is a class or instance variable. """ super().__init__(name, module, obj, docstring) self.cls = cls """ The `pdoc.Class` object if this is a class or instance variable. If not (i.e. it is a global variable), this is None. """ self.instance_var = instance_var """ True if variable is some class' instance variable (as opposed to class variable). """ self.kind = kind """ `prop` if variable is a dynamic property (has getter/setter or deleter), or `var` otherwise. """ @property def qualname(self) -> str: if self.cls: return f'{self.cls.qualname}.{self.name}' return self.name @property def refname(self) -> str: return f'{self.cls.refname if self.cls else self.module.refname}.{self.name}' def type_annotation(self, *, link=None) -> str: """Formatted variable type annotation or empty string if none.""" return Function.return_annotation(cast(Function, self), link=link)
Representation of a variable's documentation. This includes module, class, and instance variables.
Same as
Doc
, exceptcls
should be provided as aClass
object when this is a class or instance variable.Ancestors
Instance variables
var cls
-
The
Class
object if this is a class or instance variable. If not (i.e. it is a global variable), this is None. var instance_var
-
True if variable is some class' instance variable (as opposed to class variable).
var kind
-
prop
if variable is a dynamic property (has getter/setter or deleter), orvar
otherwise.
Methods
def type_annotation(self, *, link=None) ‑> str
-
Expand source code Browse git
def type_annotation(self, *, link=None) -> str: """Formatted variable type annotation or empty string if none.""" return Function.return_annotation(cast(Function, self), link=link)
Formatted variable type annotation or empty string if none.
Inherited members