Spaces:
Sleeping
Sleeping
| """Utility functions for working with docstrings.""" | |
| import typing as T | |
| from collections import ChainMap | |
| from inspect import Signature | |
| from itertools import chain | |
| from .common import ( | |
| DocstringMeta, | |
| DocstringParam, | |
| DocstringReturns, | |
| DocstringStyle, | |
| RenderingStyle, | |
| ) | |
| from .parser import compose, parse | |
| _Func = T.Callable[..., T.Any] | |
| assert DocstringReturns # used in docstring | |
| def combine_docstrings( | |
| *others: _Func, | |
| exclude: T.Iterable[T.Type[DocstringMeta]] = (), | |
| style: DocstringStyle = DocstringStyle.AUTO, | |
| rendering_style: RenderingStyle = RenderingStyle.COMPACT, | |
| ) -> _Func: | |
| """A function decorator that parses the docstrings from `others`, | |
| programmatically combines them with the parsed docstring of the decorated | |
| function, and replaces the docstring of the decorated function with the | |
| composed result. Only parameters that are part of the decorated functions | |
| signature are included in the combined docstring. When multiple sources for | |
| a parameter or docstring metadata exists then the decorator will first | |
| default to the wrapped function's value (when available) and otherwise use | |
| the rightmost definition from ``others``. | |
| The following example illustrates its usage: | |
| >>> def fun1(a, b, c, d): | |
| ... '''short_description: fun1 | |
| ... | |
| ... :param a: fun1 | |
| ... :param b: fun1 | |
| ... :return: fun1 | |
| ... ''' | |
| >>> def fun2(b, c, d, e): | |
| ... '''short_description: fun2 | |
| ... | |
| ... long_description: fun2 | |
| ... | |
| ... :param b: fun2 | |
| ... :param c: fun2 | |
| ... :param e: fun2 | |
| ... ''' | |
| >>> @combine_docstrings(fun1, fun2) | |
| >>> def decorated(a, b, c, d, e, f): | |
| ... ''' | |
| ... :param e: decorated | |
| ... :param f: decorated | |
| ... ''' | |
| >>> print(decorated.__doc__) | |
| short_description: fun2 | |
| <BLANKLINE> | |
| long_description: fun2 | |
| <BLANKLINE> | |
| :param a: fun1 | |
| :param b: fun1 | |
| :param c: fun2 | |
| :param e: fun2 | |
| :param f: decorated | |
| :returns: fun1 | |
| >>> @combine_docstrings(fun1, fun2, exclude=[DocstringReturns]) | |
| >>> def decorated(a, b, c, d, e, f): pass | |
| >>> print(decorated.__doc__) | |
| short_description: fun2 | |
| <BLANKLINE> | |
| long_description: fun2 | |
| <BLANKLINE> | |
| :param a: fun1 | |
| :param b: fun1 | |
| :param c: fun2 | |
| :param e: fun2 | |
| :param others: callables from which to parse docstrings. | |
| :param exclude: an iterable of ``DocstringMeta`` subclasses to exclude when | |
| combining docstrings. | |
| :param style: style composed docstring. The default will infer the style | |
| from the decorated function. | |
| :param rendering_style: The rendering style used to compose a docstring. | |
| :return: the decorated function with a modified docstring. | |
| """ | |
| def wrapper(func: _Func) -> _Func: | |
| sig = Signature.from_callable(func) | |
| comb_doc = parse(func.__doc__ or "") | |
| docs = [parse(other.__doc__ or "") for other in others] + [comb_doc] | |
| params = dict( | |
| ChainMap( | |
| *( | |
| {param.arg_name: param for param in doc.params} | |
| for doc in docs | |
| ) | |
| ) | |
| ) | |
| for doc in reversed(docs): | |
| if not doc.short_description: | |
| continue | |
| comb_doc.short_description = doc.short_description | |
| comb_doc.blank_after_short_description = ( | |
| doc.blank_after_short_description | |
| ) | |
| break | |
| for doc in reversed(docs): | |
| if not doc.long_description: | |
| continue | |
| comb_doc.long_description = doc.long_description | |
| comb_doc.blank_after_long_description = ( | |
| doc.blank_after_long_description | |
| ) | |
| break | |
| combined = {} | |
| for doc in docs: | |
| metas = {} | |
| for meta in doc.meta: | |
| meta_type = type(meta) | |
| if meta_type in exclude: | |
| continue | |
| metas.setdefault(meta_type, []).append(meta) | |
| for meta_type, meta in metas.items(): | |
| combined[meta_type] = meta | |
| combined[DocstringParam] = [ | |
| params[name] for name in sig.parameters if name in params | |
| ] | |
| comb_doc.meta = list(chain(*combined.values())) | |
| func.__doc__ = compose( | |
| comb_doc, style=style, rendering_style=rendering_style | |
| ) | |
| return func | |
| return wrapper | |