Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
singlestore-labs
GitHub Repository: singlestore-labs/singlestoredb-python
Path: blob/main/singlestoredb/docstring/util.py
469 views
1
"""Utility functions for working with docstrings."""
2
import typing as T
3
from collections import ChainMap
4
from inspect import Signature
5
from itertools import chain
6
7
from .common import DocstringMeta
8
from .common import DocstringParam
9
from .common import DocstringReturns # noqa: F401
10
from .common import DocstringStyle
11
from .common import RenderingStyle
12
from .parser import compose
13
from .parser import parse
14
15
_Func = T.Callable[..., T.Any]
16
17
18
def combine_docstrings(
19
*others: _Func,
20
exclude: T.Iterable[T.Type[DocstringMeta]] = (),
21
style: DocstringStyle = DocstringStyle.AUTO,
22
rendering_style: RenderingStyle = RenderingStyle.COMPACT,
23
) -> _Func:
24
"""A function decorator that parses the docstrings from `others`,
25
programmatically combines them with the parsed docstring of the decorated
26
function, and replaces the docstring of the decorated function with the
27
composed result. Only parameters that are part of the decorated functions
28
signature are included in the combined docstring. When multiple sources for
29
a parameter or docstring metadata exists then the decorator will first
30
default to the wrapped function's value (when available) and otherwise use
31
the rightmost definition from ``others``.
32
33
The following example illustrates its usage:
34
35
>>> def fun1(a, b, c, d):
36
... '''short_description: fun1
37
...
38
... :param a: fun1
39
... :param b: fun1
40
... :return: fun1
41
... '''
42
>>> def fun2(b, c, d, e):
43
... '''short_description: fun2
44
...
45
... long_description: fun2
46
...
47
... :param b: fun2
48
... :param c: fun2
49
... :param e: fun2
50
... '''
51
>>> @combine_docstrings(fun1, fun2)
52
>>> def decorated(a, b, c, d, e, f):
53
... '''
54
... :param e: decorated
55
... :param f: decorated
56
... '''
57
>>> print(decorated.__doc__)
58
short_description: fun2
59
<BLANKLINE>
60
long_description: fun2
61
<BLANKLINE>
62
:param a: fun1
63
:param b: fun1
64
:param c: fun2
65
:param e: fun2
66
:param f: decorated
67
:returns: fun1
68
>>> @combine_docstrings(fun1, fun2, exclude=[DocstringReturns])
69
>>> def decorated(a, b, c, d, e, f): pass
70
>>> print(decorated.__doc__)
71
short_description: fun2
72
<BLANKLINE>
73
long_description: fun2
74
<BLANKLINE>
75
:param a: fun1
76
:param b: fun1
77
:param c: fun2
78
:param e: fun2
79
80
:param others: callables from which to parse docstrings.
81
:param exclude: an iterable of ``DocstringMeta`` subclasses to exclude when
82
combining docstrings.
83
:param style: style composed docstring. The default will infer the style
84
from the decorated function.
85
:param rendering_style: The rendering style used to compose a docstring.
86
:return: the decorated function with a modified docstring.
87
"""
88
89
def wrapper(func: _Func) -> _Func:
90
sig = Signature.from_callable(func)
91
92
comb_doc = parse(func.__doc__ or '')
93
docs = [parse(other.__doc__ or '') for other in others] + [comb_doc]
94
params = dict(
95
ChainMap(
96
*(
97
{param.arg_name: param for param in doc.params}
98
for doc in docs
99
),
100
),
101
)
102
103
for doc in reversed(docs):
104
if not doc.short_description:
105
continue
106
comb_doc.short_description = doc.short_description
107
comb_doc.blank_after_short_description = (
108
doc.blank_after_short_description
109
)
110
break
111
112
for doc in reversed(docs):
113
if not doc.long_description:
114
continue
115
comb_doc.long_description = doc.long_description
116
comb_doc.blank_after_long_description = (
117
doc.blank_after_long_description
118
)
119
break
120
121
combined: T.Dict[T.Type[DocstringMeta], T.List[DocstringMeta]] = {}
122
for doc in docs:
123
metas: T.Dict[T.Type[DocstringMeta], T.List[DocstringMeta]] = {}
124
for meta in doc.meta:
125
meta_type = type(meta)
126
if meta_type in exclude:
127
continue
128
metas.setdefault(meta_type, []).append(meta)
129
for meta_type, meta_list in metas.items():
130
combined[meta_type] = meta_list
131
132
combined[DocstringParam] = [
133
params[name] for name in sig.parameters if name in params
134
]
135
comb_doc.meta = list(chain(*combined.values()))
136
func.__doc__ = compose(
137
comb_doc, style=style, rendering_style=rendering_style,
138
)
139
return func
140
141
return wrapper
142
143