Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pdoc/__init__.py: 18%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Python package `pdoc` provides types, functions, and a command-line
3interface for accessing public documentation of Python modules, and
4for presenting it in a user-friendly, industry-standard open format.
5It is best suited for small- to medium-sized projects with tidy,
6hierarchical APIs.
8.. include:: ./documentation.md
9"""
10import ast
11import enum
12import importlib.machinery
13import importlib.util
14import inspect
15import os
16import os.path as path
17import re
18import sys
19import typing
20from contextlib import contextmanager
21from copy import copy
22from dataclasses import is_dataclass
23from functools import cached_property, lru_cache, reduce, partial, wraps
24from itertools import tee, groupby
25from types import FunctionType, ModuleType
26from typing import ( # noqa: F401
27 cast, Any, Callable, Dict, Generator, Iterable, List, Literal, Mapping, NewType,
28 Optional, Set, Tuple, Type, TypeVar, Union,
29)
30from unittest.mock import Mock
31from warnings import warn
33from mako.lookup import TemplateLookup
34from mako.exceptions import TopLevelLookupException
35from mako.template import Template
37try:
38 from pdoc._version import version as __version__ # noqa: F401
39except ImportError:
40 __version__ = '???' # Package not installed
43_get_type_hints = lru_cache()(typing.get_type_hints)
45_URL_MODULE_SUFFIX = '.html'
46_URL_INDEX_MODULE_SUFFIX = '.m.html' # For modules named literal 'index'
47_URL_PACKAGE_SUFFIX = '/index.html'
49# type.__module__ can be None by the Python spec. In those cases, use this value
50_UNKNOWN_MODULE = '?'
52T = TypeVar('T', 'Module', 'Class', 'Function', 'Variable')
54__pdoc__: Dict[str, Union[bool, str]] = {}
56tpl_lookup = TemplateLookup(
57 cache_args=dict(cached=True,
58 cache_type='memory'),
59 input_encoding='utf-8',
60 directories=[path.join(path.dirname(__file__), "templates")],
61)
62"""
63A `mako.lookup.TemplateLookup` object that knows how to load templates
64from the file system. You may add additional paths by modifying the
65object's `directories` attribute.
66"""
67if os.getenv("XDG_CONFIG_HOME"):
68 tpl_lookup.directories.insert(0, path.join(os.getenv("XDG_CONFIG_HOME", ''), "pdoc"))
71class Context(dict):
72 """
73 The context object that maps all documented identifiers
74 (`pdoc.Doc.refname`) to their respective `pdoc.Doc` objects.
76 You can pass an instance of `pdoc.Context` to `pdoc.Module` constructor.
77 All `pdoc.Module` objects that share the same `pdoc.Context` will see
78 (and be able to link in HTML to) each other's identifiers.
80 If you don't pass your own `Context` instance to `Module` constructor,
81 a global context object will be used.
82 """
83 __pdoc__['Context.__init__'] = False
85 def __init__(self, *args, **kwargs):
86 super().__init__(*args, **kwargs)
87 # A surrogate so that the check in Module._link_inheritance()
88 # "__pdoc__-overriden key {!r} does not exist" can see the object
89 # (and not warn).
90 self.blacklisted = getattr(args[0], 'blacklisted', set()) if args else set()
93_global_context = Context()
96def reset():
97 """Resets the global `pdoc.Context` to the initial (empty) state."""
98 global _global_context
99 _global_context.clear()
101 # Clear LRU caches
102 for func in (_get_type_hints,
103 _is_blacklisted,
104 _is_whitelisted):
105 func.cache_clear()
106 for cls in (Doc, Module, Class, Function, Variable, External):
107 for _, method in inspect.getmembers(cls):
108 if isinstance(method, property):
109 method = method.fget
110 if hasattr(method, 'cache_clear'):
111 method.cache_clear()
114def _get_config(**kwargs):
115 # Apply config.mako configuration
116 MAKO_INTERNALS = Template('').module.__dict__.keys()
117 DEFAULT_CONFIG = path.join(path.dirname(__file__), 'templates', 'config.mako')
118 config = {}
119 for config_module in (Template(filename=DEFAULT_CONFIG).module,
120 tpl_lookup.get_template('/config.mako').module):
121 config.update((var, getattr(config_module, var, None))
122 for var in config_module.__dict__
123 if var not in MAKO_INTERNALS)
125 known_keys = (set(config)
126 | {'docformat'} # Feature. https://github.com/pdoc3/pdoc/issues/169
127 # deprecated
128 | {'module', 'modules', 'http_server', 'external_links', 'search_query'})
129 invalid_keys = {k: v for k, v in kwargs.items() if k not in known_keys}
130 if invalid_keys:
131 warn(f'Unknown configuration variables (not in config.mako): {invalid_keys}')
132 config.update(kwargs)
134 if 'search_query' in config:
135 warn('Option `search_query` has been deprecated. Use `google_search_query` instead',
136 DeprecationWarning, stacklevel=2)
137 config['google_search_query'] = config['search_query']
138 del config['search_query']
140 return config
143def _render_template(template_name, **kwargs):
144 """
145 Returns the Mako template with the given name. If the template
146 cannot be found, a nicer error message is displayed.
147 """
148 config = _get_config(**kwargs)
150 try:
151 t = tpl_lookup.get_template(template_name)
152 except TopLevelLookupException:
153 paths = [path.join(p, template_name.lstrip('/')) for p in tpl_lookup.directories]
154 raise OSError(f"No template found at any of: {', '.join(paths)}")
156 try:
157 return t.render(**config).strip()
158 except Exception:
159 from mako import exceptions
160 print(exceptions.text_error_template().render(),
161 file=sys.stderr)
162 raise
165def html(module_name, docfilter=None, reload=False, skip_errors=False, **kwargs) -> str:
166 """
167 Returns the documentation for the module `module_name` in HTML
168 format. The module must be a module or an importable string.
170 `docfilter` is an optional predicate that controls which
171 documentation objects are shown in the output. It is a function
172 that takes a single argument (a documentation object) and returns
173 `True` or `False`. If `False`, that object will not be documented.
174 """
175 mod = Module(import_module(module_name, reload=reload, skip_errors=False),
176 docfilter=docfilter, skip_errors=skip_errors)
177 link_inheritance()
178 return mod.html(**kwargs)
181def text(module_name, docfilter=None, reload=False, skip_errors=False, **kwargs) -> str:
182 """
183 Returns the documentation for the module `module_name` in plain
184 text format suitable for viewing on a terminal.
185 The module must be a module or an importable string.
187 `docfilter` is an optional predicate that controls which
188 documentation objects are shown in the output. It is a function
189 that takes a single argument (a documentation object) and returns
190 `True` or `False`. If `False`, that object will not be documented.
191 """
192 mod = Module(import_module(module_name, reload=reload, skip_errors=False),
193 docfilter=docfilter, skip_errors=skip_errors)
194 link_inheritance()
195 return mod.text(**kwargs)
198def import_module(
199 module: Union[str, ModuleType],
200 *,
201 reload: bool = False,
202 skip_errors: bool = False,
203) -> ModuleType:
204 """
205 Return module object matching `module` specification (either a python
206 module path or a filesystem path to file/directory).
207 """
208 @contextmanager
209 def _module_path(module):
210 from os.path import abspath, dirname, isfile, isdir, split
211 path = '_pdoc_dummy_nonexistent'
212 module_name = inspect.getmodulename(module)
213 if isdir(module):
214 path, module = split(abspath(module))
215 elif isfile(module) and module_name:
216 path, module = dirname(abspath(module)), module_name
217 try:
218 sys.path.insert(0, path)
219 yield module
220 finally:
221 sys.path.remove(path)
223 if isinstance(module, Module):
224 module = module.obj
225 if isinstance(module, str):
226 with _module_path(module) as module_path:
227 try:
228 module = importlib.import_module(module_path)
229 except Exception as e:
230 msg = f'Error importing {module!r}: {e.__class__.__name__}: {e}'
231 if not skip_errors:
232 raise ImportError(msg)
233 warn(msg, category=Module.ImportWarning, stacklevel=2)
234 module = ModuleType(module_path)
236 assert inspect.ismodule(module)
237 # If this is pdoc itself, return without reloading. Otherwise later
238 # `isinstance(..., pdoc.Doc)` calls won't work correctly.
239 if reload and not module.__name__.startswith(__name__):
240 module = importlib.reload(module)
241 # We recursively reload all submodules, in case __all_ is used - cf. issue #264
242 for mod_key, mod in list(sys.modules.items()):
243 if mod_key.startswith(module.__name__):
244 importlib.reload(mod)
245 return module
248def _pairwise(iterable):
249 """s -> (s0,s1), (s1,s2), (s2, s3), ..."""
250 a, b = tee(iterable)
251 next(b, None)
252 return zip(a, b)
255def _pep224_docstrings(doc_obj: Union['Module', 'Class'], *,
256 _init_tree=None) -> Tuple[Dict[str, str],
257 Dict[str, str]]:
258 """
259 Extracts PEP-224 docstrings and doc-comments (`#: ...`) for variables of `doc_obj`
260 (either a `pdoc.Module` or `pdoc.Class`).
262 Returns a tuple of two dicts mapping variable names to their docstrings.
263 The second dict contains instance variables and is non-empty only in case
264 `doc_obj` is a `pdoc.Class` which has `__init__` method.
265 """
266 # No variables in namespace packages
267 if isinstance(doc_obj, Module) and doc_obj.is_namespace:
268 return {}, {}
270 vars: Dict[str, str] = {}
271 instance_vars: Dict[str, str] = {}
273 if _init_tree:
274 tree = _init_tree
275 else:
276 try:
277 # Maybe raise exceptions with appropriate message
278 # before using cleaned doc_obj.source
279 _ = inspect.findsource(doc_obj.obj)
280 tree = ast.parse(doc_obj.source)
281 except (OSError, TypeError, SyntaxError, UnicodeDecodeError) as exc:
282 # Don't emit a warning for builtins that don't have source available
283 is_builtin = getattr(doc_obj.obj, '__module__', None) == 'builtins'
284 if not is_builtin:
285 warn(f"Couldn't read PEP-224 variable docstrings from {doc_obj!r}: {exc}",
286 stacklevel=3 + int(isinstance(doc_obj, Class)))
287 return {}, {}
289 if isinstance(doc_obj, Class):
290 tree = tree.body[0] # ast.parse creates a dummy ast.Module wrapper
292 # For classes, maybe add instance variables defined in __init__
293 # Get the *last* __init__ node in case it is preceded by @overloads.
294 for node in reversed(tree.body): # type: ignore
295 if isinstance(node, ast.FunctionDef) and node.name == '__init__':
296 instance_vars, _ = _pep224_docstrings(doc_obj, _init_tree=node)
297 break
299 def get_name(assign_node):
300 if isinstance(assign_node, ast.Assign) and len(assign_node.targets) == 1:
301 target = assign_node.targets[0]
302 elif isinstance(assign_node, ast.AnnAssign):
303 target = assign_node.target
304 # Skip the annotation. PEP 526 says:
305 # > Putting the instance variable annotations together in the class
306 # > makes it easier to find them, and helps a first-time reader of the code.
307 else:
308 return None
310 if not _init_tree and isinstance(target, ast.Name):
311 name = target.id
312 elif (_init_tree and
313 isinstance(target, ast.Attribute) and
314 isinstance(target.value, ast.Name) and
315 target.value.id == 'self'):
316 name = target.attr
317 else:
318 return None
320 if not _is_public(name) and not _is_whitelisted(name, doc_obj):
321 return None
323 return name
325 # For handling PEP-224 docstrings for variables
326 for assign_node, str_node in _pairwise(ast.iter_child_nodes(tree)):
327 if not (isinstance(assign_node, (ast.Assign, ast.AnnAssign)) and
328 isinstance(str_node, ast.Expr) and
329 isinstance(str_node.value, ast.Constant)):
330 continue
332 name = get_name(assign_node)
333 if not name:
334 continue
336 docstring = inspect.cleandoc(cast(str, str_node.value.value)).strip()
337 if not docstring:
338 continue
340 vars[name] = docstring
342 # For handling '#:' docstrings for variables
343 for assign_node in ast.iter_child_nodes(tree):
344 if not isinstance(assign_node, (ast.Assign, ast.AnnAssign)):
345 continue
347 name = get_name(assign_node)
348 if not name:
349 continue
351 # Already documented. PEP-224 method above takes precedence.
352 if name in vars:
353 continue
355 def get_indent(line):
356 return len(line) - len(line.lstrip())
358 source_lines = doc_obj.source.splitlines()
359 assign_line = source_lines[assign_node.lineno - 1]
360 assign_indent = get_indent(assign_line)
361 comment_lines = []
362 MARKER = '#: '
363 for line in reversed(source_lines[:assign_node.lineno - 1]):
364 if get_indent(line) == assign_indent and line.lstrip().startswith(MARKER):
365 comment_lines.append(line.split(MARKER, maxsplit=1)[1])
366 else:
367 break
369 # Since we went 'up' need to reverse lines to be in correct order
370 comment_lines = comment_lines[::-1]
372 # Finally: check for a '#: ' comment at the end of the assignment line itself.
373 if MARKER in assign_line:
374 comment_lines.append(assign_line.rsplit(MARKER, maxsplit=1)[1])
376 if comment_lines:
377 vars[name] = '\n'.join(comment_lines)
379 return vars, instance_vars
382@lru_cache()
383def _is_whitelisted(name: str, doc_obj: Union['Module', 'Class']):
384 """
385 Returns `True` if `name` (relative or absolute refname) is
386 contained in some module's __pdoc__ with a truish value.
387 """
388 refname = f'{doc_obj.refname}.{name}'
389 module: Optional[Module] = doc_obj.module
390 while module:
391 qualname = refname[len(module.refname) + 1:]
392 if module.__pdoc__.get(qualname) or module.__pdoc__.get(refname):
393 return True
394 module = module.supermodule
395 return False
398@lru_cache()
399def _is_blacklisted(name: str, doc_obj: Union['Module', 'Class']):
400 """
401 Returns `True` if `name` (relative or absolute refname) is
402 contained in some module's __pdoc__ with value False.
403 """
404 refname = f'{doc_obj.refname}.{name}'
405 module: Optional[Module] = doc_obj.module
406 while module:
407 qualname = refname[len(module.refname) + 1:]
408 if module.__pdoc__.get(qualname) is False or module.__pdoc__.get(refname) is False:
409 return True
410 module = module.supermodule
411 return False
414def _is_public(ident_name):
415 """
416 Returns `True` if `ident_name` matches the export criteria for an
417 identifier name.
418 """
419 return not ident_name.startswith("_")
422def _is_function(obj):
423 return inspect.isroutine(obj) and callable(obj) and not isinstance(obj, Mock) # Mock: GH-350
426def _is_descriptor(obj):
427 return (inspect.isdatadescriptor(obj) or
428 inspect.ismethoddescriptor(obj) or
429 inspect.isgetsetdescriptor(obj) or
430 inspect.ismemberdescriptor(obj))
433def _unwrap_descriptor(dobj):
434 obj = dobj.obj
435 if isinstance(obj, property):
436 return (getattr(obj, 'fget', False) or
437 getattr(obj, 'fset', False) or
438 getattr(obj, 'fdel', obj))
439 if isinstance(obj, cached_property):
440 return obj.func
441 if isinstance(obj, FunctionType):
442 return obj
443 if (inspect.ismemberdescriptor(obj) or
444 getattr(getattr(obj, '__class__', 0), '__name__', 0) == '_tuplegetter'):
445 class_name = dobj.qualname.rsplit('.', 1)[0]
446 obj = getattr(dobj.module.obj, class_name)
447 return obj
448 # XXX: Follow descriptor protocol? Already proved buggy in conditions above
449 return getattr(obj, '__get__', obj)
452def _unwrap_object(obj: T) -> T:
453 """
454 This is a modified version of `inspect.unwrap()` that properly handles classes.
456 Follows the chains of `__wrapped__` attributes, until either:
457 1. `obj.__wrapped__` is missing or None
458 2. `obj` is a class and `obj.__wrapped__` has a different name or module
459 """
461 orig = obj # remember the original func for error reporting
462 # Memoise by id to tolerate non-hashable objects, but store objects to
463 # ensure they aren't destroyed, which would allow their IDs to be reused.
464 memo = {id(orig): orig}
465 recursion_limit = sys.getrecursionlimit()
466 while hasattr(obj, '__wrapped__'):
467 candidate = obj.__wrapped__
468 if candidate is None:
469 break
471 if isinstance(candidate, type) and isinstance(orig, type):
472 if not (candidate.__name__ == orig.__name__
473 and candidate.__module__ == orig.__module__):
474 break
476 obj = typing.cast(T, candidate)
477 id_func = id(obj)
478 if (id_func in memo) or (len(memo) >= recursion_limit):
479 raise ValueError('wrapper loop when unwrapping {!r}'.format(orig))
480 memo[id_func] = obj
482 return obj
485def _filter_type(type: Type[T],
486 values: Union[Iterable['Doc'], Mapping[str, 'Doc']]) -> List[T]:
487 """
488 Return a list of values from `values` of type `type`.
489 """
490 if isinstance(values, dict):
491 values = values.values()
492 return [i for i in values if isinstance(i, type)]
495def _toposort(graph: Mapping[T, Set[T]]) -> Generator[T, None, None]:
496 """
497 Return items of `graph` sorted in topological order.
498 Source: https://rosettacode.org/wiki/Topological_sort#Python
499 """
500 items_without_deps = reduce(set.union, graph.values(), set()) - set(graph.keys()) # type: ignore # noqa: E501
501 yield from items_without_deps
502 ordered = items_without_deps
503 while True:
504 graph = {item: (deps - ordered)
505 for item, deps in graph.items()
506 if item not in ordered}
507 ordered = {item
508 for item, deps in graph.items()
509 if not deps}
510 yield from ordered
511 if not ordered:
512 break
513 assert not graph, f"A cyclic dependency exists amongst {graph!r}"
516def link_inheritance(context: Optional[Context] = None):
517 """
518 Link inheritance relationsships between `pdoc.Class` objects
519 (and between their members) of all `pdoc.Module` objects that
520 share the provided `context` (`pdoc.Context`).
522 You need to call this if you expect `pdoc.Doc.inherits` and
523 inherited `pdoc.Doc.docstring` to be set correctly.
524 """
525 if context is None:
526 context = _global_context
528 graph = {cls: set(cls.mro(only_documented=True))
529 for cls in _filter_type(Class, context)}
531 for cls in _toposort(graph):
532 cls._fill_inheritance()
534 for module in _filter_type(Module, context):
535 module._link_inheritance()
538class Doc:
539 """
540 A base class for all documentation objects.
542 A documentation object corresponds to *something* in a Python module
543 that has a docstring associated with it. Typically, this includes
544 modules, classes, functions, and methods. However, `pdoc` adds support
545 for extracting some docstrings from abstract syntax trees, making
546 (module, class or instance) variables supported too.
548 A special type of documentation object `pdoc.External` is used to
549 represent identifiers that are not part of the public interface of
550 a module. (The name "External" is a bit of a misnomer, since it can
551 also correspond to unexported members of the module, particularly in
552 a class's ancestor list.)
553 """
555 def __init__(self, name: str, module, obj, docstring: str = ''):
556 """
557 Initializes a documentation object, where `name` is the public
558 identifier name, `module` is a `pdoc.Module` object where raw
559 Python object `obj` is defined, and `docstring` is its
560 documentation string. If `docstring` is left empty, it will be
561 read with `inspect.getdoc()`.
562 """
563 self.module = module
564 """
565 The module documentation object that this object is defined in.
566 """
568 self.name = name
569 """
570 The identifier name for this object.
571 """
573 self.obj = obj
574 """
575 The raw python object.
576 """
578 docstring = (docstring or inspect.getdoc(obj) or '').strip()
579 if '.. include::' in docstring:
580 from pdoc.html_helpers import _ToMarkdown
581 docstring = _ToMarkdown.admonitions(docstring, self.module, ('include',))
582 self.docstring = docstring
583 """
584 The cleaned docstring for this object with any `.. include::`
585 directives resolved (i.e. content included).
586 """
588 self.inherits: Optional[Union[Class, Function, Variable]] = None
589 """
590 The Doc object (Class, Function, or Variable) this object inherits from,
591 if any.
592 """
594 def __repr__(self):
595 return f'<{self.__class__.__name__} {self.refname!r}>'
597 @property
598 @lru_cache()
599 def source(self) -> str:
600 """
601 Cleaned (dedented) source code of the Python object. If not
602 available for whatever reason, an empty string.
603 """
604 try:
605 lines, _ = inspect.getsourcelines(_unwrap_descriptor(self))
606 except (ValueError, TypeError, OSError, AttributeError):
607 return ''
608 return inspect.cleandoc(''.join(['\n'] + lines))
610 @property
611 def refname(self) -> str:
612 """
613 Reference name of this documentation
614 object, usually its fully qualified path
615 (e.g. <code>pdoc.Doc.refname</code>). Every
616 documentation object provides this property.
617 """
618 # Ok for Module and External, the rest need it overriden
619 return self.name
621 @property
622 def qualname(self) -> str:
623 """
624 Module-relative "qualified" name of this documentation
625 object, used for show (e.g. <code>Doc.qualname</code>).
626 """
627 return getattr(self.obj, '__qualname__', self.name)
629 @lru_cache()
630 def url(self, relative_to: Optional['Module'] = None, *, link_prefix: str = '',
631 top_ancestor: bool = False) -> str:
632 """
633 Canonical relative URL (including page fragment) for this
634 documentation object.
636 Specify `relative_to` (a `pdoc.Module` object) to obtain a
637 relative URL.
639 For usage of `link_prefix` see `pdoc.html()`.
641 If `top_ancestor` is `True`, the returned URL instead points to
642 the top ancestor in the object's `pdoc.Doc.inherits` chain.
643 """
644 if top_ancestor:
645 self = self._inherits_top()
647 if relative_to is None or link_prefix:
648 return link_prefix + self._url()
650 if self.module.name == relative_to.name:
651 return f'#{self.refname}'
653 # Otherwise, compute relative path from current module to link target
654 url = os.path.relpath(self._url(), relative_to.url()).replace(path.sep, '/')
655 # We have one set of '..' too many
656 if url.startswith('../'):
657 url = url[3:]
658 return url
660 def _url(self):
661 return f'{self.module._url()}#{self.refname}'
663 def _inherits_top(self):
664 """
665 Follow the `pdoc.Doc.inherits` chain and return the top object.
666 """
667 top = self
668 while top.inherits:
669 top = top.inherits
670 return top
672 def __lt__(self, other):
673 return self.refname < other.refname
676class Module(Doc):
677 """
678 Representation of a module's documentation.
679 """
680 __pdoc__["Module.name"] = """
681 The name of this module with respect to the context/path in which
682 it was imported from. It is always an absolute import path.
683 """
685 def __init__(self, module: Union[ModuleType, str], *,
686 docfilter: Optional[Callable[[Doc], bool]] = None,
687 supermodule: Optional['Module'] = None,
688 context: Optional[Context] = None,
689 skip_errors: bool = False):
690 """
691 Creates a `Module` documentation object given the actual
692 module Python object.
694 `docfilter` is an optional predicate that controls which
695 sub-objects are documentated (see also: `pdoc.html()`).
697 `supermodule` is the parent `pdoc.Module` this module is
698 a submodule of.
700 `context` is an instance of `pdoc.Context`. If `None` a
701 global context object will be used.
703 If `skip_errors` is `True` and an unimportable, erroneous
704 submodule is encountered, a warning will be issued instead
705 of raising an exception.
706 """
707 if isinstance(module, str):
708 module = import_module(module, skip_errors=skip_errors)
710 super().__init__(module.__name__, self, module)
711 if self.name.endswith('.__init__') and not self.is_package:
712 self.name = self.name[:-len('.__init__')]
714 self._context = _global_context if context is None else context
715 """
716 A lookup table for ALL doc objects of all modules that share this context,
717 mainly used in `Module.find_ident()`.
718 """
719 assert isinstance(self._context, Context), \
720 'pdoc.Module(context=) should be a pdoc.Context instance'
722 self.supermodule = supermodule
723 """
724 The parent `pdoc.Module` this module is a submodule of, or `None`.
725 """
727 self.doc: Dict[str, Union[Module, Class, Function, Variable]] = {}
728 """A mapping from identifier name to a documentation object."""
730 self._is_inheritance_linked = False
731 """Re-entry guard for `pdoc.Module._link_inheritance()`."""
733 self._skipped_submodules = set()
735 var_docstrings, _ = _pep224_docstrings(self)
737 # Populate self.doc with this module's public members
738 public_objs = []
739 if hasattr(self.obj, '__all__'):
740 for name in self.obj.__all__:
741 try:
742 obj = getattr(self.obj, name)
743 except AttributeError:
744 warn(f"Module {self.module!r} doesn't contain identifier `{name}` "
745 "exported in `__all__`")
746 else:
747 if not _is_blacklisted(name, self):
748 obj = _unwrap_object(obj)
749 public_objs.append((name, obj))
750 else:
751 def is_from_this_module(obj):
752 mod = inspect.getmodule(_unwrap_object(obj))
753 return mod is None or mod.__name__ == self.obj.__name__
755 for name, obj in inspect.getmembers(self.obj):
756 if ((_is_public(name) or
757 _is_whitelisted(name, self)) and
758 (_is_blacklisted(name, self) or # skips unwrapping that follows
759 is_from_this_module(obj) or
760 name in var_docstrings)):
762 if _is_blacklisted(name, self):
763 self._context.blacklisted.add(f'{self.refname}.{name}')
764 continue
766 obj = _unwrap_object(obj)
767 public_objs.append((name, obj))
769 index = list(self.obj.__dict__).index
770 public_objs.sort(key=lambda i: index(i[0]))
772 for name, obj in public_objs:
773 if _is_function(obj):
774 self.doc[name] = Function(name, self, obj)
775 elif inspect.isclass(obj):
776 self.doc[name] = Class(name, self, obj)
777 elif name in var_docstrings:
778 self.doc[name] = Variable(name, self, var_docstrings[name], obj=obj)
780 # If the module is a package, scan the directory for submodules
781 if self.is_package:
783 def iter_modules(paths):
784 """
785 Custom implementation of `pkgutil.iter_modules()`
786 because that one doesn't play well with namespace packages.
787 See: https://github.com/pypa/setuptools/issues/83
788 """
789 from os.path import isdir, join
790 for pth in paths:
791 if pth.startswith("__editable__."):
792 # See https://github.com/pypa/pip/issues/11380
793 continue
794 for file in os.listdir(pth):
795 if file.startswith(('.', '__pycache__', '__init__.py')):
796 continue
797 module_name = inspect.getmodulename(file)
798 if module_name:
799 yield module_name
800 if isdir(join(pth, file)) and '.' not in file:
801 yield file
803 for root in iter_modules(self.obj.__path__):
804 # Ignore if this module was already doc'd.
805 if root in self.doc:
806 continue
808 # Ignore if it isn't exported
809 if not _is_public(root) and not _is_whitelisted(root, self):
810 continue
811 if _is_blacklisted(root, self):
812 self._skipped_submodules.add(root)
813 continue
815 assert self.refname == self.name
816 fullname = f"{self.name}.{root}"
817 m = Module(import_module(fullname, skip_errors=skip_errors),
818 docfilter=docfilter, supermodule=self,
819 context=self._context, skip_errors=skip_errors)
821 self.doc[root] = m
822 # Skip empty namespace packages because they may
823 # as well be other auxiliary directories
824 if m.is_namespace and not m.doc:
825 del self.doc[root]
826 self._context.pop(m.refname)
828 # Apply docfilter
829 if docfilter:
830 for name, dobj in self.doc.copy().items():
831 if not docfilter(dobj):
832 self.doc.pop(name)
833 self._context.pop(dobj.refname, None)
835 # Build the reference name dictionary of the module
836 self._context[self.refname] = self
837 for docobj in self.doc.values():
838 self._context[docobj.refname] = docobj
839 if isinstance(docobj, Class):
840 self._context.update((obj.refname, obj)
841 for obj in docobj.doc.values())
843 class ImportWarning(UserWarning):
844 """
845 Our custom import warning because the builtin is ignored by default.
846 https://docs.python.org/3/library/warnings.html#default-warning-filter
847 """
849 __pdoc__['Module.ImportWarning'] = False
851 @property
852 def __pdoc__(self) -> dict:
853 """This module's __pdoc__ dict, or an empty dict if none."""
854 return getattr(self.obj, '__pdoc__', {})
856 def _link_inheritance(self):
857 # Inherited members are already in place since
858 # `Class._fill_inheritance()` has been called from
859 # `pdoc.fill_inheritance()`.
860 # Now look for docstrings in the module's __pdoc__ override.
862 if self._is_inheritance_linked:
863 # Prevent re-linking inheritance for modules which have already
864 # had done so. Otherwise, this would raise "does not exist"
865 # errors if `pdoc.link_inheritance()` is called multiple times.
866 return
868 # Apply __pdoc__ overrides
869 for name, docstring in self.__pdoc__.items():
870 # In case of whitelisting with "True", there's nothing to do
871 if docstring is True:
872 continue
874 refname = f"{self.refname}.{name}"
875 if docstring in (False, None):
876 if docstring is None:
877 warn('Setting `__pdoc__[key] = None` is deprecated; '
878 'use `__pdoc__[key] = False` '
879 f'(key: {name!r}, module: {self.name!r}).')
881 if name in self._skipped_submodules:
882 continue
884 if (not name.endswith('.__init__') and
885 name not in self.doc and
886 refname not in self._context and
887 refname not in self._context.blacklisted):
888 warn(f'__pdoc__-overriden key {name!r} does not exist '
889 f'in module {self.name!r}')
891 obj = self.find_ident(name)
892 cls = getattr(obj, 'cls', None)
893 if cls:
894 del cls.doc[obj.name]
895 self.doc.pop(name, None)
896 self._context.pop(refname, None)
898 # Pop also all that startwith refname
899 for key in list(self._context.keys()):
900 if key.startswith(refname + '.'):
901 del self._context[key]
903 continue
905 dobj = self.find_ident(refname)
906 if isinstance(dobj, External):
907 continue
908 if not isinstance(docstring, str):
909 raise ValueError('__pdoc__ dict values must be strings; '
910 f'__pdoc__[{name!r}] is of type {type(docstring)}')
911 dobj.docstring = inspect.cleandoc(docstring)
913 # Now after docstrings are set correctly, continue the
914 # inheritance routine, marking members inherited or not
915 for c in _filter_type(Class, self.doc):
916 c._link_inheritance()
918 self._is_inheritance_linked = True
920 def text(self, **kwargs) -> str:
921 """
922 Returns the documentation for this module as plain text.
923 """
924 txt = _render_template('/text.mako', module=self, **kwargs)
925 txt = re.sub("\n\n\n+", "\n\n", txt)
926 if not txt.endswith('\n'):
927 txt += '\n'
928 return txt
930 def html(self, minify=True, **kwargs) -> str:
931 """
932 Returns the documentation for this module as
933 self-contained HTML.
935 If `minify` is `True`, the resulting HTML is minified.
937 For explanation of other arguments, see `pdoc.html()`.
939 `kwargs` is passed to the `mako` render function.
940 """
941 html = _render_template('/html.mako', module=self, **kwargs)
942 if minify:
943 from pdoc.html_helpers import minify_html
944 html = minify_html(html)
945 if not html.endswith('\n'):
946 html = html + '\n'
947 return html
949 @property
950 def is_package(self) -> bool:
951 """
952 `True` if this module is a package.
954 Works by checking whether the module has a `__path__` attribute.
955 """
956 return hasattr(self.obj, "__path__")
958 @property
959 def is_namespace(self) -> bool:
960 """
961 `True` if this module is a namespace package.
962 """
963 try:
964 return self.obj.__spec__.origin in (None, 'namespace') # None in Py3.7+
965 except AttributeError:
966 return False
968 def find_class(self, cls: type) -> Doc:
969 """
970 Given a Python `cls` object, try to find it in this module
971 or in any of the exported identifiers of the submodules.
972 """
973 # XXX: Is this corrent? Does it always match
974 # `Class.module.name + Class.qualname`?. Especially now?
975 # If not, see what was here before.
976 return self.find_ident(f'{cls.__module__ or _UNKNOWN_MODULE}.{cls.__qualname__}')
978 def find_ident(self, name: str) -> Doc:
979 """
980 Searches this module and **all** other public modules
981 for an identifier with name `name` in its list of
982 exported identifiers.
984 The documentation object corresponding to the identifier is
985 returned. If one cannot be found, then an instance of
986 `External` is returned populated with the given identifier.
987 """
988 _name = name.rstrip('()') # Function specified with parentheses
990 if _name.endswith('.__init__'): # Ref to class' init is ref to class itself
991 _name = _name[:-len('.__init__')]
993 return (self.doc.get(_name) or
994 self._context.get(_name) or
995 self._context.get(f'{self.name}.{_name}') or
996 External(name))
998 def _filter_doc_objs(self, type: Type[T], sort=True) -> List[T]:
999 result = _filter_type(type, self.doc)
1000 return sorted(result) if sort else result
1002 def variables(self, sort=True) -> List['Variable']:
1003 """
1004 Returns all documented module-level variables in the module,
1005 optionally sorted alphabetically, as a list of `pdoc.Variable`.
1006 """
1007 return self._filter_doc_objs(Variable, sort)
1009 def classes(self, sort=True) -> List['Class']:
1010 """
1011 Returns all documented module-level classes in the module,
1012 optionally sorted alphabetically, as a list of `pdoc.Class`.
1013 """
1014 return self._filter_doc_objs(Class, sort)
1016 def functions(self, sort=True) -> List['Function']:
1017 """
1018 Returns all documented module-level functions in the module,
1019 optionally sorted alphabetically, as a list of `pdoc.Function`.
1020 """
1021 return self._filter_doc_objs(Function, sort)
1023 def submodules(self) -> List['Module']:
1024 """
1025 Returns all documented sub-modules of the module sorted
1026 alphabetically as a list of `pdoc.Module`.
1027 """
1028 return self._filter_doc_objs(Module)
1030 def _url(self):
1031 url = self.module.name.replace('.', '/')
1032 if self.is_package:
1033 return url + _URL_PACKAGE_SUFFIX
1034 elif url.endswith('/index'):
1035 return url + _URL_INDEX_MODULE_SUFFIX
1036 return url + _URL_MODULE_SUFFIX
1039def _getmembers_all(obj: type) -> List[Tuple[str, Any]]:
1040 # The following code based on inspect.getmembers() @ 5b23f7618d43
1041 mro = obj.__mro__[:-1] # Skip object
1042 names = set(dir(obj))
1043 # Add keys from bases
1044 for base in mro:
1045 names.update(base.__dict__.keys())
1046 # Add members for which type annotations exist
1047 names.update(getattr(obj, '__annotations__', {}).keys())
1049 results = []
1050 for name in names:
1051 try:
1052 value = getattr(obj, name)
1053 except AttributeError:
1054 for base in mro:
1055 if name in base.__dict__:
1056 value = base.__dict__[name]
1057 break
1058 else:
1059 # Missing slot member or a buggy __dir__;
1060 # In out case likely a type-annotated member
1061 # which we'll interpret as a variable
1062 value = None
1063 results.append((name, value))
1064 return results
1067class Class(Doc):
1068 """
1069 Representation of a class' documentation.
1070 """
1071 def __init__(self, name: str, module: Module, obj, *, docstring: Optional[str] = None):
1072 assert inspect.isclass(obj)
1074 if docstring is None:
1075 init_doc = inspect.getdoc(obj.__init__) or ''
1076 if init_doc == object.__init__.__doc__:
1077 init_doc = ''
1078 docstring = f'{inspect.getdoc(obj) or ""}\n\n{init_doc}'.strip()
1080 super().__init__(name, module, obj, docstring=docstring)
1082 self.doc: Dict[str, Union[Function, Variable]] = {}
1083 """A mapping from identifier name to a `pdoc.Doc` objects."""
1085 # Annotations for filtering.
1086 # Use only own, non-inherited annotations (the rest will be inherited)
1087 annotations = getattr(self.obj, '__annotations__', {})
1089 public_objs = []
1090 for _name, obj in _getmembers_all(self.obj):
1091 # Filter only *own* members. The rest are inherited
1092 # in Class._fill_inheritance()
1093 if ((_name in self.obj.__dict__ or
1094 _name in annotations) and
1095 (_is_public(_name) or
1096 _is_whitelisted(_name, self))):
1098 if _is_blacklisted(_name, self):
1099 self.module._context.blacklisted.add(f'{self.refname}.{_name}')
1100 continue
1102 obj = _unwrap_object(obj)
1103 public_objs.append((_name, obj))
1105 def definition_order_index(
1106 name,
1107 _annot_index=list(annotations).index,
1108 _dict_index=list(self.obj.__dict__).index):
1109 try:
1110 return _dict_index(name)
1111 except ValueError:
1112 pass
1113 try:
1114 return _annot_index(name) - len(annotations) # sort annotated first
1115 except ValueError:
1116 return 9e9
1118 public_objs.sort(key=lambda i: definition_order_index(i[0]))
1120 var_docstrings, instance_var_docstrings = _pep224_docstrings(self)
1122 # Convert the public Python objects to documentation objects.
1123 for name, obj in public_objs:
1124 if _is_function(obj):
1125 self.doc[name] = Function(
1126 name, self.module, obj, cls=self)
1127 else:
1128 self.doc[name] = Variable(
1129 name, self.module,
1130 docstring=(
1131 var_docstrings.get(name) or
1132 (inspect.isclass(obj) or _is_descriptor(obj)) and inspect.getdoc(obj)),
1133 cls=self,
1134 kind="prop" if isinstance(obj, property) else "var",
1135 obj=_is_descriptor(obj) and obj or None,
1136 instance_var=(_is_descriptor(obj) or
1137 name in getattr(self.obj, '__slots__', ()) or
1138 (is_dataclass(self.obj) and name in annotations)))
1140 for name, docstring in instance_var_docstrings.items():
1141 self.doc[name] = Variable(
1142 name, self.module, docstring, cls=self,
1143 obj=getattr(self.obj, name, None),
1144 instance_var=True)
1146 @staticmethod
1147 def _method_type(cls: type, name: str):
1148 """
1149 Returns `None` if the method `name` of class `cls`
1150 is a regular method. Otherwise, it returns
1151 `classmethod` or `staticmethod`, as appropriate.
1152 """
1153 func = getattr(cls, name, None)
1154 if inspect.ismethod(func):
1155 # If the function is already bound, it's a classmethod.
1156 # Regular methods are not bound before initialization.
1157 return classmethod
1158 for c in inspect.getmro(cls):
1159 if name in c.__dict__:
1160 if isinstance(c.__dict__[name], staticmethod):
1161 return staticmethod
1162 return None
1163 raise RuntimeError(f"{cls}.{name} not found")
1165 @property
1166 def refname(self) -> str:
1167 return f'{self.module.name}.{self.qualname}'
1169 def mro(self, only_documented=False) -> List['Class']:
1170 """
1171 Returns a list of ancestor (superclass) documentation objects
1172 in method resolution order.
1174 The list will contain objects of type `pdoc.Class`
1175 if the types are documented, and `pdoc.External` otherwise.
1176 """
1177 classes = [cast(Class, self.module.find_class(c))
1178 for c in inspect.getmro(self.obj)
1179 if c not in (self.obj, object)]
1180 if self in classes:
1181 # This can contain self in case of a class inheriting from
1182 # a class with (previously) the same name. E.g.
1183 #
1184 # class Loc(namedtuple('Loc', 'lat lon')): ...
1185 #
1186 # We remove it from ancestors so that toposort doesn't break.
1187 classes.remove(self)
1188 if only_documented:
1189 classes = _filter_type(Class, classes)
1190 return classes
1192 def subclasses(self) -> List['Class']:
1193 """
1194 Returns a list of subclasses of this class that are visible to the
1195 Python interpreter (obtained from `type.__subclasses__()`).
1197 The objects in the list are of type `pdoc.Class` if available,
1198 and `pdoc.External` otherwise.
1199 """
1200 return sorted(cast(Class, self.module.find_class(c))
1201 for c in type.__subclasses__(self.obj))
1203 def params(self, *, annotate=False, link=None) -> List[str]:
1204 """
1205 Return a list of formatted parameters accepted by the
1206 class constructor (method `__init__`). See `pdoc.Function.params`.
1207 """
1208 name = self.name + '.__init__'
1209 qualname = self.qualname + '.__init__'
1210 refname = self.refname + '.__init__'
1211 exclusions = self.module.__pdoc__
1212 if name in exclusions or qualname in exclusions or refname in exclusions:
1213 return []
1215 return Function._params(self, annotate=annotate, link=link, module=self.module)
1217 def _filter_doc_objs(self, type: Type[T], include_inherited=True,
1218 filter_func: Callable[[T], bool] = lambda x: True,
1219 sort=True) -> List[T]:
1220 result = [obj for obj in _filter_type(type, self.doc)
1221 if (include_inherited or not obj.inherits) and filter_func(obj)]
1222 return sorted(result) if sort else result
1224 def class_variables(self, include_inherited=True, sort=True) -> List['Variable']:
1225 """
1226 Returns an optionally-sorted list of `pdoc.Variable` objects that
1227 represent this class' class variables.
1228 """
1229 return self._filter_doc_objs(
1230 Variable, include_inherited, lambda dobj: not dobj.instance_var,
1231 sort)
1233 def instance_variables(self, include_inherited=True, sort=True) -> List['Variable']:
1234 """
1235 Returns an optionally-sorted list of `pdoc.Variable` objects that
1236 represent this class' instance variables. Instance variables
1237 are those defined in a class's `__init__` as `self.variable = ...`.
1238 """
1239 return self._filter_doc_objs(
1240 Variable, include_inherited, lambda dobj: dobj.instance_var,
1241 sort)
1243 def methods(self, include_inherited=True, sort=True) -> List['Function']:
1244 """
1245 Returns an optionally-sorted list of `pdoc.Function` objects that
1246 represent this class' methods.
1247 """
1248 return self._filter_doc_objs(
1249 Function, include_inherited, lambda dobj: dobj.is_method,
1250 sort)
1252 def functions(self, include_inherited=True, sort=True) -> List['Function']:
1253 """
1254 Returns an optionally-sorted list of `pdoc.Function` objects that
1255 represent this class' static functions.
1256 """
1257 return self._filter_doc_objs(
1258 Function, include_inherited, lambda dobj: not dobj.is_method,
1259 sort)
1261 def inherited_members(self) -> List[Tuple['Class', List[Doc]]]:
1262 """
1263 Returns all inherited members as a list of tuples
1264 (ancestor class, list of ancestor class' members sorted by name),
1265 sorted by MRO.
1266 """
1267 return sorted(((cast(Class, k), sorted(g))
1268 for k, g in groupby((i.inherits
1269 for i in self.doc.values() if i.inherits),
1270 key=lambda i: i.cls)), # type: ignore
1271 key=lambda x, _mro_index=self.mro().index: _mro_index(x[0])) # type: ignore
1273 def _fill_inheritance(self):
1274 """
1275 Traverses this class's ancestor list and attempts to fill in
1276 missing documentation objects from its ancestors.
1278 Afterwards, call to `pdoc.Class._link_inheritance()` to also
1279 set `pdoc.Doc.inherits` pointers.
1280 """
1281 super_members = self._super_members = {}
1282 for cls in self.mro(only_documented=True):
1283 for name, dobj in cls.doc.items():
1284 if name not in super_members and dobj.docstring:
1285 super_members[name] = dobj
1286 if name not in self.doc:
1287 dobj = copy(dobj)
1288 dobj.cls = self
1290 self.doc[name] = dobj
1291 self.module._context[dobj.refname] = dobj
1293 def _link_inheritance(self):
1294 """
1295 Set `pdoc.Doc.inherits` pointers to inherited ancestors' members,
1296 as appropriate. This must be called after
1297 `pdoc.Class._fill_inheritance()`.
1299 The reason this is split in two parts is that in-between
1300 the `__pdoc__` overrides are applied.
1301 """
1302 if not hasattr(self, '_super_members'):
1303 return
1305 for name, parent_dobj in self._super_members.items():
1306 try:
1307 dobj = self.doc[name]
1308 except KeyError:
1309 # There is a key in some __pdoc__ dict blocking this member
1310 continue
1311 if (dobj.obj is parent_dobj.obj or
1312 (dobj.docstring or parent_dobj.docstring) == parent_dobj.docstring):
1313 dobj.inherits = parent_dobj
1314 dobj.docstring = parent_dobj.docstring
1315 del self._super_members
1318def maybe_lru_cache(func):
1319 cached_func = lru_cache()(func)
1321 @wraps(func)
1322 def wrapper(*args):
1323 try:
1324 return cached_func(*args)
1325 except TypeError:
1326 return func(*args)
1328 return wrapper
1331@maybe_lru_cache
1332def _formatannotation(annot):
1333 """
1334 Format typing annotation with better handling of `typing.NewType`,
1335 `typing.Optional`, `nptyping.NDArray` and other types.
1337 >>> _formatannotation(NewType('MyType', str))
1338 'pdoc.MyType'
1339 >>> _formatannotation(Optional[Tuple[Optional[int], None]])
1340 'Tuple[int | None, None] | None'
1341 >>> _formatannotation(Optional[Union[int, float, None]])
1342 'int | float | None'
1343 >>> _formatannotation(Union[int, float])
1344 'int | float'
1345 >>> from typing import Callable
1346 >>> _formatannotation(Callable[[Optional[int]], float])
1347 'Callable[[int | None], float]'
1348 >>> from collections.abc import Callable
1349 >>> _formatannotation(Callable[[Optional[int]], float])
1350 'Callable[[int | None], float]'
1351 """
1352 class force_repr(str):
1353 __repr__ = str.__str__
1355 def maybe_replace_reprs(a):
1356 # NoneType -> None
1357 if a is type(None): # noqa: E721
1358 return force_repr('None')
1359 # Union[T, None] -> Optional[T]
1360 if getattr(a, '__origin__', None) is typing.Union:
1361 union_args = a.__args__
1362 is_optional = type(None) in union_args
1363 if is_optional:
1364 union_args = (x for x in union_args if x is not type(None))
1365 t = ' | '.join(inspect.formatannotation(maybe_replace_reprs(x))
1366 for x in union_args)
1367 if is_optional:
1368 t += ' | None'
1369 return force_repr(t)
1370 # typing.NewType('T', foo) -> T
1371 module = getattr(a, '__module__', '')
1372 if module == 'typing' and getattr(a, '__qualname__', '').startswith('NewType.'):
1373 return force_repr(a.__name__)
1374 # nptyping.types._ndarray.NDArray -> NDArray[(Any,), Int[64]] # GH-231
1375 if module.startswith('nptyping.'):
1376 return force_repr(repr(a))
1377 # Recurse into typing.Callable/etc. args
1378 if hasattr(a, '__args__'):
1379 if hasattr(a, 'copy_with'):
1380 if a is typing.Callable:
1381 # Bug on Python < 3.9, https://bugs.python.org/issue42195
1382 return a
1383 a = a.copy_with(tuple([maybe_replace_reprs(arg) for arg in a.__args__]))
1384 elif hasattr(a, '__origin__'):
1385 args = tuple(map(maybe_replace_reprs, a.__args__))
1386 try:
1387 a = a.__origin__[args]
1388 except TypeError:
1389 # XXX: Python 3.10-only: Convert to list since _CallableGenericAlias.__new__
1390 # currently cannot have tuples as arguments.
1391 args_in = list(args[:-1])
1392 arg_out = args[-1]
1393 # collections.abc.Callable takes "([in], out)"
1394 a = a.__origin__[(args_in, arg_out)]
1395 # Recurse into lists
1396 if isinstance(a, (list, tuple)):
1397 return type(a)(map(maybe_replace_reprs, a))
1398 # Shorten standard collections: collections.abc.Callable -> Callable
1399 if module == 'collections.abc':
1400 return force_repr(repr(a).removeprefix('collections.abc.'))
1401 return a
1403 return str(inspect.formatannotation(maybe_replace_reprs(annot)))
1406class Function(Doc):
1407 """
1408 Representation of documentation for a function or method.
1409 """
1410 def __init__(self, name: str, module: Module, obj, *, cls: Optional[Class] = None):
1411 """
1412 Same as `pdoc.Doc`, except `obj` must be a
1413 Python function object. The docstring is gathered automatically.
1415 `cls` should be set when this is a method or a static function
1416 beloing to a class. `cls` should be a `pdoc.Class` object.
1418 `method` should be `True` when the function is a method. In
1419 all other cases, it should be `False`.
1420 """
1421 assert callable(obj), (name, module, obj)
1422 super().__init__(name, module, obj)
1424 self.cls = cls
1425 """
1426 The `pdoc.Class` documentation object if the function is a method.
1427 If not, this is None.
1428 """
1430 @property
1431 def is_method(self) -> bool:
1432 """
1433 Whether this function is a normal bound method.
1435 In particular, static and class methods have this set to False.
1436 """
1437 assert self.cls
1438 return not Class._method_type(self.cls.obj, self.name)
1440 @property
1441 def method(self):
1442 warn('`Function.method` is deprecated. Use: `Function.is_method`', DeprecationWarning,
1443 stacklevel=2)
1444 return self.is_method
1446 __pdoc__['Function.method'] = False
1448 def funcdef(self) -> str:
1449 """
1450 Generates the string of keywords used to define the function,
1451 for example `def` or `async def`.
1452 """
1453 return 'async def' if self._is_async else 'def'
1455 @property
1456 def _is_async(self):
1457 """
1458 Returns whether is function is asynchronous, either as a coroutine or an async
1459 generator.
1460 """
1461 try:
1462 # Both of these are required because coroutines aren't classified as async
1463 # generators and vice versa.
1464 obj = _unwrap_object(self.obj)
1465 return (inspect.iscoroutinefunction(obj) or
1466 inspect.isasyncgenfunction(obj))
1467 except AttributeError:
1468 return False
1470 def return_annotation(self, *, link=None) -> str:
1471 """Formatted function return type annotation or empty string if none."""
1472 annot = ''
1473 for method in (
1474 lambda: _get_type_hints(self.obj)['return'],
1475 # Mainly for non-property variables
1476 lambda: _get_type_hints(cast(Class, self.cls).obj)[self.name],
1477 # global variables
1478 lambda: _get_type_hints(not self.cls and self.module.obj)[self.name],
1479 # properties
1480 lambda: inspect.signature(_unwrap_descriptor(self)).return_annotation,
1481 # Use raw annotation strings in unmatched forward declarations
1482 lambda: cast(Class, self.cls).obj.__annotations__[self.name],
1483 # Extract annotation from the docstring for C builtin function
1484 lambda: Function._signature_from_string(self).return_annotation,
1485 ):
1486 try:
1487 annot = method()
1488 except Exception:
1489 continue
1490 else:
1491 break
1492 else:
1493 # Don't warn on variables. The annotation just isn't available.
1494 if not isinstance(self, Variable):
1495 warn(f"Error handling return annotation for {self!r}", stacklevel=3)
1497 if annot is inspect.Parameter.empty or not annot:
1498 return ''
1500 if isinstance(annot, str):
1501 s = annot
1502 else:
1503 s = _formatannotation(annot)
1504 s = re.sub(r'\bForwardRef\((?P<quot>[\"\'])(?P<str>.*?)(?P=quot)\)',
1505 r'\g<str>', s)
1506 s = s.replace(' ', '\N{NBSP}') # Better line breaks in html signatures
1508 if link:
1509 from pdoc.html_helpers import _linkify
1510 s = re.sub(r'[\w\.]+', partial(_linkify, link=link, module=self.module), s)
1511 return s
1513 def params(self, *, annotate: bool = False,
1514 link: Optional[Callable[[Doc], str]] = None) -> List[str]:
1515 """
1516 Returns a list where each element is a nicely formatted
1517 parameter of this function. This includes argument lists,
1518 keyword arguments and default values, and it doesn't include any
1519 optional arguments whose names begin with an underscore.
1521 If `annotate` is True, the parameter strings include [PEP 484]
1522 type hint annotations.
1524 [PEP 484]: https://www.python.org/dev/peps/pep-0484/
1525 """
1526 return self._params(self, annotate=annotate, link=link, module=self.module)
1528 @staticmethod
1529 def _params(doc_obj, annotate=False, link=None, module=None):
1530 try:
1531 # We want __init__ to actually be implemented somewhere in the
1532 # MRO to still satisfy https://github.com/pdoc3/pdoc/issues/124
1533 if (
1534 inspect.isclass(doc_obj.obj)
1535 and doc_obj.obj.__init__ is not object.__init__
1536 ):
1537 # Remove the first argument (self) from __init__ signature
1538 init_sig = inspect.signature(doc_obj.obj.__init__)
1539 init_params = list(init_sig.parameters.values())
1540 signature = init_sig.replace(parameters=init_params[1:])
1541 else:
1542 signature = inspect.signature(doc_obj.obj)
1543 except ValueError:
1544 signature = Function._signature_from_string(doc_obj)
1545 if not signature:
1546 return ['...']
1548 def safe_default_value(p: inspect.Parameter):
1549 value = p.default
1550 if value is inspect.Parameter.empty:
1551 return p
1553 replacement = next((i for i in ('os.environ',
1554 'sys.stdin',
1555 'sys.stdout',
1556 'sys.stderr',)
1557 if value is eval(i)), None)
1558 if not replacement:
1559 if isinstance(value, enum.Enum):
1560 replacement = str(value)
1561 elif inspect.isclass(value):
1562 replacement = f'{value.__module__ or _UNKNOWN_MODULE}.{value.__qualname__}'
1563 elif ' at 0x' in repr(value):
1564 replacement = re.sub(r' at 0x\w+', '', repr(value))
1566 nonlocal link
1567 if link and ('<' in repr(value) or '>' in repr(value)):
1568 import html
1569 replacement = html.escape(replacement or repr(value))
1571 if replacement:
1572 class mock:
1573 def __repr__(self):
1574 return replacement
1575 return p.replace(default=mock())
1576 return p
1578 params = []
1579 kw_only = False
1580 pos_only = False
1581 EMPTY = inspect.Parameter.empty
1583 if link:
1584 from pdoc.html_helpers import _linkify
1585 _linkify = partial(_linkify, link=link, module=module)
1587 for p in signature.parameters.values(): # type: inspect.Parameter
1588 if not _is_public(p.name) and p.default is not EMPTY:
1589 continue
1591 if p.kind == p.POSITIONAL_ONLY:
1592 pos_only = True
1593 elif pos_only:
1594 params.append("/")
1595 pos_only = False
1597 if p.kind == p.VAR_POSITIONAL:
1598 kw_only = True
1599 if p.kind == p.KEYWORD_ONLY and not kw_only:
1600 kw_only = True
1601 params.append('*')
1603 p = safe_default_value(p)
1605 if not annotate:
1606 p = p.replace(annotation=EMPTY)
1608 formatted = p.name
1609 if p.annotation is not EMPTY:
1610 annotation = _formatannotation(p.annotation).replace(' ', '\N{NBSP}')
1611 # "Eval" forward-declarations (typing string literals)
1612 if isinstance(p.annotation, str):
1613 annotation = annotation.strip("'")
1614 if link:
1615 annotation = re.sub(r'[\w\.]+', _linkify, annotation)
1616 formatted += f':\N{NBSP}{annotation}'
1617 if p.default is not EMPTY:
1618 if p.annotation is not EMPTY:
1619 formatted += f'\N{NBSP}=\N{NBSP}{repr(p.default)}'
1620 else:
1621 formatted += f'={repr(p.default)}'
1622 if p.kind == p.VAR_POSITIONAL:
1623 formatted = f'*{formatted}'
1624 elif p.kind == p.VAR_KEYWORD:
1625 formatted = f'**{formatted}'
1627 params.append(formatted)
1629 if pos_only:
1630 params.append("/")
1632 return params
1634 @staticmethod
1635 @lru_cache()
1636 def _signature_from_string(self):
1637 signature = None
1638 for expr, cleanup_docstring, filter in (
1639 # Full proper typed signature, such as one from pybind11
1640 (r'^{}\(.*\)(?: -> .*)?$', True, lambda s: s),
1641 # Human-readable, usage-like signature from some Python builtins
1642 # (e.g. `range` or `slice` or `itertools.repeat` or `numpy.arange`)
1643 (r'^{}\(.*\)(?= -|$)', False, lambda s: s.replace('[', '').replace(']', '')),
1644 ):
1645 strings = sorted(re.findall(expr.format(self.name),
1646 self.docstring, re.MULTILINE),
1647 key=len, reverse=True)
1648 if strings:
1649 string = filter(strings[0])
1650 _locals, _globals = {}, {}
1651 _globals.update({'capsule': None}) # pybind11 capsule data type
1652 _globals.update(typing.__dict__)
1653 _globals.update(self.module.obj.__dict__)
1654 # Trim binding module basename from type annotations
1655 # See: https://github.com/pdoc3/pdoc/pull/148#discussion_r407114141
1656 module_basename = self.module.name.rsplit('.', maxsplit=1)[-1]
1657 if module_basename in string and module_basename not in _globals:
1658 string = re.sub(fr'(?<!\.)\b{module_basename}\.\b', '', string)
1660 try:
1661 exec(f'def {string}: pass', _globals, _locals)
1662 except Exception:
1663 continue
1664 signature = inspect.signature(_locals[self.name])
1665 if cleanup_docstring and len(strings) == 1:
1666 # Remove signature from docstring variable
1667 self.docstring = self.docstring.replace(strings[0], '')
1668 break
1669 return signature
1671 @property
1672 def refname(self) -> str:
1673 return f'{self.cls.refname if self.cls else self.module.refname}.{self.name}'
1676class Variable(Doc):
1677 """
1678 Representation of a variable's documentation. This includes
1679 module, class, and instance variables.
1680 """
1681 def __init__(self, name: str, module: Module, docstring, *,
1682 obj=None, cls: Optional[Class] = None, instance_var: bool = False,
1683 kind: Literal["prop", "var"] = 'var'):
1684 """
1685 Same as `pdoc.Doc`, except `cls` should be provided
1686 as a `pdoc.Class` object when this is a class or instance
1687 variable.
1688 """
1689 super().__init__(name, module, obj, docstring)
1691 self.cls = cls
1692 """
1693 The `pdoc.Class` object if this is a class or instance
1694 variable. If not (i.e. it is a global variable), this is None.
1695 """
1697 self.instance_var = instance_var
1698 """
1699 True if variable is some class' instance variable (as
1700 opposed to class variable).
1701 """
1703 self.kind = kind
1704 """
1705 `prop` if variable is a dynamic property (has getter/setter or deleter),
1706 or `var` otherwise.
1707 """
1709 @property
1710 def qualname(self) -> str:
1711 if self.cls:
1712 return f'{self.cls.qualname}.{self.name}'
1713 return self.name
1715 @property
1716 def refname(self) -> str:
1717 return f'{self.cls.refname if self.cls else self.module.refname}.{self.name}'
1719 def type_annotation(self, *, link=None) -> str:
1720 """Formatted variable type annotation or empty string if none."""
1721 return Function.return_annotation(cast(Function, self), link=link)
1724class External(Doc):
1725 """
1726 A representation of an external identifier. The textual
1727 representation is the same as an internal identifier.
1729 External identifiers are also used to represent something that is
1730 not documented but appears somewhere in the public interface (like
1731 the ancestor list of a class).
1732 """
1734 __pdoc__["External.docstring"] = """
1735 An empty string. External identifiers do not have
1736 docstrings.
1737 """
1738 __pdoc__["External.module"] = """
1739 Always `None`. External identifiers have no associated
1740 `pdoc.Module`.
1741 """
1742 __pdoc__["External.name"] = """
1743 Always equivalent to `pdoc.External.refname` since external
1744 identifiers are always expressed in their fully qualified
1745 form.
1746 """
1748 def __init__(self, name: str):
1749 """
1750 Initializes an external identifier with `name`, where `name`
1751 should be a fully qualified name.
1752 """
1753 super().__init__(name, None, None)
1755 def url(self, *args, **kwargs):
1756 """
1757 `External` objects return absolute urls matching `/{name}.ext`.
1758 """
1759 return f'/{self.name}.ext'