Coverage for tests/test_parsers/test_docstrings/test_numpy.py: 93.14%
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Tests for [the `parsers.docstrings.numpy` module][pytkdocs.parsers.docstrings.numpy]."""
3import inspect
4from textwrap import dedent
6from pytkdocs.loader import Loader
7from pytkdocs.parsers.docstrings.base import Section
8from pytkdocs.parsers.docstrings.numpy import Numpy
11class DummyObject:
12 path = "o"
15def parse(
16 docstring,
17 signature=None,
18 return_type=inspect.Signature.empty,
19 trim_doctest=False,
20):
21 """Helper to parse a doctring."""
22 parser = Numpy(trim_doctest_flags=trim_doctest)
24 return parser.parse(
25 dedent(docstring).strip(),
26 context={"obj": DummyObject(), "signature": signature, "type": return_type},
27 )
30def test_simple_docstring():
31 """Parse a simple docstring."""
32 sections, errors = parse("A simple docstring.")
33 assert len(sections) == 1
34 assert not errors
37def test_multi_line_docstring():
38 """Parse a multi-line docstring."""
39 sections, errors = parse(
40 """
41 A somewhat longer docstring.
43 Blablablabla.
44 """
45 )
46 assert len(sections) == 1
47 assert not errors
50def test_sections_without_signature():
51 """Parse a docstring without a signature."""
52 # type of return value always required
53 sections, errors = parse(
54 """
55 Sections without signature.
57 Parameters
58 ----------
59 void :
60 SEGFAULT.
61 niet :
62 SEGFAULT.
63 nada :
64 SEGFAULT.
65 rien :
66 SEGFAULT.
68 Raises
69 ------
70 GlobalError
71 when nothing works as expected.
73 Returns
74 -------
75 bool
76 Itself.
77 """
78 )
79 assert len(sections) == 4
80 assert len(errors) == 4 # missing annotations for params
81 for error in errors:
82 assert "param" in error
85def test_sections_without_description():
86 """Parse a docstring without descriptions."""
87 # type of return value always required
88 sections, errors = parse(
89 """
90 Sections without descriptions.
92 Parameters
93 ----------
94 void : str
95 niet : str
97 Raises
98 ------
99 GlobalError
101 Returns
102 -------
103 bool
104 """
105 )
107 # Assert that errors are as expected
108 assert len(sections) == 4
109 assert len(errors) == 6
110 for error in errors[:4]:
111 assert "param" in error
112 assert "exception" in errors[4]
113 assert "return description" in errors[5]
115 # Assert that no descriptions are ever None (can cause exceptions downstream)
116 assert sections[1].type is Section.Type.PARAMETERS
117 for p in sections[1].value:
118 assert p.description is not None
120 assert sections[2].type is Section.Type.EXCEPTIONS
121 for p in sections[2].value:
122 assert p.description is not None
124 assert sections[3].type is Section.Type.RETURN
125 assert sections[3].value.description is not None
128def test_property_docstring():
129 """Parse a property docstring."""
130 class_ = Loader().get_object_documentation("tests.fixtures.parsing.docstrings.NotDefinedYet")
131 prop = class_.attributes[0]
132 sections, errors = prop.docstring_sections, prop.docstring_errors
133 assert len(sections) == 2
134 assert not errors
137def test_function_without_annotations():
138 """Parse a function docstring without signature annotations."""
140 def f(x, y):
141 """
142 This function has no annotations.
144 Parameters
145 ----------
146 x:
147 X value.
148 y:
149 Y value.
151 Returns
152 -------
153 float
154 Sum X + Y.
155 """
156 return x + y
158 sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
159 assert len(sections) == 3
160 assert not errors
163def test_function_with_annotations():
164 """Parse a function docstring with signature annotations."""
166 def f(x: int, y: int) -> int:
167 """
168 This function has annotations.
170 Parameters
171 ----------
172 x:
173 X value.
174 y:
175 Y value.
177 Returns
178 -------
179 int
180 Sum X + Y.
181 """
182 return x + y
184 sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
185 assert len(sections) == 3
186 assert not errors
189def test_function_with_examples_trim_doctest():
190 """Parse example docstring with trim_doctest_flags option."""
192 def f(x: int) -> int:
193 """Test function.
195 Example
196 -------
197 We want to skip the following test.
198 >>> 1 + 1 == 3 # doctest: +SKIP
199 True
201 And then a few more examples here:
202 >>> print("a\\n\\nb")
203 a
204 <BLANKLINE>
205 b
206 >>> 1 + 1 == 2 # doctest: +SKIP
207 >>> print(list(range(1, 100))) # doctest: +ELLIPSIS
208 [1, 2, ..., 98, 99]
209 """
210 return x
212 sections, errors = parse(
213 inspect.getdoc(f),
214 inspect.signature(f),
215 trim_doctest=True,
216 )
217 assert len(sections) == 2
218 assert len(sections[1].value) == 4
219 assert not errors
221 # Verify that doctest flags have indeed been trimmed
222 example_str = sections[1].value[1][1]
223 assert "# doctest: +SKIP" not in example_str
224 example_str = sections[1].value[3][1]
225 assert "<BLANKLINE>" not in example_str
226 assert "\n>>> print(list(range(1, 100)))\n" in example_str
229def test_function_with_examples():
230 """Parse a function docstring with examples."""
232 def f(x: int, y: int) -> int:
233 """
234 This function has annotations.
236 Examples
237 --------
238 Some examples that will create an unified code block:
240 >>> 2 + 2 == 5
241 False
242 >>> print("examples")
243 "examples"
245 This is just a random comment in the examples section.
247 These examples will generate two different code blocks. Note the blank line.
249 >>> print("I'm in the first code block!")
250 "I'm in the first code block!"
252 >>> print("I'm in other code block!")
253 "I'm in other code block!"
255 We also can write multiline examples:
257 >>> x = 3 + 2
258 >>> y = x + 10
259 >>> y
260 15
262 This is just a typical Python code block:
264 ```python
265 print("examples")
266 return 2 + 2
267 ```
269 Even if it contains doctests, the following block is still considered a normal code-block.
271 ```python
272 >>> print("examples")
273 "examples"
274 >>> 2 + 2
275 4
276 ```
278 The blank line before an example is optional.
279 >>> x = 3
280 >>> y = "apple"
281 >>> z = False
282 >>> l = [x, y, z]
283 >>> my_print_list_function(l)
284 3
285 "apple"
286 False
287 """
288 return x + y
290 sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
291 assert len(sections) == 2
292 assert len(sections[1].value) == 9
293 assert not errors
296def test_types_in_docstring():
297 """Parse types in docstring."""
299 def f(x, y):
300 """
301 The types are written in the docstring.
303 Parameters
304 ----------
305 x : int
306 X value.
307 y : int
308 Y value.
310 Returns
311 -------
312 int
313 Sum X + Y.
314 """
315 return x + y
317 sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
318 assert len(sections) == 3
319 assert not errors
321 x, y = sections[1].value
322 r = sections[2].value
324 assert x.name == "x"
325 assert x.annotation == "int"
326 assert x.description == "X value."
327 assert x.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD
328 assert x.default is inspect.Signature.empty
330 assert y.name == "y"
331 assert y.annotation == "int"
332 assert y.description == "Y value."
333 assert y.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD
334 assert y.default is inspect.Signature.empty
336 assert r.annotation == "int"
337 assert r.description == "Sum X + Y."
340def test_types_and_optional_in_docstring():
341 """Parse optional types in docstring."""
343 def f(x=1, y=None):
344 """
345 The types are written in the docstring.
347 Parameters
348 ----------
349 x : int
350 X value.
351 y : int, optional
352 Y value.
354 Returns
355 -------
356 int
357 Sum X + Y.
358 """
359 return x + (y or 1)
361 sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
362 assert len(sections) == 3
363 assert not errors
365 x, y = sections[1].value
367 assert x.name == "x"
368 assert x.annotation == "int"
369 assert x.description == "X value."
370 assert x.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD
371 assert x.default == 1
373 assert y.name == "y"
374 assert y.annotation == "int"
375 assert y.description == "Y value."
376 assert y.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD
377 assert y.default is None
380def test_types_in_signature_and_docstring():
381 """Parse types in both signature and docstring."""
383 def f(x: int, y: int) -> int:
384 """
385 The types are written both in the signature and in the docstring.
387 Parameters
388 ----------
389 x : int
390 X value.
391 y : int
392 Y value.
394 Returns
395 -------
396 int
397 Sum X + Y.
398 """
399 return x + y
401 sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
402 assert len(sections) == 3
403 assert not errors
406def test_close_sections():
407 """Parse sections without blank lines in between."""
409 def f(x, y, z):
410 """
411 Parameters
412 ----------
413 x :
414 X
415 y :
416 Y
417 z :
418 Z
419 Raises
420 ------
421 Error2
422 error.
423 Error1
424 error.
425 Returns
426 -------
427 str
428 value
429 """
430 return x + y + z
432 sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
433 assert len(sections) == 3
434 assert not errors
437# test_code_blocks was removed as docstrings within a code block
438# are not applicable to numpy docstrings
441def test_extra_parameter():
442 """Warn on extra parameter in docstring."""
444 def f(x):
445 """
446 Parameters
447 ----------
448 x :
449 Integer.
450 y :
451 Integer.
452 """
453 return x
455 sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
456 assert len(sections) == 1
457 assert len(errors) == 1
458 assert "No type" in errors[0]
461def test_missing_parameter():
462 """Don't warn on missing parameter in docstring."""
463 # FIXME: could warn
464 def f(x, y):
465 """
466 Parameters
467 ----------
468 x :
469 Integer.
470 """
471 return x + y
473 sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
474 assert len(sections) == 1
475 assert not errors
478def test_multiple_lines_in_sections_items():
479 """Parse multi-line item description."""
481 def f(p: str, q: str):
482 """
483 Hi.
485 Parameters
486 ----------
487 p :
488 This argument
489 has a description
490 spawning on multiple lines.
492 It even has blank lines in it.
493 Some of these lines
494 are indented for no reason.
495 q :
496 What if the first line is blank?
497 """
498 return p + q
500 sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
501 assert len(sections) == 2
502 assert len(sections[1].value) == 2
503 # numpy docstrings parameter description can be parsed even if misindentated
504 assert not errors
507def test_parse_args_kwargs():
508 """Parse args and kwargs."""
510 def f(a, *args, **kwargs):
511 """
512 Parameters
513 ----------
514 a :
515 a parameter.
516 *args :
517 args parameters.
518 **kwargs :
519 kwargs parameters.
520 """
521 return 1
523 sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
524 assert len(sections) == 1
525 expected_parameters = {
526 "a": "a parameter.",
527 "*args": "args parameters.",
528 "**kwargs": "kwargs parameters.",
529 }
530 for param in sections[0].value:
531 assert param.name in expected_parameters
532 assert expected_parameters[param.name] == param.description
533 assert not errors
536def test_different_indentation():
537 """Parse different indentations, warn on confusing indentation."""
539 def f():
540 """
541 Hello.
543 Raises
544 ------
545 StartAt5
546 this section's items starts with x spaces of indentation.
547 Well indented continuation line.
548 Badly indented continuation line (will not trigger an error).
550 Empty lines are preserved, as well as extra-indentation (this line is a code block).
551 AnyOtherLine
552 ...starting with exactly 5 spaces is a new item.
553 """
555 sections, errors = parse(inspect.getdoc(f), inspect.signature(f))
556 assert len(sections) == 2
557 assert len(sections[1].value) == 2
558 assert sections[1].value[0].description == (
559 "this section's items starts with x spaces of indentation.\n"
560 "Well indented continuation line.\n"
561 " Badly indented continuation line (will not trigger an error).\n"
562 "\n"
563 " Empty lines are preserved, as well as extra-indentation (this line is a code block)."
564 )
565 assert not errors