Skip to content

Commit 449d789

Browse files
authored
Merge pull request #101 from amueller/unknown_section_error
Add class and line number to warning about unknown section
2 parents b215bed + 850232c commit 449d789

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

@@ -153,13 +154,16 @@ def test_signature():
153154
assert doc['Signature'].startswith('numpy.multivariate_normal(')
154155
assert doc['Signature'].endswith('spam=None)')
155156

157+
156158
def test_summary():
157159
assert doc['Summary'][0].startswith('Draw values')
158160
assert doc['Summary'][-1].endswith('covariance.')
159161

162+
160163
def test_extended_summary():
161164
assert doc['Extended Summary'][0].startswith('The multivariate normal')
162165

166+
163167
def test_parameters():
164168
assert_equal(len(doc['Parameters']), 3)
165169
assert_equal([n for n,_,_ in doc['Parameters']], ['mean','cov','shape'])
@@ -169,13 +173,15 @@ def test_parameters():
169173
assert desc[0].startswith('Covariance matrix')
170174
assert doc['Parameters'][0][-1][-2] == ' (1+2+3)/3'
171175

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

184+
179185
def test_returns():
180186
assert_equal(len(doc['Returns']), 2)
181187
arg, arg_type, desc = doc['Returns'][0]
@@ -190,6 +196,7 @@ def test_returns():
190196
assert desc[0].startswith('This is not a real')
191197
assert desc[-1].endswith('anonymous return values.')
192198

199+
193200
def test_yields():
194201
section = doc_yields['Yields']
195202
assert_equal(len(section), 3)
@@ -202,6 +209,7 @@ def test_yields():
202209
assert desc[0].startswith('The number of')
203210
assert desc[0].endswith(end)
204211

212+
205213
def test_returnyield():
206214
doc_text = """
207215
Test having returns and yields.
@@ -291,26 +299,31 @@ def test_notes():
291299
assert doc['Notes'][-1].endswith('definite.')
292300
assert_equal(len(doc['Notes']), 17)
293301

302+
294303
def test_references():
295304
assert doc['References'][0].startswith('..')
296305
assert doc['References'][-1].endswith('2001.')
297306

307+
298308
def test_examples():
299309
assert doc['Examples'][0].startswith('>>>')
300310
assert doc['Examples'][-1].endswith('True]')
301311

312+
302313
def test_index():
303314
assert_equal(doc['index']['default'], 'random')
304315
assert_equal(len(doc['index']), 2)
305316
assert_equal(len(doc['index']['refguide']), 2)
306317

307-
def non_blank_line_by_line_compare(a,b):
318+
319+
def non_blank_line_by_line_compare(a, b):
308320
a = textwrap.dedent(a)
309321
b = textwrap.dedent(b)
310322
a = [l.rstrip() for l in a.split('\n') if l.strip()]
311323
b = [l.rstrip() for l in b.split('\n') if l.strip()]
312324
assert_list_equal(a, b)
313325

326+
314327
def test_str():
315328
# doc_txt has the order of Notes and See Also sections flipped.
316329
# This should be handled automatically, and so, one thing this test does
@@ -597,15 +610,18 @@ def test_sphinx_yields_str():
597610
If None, the index is into the flattened array, otherwise along
598611
the specified axis""")
599612

613+
600614
def test_parameters_without_extended_description():
601615
assert_equal(len(doc2['Parameters']), 2)
602616

617+
603618
doc3 = NumpyDocString("""
604619
my_signature(*params, **kwds)
605620
606621
Return this and that.
607622
""")
608623

624+
609625
def test_escape_stars():
610626
signature = str(doc3).split('\n')[0]
611627
assert_equal(signature, 'my_signature(\*params, \*\*kwds)')
@@ -616,14 +632,17 @@ def my_func(a, b, **kwargs):
616632
fdoc = FunctionDoc(func=my_func)
617633
assert_equal(fdoc['Signature'], 'my_func(a, b, \*\*kwargs)')
618634

635+
619636
doc4 = NumpyDocString(
620637
"""a.conj()
621638
622639
Return an array with all complex-valued elements conjugated.""")
623640

641+
624642
def test_empty_extended_summary():
625643
assert_equal(doc4['Extended Summary'], [])
626644

645+
627646
doc5 = NumpyDocString(
628647
"""
629648
a.something()
@@ -639,18 +658,21 @@ def test_empty_extended_summary():
639658
If needed
640659
""")
641660

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

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

675+
654676
def test_see_also():
655677
doc6 = NumpyDocString(
656678
"""
@@ -728,12 +750,45 @@ class Dummy(object):
728750
assert(' some relationship' in s)
729751
assert(':func:`func_d`' in s)
730752

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

791+
737792
def test_empty_first_line():
738793
assert doc7['Summary'][0].startswith('Doc starts')
739794

@@ -764,6 +819,7 @@ def test_unicode():
764819
assert isinstance(doc['Summary'][0], str)
765820
assert doc['Summary'][0] == 'öäöäöäöäöåååå'
766821

822+
767823
def test_plot_examples():
768824
cfg = dict(use_plots=True)
769825

@@ -787,6 +843,7 @@ def test_plot_examples():
787843
""", config=cfg)
788844
assert str(doc).count('plot::') == 1, str(doc)
789845

846+
790847
def test_class_members():
791848

792849
class Dummy(object):
@@ -868,6 +925,7 @@ def bar(self, a, b):
868925
else:
869926
assert 'Spammity index' in str(doc), str(doc)
870927

928+
871929
def test_duplicate_signature():
872930
# Duplicate function signatures occur e.g. in ufuncs, when the
873931
# automatic mechanism adds one, and a more detailed comes from the
@@ -922,6 +980,7 @@ def test_duplicate_signature():
922980
For usage examples, see `ode`.
923981
"""
924982

983+
925984
def test_class_members_doc():
926985
doc = ClassDoc(None, class_doc_txt)
927986
non_blank_line_by_line_compare(str(doc),
@@ -969,6 +1028,7 @@ def test_class_members_doc():
9691028
9701029
""")
9711030

1031+
9721032
def test_class_members_doc_sphinx():
9731033
class Foo:
9741034
@property
@@ -1064,6 +1124,7 @@ def no_period(self):
10641124
10651125
""")
10661126

1127+
10671128
def test_templated_sections():
10681129
doc = SphinxClassDoc(None, class_doc_txt,
10691130
config={'template': jinja2.Template('{{examples}}{{parameters}}')})
@@ -1087,8 +1148,6 @@ def test_templated_sections():
10871148
""")
10881149

10891150

1090-
1091-
10921151
if __name__ == "__main__":
10931152
import nose
10941153
nose.run()

0 commit comments

Comments
 (0)