Skip to content

Commit 850232c

Browse files
committed
refactor location code, add to warning about unknown section
1 parent 057ef57 commit 850232c

File tree

2 files changed

+79
-17
lines changed

2 files changed

+79
-17
lines changed

numpydoc/docscrape.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def __getitem__(self, key):
136136

137137
def __setitem__(self, key, val):
138138
if key not in self._parsed_data:
139-
warn("Unknown section %s" % key)
139+
self._error_location("Unknown section %s" % key, error=False)
140140
else:
141141
self._parsed_data[key] = val
142142

@@ -331,19 +331,8 @@ def _parse(self):
331331
section = (s.capitalize() for s in section.split(' '))
332332
section = ' '.join(section)
333333
if self.get(section):
334-
if hasattr(self, '_obj'):
335-
# we know where the docs came from:
336-
try:
337-
filename = inspect.getsourcefile(self._obj)
338-
except TypeError:
339-
filename = None
340-
msg = ("The section %s appears twice in "
341-
"the docstring of %s in %s." %
342-
(section, self._obj, filename))
343-
raise ValueError(msg)
344-
else:
345-
msg = ("The section %s appears twice" % section)
346-
raise ValueError(msg)
334+
self._error_location("The section %s appears twice"
335+
% section)
347336

348337
if section in ('Parameters', 'Returns', 'Yields', 'Raises',
349338
'Warns', 'Other Parameters', 'Attributes',
@@ -356,6 +345,20 @@ def _parse(self):
356345
else:
357346
self[section] = content
358347

348+
def _error_location(self, msg, error=True):
349+
if hasattr(self, '_obj'):
350+
# we know where the docs came from:
351+
try:
352+
filename = inspect.getsourcefile(self._obj)
353+
except TypeError:
354+
filename = None
355+
msg = msg + (" in the docstring of %s in %s."
356+
% (self._obj, filename))
357+
if error:
358+
raise ValueError(msg)
359+
else:
360+
warn(msg)
361+
359362
# string conversion routines
360363

361364
def _str_header(self, name, symbol='-'):

numpydoc/tests/test_docscrape.py

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import sys
55
import textwrap
6+
import warnings
67

78
import jinja2
89

@@ -151,13 +152,16 @@ def test_signature():
151152
assert doc['Signature'].startswith('numpy.multivariate_normal(')
152153
assert doc['Signature'].endswith('spam=None)')
153154

155+
154156
def test_summary():
155157
assert doc['Summary'][0].startswith('Draw values')
156158
assert doc['Summary'][-1].endswith('covariance.')
157159

160+
158161
def test_extended_summary():
159162
assert doc['Extended Summary'][0].startswith('The multivariate normal')
160163

164+
161165
def test_parameters():
162166
assert_equal(len(doc['Parameters']), 3)
163167
assert_equal([n for n,_,_ in doc['Parameters']], ['mean','cov','shape'])
@@ -167,13 +171,15 @@ def test_parameters():
167171
assert desc[0].startswith('Covariance matrix')
168172
assert doc['Parameters'][0][-1][-2] == ' (1+2+3)/3'
169173

174+
170175
def test_other_parameters():
171176
assert_equal(len(doc['Other Parameters']), 1)
172177
assert_equal([n for n,_,_ in doc['Other Parameters']], ['spam'])
173178
arg, arg_type, desc = doc['Other Parameters'][0]
174179
assert_equal(arg_type, 'parrot')
175180
assert desc[0].startswith('A parrot off its mortal coil')
176181

182+
177183
def test_returns():
178184
assert_equal(len(doc['Returns']), 2)
179185
arg, arg_type, desc = doc['Returns'][0]
@@ -188,6 +194,7 @@ def test_returns():
188194
assert desc[0].startswith('This is not a real')
189195
assert desc[-1].endswith('anonymous return values.')
190196

197+
191198
def test_yields():
192199
section = doc_yields['Yields']
193200
assert_equal(len(section), 3)
@@ -200,6 +207,7 @@ def test_yields():
200207
assert desc[0].startswith('The number of')
201208
assert desc[0].endswith(end)
202209

210+
203211
def test_returnyield():
204212
doc_text = """
205213
Test having returns and yields.
@@ -289,26 +297,31 @@ def test_notes():
289297
assert doc['Notes'][-1].endswith('definite.')
290298
assert_equal(len(doc['Notes']), 17)
291299

300+
292301
def test_references():
293302
assert doc['References'][0].startswith('..')
294303
assert doc['References'][-1].endswith('2001.')
295304

305+
296306
def test_examples():
297307
assert doc['Examples'][0].startswith('>>>')
298308
assert doc['Examples'][-1].endswith('True]')
299309

310+
300311
def test_index():
301312
assert_equal(doc['index']['default'], 'random')
302313
assert_equal(len(doc['index']), 2)
303314
assert_equal(len(doc['index']['refguide']), 2)
304315

305-
def non_blank_line_by_line_compare(a,b):
316+
317+
def non_blank_line_by_line_compare(a, b):
306318
a = textwrap.dedent(a)
307319
b = textwrap.dedent(b)
308320
a = [l.rstrip() for l in a.split('\n') if l.strip()]
309321
b = [l.rstrip() for l in b.split('\n') if l.strip()]
310322
assert_list_equal(a, b)
311323

324+
312325
def test_str():
313326
# doc_txt has the order of Notes and See Also sections flipped.
314327
# This should be handled automatically, and so, one thing this test does
@@ -595,15 +608,18 @@ def test_sphinx_yields_str():
595608
If None, the index is into the flattened array, otherwise along
596609
the specified axis""")
597610

