Module 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
All objects (modules, functions, classes, variables) are only
considered public if their identifiers don't begin with an
underscore ( _ ).3
In addition, if a module defines __all__, then only
the identifiers contained in this list will be considered public.
Otherwise, a module's global identifiers are considered public
only if they don't begin with an underscore and are defined
in this exact module (i.e. not imported from somewhere else).
By transitivity, sub-objects of non-public objects (e.g. submodules of non-public modules, methods of non-public classes etc.) are not public and thus not documented.
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.
When pdoc generates documentation for the code such as above,
it will automatically attach the docstring for A.test to
B.test if B.test does not define its own docstring.
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 docstrings attached to module (or global)
variables, class variables, and object instance variables; all in
the same way as proposed in PEP-224, with a docstring following the
variable assignment.
For example:
module_variable = 1
"""Docstring for module_variable."""
class C:
class_variable = 2
"""Docstring for class_variable."""
def __init__(self):
self.variable = 3
"""Docstring for instance variable."""
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 or overridden 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'.
If __pdoc__[key] = False, then key (and its members) will be
excluded from the documentation of the module.
Alternatively, the values of __pdoc__ should be the overriding docstrings.
This particular feature is useful when there's no feasible way of
attaching a docstring to something. A good example of this 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.
Supported reST directives
The following reST directives should work:
- specific and generic admonitions,
.. image::or.. figure::(without options),.. include::, with support for the options::start-line:,:end-line:,:start-after:and:end-before:... 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 ( ` ).
The identifier name must be fully qualified, for example
`pdoc.Doc.docstring` is correct (and will link to
Doc.docstring) while `Doc.docstring` is not.
Command-line interface
pdoc includes a feature-rich "binary" program for producing
HTML and plain text documentation of your modules.
To produce HTML documentation of your whole package in subdirectory
'build' of the current directory, using the default HTML template, run:
$ pdoc --html --html-dir build my_package
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
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.
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.5+.
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 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. ↩
Source code
"""
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.
.. include:: ./documentation.md
"""
import ast
import importlib.machinery
import importlib.util
import inspect
import os
import os.path as path
import re
import sys
import typing
from contextlib import contextmanager
from copy import copy
from functools import lru_cache, reduce, partial
from itertools import tee, groupby
from types import ModuleType
from typing import Dict, Iterable, List, Set, Type, TypeVar, Union, Tuple, Generator, Callable
from warnings import warn
from mako.lookup import TemplateLookup
from mako.exceptions import TopLevelLookupException
from mako.template import Template
try:
from pdoc._version import version as __version__ # noqa: F401
except ImportError:
__version__ = '???' # Package not installed
_URL_MODULE_SUFFIX = '.html'
_URL_INDEX_MODULE_SUFFIX = '.m.html' # For modules named literal 'index'
_URL_PACKAGE_SUFFIX = '/index.html'
_SOURCE_SUFFIXES = tuple(importlib.machinery.SOURCE_SUFFIXES)
T = TypeVar('T', bound='Doc')
__pdoc__ = {} # type: Dict[str, Union[bool, str]]
tpl_lookup = TemplateLookup(
cache_args=dict(cached=True,
cache_type='memory'),
input_encoding='utf-8',
directories=[path.join(path.dirname(__file__), "templates")],
)
"""
A `mako.lookup.TemplateLookup` object that knows how to load templates
from the file system. You may add additional paths by modifying the
object's `directories` attribute.
"""
if os.getenv("XDG_CONFIG_HOME"):
tpl_lookup.directories.insert(0, path.join(os.getenv("XDG_CONFIG_HOME", ''), "pdoc"))
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
_global_context = Context()
def reset():
"""Resets the global `pdoc.Context` to the initial (empty) state."""
global _global_context
_global_context.clear()
# Clear LRU caches
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()
def _render_template(template_name, **kwargs):
"""
Returns the Mako template with the given name. If the template
cannot be found, a nicer error message is displayed.
"""
# Apply config.mako configuration
MAKO_INTERNALS = Template('').module.__dict__.keys()
DEFAULT_CONFIG = path.join(path.dirname(__file__), 'templates', 'config.mako')
config = {}
for config_module in (Template(filename=DEFAULT_CONFIG).module,
tpl_lookup.get_template('/config.mako').module):
config.update((var, getattr(config_module, var, None))
for var in config_module.__dict__
if var not in MAKO_INTERNALS)
config.update(kwargs)
try:
t = tpl_lookup.get_template(template_name)
except TopLevelLookupException:
raise OSError(
"No template found at any of: {}".format(
', '.join(path.join(p, template_name.lstrip("/"))
for p in tpl_lookup.directories)))
try:
return t.render(**config).strip()
except Exception:
from mako import exceptions
print(exceptions.text_error_template().render(),
file=sys.stderr)
raise
def html(module_name, docfilter=None, reload=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), docfilter=docfilter)
link_inheritance()
return mod.html(**kwargs)
def text(module_name, docfilter=None, reload=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), docfilter=docfilter)
link_inheritance()
return mod.text(**kwargs)
def import_module(module, *, reload: 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 isfile, isdir, split, abspath, splitext
path, module = '_pdoc_dummy_nonexistent', module
if isdir(module) or isfile(module) and module.endswith(_SOURCE_SUFFIXES):
path, module = split(splitext(abspath(module))[0])
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:
raise ImportError('Error importing {!r}: {}'.format(module, e))
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)
return module
def _pairwise(iterable):
"""s -> (s0,s1), (s1,s2), (s2, s3), ..."""
a, b = tee(iterable)
next(b, None)
return zip(a, b)
def _var_docstrings(doc_obj: Union['Module', 'Class'], *,
_init_tree: ast.FunctionDef = None) -> Dict[str, 'Variable']:
"""
Extracts docstrings for variables of `doc_obj`
(either a `pdoc.Module` or `pdoc.Class`).
Returns a dict mapping variable names to `pdoc.Variable` objects.
For `pdoc.Class` objects, the dict contains class' instance
variables (defined as `self.something` in class' `__init__`),
recognized by `Variable.instance_var == True`.
"""
if _init_tree:
tree = _init_tree # type: Union[ast.Module, ast.FunctionDef]
else:
# No variables in namespace packages
if isinstance(doc_obj, Module) and doc_obj.is_namespace:
return {}
try:
tree = ast.parse(inspect.getsource(doc_obj.obj)) # type: ignore
except (OSError, TypeError, SyntaxError):
warn("Couldn't get/parse source of '{!r}'".format(doc_obj))
return {}
if isinstance(doc_obj, Class):
tree = tree.body[0] # type: ignore # ast.parse creates a dummy ast.Module wrapper
vs = {} # type: Dict[str, Variable]
cls = None
module = doc_obj
module_all = getattr(module.obj, '__all__', None)
if isinstance(doc_obj, Class):
cls = doc_obj
module = doc_obj.module
module_all = None # If documenting a class, we needn't look into __all__
# For classes, first add instance variables defined in __init__
if not _init_tree:
try:
# Recursive call with just the __init__ tree
vs = _var_docstrings(
doc_obj, _init_tree=next(
node for node in tree.body
if (isinstance(node, ast.FunctionDef) and
node.name == '__init__')))
except StopIteration:
pass
if module_all is not None:
module_all = set(module_all)
try:
ast_AnnAssign = ast.AnnAssign # type: Type
except AttributeError: # Python < 3.6
ast_AnnAssign = type(None)
ast_Assignments = (ast.Assign, ast_AnnAssign)
for assign_node, str_node in _pairwise(ast.iter_child_nodes(tree)):
if not (isinstance(assign_node, ast_Assignments) and
isinstance(str_node, ast.Expr) and
isinstance(str_node.value, ast.Str)):
continue
if isinstance(assign_node, ast.Assign) and len(assign_node.targets) == 1:
target = assign_node.targets[0]
elif isinstance(assign_node, ast_AnnAssign):
target = assign_node.target
# TODO: use annotation
else:
continue
if not _init_tree and isinstance(target, ast.Name):
name = target.id
elif (_init_tree and
isinstance(target, ast.Attribute) and
isinstance(target.value, ast.Name) and
target.value.id == 'self'):
name = target.attr
else:
continue
if not _is_public(name):
continue
if module_all is not None and name not in module_all:
continue
docstring = inspect.cleandoc(str_node.value.s).strip()
if not docstring:
continue
vs[name] = Variable(name, module, docstring,
cls=cls, instance_var=bool(_init_tree))
return vs
def _is_public(ident_name):
"""
Returns `True` if `ident_name` matches the export criteria for an
identifier name.
"""
return not ident_name.startswith("_")
def _filter_type(type: Type[T],
values: Union[Iterable['Doc'], Dict[str, 'Doc']]) -> List[T]:
"""
Return a list of values from `values` of type `type`.
"""
if isinstance(values, dict):
values = values.values()
return [i for i in values if isinstance(i, type)]
def _toposort(graph: Dict[T, Set[T]]) -> Generator[T, None, None]:
"""
Return items of `graph` sorted in topological order.
Source: https://rosettacode.org/wiki/Topological_sort#Python
"""
items_without_deps = reduce(set.union, graph.values(), set()) - set(graph.keys()) # type: ignore # noqa: E501
yield from items_without_deps
ordered = items_without_deps
while True:
graph = {item: (deps - ordered)
for item, deps in graph.items()
if item not in ordered}
ordered = {item
for item, deps in graph.items()
if not deps}
yield from ordered
if not ordered:
break
assert not graph, "A cyclic dependency exists amongst %r" % graph
def link_inheritance(context: 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()
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.)
"""
__slots__ = ('module', 'name', 'obj', 'docstring', 'inherits')
def __init__(self, name, module, obj, docstring=None):
"""
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 = None # type: Union[Class, Function, Variable]
"""
The Doc object (Class, Function, or Variable) this object inherits from,
if any.
"""
def __repr__(self):
return '<{} {!r}>'.format(self.__class__.__name__, self.refname)
@property # type: ignore
@lru_cache()
def source(self) -> str:
"""
Cleaned (dedented) source code of the Python object. If not
available, an empty string.
"""
try:
lines, _ = inspect.getsourcelines(self.obj)
except (ValueError, TypeError, OSError):
return ''
return inspect.cleandoc(''.join(['\n'] + lines))
@property
def refname(self):
"""
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):
"""
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: 'Module' = None, *, link_prefix: str = '',
top_ancestor: bool = False):
"""
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 '#' + 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 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.name < other.name
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.
"""
__slots__ = ('supermodule', 'doc', '_context', '_is_inheritance_linked')
def __init__(self, module: ModuleType, *, docfilter: Callable[[Doc], bool] = None,
supermodule: 'Module' = None, context: Context = None):
"""
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.
"""
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()`.
"""
self.supermodule = supermodule
"""
The parent `pdoc.Module` this module is a submodule of, or `None`.
"""
self.doc = {} # type: Dict[str, Doc]
"""A mapping from identifier name to a documentation object."""
self._is_inheritance_linked = False
"""Re-entry guard for `pdoc.Module._link_inheritance()`."""
# Populate self.doc with this module's public members
if hasattr(self.obj, '__all__'):
public_objs = []
for name in self.obj.__all__:
try:
public_objs.append((name, getattr(self.obj, name)))
except AttributeError:
warn("Module {!r} doesn't contain identifier `{}` "
"exported in `__all__`".format(self.module, name))
else:
def is_from_this_module(obj):
mod = inspect.getmodule(obj)
return mod is None or mod.__name__ == self.obj.__name__
public_objs = [(name, inspect.unwrap(obj))
for name, obj in inspect.getmembers(self.obj)
if (_is_public(name) and
is_from_this_module(obj))]
index = list(self.obj.__dict__).index
public_objs.sort(key=lambda i: index(i[0]))
for name, obj in public_objs:
if inspect.isroutine(obj):
self.doc[name] = Function(name, self, obj)
elif inspect.isclass(obj):
self.doc[name] = Class(name, self, obj)
self.doc.update(_var_docstrings(self))
# 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, splitext
for pth in paths:
for file in os.listdir(pth):
if file.startswith(('.', '__pycache__', '__init__.py')):
continue
if file.endswith(_SOURCE_SUFFIXES) or isdir(join(pth, file)):
yield splitext(file)[0]
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):
continue
assert self.refname == self.name
fullname = "%s.%s" % (self.name, root)
m = import_module(fullname)
self.doc[root] = Module(
m, docfilter=docfilter, supermodule=self, context=self._context)
# Apply docfilter
if docfilter:
for name, dobj in self.doc.copy().items():
if not docfilter(dobj):
self.doc.pop(name)
# 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())
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
for name, docstring in getattr(self.obj, "__pdoc__", {}).items():
refname = "%s.%s" % (self.refname, name)
if docstring in (False, None):
if docstring is None:
warn('Setting `__pdoc__[key] = None` is deprecated; '
'use `__pdoc__[key] = False` '
'(key: {!r}, module: {!r}).'.format(name, self.name))
if (not name.endswith('.__init__') and
name not in self.doc and refname not in self._context):
warn('__pdoc__-overriden key {!r} does not exist '
'in module {!r}'.format(name, self.name))
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;'
'__pdoc__[{!r}] is of type {}'.format(name, 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)
return html
@property
def is_package(self):
"""
`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):
"""
`True` if this module is a namespace package.
"""
return self.obj.__spec__.origin in (None, 'namespace') # None in Py3.7+
def find_class(self, cls: type):
"""
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(cls.__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(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):
"""
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):
"""
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):
"""
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):
"""
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
class Class(Doc):
"""
Representation of a class' documentation.
"""
__slots__ = ('doc', '_super_members')
def __init__(self, name, module, obj, *, docstring=None):
assert isinstance(obj, type)
if docstring is None:
init_doc = inspect.getdoc(obj.__init__) or ''
if init_doc == object.__init__.__doc__:
init_doc = ''
docstring = ((inspect.getdoc(obj) or '') + '\n\n' + init_doc).strip()
super().__init__(name, module, obj, docstring=docstring)
self.doc = {}
"""A mapping from identifier name to a `pdoc.Doc` objects."""
self.doc.update(_var_docstrings(self))
public_objs = [(name, inspect.unwrap(obj))
for name, obj in inspect.getmembers(self.obj)
# Filter only *own* members. The rest are inherited
# in Class._fill_inheritance()
if name in self.obj.__dict__ and _is_public(name)]
index = list(self.obj.__dict__).index
public_objs.sort(key=lambda i: index(i[0]))
# Convert the public Python objects to documentation objects.
for name, obj in public_objs:
if name in self.doc and self.doc[name].docstring:
continue
if inspect.isroutine(obj):
self.doc[name] = Function(
name, self.module, obj, cls=self,
method=not self._method_type(self.obj, name))
elif (inspect.isdatadescriptor(obj) or
inspect.isgetsetdescriptor(obj) or
inspect.ismemberdescriptor(obj)):
self.doc[name] = Variable(
name, self.module, inspect.getdoc(obj),
obj=getattr(obj, 'fget', obj),
cls=self, instance_var=True)
else:
self.doc[name] = Variable(
name, self.module,
docstring=isinstance(obj, type) and inspect.getdoc(obj) or "",
cls=self,
instance_var=name in getattr(self.obj, "__slots__", ()))
@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("{}.{} not found".format(cls, name))
@property
def refname(self):
return 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 = [self.module.find_class(c)
for c in inspect.getmro(self.obj)
if c not in (self.obj, object)]
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 [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 = getattr(self.module.obj, "__pdoc__", {})
if name in exclusions or qualname in exclusions or refname in exclusions:
return []
params = Function._params(self.obj.__init__,
annotate=annotate, link=link, module=self.module)
params = params[1:] if params[0] == 'self' else params
return params
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):
"""
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):
"""
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):
"""
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.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.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(((k, sorted(g))
for k, g in groupby((i.inherits
for i in self.doc.values() if i.inherits),
key=lambda i: i.cls)),
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():
dobj = self.doc[name]
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
class Function(Doc):
"""
Representation of documentation for a function or method.
"""
__slots__ = ('cls', 'method')
def __init__(self, name, module, obj, *, cls: Class = None, method=False):
"""
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)
super().__init__(name, module, obj)
self.cls = cls
"""
The `pdoc.Class` documentation object if the function is a method.
If not, this is None.
"""
self.method = method
"""
Whether this function is a normal bound method.
In particular, static and class methods have this set to False.
"""
def funcdef(self):
"""
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):
"""Formatted function return type annotation or empty string if none."""
try:
annot = typing.get_type_hints(self.obj).get('return', '')
except NameError as e:
warn("Error handling return annotation for {}: {}", self.name, e.args[0])
annot = inspect.signature(inspect.unwrap(self.obj)).return_annotation
if annot == inspect.Parameter.empty:
annot = ''
if not annot:
return ''
s = inspect.formatannotation(annot).replace(' ', '\xA0') # NBSP for better line breaks
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: 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.obj, annotate=annotate, link=link, module=self.module)
@staticmethod
def _params(func_obj, annotate=False, link=None, module=None):
try:
signature = inspect.signature(inspect.unwrap(func_obj))
except ValueError:
# I guess this is for C builtin functions?
return ["..."]
def safe_default_value(p: inspect.Parameter):
if p.default is os.environ:
class mock:
def __repr__(self):
return 'os.environ'
return p.replace(default=mock())
return p
params = []
kw_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.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)
s = str(p)
if p.annotation is not EMPTY:
if sys.version_info < (3, 7):
# PEP8-normalize whitespace
s = re.sub(r'(?<!\s)=(?!\s)', ' = ', re.sub(r':(?!\s)', ': ', s, 1), 1)
# "Eval" forward-declarations (typing string literals)
s = re.sub(r'(?<=: )[\'"]|[\'"](?= = )', '', s, 2)
s = s.replace(' ', '\xA0') # Neater line breaking with NBSPs
if link:
s = re.sub(r'[\w\.]+', _linkify, s)
params.append(s)
return params
@property
def refname(self):
return (self.cls.refname if self.cls else self.module.refname) + '.' + self.name
class Variable(Doc):
"""
Representation of a variable's documentation. This includes
module, class, and instance variables.
"""
__slots__ = ('cls', 'instance_var')
def __init__(self, name, module, docstring, *,
obj=None, cls: Class = None, instance_var=False):
"""
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, this is None.
"""
self.instance_var = instance_var
"""
True if variable is some class' instance variable (as
opposed to class variable).
"""
@property
def qualname(self):
if self.cls:
return self.cls.qualname + '.' + self.name
return self.name
@property
def refname(self):
return (self.cls.refname if self.cls else self.module.refname) + '.' + self.name
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):
"""
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 '/%s.ext' % self.name
Sub-modules
pdoc.cli-
pdoc's CLI interface and helper functions.
pdoc.html_helpers-
Helper functions for HTML output.
pdoc.templatespdoc.test-
Unit tests for pdoc package.
Global variables
var tpl_lookup-
A
mako.lookup.TemplateLookupobject that knows how to load templates from the file system. You may add additional paths by modifying the object'sdirectoriesattribute.
Functions
def html(module_name, docfilter=None, reload=False, **kwargs)-
Returns the documentation for the module
module_namein HTML format. The module must be a module or an importable string.docfilteris 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 returnsTrueorFalse. IfFalse, that object will not be documented.Source code
def html(module_name, docfilter=None, reload=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), docfilter=docfilter) link_inheritance() return mod.html(**kwargs) def import_module(module, *, reload=False)-
Return module object matching
modulespecification (either a python module path or a filesystem path to file/directory).Source code
def import_module(module, *, reload: 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 isfile, isdir, split, abspath, splitext path, module = '_pdoc_dummy_nonexistent', module if isdir(module) or isfile(module) and module.endswith(_SOURCE_SUFFIXES): path, module = split(splitext(abspath(module))[0]) 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: raise ImportError('Error importing {!r}: {}'.format(module, e)) 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) return module def link_inheritance(context=None)-
Link inheritance relationsships between
Classobjects (and between their members) of allModuleobjects that share the providedcontext(Context).You need to call this if you expect
Doc.inheritsand inheritedDoc.docstringto be set correctly.Source code
def link_inheritance(context: 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() def reset()-
Resets the global
Contextto the initial (empty) state.Source code
def reset(): """Resets the global `pdoc.Context` to the initial (empty) state.""" global _global_context _global_context.clear() # Clear LRU caches 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() def text(module_name, docfilter=None, reload=False, **kwargs)-
Returns the documentation for the module
module_namein plain text format suitable for viewing on a terminal. The module must be a module or an importable string.docfilteris 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 returnsTrueorFalse. IfFalse, that object will not be documented.Source code
def text(module_name, docfilter=None, reload=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), docfilter=docfilter) link_inheritance() return mod.text(**kwargs)
Classes
class Class (name, module, obj, *, docstring=None)-
Representation of a class' documentation.
Initializes a documentation object, where
nameis the public identifier name,moduleis aModuleobject where raw Python objectobjis defined, anddocstringis its documentation string. Ifdocstringis left empty, it will be read withinspect.getdoc().Source code
class Class(Doc): """ Representation of a class' documentation. """ __slots__ = ('doc', '_super_members') def __init__(self, name, module, obj, *, docstring=None): assert isinstance(obj, type) if docstring is None: init_doc = inspect.getdoc(obj.__init__) or '' if init_doc == object.__init__.__doc__: init_doc = '' docstring = ((inspect.getdoc(obj) or '') + '\n\n' + init_doc).strip() super().__init__(name, module, obj, docstring=docstring) self.doc = {} """A mapping from identifier name to a `pdoc.Doc` objects.""" self.doc.update(_var_docstrings(self)) public_objs = [(name, inspect.unwrap(obj)) for name, obj in inspect.getmembers(self.obj) # Filter only *own* members. The rest are inherited # in Class._fill_inheritance() if name in self.obj.__dict__ and _is_public(name)] index = list(self.obj.__dict__).index public_objs.sort(key=lambda i: index(i[0])) # Convert the public Python objects to documentation objects. for name, obj in public_objs: if name in self.doc and self.doc[name].docstring: continue if inspect.isroutine(obj): self.doc[name] = Function( name, self.module, obj, cls=self, method=not self._method_type(self.obj, name)) elif (inspect.isdatadescriptor(obj) or inspect.isgetsetdescriptor(obj) or inspect.ismemberdescriptor(obj)): self.doc[name] = Variable( name, self.module, inspect.getdoc(obj), obj=getattr(obj, 'fget', obj), cls=self, instance_var=True) else: self.doc[name] = Variable( name, self.module, docstring=isinstance(obj, type) and inspect.getdoc(obj) or "", cls=self, instance_var=name in getattr(self.obj, "__slots__", ())) @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("{}.{} not found".format(cls, name)) @property def refname(self): return 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 = [self.module.find_class(c) for c in inspect.getmro(self.obj) if c not in (self.obj, object)] 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 [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 = getattr(self.module.obj, "__pdoc__", {}) if name in exclusions or qualname in exclusions or refname in exclusions: return [] params = Function._params(self.obj.__init__, annotate=annotate, link=link, module=self.module) params = params[1:] if params[0] == 'self' else params return params 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): """ 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): """ 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): """ 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.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.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(((k, sorted(g)) for k, g in groupby((i.inherits for i in self.doc.values() if i.inherits), key=lambda i: i.cls)), 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(): dobj = self.doc[name] 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_membersAncestors
Instance variables
var doc-
A mapping from identifier name to a
Docobjects.
Methods
def class_variables(self, include_inherited=True, sort=True)-
Returns an optionally-sorted list of
Variableobjects that represent this class' class variables.Source code
def class_variables(self, include_inherited=True, sort=True): """ 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 functions(self, include_inherited=True, sort=True)-
Returns an optionally-sorted list of
Functionobjects that represent this class' static functions.Source code
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.method, sort) def inherited_members(self)-
Returns all inherited members as a list of tuples (ancestor class, list of ancestor class' members sorted by name), sorted by MRO.
Source code
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(((k, sorted(g)) for k, g in groupby((i.inherits for i in self.doc.values() if i.inherits), key=lambda i: i.cls)), key=lambda x, _mro_index=self.mro().index: _mro_index(x[0])) # type: ignore def instance_variables(self, include_inherited=True, sort=True)-
Returns an optionally-sorted list of
Variableobjects that represent this class' instance variables. Instance variables are those defined in a class's__init__asself.variable = ....Source code
def instance_variables(self, include_inherited=True, sort=True): """ 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)-
Returns an optionally-sorted list of
Functionobjects that represent this class' methods.Source code
def methods(self, include_inherited=True, sort=True): """ 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.method, sort) def mro(self, only_documented=False)-
Returns a list of ancestor (superclass) documentation objects in method resolution order.
The list will contain objects of type
Classif the types are documented, andExternalotherwise.Source code
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 = [self.module.find_class(c) for c in inspect.getmro(self.obj) if c not in (self.obj, object)] if only_documented: classes = _filter_type(Class, classes) return classes def params(self, *, annotate=False, link=None)-
Return a list of formatted parameters accepted by the class constructor (method
__init__). SeeFunction.params().Source code
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 = getattr(self.module.obj, "__pdoc__", {}) if name in exclusions or qualname in exclusions or refname in exclusions: return [] params = Function._params(self.obj.__init__, annotate=annotate, link=link, module=self.module) params = params[1:] if params[0] == 'self' else params return params def subclasses(self)-
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
Classif available, andExternalotherwise.Source code
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 [self.module.find_class(c) for c in type.__subclasses__(self.obj)]
Inherited members
class Context-
The context object that maps all documented identifiers (
Doc.refname) to their respectiveDocobjects.You can pass an instance of
ContexttoModuleconstructor. AllModuleobjects that share the sameContextwill see (and be able to link in HTML to) each other's identifiers.If you don't pass your own
Contextinstance toModuleconstructor, a global context object will be used.Source code
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__'] = FalseAncestors
- builtins.dict
class Doc (name, module, obj, docstring=None)-
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,
pdocadds support for extracting some docstrings from abstract syntax trees, making (module, class or instance) variables supported too.A special type of documentation object
Externalis 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
nameis the public identifier name,moduleis aModuleobject where raw Python objectobjis defined, anddocstringis its documentation string. Ifdocstringis left empty, it will be read withinspect.getdoc().Source code
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.) """ __slots__ = ('module', 'name', 'obj', 'docstring', 'inherits') def __init__(self, name, module, obj, docstring=None): """ 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 = None # type: Union[Class, Function, Variable] """ The Doc object (Class, Function, or Variable) this object inherits from, if any. """ def __repr__(self): return '<{} {!r}>'.format(self.__class__.__name__, self.refname) @property # type: ignore @lru_cache() def source(self) -> str: """ Cleaned (dedented) source code of the Python object. If not available, an empty string. """ try: lines, _ = inspect.getsourcelines(self.obj) except (ValueError, TypeError, OSError): return '' return inspect.cleandoc(''.join(['\n'] + lines)) @property def refname(self): """ 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): """ 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: 'Module' = None, *, link_prefix: str = '', top_ancestor: bool = False): """ 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 '#' + 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 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.name < other.nameSubclasses
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.
var qualname-
Module-relative "qualified" name of this documentation object, used for show (e.g.
Doc.qualname).Source code
@property def qualname(self): """ Module-relative "qualified" name of this documentation object, used for show (e.g. <code>Doc.qualname</code>). """ return getattr(self.obj, '__qualname__', self.name) var refname-
Reference name of this documentation object, usually its fully qualified path (e.g.
pdoc.Doc.refname). Every documentation object provides this property.Source code
@property def refname(self): """ 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 var source-
Cleaned (dedented) source code of the Python object. If not available, an empty string.
Source code
@property # type: ignore @lru_cache() def source(self) -> str: """ Cleaned (dedented) source code of the Python object. If not available, an empty string. """ try: lines, _ = inspect.getsourcelines(self.obj) except (ValueError, TypeError, OSError): return '' return inspect.cleandoc(''.join(['\n'] + lines))
Methods
def url(self, relative_to=None, *, link_prefix='', top_ancestor=False)-
Canonical relative URL (including page fragment) for this documentation object.
Specify
relative_to(aModuleobject) to obtain a relative URL.For usage of
link_prefixseehtml().If
top_ancestorisTrue, the returned URL instead points to the top ancestor in the object'sDoc.inheritschain.Source code
@lru_cache() def url(self, relative_to: 'Module' = None, *, link_prefix: str = '', top_ancestor: bool = False): """ 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 '#' + 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
class External (name)-
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, wherenameshould be a fully qualified name.Source code
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): """ 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 '/%s.ext' % self.nameAncestors
Methods
def url(self, *args, **kwargs)-
Externalobjects return absolute urls matching/{name}.ext.Source code
def url(self, *args, **kwargs): """ `External` objects return absolute urls matching `/{name}.ext`. """ return '/%s.ext' % self.name
Inherited members
class Function (name, module, obj, *, cls=None, method=False)-
Representation of documentation for a function or method.
Same as
Doc, exceptobjmust be a Python function object. The docstring is gathered automatically.clsshould be set when this is a method or a static function beloing to a class.clsshould be aClassobject.methodshould beTruewhen the function is a method. In all other cases, it should beFalse.Source code
class Function(Doc): """ Representation of documentation for a function or method. """ __slots__ = ('cls', 'method') def __init__(self, name, module, obj, *, cls: Class = None, method=False): """ 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) super().__init__(name, module, obj) self.cls = cls """ The `pdoc.Class` documentation object if the function is a method. If not, this is None. """ self.method = method """ Whether this function is a normal bound method. In particular, static and class methods have this set to False. """ def funcdef(self): """ 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): """Formatted function return type annotation or empty string if none.""" try: annot = typing.get_type_hints(self.obj).get('return', '') except NameError as e: warn("Error handling return annotation for {}: {}", self.name, e.args[0]) annot = inspect.signature(inspect.unwrap(self.obj)).return_annotation if annot == inspect.Parameter.empty: annot = '' if not annot: return '' s = inspect.formatannotation(annot).replace(' ', '\xA0') # NBSP for better line breaks 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: 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.obj, annotate=annotate, link=link, module=self.module) @staticmethod def _params(func_obj, annotate=False, link=None, module=None): try: signature = inspect.signature(inspect.unwrap(func_obj)) except ValueError: # I guess this is for C builtin functions? return ["..."] def safe_default_value(p: inspect.Parameter): if p.default is os.environ: class mock: def __repr__(self): return 'os.environ' return p.replace(default=mock()) return p params = [] kw_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.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) s = str(p) if p.annotation is not EMPTY: if sys.version_info < (3, 7): # PEP8-normalize whitespace s = re.sub(r'(?<!\s)=(?!\s)', ' = ', re.sub(r':(?!\s)', ': ', s, 1), 1) # "Eval" forward-declarations (typing string literals) s = re.sub(r'(?<=: )[\'"]|[\'"](?= = )', '', s, 2) s = s.replace(' ', '\xA0') # Neater line breaking with NBSPs if link: s = re.sub(r'[\w\.]+', _linkify, s) params.append(s) return params @property def refname(self): return (self.cls.refname if self.cls else self.module.refname) + '.' + self.nameAncestors
Instance variables
var cls-
The
Classdocumentation object if the function is a method. If not, this is None. var method-
Whether this function is a normal bound method.
In particular, static and class methods have this set to False.
Methods
def funcdef(self)-
Generates the string of keywords used to define the function, for example
deforasync def.Source code
def funcdef(self): """ 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' def params(self, *, annotate=False, link=None)-
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
annotateis True, the parameter strings include PEP 484 type hint annotations.Source code
def params(self, *, annotate: bool = False, link: 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.obj, annotate=annotate, link=link, module=self.module) def return_annotation(self, *, link=None)-
Formatted function return type annotation or empty string if none.
Source code
def return_annotation(self, *, link=None): """Formatted function return type annotation or empty string if none.""" try: annot = typing.get_type_hints(self.obj).get('return', '') except NameError as e: warn("Error handling return annotation for {}: {}", self.name, e.args[0]) annot = inspect.signature(inspect.unwrap(self.obj)).return_annotation if annot == inspect.Parameter.empty: annot = '' if not annot: return '' s = inspect.formatannotation(annot).replace(' ', '\xA0') # NBSP for better line breaks if link: from pdoc.html_helpers import _linkify s = re.sub(r'[\w\.]+', partial(_linkify, link=link, module=self.module), s) return s
Inherited members
class Module (module, *, docfilter=None, supermodule=None, context=None)-
Representation of a module's documentation.
Creates a
Moduledocumentation object given the actual module Python object.docfilteris an optional predicate that controls which sub-objects are documentated (see also:html()).supermoduleis the parentModulethis module is a submodule of.contextis an instance ofContext. IfNonea global context object will be used.Source code
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. """ __slots__ = ('supermodule', 'doc', '_context', '_is_inheritance_linked') def __init__(self, module: ModuleType, *, docfilter: Callable[[Doc], bool] = None, supermodule: 'Module' = None, context: Context = None): """ 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. """ 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()`. """ self.supermodule = supermodule """ The parent `pdoc.Module` this module is a submodule of, or `None`. """ self.doc = {} # type: Dict[str, Doc] """A mapping from identifier name to a documentation object.""" self._is_inheritance_linked = False """Re-entry guard for `pdoc.Module._link_inheritance()`.""" # Populate self.doc with this module's public members if hasattr(self.obj, '__all__'): public_objs = [] for name in self.obj.__all__: try: public_objs.append((name, getattr(self.obj, name))) except AttributeError: warn("Module {!r} doesn't contain identifier `{}` " "exported in `__all__`".format(self.module, name)) else: def is_from_this_module(obj): mod = inspect.getmodule(obj) return mod is None or mod.__name__ == self.obj.__name__ public_objs = [(name, inspect.unwrap(obj)) for name, obj in inspect.getmembers(self.obj) if (_is_public(name) and is_from_this_module(obj))] index = list(self.obj.__dict__).index public_objs.sort(key=lambda i: index(i[0])) for name, obj in public_objs: if inspect.isroutine(obj): self.doc[name] = Function(name, self, obj) elif inspect.isclass(obj): self.doc[name] = Class(name, self, obj) self.doc.update(_var_docstrings(self)) # 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, splitext for pth in paths: for file in os.listdir(pth): if file.startswith(('.', '__pycache__', '__init__.py')): continue if file.endswith(_SOURCE_SUFFIXES) or isdir(join(pth, file)): yield splitext(file)[0] 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): continue assert self.refname == self.name fullname = "%s.%s" % (self.name, root) m = import_module(fullname) self.doc[root] = Module( m, docfilter=docfilter, supermodule=self, context=self._context) # Apply docfilter if docfilter: for name, dobj in self.doc.copy().items(): if not docfilter(dobj): self.doc.pop(name) # 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()) 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 for name, docstring in getattr(self.obj, "__pdoc__", {}).items(): refname = "%s.%s" % (self.refname, name) if docstring in (False, None): if docstring is None: warn('Setting `__pdoc__[key] = None` is deprecated; ' 'use `__pdoc__[key] = False` ' '(key: {!r}, module: {!r}).'.format(name, self.name)) if (not name.endswith('.__init__') and name not in self.doc and refname not in self._context): warn('__pdoc__-overriden key {!r} does not exist ' 'in module {!r}'.format(name, self.name)) 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;' '__pdoc__[{!r}] is of type {}'.format(name, 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) return html @property def is_package(self): """ `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): """ `True` if this module is a namespace package. """ return self.obj.__spec__.origin in (None, 'namespace') # None in Py3.7+ def find_class(self, cls: type): """ 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(cls.__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(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): """ 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): """ 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): """ 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): """ 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_SUFFIXAncestors
Instance variables
var doc-
A mapping from identifier name to a documentation object.
var is_namespace-
Trueif this module is a namespace package.Source code
@property def is_namespace(self): """ `True` if this module is a namespace package. """ return self.obj.__spec__.origin in (None, 'namespace') # None in Py3.7+ var is_package-
Trueif this module is a package.Works by checking whether the module has a
__path__attribute.Source code
@property def is_package(self): """ `True` if this module is a package. Works by checking whether the module has a `__path__` attribute. """ return hasattr(self.obj, "__path__") var supermodule-
The parent
Modulethis module is a submodule of, orNone.
Methods
def classes(self, sort=True)-
Returns all documented module-level classes in the module, optionally sorted alphabetically, as a list of
Class.Source code
def classes(self, sort=True): """ 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 find_class(self, cls)-
Given a Python
clsobject, try to find it in this module or in any of the exported identifiers of the submodules.Source code
def find_class(self, cls: type): """ 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(cls.__module__ + '.' + cls.__qualname__) def find_ident(self, name)-
Searches this module and all other public modules for an identifier with name
namein its list of exported identifiers.The documentation object corresponding to the identifier is returned. If one cannot be found, then an instance of
Externalis returned populated with the given identifier.Source code
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(self.name + '.' + _name) or External(name)) def functions(self, sort=True)-
Returns all documented module-level functions in the module, optionally sorted alphabetically, as a list of
Function.Source code
def functions(self, sort=True): """ 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 html(self, minify=True, **kwargs)-
Returns the documentation for this module as self-contained HTML.
If
minifyisTrue, the resulting HTML is minified.For explanation of other arguments, see
html().kwargsis passed to themakorender function.Source code
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) return html def submodules(self)-
Returns all documented sub-modules of the module sorted alphabetically as a list of
Module.Source code
def submodules(self): """ Returns all documented sub-modules of the module sorted alphabetically as a list of `pdoc.Module`. """ return self._filter_doc_objs(Module) def text(self, **kwargs)-
Returns the documentation for this module as plain text.
Source code
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 variables(self, sort=True)-
Returns all documented module-level variables in the module, optionally sorted alphabetically, as a list of
Variable.Source code
def variables(self, sort=True): """ 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)
Inherited members
class Variable (name, module, docstring, *, obj=None, cls=None, instance_var=False)-
Representation of a variable's documentation. This includes module, class, and instance variables.
Same as
Doc, exceptclsshould be provided as aClassobject when this is a class or instance variable.Source code
class Variable(Doc): """ Representation of a variable's documentation. This includes module, class, and instance variables. """ __slots__ = ('cls', 'instance_var') def __init__(self, name, module, docstring, *, obj=None, cls: Class = None, instance_var=False): """ 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, this is None. """ self.instance_var = instance_var """ True if variable is some class' instance variable (as opposed to class variable). """ @property def qualname(self): if self.cls: return self.cls.qualname + '.' + self.name return self.name @property def refname(self): return (self.cls.refname if self.cls else self.module.refname) + '.' + self.nameAncestors
Instance variables
var cls-
The
Classobject if this is a class or instance variable. If not, this is None. var instance_var-
True if variable is some class' instance variable (as opposed to class variable).
Inherited members