Coverage for tests/test_docstrings/test_numpy.py: 100.00%
407 statements
« prev ^ index » next coverage.py v7.6.2, created at 2024-10-12 01:34 +0200
« prev ^ index » next coverage.py v7.6.2, created at 2024-10-12 01:34 +0200
1"""Tests for the [Numpy-style parser][griffe.docstrings.numpy]."""
3from __future__ import annotations
5import logging
6from typing import TYPE_CHECKING
8import pytest
10from griffe import (
11 Attribute,
12 Class,
13 Docstring,
14 DocstringSectionKind,
15 ExprName,
16 Function,
17 Module,
18 Parameter,
19 Parameters,
20 parse_docstring_annotation,
21)
23if TYPE_CHECKING:
24 from tests.test_docstrings.helpers import ParserType
27# =============================================================================================
28# Markup flow (multilines, indentation, etc.)
29def test_simple_docstring(parse_numpy: ParserType) -> None:
30 """Parse a simple docstring.
32 Parameters:
33 parse_numpy: Fixture parser.
34 """
35 sections, warnings = parse_numpy("A simple docstring.")
36 assert len(sections) == 1
37 assert not warnings
40def test_multiline_docstring(parse_numpy: ParserType) -> None:
41 """Parse a multi-line docstring.
43 Parameters:
44 parse_numpy: Fixture parser.
45 """
46 sections, warnings = parse_numpy(
47 """
48 A somewhat longer docstring.
50 Blablablabla.
51 """,
52 )
53 assert len(sections) == 1
54 assert not warnings
57def test_code_blocks(parse_numpy: ParserType) -> None:
58 """Parse code blocks.
60 Parameters:
61 parse_numpy: Fixture parser.
62 """
63 docstring = """
64 This docstring contains a code block!
66 ```python
67 print("hello")
68 ```
69 """
71 sections, warnings = parse_numpy(docstring)
72 assert len(sections) == 1
73 assert not warnings
76def test_indented_code_block(parse_numpy: ParserType) -> None:
77 """Parse indented code blocks.
79 Parameters:
80 parse_numpy: Fixture parser.
81 """
82 docstring = """
83 This docstring contains a docstring in a code block o_O!
85 \"\"\"
86 This docstring is contained in another docstring O_o!
88 Parameters:
89 s: A string.
90 \"\"\"
91 """
93 sections, warnings = parse_numpy(docstring)
94 assert len(sections) == 1
95 assert not warnings
98def test_empty_indented_lines_in_section_with_items(parse_numpy: ParserType) -> None:
99 """In sections with items, don't treat lines with just indentation as items.
101 Parameters:
102 parse_numpy: Fixture parser.
103 """
104 docstring = "Returns\n-------\nonly_item : type\n Description.\n \n \n\nSomething."
105 sections, _ = parse_numpy(docstring)
106 assert len(sections) == 1
107 assert len(sections[0].value) == 2
110def test_doubly_indented_lines_in_section_items(parse_numpy: ParserType) -> None:
111 """In sections with items, don't remove all spaces on the left of indented lines.
113 Parameters:
114 parse_numpy: Fixture parser.
115 """
116 docstring = "Returns\n-------\nonly_item : type\n Description:\n\n - List item.\n - Sublist item."
117 sections, _ = parse_numpy(docstring)
118 assert len(sections) == 1
119 lines = sections[0].value[0].description.split("\n")
120 assert lines[-1].startswith(4 * " " + "- ")
123# =============================================================================================
124# Admonitions
125def test_admonition_see_also(parse_numpy: ParserType) -> None:
126 """Test a "See Also" admonition.
128 Parameters:
129 parse_numpy: Fixture parser.
130 """
131 docstring = """
132 Summary text.
134 See Also
135 --------
136 some_function
138 more text
139 """
141 sections, _ = parse_numpy(docstring)
142 assert len(sections) == 2
143 assert sections[0].value == "Summary text."
144 assert sections[1].title == "See Also"
145 assert sections[1].value.description == "some_function\n\nmore text"
148def test_admonition_empty(parse_numpy: ParserType) -> None:
149 """Test an empty "See Also" admonition.
151 Parameters:
152 parse_numpy: Fixture parser.
153 """
154 docstring = """
155 Summary text.
157 See Also
158 --------
159 """
161 sections, _ = parse_numpy(docstring)
162 assert len(sections) == 2
163 assert sections[0].value == "Summary text."
164 assert sections[1].title == "See Also"
165 assert sections[1].value.description == ""
168def test_isolated_dash_lines_do_not_create_sections(parse_numpy: ParserType) -> None:
169 """An isolated dash-line (`---`) should not be parsed as a section.
171 Parameters:
172 parse_numpy: Fixture parser.
173 """
174 docstring = """
175 Summary text.
177 ---
178 Text.
180 Note
181 ----
182 Note contents.
184 ---
185 Text.
186 """
188 sections, _ = parse_numpy(docstring)
189 assert len(sections) == 2
190 assert sections[0].value == "Summary text.\n\n---\nText."
191 assert sections[1].title == "Note"
192 assert sections[1].value.description == "Note contents.\n\n---\nText."
195def test_admonition_warnings_special_case(parse_numpy: ParserType) -> None:
196 """Test that the "Warnings" section renders as a warning admonition.
198 Parameters:
199 parse_numpy: Fixture parser.
200 """
201 docstring = """
202 Summary text.
204 Warnings
205 --------
206 Be careful!!!
208 more text
209 """
211 sections, _ = parse_numpy(docstring)
212 assert len(sections) == 2
213 assert sections[0].value == "Summary text."
214 assert sections[1].title == "Warnings"
215 assert sections[1].value.description == "Be careful!!!\n\nmore text"
216 assert sections[1].value.kind == "warning"
219def test_admonition_notes_special_case(parse_numpy: ParserType) -> None:
220 """Test that the "Warnings" section renders as a warning admonition.
222 Parameters:
223 parse_numpy: Fixture parser.
224 """
225 docstring = """
226 Summary text.
228 Notes
229 -----
230 Something noteworthy.
232 more text
233 """
235 sections, _ = parse_numpy(docstring)
236 assert len(sections) == 2
237 assert sections[0].value == "Summary text."
238 assert sections[1].title == "Notes"
239 assert sections[1].value.description == "Something noteworthy.\n\nmore text"
240 assert sections[1].value.kind == "note"
243# =============================================================================================
244# Annotations
245def test_prefer_docstring_type_over_annotation(parse_numpy: ParserType) -> None:
246 """Prefer the type written in the docstring over the annotation in the parent.
248 Parameters:
249 parse_numpy: Fixture parser.
250 """
251 docstring = """
252 Parameters
253 ----------
254 a : int
255 """
257 sections, _ = parse_numpy(
258 docstring,
259 parent=Function("func", parameters=Parameters(Parameter("a", annotation="str"))),
260 )
261 assert len(sections) == 1
262 param = sections[0].value[0]
263 assert param.name == "a"
264 assert param.description == ""
265 assert param.annotation.name == "int"
268def test_parse_complex_annotations(parse_numpy: ParserType) -> None:
269 """Check the type regex accepts all the necessary characters.
271 Parameters:
272 parse_numpy: Fixture parser.
273 """
274 docstring = """
275 Parameters
276 ----------
277 a : typing.Tuple[str, random0123456789]
278 b : int | float | None
279 c : Literal['hello'] | Literal["world"]
280 """
282 sections, _ = parse_numpy(docstring)
283 assert len(sections) == 1
284 param_a, param_b, param_c = sections[0].value
285 assert param_a.name == "a"
286 assert param_a.description == ""
287 assert param_a.annotation == "typing.Tuple[str, random0123456789]"
288 assert param_b.name == "b"
289 assert param_b.description == ""
290 assert param_b.annotation == "int | float | None"
291 assert param_c.name == "c"
292 assert param_c.description == ""
293 assert param_c.annotation == "Literal['hello'] | Literal[\"world\"]"
296@pytest.mark.parametrize(
297 ("docstring", "name"),
298 [
299 ("Attributes\n---\na : {name}\n Description.\n", "int"),
300 ("Parameters\n---\na : {name}\n Description.\n", "int"),
301 ("Other Parameters\n---\na : {name}\n Description.\n", "int"),
302 ("Yields\n---\na : {name}\n Description.\n", "int"),
303 ("Receives\n---\na : {name}\n Description.\n", "int"),
304 ("Returns\n---\na : {name}\n Description.\n", "int"),
305 ("Raises\n---\n{name}\n Description.\n", "RuntimeError"),
306 ("Warns\n---\n{name}\n Description.\n", "UserWarning"),
307 ],
308)
309def test_parse_annotations_in_all_sections(parse_numpy: ParserType, docstring: str, name: str) -> None:
310 """Assert annotations are parsed in all relevant sections.
312 Parameters:
313 parse_numpy: Fixture parser.
314 docstring: Parametrized docstring.
315 name: Parametrized name in annotation.
316 """
317 docstring = docstring.format(name=name)
318 sections, _ = parse_numpy(docstring, parent=Function("f"))
319 assert len(sections) == 1
320 assert sections[0].value[0].annotation.name == name
323def test_dont_crash_on_text_annotations(parse_numpy: ParserType, caplog: pytest.LogCaptureFixture) -> None:
324 """Don't crash while parsing annotations containing unhandled nodes.
326 Parameters:
327 parse_numpy: Fixture parser.
328 caplog: Pytest fixture used to capture logs.
329 """
330 docstring = """
331 Attributes
332 ----------
333 region : str, list-like, geopandas.GeoSeries, geopandas.GeoDataFrame, geometric
334 Description.
336 Parameters
337 ----------
338 region : str, list-like, geopandas.GeoSeries, geopandas.GeoDataFrame, geometric
339 Description.
341 Returns
342 -------
343 str or bytes
344 Description.
346 Receives
347 --------
348 region : str, list-like, geopandas.GeoSeries, geopandas.GeoDataFrame, geometric
349 Description.
351 Yields
352 ------
353 str or bytes
354 Description.
355 """
356 caplog.set_level(logging.DEBUG)
357 assert parse_numpy(docstring, parent=Function("f"))
358 assert all(record.levelname == "DEBUG" for record in caplog.records if "Failed to parse" in record.message)
361# =============================================================================================
362# Sections
363def test_parameters_section(parse_numpy: ParserType) -> None:
364 """Parse parameters section.
366 Parameters:
367 parse_numpy: Fixture parser.
368 """
369 docstring = """
370 Parameters
371 ----------
372 a
373 b : int
374 c : str, optional
375 d : float, default=1.0
376 e, f
377 g, h : bytes, optional, default=b''
378 i : {0, 1, 2}
379 j : {"a", 1, None, True}
380 k
381 K's description.
382 """
384 sections, _ = parse_numpy(docstring)
385 assert len(sections) == 1
388def test_parse_starred_parameters(parse_numpy: ParserType) -> None:
389 """Parse parameters names with stars in them.
391 Parameters:
392 parse_numpy: Fixture parser.
393 """
394 docstring = """
395 Parameters
396 ----------
397 *a : str
398 **b : int
399 ***c : float
400 """
402 sections, warnings = parse_numpy(docstring)
403 assert len(sections) == 1
404 assert len(warnings) == 1
407def test_other_parameters_section(parse_numpy: ParserType) -> None:
408 """Parse other parameters section.
410 Parameters:
411 parse_numpy: Fixture parser.
412 """
413 docstring = """
414 Other Parameters
415 ----------------
416 a
417 b : int
418 c : str, optional
419 d : float, default=1.0
420 e, f
421 g, h : bytes, optional, default=b''
422 i : {0, 1, 2}
423 j : {"a", 1, None, True}
424 k
425 K's description.
426 """
428 sections, _ = parse_numpy(docstring)
429 assert len(sections) == 1
432def test_retrieve_annotation_from_parent(parse_numpy: ParserType) -> None:
433 """Retrieve parameter annotation from the parent object.
435 Parameters:
436 parse_numpy: Fixture parser.
437 """
438 docstring = """
439 Parameters
440 ----------
441 a
442 """
444 sections, _ = parse_numpy(
445 docstring,
446 parent=Function("func", parameters=Parameters(Parameter("a", annotation="str"))),
447 )
448 assert len(sections) == 1
449 param = sections[0].value[0]
450 assert param.name == "a"
451 assert param.description == ""
452 assert param.annotation == "str"
455def test_deprecated_section(parse_numpy: ParserType) -> None:
456 """Parse deprecated section.
458 Parameters:
459 parse_numpy: Fixture parser.
460 """
461 docstring = """
462 Deprecated
463 ----------
464 1.23.4
465 Deprecated.
466 Sorry.
467 """
469 sections, _ = parse_numpy(docstring)
470 assert len(sections) == 1
471 assert sections[0].value.version == "1.23.4"
472 assert sections[0].value.description == "Deprecated.\nSorry."
475def test_returns_section(parse_numpy: ParserType) -> None:
476 """Parse returns section.
478 Parameters:
479 parse_numpy: Fixture parser.
480 """
481 docstring = """
482 Returns
483 -------
484 list of int
485 A list of integers.
486 flag : bool
487 Some kind
488 of flag.
489 x :
490 Name only
491 :
492 No name or annotation
493 : int
494 Only annotation
495 """
497 sections, _ = parse_numpy(docstring)
498 assert len(sections) == 1
500 param = sections[0].value[0]
501 assert param.name == ""
502 assert param.description == "A list of integers."
503 assert param.annotation == "list of int"
505 param = sections[0].value[1]
506 assert param.name == "flag"
507 assert param.description == "Some kind\nof flag."
508 assert param.annotation == "bool"
510 param = sections[0].value[2]
511 assert param.name == "x"
512 assert param.description == "Name only"
513 assert param.annotation is None
515 param = sections[0].value[3]
516 assert param.name == ""
517 assert param.description == "No name or annotation"
518 assert param.annotation is None
520 param = sections[0].value[4]
521 assert param.name == ""
522 assert param.description == "Only annotation"
523 assert param.annotation == "int"
526def test_yields_section(parse_numpy: ParserType) -> None:
527 """Parse yields section.
529 Parameters:
530 parse_numpy: Fixture parser.
531 """
532 docstring = """
533 Yields
534 ------
535 list of int
536 A list of integers.
537 flag : bool
538 Some kind
539 of flag.
540 """
542 sections, _ = parse_numpy(docstring)
543 assert len(sections) == 1
544 param = sections[0].value[0]
545 assert param.name == ""
546 assert param.description == "A list of integers."
547 assert param.annotation == "list of int"
549 param = sections[0].value[1]
550 assert param.name == "flag"
551 assert param.description == "Some kind\nof flag."
552 assert param.annotation == "bool"
555def test_receives_section(parse_numpy: ParserType) -> None:
556 """Parse receives section.
558 Parameters:
559 parse_numpy: Fixture parser.
560 """
561 docstring = """
562 Receives
563 --------
564 list of int
565 A list of integers.
566 flag : bool
567 Some kind
568 of flag.
569 """
571 sections, _ = parse_numpy(docstring)
572 assert len(sections) == 1
573 param = sections[0].value[0]
574 assert param.name == ""
575 assert param.description == "A list of integers."
576 assert param.annotation == "list of int"
577 param = sections[0].value[1]
578 assert param.name == "flag"
579 assert param.description == "Some kind\nof flag."
580 assert param.annotation == "bool"
583def test_raises_section(parse_numpy: ParserType) -> None:
584 """Parse raises section.
586 Parameters:
587 parse_numpy: Fixture parser.
588 """
589 docstring = """
590 Raises
591 ------
592 RuntimeError
593 There was an issue.
594 """
596 sections, _ = parse_numpy(docstring)
597 assert len(sections) == 1
598 param = sections[0].value[0]
599 assert param.description == "There was an issue."
600 assert param.annotation == "RuntimeError"
603def test_warns_section(parse_numpy: ParserType) -> None:
604 """Parse warns section.
606 Parameters:
607 parse_numpy: Fixture parser.
608 """
609 docstring = """
610 Warns
611 -----
612 ResourceWarning
613 Heads up.
614 """
616 sections, _ = parse_numpy(docstring)
617 assert len(sections) == 1
618 param = sections[0].value[0]
619 assert param.description == "Heads up."
620 assert param.annotation == "ResourceWarning"
623def test_attributes_section(parse_numpy: ParserType) -> None:
624 """Parse attributes section.
626 Parameters:
627 parse_numpy: Fixture parser.
628 """
629 docstring = """
630 Attributes
631 ----------
632 a
633 Hello.
634 m
635 z : int
636 Bye.
637 """
639 sections, _ = parse_numpy(docstring)
640 assert len(sections) == 1
641 param = sections[0].value[0]
642 assert param.name == "a"
643 assert param.description == "Hello."
644 assert param.annotation is None
646 param = sections[0].value[1]
647 assert param.name == "m"
648 assert param.description == ""
649 assert param.annotation is None
651 param = sections[0].value[2]
652 assert param.name == "z"
653 assert param.description == "Bye."
654 assert param.annotation == "int"
657def test_parse_functions_section(parse_numpy: ParserType) -> None:
658 """Parse Functions/Methods sections.
660 Parameters:
661 parse_numpy: Fixture parser.
662 """
663 docstring = """
664 Functions
665 ---------
666 f(a, b=2)
667 Hello.
668 g
669 Hi.
671 Methods
672 -------
673 f(a, b=2)
674 Hello.
675 g
676 Hi.
677 """
679 sections, warnings = parse_numpy(docstring)
680 assert len(sections) == 2
681 for section in sections:
682 assert section.kind is DocstringSectionKind.functions
683 func_f = section.value[0]
684 assert func_f.name == "f"
685 assert func_f.signature == "f(a, b=2)"
686 assert func_f.description == "Hello."
687 func_g = section.value[1]
688 assert func_g.name == "g"
689 assert func_g.signature is None
690 assert func_g.description == "Hi."
691 assert not warnings
694def test_parse_classes_section(parse_numpy: ParserType) -> None:
695 """Parse Classes sections.
697 Parameters:
698 parse_numpy: Fixture parser.
699 """
700 docstring = """
701 Classes
702 -------
703 C(a, b=2)
704 Hello.
705 D
706 Hi.
707 """
709 sections, warnings = parse_numpy(docstring)
710 assert len(sections) == 1
711 assert sections[0].kind is DocstringSectionKind.classes
712 class_c = sections[0].value[0]
713 assert class_c.name == "C"
714 assert class_c.signature == "C(a, b=2)"
715 assert class_c.description == "Hello."
716 class_d = sections[0].value[1]
717 assert class_d.name == "D"
718 assert class_d.signature is None
719 assert class_d.description == "Hi."
720 assert not warnings
723def test_parse_modules_section(parse_numpy: ParserType) -> None:
724 """Parse Modules sections.
726 Parameters:
727 parse_numpy: Fixture parser.
728 """
729 docstring = """
730 Modules
731 -------
732 m
733 Hello.
734 n
735 Hi.
736 """
738 sections, warnings = parse_numpy(docstring)
739 assert len(sections) == 1
740 assert sections[0].kind is DocstringSectionKind.modules
741 module_m = sections[0].value[0]
742 assert module_m.name == "m"
743 assert module_m.description == "Hello."
744 module_n = sections[0].value[1]
745 assert module_n.name == "n"
746 assert module_n.description == "Hi."
747 assert not warnings
750def test_examples_section(parse_numpy: ParserType) -> None:
751 """Parse examples section.
753 Parameters:
754 parse_numpy: Fixture parser.
755 """
756 docstring = """
757 Examples
758 --------
759 Hello.
761 >>> 1 + 2
762 3
764 ```pycon
765 >>> print("Hello again.")
766 ```
768 >>> a = 0 # doctest: +SKIP
769 >>> b = a + 1
770 >>> print(b)
771 1
773 Bye.
775 --------
777 Not in the section.
778 """
780 sections, _ = parse_numpy(docstring, trim_doctest_flags=False)
781 assert len(sections) == 2
782 examples = sections[0]
783 assert len(examples.value) == 5
784 assert examples.value[0] == (DocstringSectionKind.text, "Hello.")
785 assert examples.value[1] == (DocstringSectionKind.examples, ">>> 1 + 2\n3")
786 assert examples.value[3][1].startswith(">>> a = 0 # doctest: +SKIP")
789def test_examples_section_when_followed_by_named_section(parse_numpy: ParserType) -> None:
790 """Parse examples section followed by another section.
792 Parameters:
793 parse_numpy: Parse function (fixture).
794 """
795 docstring = """
796 Examples
797 --------
798 Hello, hello.
800 Parameters
801 ----------
802 foo : int
803 """
805 sections, _ = parse_numpy(docstring, trim_doctest_flags=False)
806 assert len(sections) == 2
807 assert sections[0].kind is DocstringSectionKind.examples
808 assert sections[1].kind is DocstringSectionKind.parameters
811def test_examples_section_as_last(parse_numpy: ParserType) -> None:
812 """Parse examples section being last in the docstring.
814 Parameters:
815 parse_numpy: Parse function (fixture).
816 """
817 docstring = """
818 Lorem ipsum dolor sit amet, consectetur adipiscing elit...
820 Examples
821 --------
822 ```pycon
823 >>> LoremIpsum.from_string("consectetur")
824 <foofoo: Ipsum.Lorem>
826 ```
827 """
829 sections, _ = parse_numpy(docstring)
830 assert len(sections) == 2
831 assert sections[0].kind is DocstringSectionKind.text
832 assert sections[1].kind is DocstringSectionKind.examples
835def test_blank_lines_in_section(parse_numpy: ParserType) -> None:
836 """Support blank lines in the middle of sections.
838 Parameters:
839 parse_numpy: Fixture parser.
840 """
841 docstring = """
842 Examples
843 --------
844 Line 1.
846 Line 2.
847 """
848 sections, _ = parse_numpy(docstring)
849 assert len(sections) == 1
852# =============================================================================================
853# Attributes sections
854def test_retrieve_attributes_annotation_from_parent(parse_numpy: ParserType) -> None:
855 """Retrieve the annotations of attributes from the parent object.
857 Parameters:
858 parse_numpy: Fixture parser.
859 """
860 docstring = """
861 Summary.
863 Attributes
864 ----------
865 a :
866 Whatever.
867 b :
868 Whatever.
869 """
870 parent = Class("cls")
871 parent["a"] = Attribute("a", annotation=ExprName("int"))
872 parent["b"] = Attribute("b", annotation=ExprName("str"))
873 sections, _ = parse_numpy(docstring, parent=parent)
874 attributes = sections[1].value
875 assert attributes[0].name == "a"
876 assert attributes[0].annotation.name == "int"
877 assert attributes[1].name == "b"
878 assert attributes[1].annotation.name == "str"
881# =============================================================================================
882# Parameters sections
883def test_warn_about_unknown_parameters(parse_numpy: ParserType) -> None:
884 """Warn about unknown parameters in "Parameters" sections.
886 Parameters:
887 parse_numpy: Fixture parser.
888 """
889 docstring = """
890 Parameters
891 ----------
892 x : int
893 Integer.
894 y : int
895 Integer.
896 """
898 _, warnings = parse_numpy(
899 docstring,
900 parent=Function(
901 "func",
902 parameters=Parameters(
903 Parameter("a"),
904 Parameter("y"),
905 ),
906 ),
907 )
908 assert len(warnings) == 1
909 assert "'x' does not appear in the function signature" in warnings[0]
912def test_never_warn_about_unknown_other_parameters(parse_numpy: ParserType) -> None:
913 """Never warn about unknown parameters in "Other parameters" sections.
915 Parameters:
916 parse_numpy: Fixture parser.
917 """
918 docstring = """
919 Other Parameters
920 ----------------
921 x : int
922 Integer.
923 z : int
924 Integer.
925 """
927 _, warnings = parse_numpy(
928 docstring,
929 parent=Function(
930 "func",
931 parameters=Parameters(
932 Parameter("a"),
933 Parameter("y"),
934 ),
935 ),
936 )
937 assert not warnings
940def test_unknown_params_scan_doesnt_crash_without_parameters(parse_numpy: ParserType) -> None:
941 """Assert we don't crash when parsing parameters sections and parent object does not have parameters.
943 Parameters:
944 parse_numpy: Fixture parser.
945 """
946 docstring = """
947 Parameters
948 ----------
949 this : str
950 This.
951 that : str
952 That.
953 """
955 _, warnings = parse_numpy(docstring, parent=Module("mod"))
956 assert not warnings
959def test_class_uses_init_parameters(parse_numpy: ParserType) -> None:
960 """Assert we use the `__init__` parameters when parsing classes' parameters sections.
962 Parameters:
963 parse_numpy: Fixture parser.
964 """
965 docstring = """
966 Parameters
967 ----------
968 x :
969 X value.
970 """
972 parent = Class("c")
973 parent["__init__"] = Function("__init__", parameters=Parameters(Parameter("x", annotation="int")))
974 sections, warnings = parse_numpy(docstring, parent=parent)
975 assert not warnings
976 argx = sections[0].value[0]
977 assert argx.name == "x"
978 assert argx.annotation == "int"
979 assert argx.description == "X value."
982def test_detect_optional_flag(parse_numpy: ParserType) -> None:
983 """Detect the optional part of a parameter docstring.
985 Parameters:
986 parse_numpy: Fixture parser.
987 """
988 docstring = """
989 Parameters
990 ----------
991 a : str, optional
992 g, h : bytes, optional, default=b''
993 """
995 sections, _ = parse_numpy(docstring)
996 assert len(sections) == 1
997 assert sections[0].value[0].annotation == "str"
998 assert sections[0].value[1].annotation == "bytes"
999 assert sections[0].value[1].default == "b''"
1000 assert sections[0].value[2].annotation == "bytes"
1001 assert sections[0].value[2].default == "b''"
1004@pytest.mark.parametrize("newlines", [1, 2, 3])
1005def test_blank_lines_in_item_descriptions(parse_numpy: ParserType, newlines: int) -> None:
1006 """Support blank lines in the middle of item descriptions.
1008 Parameters:
1009 parse_numpy: Fixture parser.
1010 newlines: Number of new lines between item summary and its body.
1011 """
1012 nl = "\n"
1013 nlindent = "\n" + " " * 12
1014 docstring = f"""
1015 Parameters
1016 ----------
1017 a : str
1018 Summary.{nlindent * newlines}Body.
1019 """
1020 sections, _ = parse_numpy(docstring)
1021 assert len(sections) == 1
1022 assert sections[0].value[0].annotation == "str"
1023 assert sections[0].value[0].description == f"Summary.{nl * newlines}Body."
1026# =============================================================================================
1027# Yields sections
1028@pytest.mark.parametrize(
1029 "return_annotation",
1030 [
1031 "Iterator[tuple[int, float]]",
1032 "Generator[tuple[int, float], ..., ...]",
1033 ],
1034)
1035def test_parse_yields_tuple_in_iterator_or_generator(parse_numpy: ParserType, return_annotation: str) -> None:
1036 """Parse Yields annotations in Iterator or Generator types.
1038 Parameters:
1039 parse_numpy: Fixture parser.
1040 return_annotation: Parametrized return annotation as a string.
1041 """
1042 docstring = """
1043 Summary.
1045 Yields
1046 ------
1047 a :
1048 Whatever.
1049 b :
1050 Whatever.
1051 """
1052 sections, _ = parse_numpy(
1053 docstring,
1054 parent=Function(
1055 "func",
1056 returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))),
1057 ),
1058 )
1059 yields = sections[1].value
1060 assert yields[0].name == "a"
1061 assert yields[0].annotation.name == "int"
1062 assert yields[1].name == "b"
1063 assert yields[1].annotation.name == "float"
1066@pytest.mark.parametrize(
1067 "return_annotation",
1068 [
1069 "Iterator[int]",
1070 "Generator[int, None, None]",
1071 ],
1072)
1073def test_extract_yielded_type_with_single_return_item(parse_numpy: ParserType, return_annotation: str) -> None:
1074 """Extract main type annotation from Iterator or Generator.
1076 Parameters:
1077 parse_numpy: Fixture parser.
1078 return_annotation: Parametrized return annotation as a string.
1079 """
1080 docstring = """
1081 Summary.
1083 Yields
1084 ------
1085 a :
1086 A number.
1087 """
1088 sections, _ = parse_numpy(
1089 docstring,
1090 parent=Function(
1091 "func",
1092 returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))),
1093 ),
1094 )
1095 yields = sections[1].value
1096 assert yields[0].annotation.name == "int"
1099def test_yield_section_in_property(parse_numpy: ParserType) -> None:
1100 """No warnings when parsing Yields section in a property.
1102 Parameters:
1103 parse_numpy: Fixture parser.
1104 """
1105 docstring = """
1106 Summary.
1108 Yields
1109 ------
1110 :
1111 A number.
1112 """
1113 sections, warnings = parse_numpy(
1114 docstring,
1115 parent=Attribute(
1116 "prop",
1117 annotation=parse_docstring_annotation("Iterator[int]", Docstring("d", parent=Attribute("a"))),
1118 ),
1119 )
1120 assert not warnings
1121 yields = sections[1].value
1122 assert yields[0].annotation.name == "int"
1125# =============================================================================================
1126# Receives sections
1127def test_parse_receives_tuple_in_generator(parse_numpy: ParserType) -> None:
1128 """Parse Receives annotations in Generator type.
1130 Parameters:
1131 parse_numpy: Fixture parser.
1132 """
1133 docstring = """
1134 Summary.
1136 Receives
1137 --------
1138 a :
1139 Whatever.
1140 b :
1141 Whatever.
1142 """
1143 sections, _ = parse_numpy(
1144 docstring,
1145 parent=Function(
1146 "func",
1147 returns=parse_docstring_annotation(
1148 "Generator[..., tuple[int, float], ...]",
1149 Docstring("d", parent=Function("f")),
1150 ),
1151 ),
1152 )
1153 receives = sections[1].value
1154 assert receives[0].name == "a"
1155 assert receives[0].annotation.name == "int"
1156 assert receives[1].name == "b"
1157 assert receives[1].annotation.name == "float"
1160@pytest.mark.parametrize(
1161 "return_annotation",
1162 [
1163 "Generator[int, float, None]",
1164 ],
1165)
1166def test_extract_received_type_with_single_return_item(parse_numpy: ParserType, return_annotation: str) -> None:
1167 """Extract main type annotation from Iterator or Generator.
1169 Parameters:
1170 parse_numpy: Fixture parser.
1171 return_annotation: Parametrized return annotation as a string.
1172 """
1173 docstring = """
1174 Summary.
1176 Receives
1177 --------
1178 a :
1179 A floating point number.
1180 """
1181 sections, _ = parse_numpy(
1182 docstring,
1183 parent=Function(
1184 "func",
1185 returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))),
1186 ),
1187 )
1188 receives = sections[1].value
1189 assert receives[0].annotation.name == "float"
1192# =============================================================================================
1193# Returns sections
1194def test_parse_returns_tuple_in_generator(parse_numpy: ParserType) -> None:
1195 """Parse Returns annotations in Generator type.
1197 Parameters:
1198 parse_numpy: Fixture parser.
1199 """
1200 docstring = """
1201 Summary.
1203 Returns
1204 -------
1205 a :
1206 Whatever.
1207 b :
1208 Whatever.
1209 """
1210 sections, _ = parse_numpy(
1211 docstring,
1212 parent=Function(
1213 "func",
1214 returns=parse_docstring_annotation(
1215 "Generator[..., ..., tuple[int, float]]",
1216 Docstring("d", parent=Function("f")),
1217 ),
1218 ),
1219 )
1220 returns = sections[1].value
1221 assert returns[0].name == "a"
1222 assert returns[0].annotation.name == "int"
1223 assert returns[1].name == "b"
1224 assert returns[1].annotation.name == "float"
1227# =============================================================================================
1228# Parser special features
1229@pytest.mark.parametrize(
1230 "docstring",
1231 [
1232 "",
1233 "\n",
1234 "\n\n",
1235 "Summary.",
1236 "Summary.\n\n\n",
1237 "Summary.\n\nParagraph.",
1238 "Summary\non two lines.",
1239 "Summary\non two lines.\n\nParagraph.",
1240 ],
1241)
1242def test_ignore_init_summary(parse_numpy: ParserType, docstring: str) -> None:
1243 """Correctly ignore summary in `__init__` methods' docstrings.
1245 Parameters:
1246 parse_numpy: Fixture parser.
1247 docstring: The docstring to parse (parametrized).
1248 """
1249 sections, _ = parse_numpy(docstring, parent=Function("__init__", parent=Class("C")), ignore_init_summary=True)
1250 for section in sections:
1251 assert "Summary" not in section.value
1253 if docstring.strip():
1254 sections, _ = parse_numpy(docstring, parent=Function("__init__", parent=Module("M")), ignore_init_summary=True)
1255 assert "Summary" in sections[0].value
1256 sections, _ = parse_numpy(docstring, parent=Function("f", parent=Class("C")), ignore_init_summary=True)
1257 assert "Summary" in sections[0].value
1258 sections, _ = parse_numpy(docstring, ignore_init_summary=True)
1259 assert "Summary" in sections[0].value
1262@pytest.mark.parametrize(
1263 "docstring",
1264 [
1265 """
1266 Examples
1267 --------
1268 Base case 1. We want to skip the following test.
1269 >>> 1 + 1 == 3 # doctest: +SKIP
1270 True
1271 """,
1272 r"""
1273 Examples
1274 --------
1276 Base case 2. We have a blankline test.
1277 >>> print("a\n\nb")
1278 a
1279 <BLANKLINE>
1280 b
1281 """,
1282 ],
1283)
1284def test_trim_doctest_flags_basic_example(parse_numpy: ParserType, docstring: str) -> None:
1285 """Correctly parse simple example docstrings when `trim_doctest_flags` option is turned on.
1287 Parameters:
1288 parse_numpy: Fixture parser.
1289 docstring: The docstring to parse_numpy (parametrized).
1290 """
1291 sections, warnings = parse_numpy(docstring, trim_doctest_flags=True)
1292 assert len(sections) == 1
1293 assert len(sections[0].value) == 2
1294 assert not warnings
1296 # verify that doctest flags have indeed been trimmed
1297 example_str = sections[0].value[1][1]
1298 assert "# doctest: +SKIP" not in example_str
1299 assert "<BLANKLINE>" not in example_str
1302def test_trim_doctest_flags_multi_example(parse_numpy: ParserType) -> None:
1303 """Correctly parse multiline example docstrings when `trim_doctest_flags` option is turned on.
1305 Parameters:
1306 parse_numpy: Fixture parser.
1307 """
1308 docstring = r"""
1309 Examples
1310 --------
1312 Test multiline example blocks.
1313 We want to skip the following test.
1314 >>> 1 + 1 == 3 # doctest: +SKIP
1315 True
1317 And then a few more examples here:
1318 >>> print("a\n\nb")
1319 a
1320 <BLANKLINE>
1321 b
1322 >>> 1 + 1 == 2 # doctest: +SKIP
1323 >>> print(list(range(1, 100))) # doctest: +ELLIPSIS
1324 [1, 2, ..., 98, 99]
1325 """
1326 sections, warnings = parse_numpy(docstring, trim_doctest_flags=True)
1327 assert len(sections) == 1
1328 assert len(sections[0].value) == 4
1329 assert not warnings
1331 # verify that doctest flags have indeed been trimmed
1332 example_str = sections[0].value[1][1]
1333 assert "# doctest: +SKIP" not in example_str
1334 example_str = sections[0].value[3][1]
1335 assert "<BLANKLINE>" not in example_str
1336 assert "\n>>> print(list(range(1, 100)))\n" in example_str
1339def test_parsing_choices(parse_numpy: ParserType) -> None:
1340 """Correctly parse choices.
1342 Parameters:
1343 parse_numpy: Fixture parser.
1344 """
1345 docstring = r"""
1346 Parameters
1347 --------
1348 order : {'C', 'F', 'A'}
1349 Description of `order`.
1350 """
1351 sections, warnings = parse_numpy(docstring, trim_doctest_flags=True)
1352 assert sections[0].value[0].annotation == "'C', 'F', 'A'"
1353 assert not warnings