611+
598612
def test_parameters_without_extended_description():
599613
assert_equal(len(doc2['Parameters']), 2)
600614

615+
601616
doc3 = NumpyDocString("""
602617
my_signature(*params, **kwds)
603618
604619
Return this and that.
605620
""")
606621

622+
607623
def test_escape_stars():
608624
signature = str(doc3).split('\n')[0]
609625
assert_equal(signature, 'my_signature(\*params, \*\*kwds)')
@@ -614,14 +630,17 @@ def my_func(a, b, **kwargs):
614630
fdoc = FunctionDoc(func=my_func)
615631
assert_equal(fdoc['Signature'], 'my_func(a, b, \*\*kwargs)')
616632

633+
617634
doc4 = NumpyDocString(
618635
"""a.conj()
619636
620637
Return an array with all complex-valued elements conjugated.""")
621638

639+
622640
def test_empty_extended_summary():
623641
assert_equal(doc4['Extended Summary'], [])
624642

643+
625644
doc5 = NumpyDocString(
626645
"""
627646
a.something()
@@ -637,18 +656,21 @@ def test_empty_extended_summary():
637656
If needed
638657
""")
639658

659+
640660
def test_raises():
641661
assert_equal(len(doc5['Raises']), 1)
642662
name,_,desc = doc5['Raises'][0]
643663
assert_equal(name,'LinAlgException')
644664
assert_equal(desc,['If array is singular.'])
645665

666+
646667
def test_warns():
647668
assert_equal(len(doc5['Warns']), 1)
648669
name,_,desc = doc5['Warns'][0]
649670
assert_equal(name,'SomeWarning')
650671
assert_equal(desc,['If needed'])
651672

673+
652674
def test_see_also():
653675
doc6 = NumpyDocString(
654676
"""
@@ -726,12 +748,45 @@ class Dummy(object):
726748
assert(' some relationship' in s)
727749
assert(':func:`func_d`' in s)
728750

751+
752+
def test_unknown_section():
753+
doc_text = """
754+
Test having an unknown section
755+
756+
Mope
757+
----
758+
This should be ignored and warned about
759+
"""
760+
761+
class BadSection(object):
762+
"""Class with bad section.
763+
764+
Nope
765+
----
766+
This class has a nope section.
767+
"""
768+
pass
769+
770+
with warnings.catch_warnings(record=True) as w:
771+
NumpyDocString(doc_text)
772+
assert len(w) == 1
773+
assert "Unknown section Mope" == str(w[0].message)
774+
775+
with warnings.catch_warnings(record=True) as w:
776+
SphinxClassDoc(BadSection)
777+
assert len(w) == 1
778+
assert_true('test_docscrape.test_unknown_section.<locals>.BadSection'
779+
in str(w[0].message)
780+
or 'test_docscrape.BadSection' in str(w[0].message))
781+
782+
729783
doc7 = NumpyDocString("""
730784
731785
Doc starts on second line.
732786
733787
""")
734788

789+
735790
def test_empty_first_line():
736791
assert doc7['Summary'][0].startswith('Doc starts')
737792

@@ -762,6 +817,7 @@ def test_unicode():
762817
assert isinstance(doc['Summary'][0], str)
763818
assert doc['Summary'][0] == 'öäöäöäöäöåååå'
764819

820+
765821
def test_plot_examples():
766822
cfg = dict(use_plots=True)
767823

@@ -785,6 +841,7 @@ def test_plot_examples():
785841
""", config=cfg)
786842
assert str(doc).count('plot::') == 1, str(doc)
787843

844+
788845
def test_class_members():
789846

790847
class Dummy(object):
@@ -866,6 +923,7 @@ def bar(self, a, b):
866923
else:
867924
assert 'Spammity index' in str(doc), str(doc)
868925

926+
869927
def test_duplicate_signature():
870928
# Duplicate function signatures occur e.g. in ufuncs, when the
871929
# automatic mechanism adds one, and a more detailed comes from the
@@ -911,6 +969,7 @@ def test_duplicate_signature():
911969
For usage examples, see `ode`.
912970
"""
913971

972+
914973
def test_class_members_doc():
915974
doc = ClassDoc(None, class_doc_txt)
916975
non_blank_line_by_line_compare(str(doc),
@@ -949,6 +1008,7 @@ def test_class_members_doc():
9491008
9501009
""")
9511010

1011+
9521012
def test_class_members_doc_sphinx():
9531013
class Foo:
9541014
@property
@@ -997,6 +1057,7 @@ def x(self):
9971057
9981058
""")
9991059

1060+
10001061
def test_templated_sections():
10011062
doc = SphinxClassDoc(None, class_doc_txt,
10021063
config={'template': jinja2.Template('{{examples}}{{parameters}}')})
@@ -1020,8 +1081,6 @@ def test_templated_sections():
10201081
""")
10211082

10221083

1023-
1024-
10251084
if __name__ == "__main__":
10261085
import nose
10271086
nose.run()

0 commit comments

Comments
 (0)