1111libsemigroups_pybind11.
1212"""
1313
14+ import re as _re
1415
15- def copydoc (original , * args ):
16+
17+ def _get_overloaded_doc (func ):
18+ """Return the docstring of a function as if that function was overloaded"""
19+ doc = func .__doc__
20+ if "Overloaded function." in doc :
21+ return doc
22+ return "1. " + doc
23+
24+
25+ def _correct_overloads (target , base_func , extra_funcs ):
26+ """
27+ Fix the docstring of copied overloaded functions
28+
29+ When functions are bound, pybind11 adds some decorations to the docstring
30+ (unless this is explicitly turned off using
31+ py::options::disable_function_signatures()). When a function is overloaded,
32+ this decoration includes the string 'Overloaded function.', a new signature
33+ with parameters *args and **kwargs, and some enumeration of the different
34+ overloads.
35+
36+ To combine multiple docstrings of functions, it is necessary to:
37+ * remove all but the first occurrence of the pybind11 inserted strings;
38+ * ensure every function is documented as if it was overloaded; and
39+ * number the overloads 1, 2, ..., N.
40+
41+ This function does these things.
42+ """
43+ target_name = target .__name__
44+ old_names = set (fn .__name__ for fn in extra_funcs ) | {base_func .__name__ }
45+ base_doc = _get_overloaded_doc (base_func )
46+ extra_doc = "" .join ([_get_overloaded_doc (func ) for func in extra_funcs ])
47+
48+ # Remove pybind11 inserted strings
49+ replacements = {
50+ f"{ old_name } (*args, **kwargs)\n " : "" for old_name in old_names
51+ }
52+ replacements ["Overloaded function.\n " ] = ""
53+ for old , new in replacements .items ():
54+ extra_doc = extra_doc .replace (old , new )
55+
56+ # Fix overload numbering
57+ overload_counter = 1
58+ doc_blocks = _re .split (
59+ rf"\d+\. (?:{ '|' .join (old_names )} )" , base_doc + extra_doc
60+ )
61+ new_doc = doc_blocks [0 ]
62+ for doc_block in doc_blocks [1 :]:
63+ new_doc += f"{ overload_counter } . { target_name } " + doc_block
64+ overload_counter += 1
65+ return new_doc
66+
67+
68+ def copydoc (func , * extra_funcs ):
1669 """
1770 Decorator that can be used to copy the doc from one function to another,
1871 for example:
@@ -21,17 +74,13 @@ def copydoc(original, *args):
2174 def __init___(self) -> None:
2275 pass
2376 """
24-
25- original_doc = original .__doc__
26- patterns = {"Overloaded function." : "" , "__init__(*args, **kwargs)" : "" }
27- for arg in args :
28- doc = arg .__doc__
29- for old , new in patterns .items ():
30- doc = doc .replace (old , new )
31- original_doc += doc
77+ new_doc = func .__doc__
3278
3379 def wrapper (target ):
34- target .__doc__ = original_doc
80+ if extra_funcs :
81+ target .__doc__ = _correct_overloads (target , func , extra_funcs )
82+ else :
83+ target .__doc__ = new_doc
3584 return target
3685
3786 return wrapper
0 commit comments