-
Notifications
You must be signed in to change notification settings - Fork 244
Expand file tree
/
Copy pathoverload_docstrings.py
More file actions
179 lines (166 loc) · 6.7 KB
/
overload_docstrings.py
File metadata and controls
179 lines (166 loc) · 6.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import re
def _get_overload_params_docstring(file_str, tag, class_method=True, indent=' ' * 4):
method_start_regex = f'# overload_inject_start_{tag}'
params_start_regex = f':param|EXAMPLE USAGE|.. code-block::'
docstring_end_regex = f'# overload_inject_end_{tag}|.. # noqa:|:return:'
# match begin of the method to the end of the docstring (='stub')
stub_match = re.search(
rf'({method_start_regex}).*?({docstring_end_regex})', file_str, flags=re.DOTALL
)
stub_str = file_str[stub_match.span()[0] : stub_match.span()[1]]
# match only the params section in the docstring of the stub
params_match = re.search(
rf'({params_start_regex}).*?({docstring_end_regex})', stub_str, flags=re.DOTALL
)
params_str = stub_str[params_match.span()[0] : params_match.span()[1]]
#### cleanup ####
params_str = re.sub('"""', '', params_str, 0, re.DOTALL) # delete """
params_str = re.sub(
rf'{docstring_end_regex}', '', params_str, 1, re.DOTALL
) # delete end regex
params_str = re.sub(
'\n+$', '', params_str, 1, re.DOTALL
) # delete trailing white space
params_str = re.sub(
'^\n+', '', params_str, 1, re.DOTALL
) # delete leading white space
params_str = re.sub('\s+$', '', params_str, 1, re.DOTALL) # delete trailing newline
params_str = re.sub('^\s+', '', params_str, 1, re.DOTALL) # delete leading newline
# add indent back in
params_str = (
f'{indent}{indent}{params_str}' if class_method else f'{indent}{params_str}'
)
return params_str
def _get_docstring_title(file_str, tag):
# extracts the description ('title') of a docstring, i.e. the initial part that has no :param:, :return: etc.
title_start_regex = f'# implementation_stub_inject_start_{tag}'
title_end_regex = f':param|EXAMPLE USAGE|.. code-block::|:return:|# implementation_stub_inject_end_{tag}|.. # noqa:'
doc_str_title_match = re.search(
rf'({title_start_regex}).*?({title_end_regex})', file_str, flags=re.DOTALL
)
doc_str_title = file_str[
doc_str_title_match.span()[0] : doc_str_title_match.span()[1]
]
# trim of start and end patterns
doc_str_title = re.sub('"""', '', doc_str_title, 0, re.DOTALL) # delete """
doc_str_title = re.sub(
rf'{title_start_regex}', '', doc_str_title, 1, re.DOTALL
) # delete start regex
doc_str_title = re.sub(
rf'{title_end_regex}', '', doc_str_title, 1, re.DOTALL
) # delete end regex
doc_str_title = re.sub(
'\n+$', '', doc_str_title, 1, re.DOTALL
) # delete trailing white space
doc_str_title = re.sub(
'^\n+', '', doc_str_title, 1, re.DOTALL
) # delete leading white space
doc_str_title = re.sub(
'\s+$', '', doc_str_title, 1, re.DOTALL
) # delete trailing newline
doc_str_title = re.sub(
'^\s+', '', doc_str_title, 1, re.DOTALL
) # delete leading newline
return doc_str_title
def fill_implementation_stub(
doc_str_return,
return_type,
filepath,
overload_fn,
class_method,
indent=' ' * 4,
overload_tags=[], # from which methods should we gather the docstrings?
regex_tag=None,
additional_params=[], # :param: lines that do not come from the override methods, but from the implementation stub itself
):
# collects all :param: descriptions from overload methods and adds them to the method stub that has the actual implementation
file_str = open(filepath).read()
overload_fn = overload_fn.lower()
relevant_docstrings = [
_get_overload_params_docstring(file_str, t, class_method=class_method)
for t in overload_tags
]
add_param_indent = f'{indent}{indent}' if class_method else f'{indent}'
relevant_docstrings += [add_param_indent + p for p in additional_params]
if class_method:
doc_str = ''
for i, s in enumerate(relevant_docstrings):
if i != 0:
doc_str += '\n'
doc_str += s
noqa_str = '\n'.join(
f'{indent}{indent}.. # noqa: DAR{j}' for j in ['102', '202', '101', '003']
)
if return_type:
return_str = f'\n{indent}{indent}:return: {doc_str_return}'
else:
return_str = ''
else:
doc_str = ''
for i, s in enumerate(relevant_docstrings):
if i != 0:
doc_str += '\n'
doc_str += s
noqa_str = '\n'.join(
f'{indent}.. # noqa: DAR{j}' for j in ['102', '202', '101', '003']
)
if return_type:
return_str = f'\n{indent}:return: {doc_str_return}'
else:
return_str = ''
if class_method:
doc_str_title = _get_docstring_title(file_str, regex_tag or overload_fn)
final_str = f'\n{indent}{indent}"""{doc_str_title}\n\n{doc_str}{return_str}\n\n{noqa_str}\n{indent}{indent}"""'
final_code = re.sub(
rf'(# implementation_stub_inject_start_{regex_tag or overload_fn}).*(# implementation_stub_inject_end_{regex_tag or overload_fn}\s)',
f'\\1\n{indent}{final_str}\n{indent}\\2',
file_str,
0,
re.DOTALL,
)
else:
doc_str_title = _get_docstring_title(file_str, regex_tag or overload_fn)
final_str = f'\n{indent}"""{doc_str_title}\n\n{doc_str}{return_str}\n\n{noqa_str}\n{indent}"""'
final_code = re.sub(
rf'(# implementation_stub_inject_start_{regex_tag or overload_fn}).*(# implementation_stub_inject_end_{regex_tag or overload_fn}\s)',
f'\\1\n{final_str}\n{indent}\\2',
file_str,
0,
re.DOTALL,
)
with open(filepath, 'w') as fp:
fp.write(final_code)
# param
entries = [
dict(
doc_str_return='itself after modification',
return_type="'T'",
filepath='../docarray/array/mixins/parallel.py',
overload_fn='apply_batch',
class_method=True, # if it is a method inside class.
overload_tags=['apply_batch'],
),
dict(
doc_str_return='itself after modification',
return_type="'T'",
filepath='../docarray/array/mixins/parallel.py',
overload_fn='apply',
class_method=True, # if it is a method inside class.
overload_tags=['apply'],
),
dict(
doc_str_return='itself after modification',
return_type="'T'",
filepath='../docarray/document/mixins/sugar.py',
overload_fn='match',
class_method=True, # if it is a method inside class.
overload_tags=['match'],
),
]
if __name__ == '__main__':
all_changed_files = set()
for d in entries:
fill_implementation_stub(**d)
all_changed_files.add(d['filepath'])
for f in all_changed_files:
print(f)