Skip to content

Commit 3014085

Browse files
committed
add new docstring functions
1 parent 101cc55 commit 3014085

File tree

2 files changed

+150
-0
lines changed

2 files changed

+150
-0
lines changed

fooof/core/modutils.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,38 @@ def wrapped_func(*args, **kwargs):
7777
return wrap
7878

7979

80+
DOCSTRING_SECTIONS = ['Parameters', 'Returns', 'Yields', 'Raises',
81+
'Warns', 'Examples', 'References', 'Notes',
82+
'Attributes', 'Methods']
83+
84+
85+
def get_docs_indices(docstring, sections=DOCSTRING_SECTIONS):
86+
"""Get the indices of each section within a docstring.
87+
88+
Parameters
89+
----------
90+
docstring : str
91+
Docstring to check indices for.
92+
sections : list of str, optional
93+
List of sections to check and get indices for.
94+
If not provided, uses the default set of
95+
96+
Returns
97+
-------
98+
inds : dict
99+
Dictionary in which each key is a section label, and each value is the corresponding index.
100+
"""
101+
102+
inds = {label : None for label in DOCSTRING_SECTIONS}
103+
104+
for ind, line in enumerate(docstring.split('\n')):
105+
for key, val in inds.items():
106+
if key in line:
107+
inds[key] = ind
108+
109+
return inds
110+
111+
80112
def docs_drop_param(docstring):
81113
"""Drop the first parameter description for a string representation of a docstring.
82114
@@ -132,6 +164,91 @@ def docs_append_to_section(docstring, section, add):
132164
for split in docstring.split('\n\n')])
133165

134166

167+
def docs_get_section(docstring, section, output='extract'):
168+
"""Extract and/or remove a specified section from a docstring.
169+
170+
Parameters
171+
----------
172+
docstring : str
173+
Docstring to extract / remove a section from.
174+
section : str
175+
Label of the section to extract / remove.
176+
mode : {'extract', 'remove'}
177+
Run mode, options:
178+
'extract' - returns the extracted section from the docstring.
179+
'remove' - returns the docstring after removing the specified section.
180+
181+
Returns
182+
-------
183+
out_docstring : str
184+
Extracted / updated docstring.
185+
"""
186+
187+
outs = []
188+
in_section = False
189+
190+
docstring_split = docstring.split('\n')
191+
for ind, line in enumerate(docstring_split):
192+
193+
# Track whether in the desired section
194+
if section in line and '--' in docstring_split[ind + 1]:
195+
in_section = True
196+
if in_section and line == '':
197+
in_section = False
198+
199+
# Collect desired outputs based on whether extracting or removing section
200+
if output == 'extract' and in_section:
201+
outs.append(line)
202+
if output == 'remove' and not in_section:
203+
outs.append(line)
204+
205+
# As a special case, when removing section, end section marker if there is a '%' line
206+
if in_section and output == 'remove' and not line.isspace() and line.strip()[0] == '%':
207+
in_section = False
208+
209+
out_docstring = '\n'.join(outs)
210+
211+
return out_docstring
212+
213+
214+
def docs_add_section(docstring, section):
215+
"""Add a section to a specified index of a docstring.
216+
217+
Parameters
218+
----------
219+
docstring : str
220+
Docstring to add section to.
221+
section : str
222+
New section to add to docstring.
223+
224+
Returns
225+
-------
226+
out_docstring : str
227+
Updated docstring, with the new section added.
228+
"""
229+
230+
inds = get_docs_indices(docstring)
231+
232+
# Split the section, extract the label, and check it's a known docstring section
233+
split_section = section.split('\n')
234+
section_label = split_section[0].strip()
235+
assert section_label in inds, 'Section label does not match expected list.'
236+
237+
# Remove the header section from the docstring (to replace it)
238+
docstring = docs_get_section(docstring, section_label, 'remove')
239+
240+
# Check for and drop leading and trailing empty lines
241+
split_section = split_section[1:] if split_section[0] == '' else split_section
242+
split_section = split_section[:-1] if split_section[-1] == ' ' else split_section
243+
244+
# Insert the new section into the docstring and rejoin it together
245+
split_docstring = docstring.split('\n')
246+
split_docstring[inds[section_label]:inds[section_label]] = split_section
247+
new_docstring = '\n'.join(split_docstring)
248+
249+
return new_docstring
250+
251+
135252
def copy_doc_func_to_method(source):
136253
"""Decorator that copies method docstring from function, dropping first parameter.
137254

fooof/tests/core/test_modutils.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,36 @@ def test_docs_append_to_section(tdocstring):
5252

5353
assert 'third' in new_ds
5454
assert 'Added description' in new_ds
55+
56+
def test_get_docs_indices(tdocstring):
57+
58+
inds = get_docs_indices(tdocstring)
59+
60+
for el in DOCSTRING_SECTIONS:
61+
assert el in inds.keys()
62+
63+
assert inds['Parameters'] == 2
64+
assert inds['Returns'] == 9
65+
66+
def test_docs_get_section(tdocstring):
67+
68+
out1 = docs_get_section(tdocstring, 'Parameters', output='extract')
69+
assert 'Parameters' in out1
70+
assert 'Returns' not in out1
71+
72+
out2 = docs_get_section(tdocstring, 'Parameters', output='remove')
73+
assert 'Parameters' not in out2
74+
assert 'Returns' in out2
75+
76+
def test_docs_add_section(tdocstring):
77+
78+
tdocstring = tdocstring + \
79+
"""\nNotes\n-----\n % copied in at runtime"""
80+
81+
new_section = \
82+
"""Notes\n-----\n \nThis is a new note."""
83+
new_docstring = docs_add_section(tdocstring, new_section)
84+
85+
assert 'Notes' in new_docstring
86+
assert '%' not in new_docstring
87+
assert 'new note' in new_docstring

0 commit comments

Comments
 (0)