Coverage for tests/test_docstrings/test_google.py: 100.00%
459 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-15 16:47 +0200
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-15 16:47 +0200
1"""Tests for the [Google-style parser][griffe.docstrings.google]."""
3from __future__ import annotations
5import inspect
6from typing import TYPE_CHECKING
8import pytest
10from griffe import (
11 Attribute,
12 Class,
13 Docstring,
14 DocstringReturn,
15 DocstringSectionKind,
16 ExprName,
17 Function,
18 Module,
19 Parameter,
20 Parameters,
21 parse_docstring_annotation,
22)
24if TYPE_CHECKING:
25 from tests.test_docstrings.helpers import ParserType
28# =============================================================================================
29# Markup flow (multilines, indentation, etc.)
30def test_simple_docstring(parse_google: ParserType) -> None:
31 """Parse a simple docstring.
33 Parameters:
34 parse_google: Fixture parser.
35 """
36 sections, warnings = parse_google("A simple docstring.")
37 assert len(sections) == 1
38 assert not warnings
41def test_multiline_docstring(parse_google: ParserType) -> None:
42 """Parse a multi-line docstring.
44 Parameters:
45 parse_google: Fixture parser.
46 """
47 sections, warnings = parse_google(
48 """
49 A somewhat longer docstring.
51 Blablablabla.
52 """,
53 )
54 assert len(sections) == 1
55 assert not warnings
58def test_parse_partially_indented_lines(parse_google: ParserType) -> None:
59 """Properly handle partially indented lines.
61 Parameters:
62 parse_google: Fixture parser.
63 """
64 docstring = """
65 The available formats are:
66 - JSON
68 The unavailable formats are:
69 - YAML
70 """
71 sections, warnings = parse_google(docstring)
72 assert len(sections) == 2
73 assert sections[0].kind is DocstringSectionKind.admonition
74 assert sections[1].kind is DocstringSectionKind.admonition
75 assert not warnings
78def test_multiple_lines_in_sections_items(parse_google: ParserType) -> None:
79 """Parse multi-line item description.
81 Parameters:
82 parse_google: Fixture parser.
83 """
84 docstring = """
85 Parameters:
86 p (int): This parameter
87 has a description
88 spawning on multiple lines.
90 It even has blank lines in it.
91 Some of these lines
92 are indented for no reason.
93 q (int):
94 What if the first line is blank?
95 """
97 sections, warnings = parse_google(docstring)
98 assert len(sections) == 1
99 assert len(sections[0].value) == 2
100 assert warnings
101 for warning in warnings:
102 assert "should be 4 * 2 = 8 spaces, not" in warning
105def test_code_blocks(parse_google: ParserType) -> None:
106 """Parse code blocks.
108 Parameters:
109 parse_google: Fixture parser.
110 """
111 docstring = """
112 This docstring contains a code block!
114 ```python
115 print("hello")
116 ```
117 """
119 sections, warnings = parse_google(docstring)
120 assert len(sections) == 1
121 assert not warnings
124def test_indented_code_block(parse_google: ParserType) -> None:
125 """Parse indented code blocks.
127 Parameters:
128 parse_google: Fixture parser.
129 """
130 docstring = """
131 This docstring contains a docstring in a code block o_O!
133 \"\"\"
134 This docstring is contained in another docstring O_o!
136 Parameters:
137 s: A string.
138 \"\"\"
139 """
141 sections, warnings = parse_google(docstring)
142 assert len(sections) == 1
143 assert not warnings
146def test_different_indentation(parse_google: ParserType) -> None:
147 """Parse different indentations, warn on confusing indentation.
149 Parameters:
150 parse_google: Fixture parser.
151 """
152 docstring = """
153 Raises:
154 StartAt5: this section's items starts with 5 spaces of indentation.
155 Well indented continuation line.
156 Badly indented continuation line (will trigger a warning).
158 Empty lines are preserved, as well as extra-indentation (this line is a code block).
159 AnyOtherLine: ...starting with exactly 5 spaces is a new item.
160 AnyLine: ...indented with less than 5 spaces signifies the end of the section.
161 """
163 sections, warnings = parse_google(docstring)
164 assert len(sections) == 2
165 assert len(sections[0].value) == 2
166 assert sections[0].value[0].description == (
167 "this section's items starts with 5 spaces of indentation.\n"
168 "Well indented continuation line.\n"
169 "Badly indented continuation line (will trigger a warning).\n"
170 "\n"
171 " Empty lines are preserved, as well as extra-indentation (this line is a code block)."
172 )
173 assert sections[1].value == " AnyLine: ...indented with less than 5 spaces signifies the end of the section."
174 assert len(warnings) == 1
175 assert "should be 5 * 2 = 10 spaces, not 6" in warnings[0]
178def test_empty_indented_lines_in_section_with_items(parse_google: ParserType) -> None:
179 """In sections with items, don't treat lines with just indentation as items.
181 Parameters:
182 parse_google: Fixture parser.
183 """
184 docstring = "Returns:\n only_item: Description.\n \n \n\nSomething."
185 sections, _ = parse_google(docstring)
186 assert len(sections) == 2
187 assert len(sections[0].value) == 1
190@pytest.mark.parametrize(
191 "section",
192 [
193 "Attributes",
194 "Other Parameters",
195 "Parameters",
196 "Raises",
197 "Receives",
198 "Returns",
199 "Warns",
200 "Yields",
201 ],
202)
203def test_starting_item_description_on_new_line(parse_google: ParserType, section: str) -> None:
204 """In sections with items, allow starting item descriptions on a new (indented) line.
206 Parameters:
207 parse_google: Fixture parser.
208 section: A parametrized section name.
209 """
210 docstring = f"\n{section}:\n only_item:\n Description."
211 sections, _ = parse_google(docstring)
212 assert len(sections) == 1
213 assert len(sections[0].value) == 1
214 assert sections[0].value[0].description.strip() == "Description."
217# =============================================================================================
218# Annotations
219def test_parse_without_parent(parse_google: ParserType) -> None:
220 """Parse a docstring without a parent function.
222 Parameters:
223 parse_google: Fixture parser.
224 """
225 sections, warnings = parse_google(
226 """
227 Parameters:
228 void: SEGFAULT.
229 niet: SEGFAULT.
230 nada: SEGFAULT.
231 rien: SEGFAULT.
233 Keyword Args:
234 keywd: SEGFAULT.
236 Exceptions:
237 GlobalError: when nothing works as expected.
239 Returns:
240 Itself.
241 """,
242 )
244 assert len(sections) == 4
245 assert len(warnings) == 6 # missing annotations for parameters and return
246 for warning in warnings[:-1]:
247 assert "parameter" in warning
248 assert "return" in warnings[-1]
251def test_parse_without_annotations(parse_google: ParserType) -> None:
252 """Parse a function docstring without signature annotations.
254 Parameters:
255 parse_google: Fixture parser.
256 """
257 docstring = """
258 Parameters:
259 x: X value.
261 Keyword Args:
262 y: Y value.
264 Returns:
265 Sum X + Y + Z.
266 """
268 sections, warnings = parse_google(
269 docstring,
270 parent=Function(
271 "func",
272 parameters=Parameters(
273 Parameter("x"),
274 Parameter("y"),
275 ),
276 ),
277 )
278 assert len(sections) == 3
279 assert len(warnings) == 3
280 for warning in warnings[:-1]:
281 assert "parameter" in warning
282 assert "return" in warnings[-1]
285def test_parse_with_annotations(parse_google: ParserType) -> None:
286 """Parse a function docstring with signature annotations.
288 Parameters:
289 parse_google: Fixture parser.
290 """
291 docstring = """
292 Parameters:
293 x: X value.
295 Keyword Parameters:
296 y: Y value.
298 Returns:
299 Sum X + Y.
300 """
302 sections, warnings = parse_google(
303 docstring,
304 parent=Function(
305 "func",
306 parameters=Parameters(
307 Parameter("x", annotation="int"),
308 Parameter("y", annotation="int"),
309 ),
310 returns="int",
311 ),
312 )
313 assert len(sections) == 3
314 assert not warnings
317# =============================================================================================
318# Sections
319def test_parse_attributes_section(parse_google: ParserType) -> None:
320 """Parse Attributes sections.
322 Parameters:
323 parse_google: Fixture parser.
324 """
325 docstring = """
326 Attributes:
327 hey: Hey.
328 ho: Ho.
329 """
331 sections, warnings = parse_google(docstring)
332 assert len(sections) == 1
333 assert not warnings
336def test_parse_functions_section(parse_google: ParserType) -> None:
337 """Parse Functions/Methods sections.
339 Parameters:
340 parse_google: Fixture parser.
341 """
342 docstring = """
343 Functions:
344 f(a, b=2): Hello.
345 g: Hi.
347 Methods:
348 f(a, b=2): Hello.
349 g: Hi.
350 """
352 sections, warnings = parse_google(docstring)
353 assert len(sections) == 2
354 for section in sections:
355 assert section.kind is DocstringSectionKind.functions
356 func_f = section.value[0]
357 assert func_f.name == "f"
358 assert func_f.signature == "f(a, b=2)"
359 assert func_f.description == "Hello."
360 func_g = section.value[1]
361 assert func_g.name == "g"
362 assert func_g.signature is None
363 assert func_g.description == "Hi."
364 assert not warnings
367def test_parse_classes_section(parse_google: ParserType) -> None:
368 """Parse Classes sections.
370 Parameters:
371 parse_google: Fixture parser.
372 """
373 docstring = """
374 Classes:
375 C(a, b=2): Hello.
376 D: Hi.
377 """
379 sections, warnings = parse_google(docstring)
380 assert len(sections) == 1
381 assert sections[0].kind is DocstringSectionKind.classes
382 class_c = sections[0].value[0]
383 assert class_c.name == "C"
384 assert class_c.signature == "C(a, b=2)"
385 assert class_c.description == "Hello."
386 class_d = sections[0].value[1]
387 assert class_d.name == "D"
388 assert class_d.signature is None
389 assert class_d.description == "Hi."
390 assert not warnings
393def test_parse_modules_section(parse_google: ParserType) -> None:
394 """Parse Modules sections.
396 Parameters:
397 parse_google: Fixture parser.
398 """
399 docstring = """
400 Modules:
401 m: Hello.
402 n: Hi.
403 """
405 sections, warnings = parse_google(docstring)
406 assert len(sections) == 1
407 assert sections[0].kind is DocstringSectionKind.modules
408 module_m = sections[0].value[0]
409 assert module_m.name == "m"
410 assert module_m.description == "Hello."
411 module_n = sections[0].value[1]
412 assert module_n.name == "n"
413 assert module_n.description == "Hi."
414 assert not warnings
417def test_parse_examples_sections(parse_google: ParserType) -> None:
418 """Parse a function docstring with examples.
420 Parameters:
421 parse_google: Fixture parser.
422 """
423 docstring = """
424 Examples:
425 Some examples that will create a unified code block:
427 >>> 2 + 2 == 5
428 False
429 >>> print("examples")
430 "examples"
432 This is just a random comment in the examples section.
434 These examples will generate two different code blocks. Note the blank line.
436 >>> print("I'm in the first code block!")
437 "I'm in the first code block!"
439 >>> print("I'm in other code block!")
440 "I'm in other code block!"
442 We also can write multiline examples:
444 >>> x = 3 + 2 # doctest: +SKIP
445 >>> y = x + 10
446 >>> y
447 15
449 This is just a typical Python code block:
451 ```python
452 print("examples")
453 return 2 + 2
454 ```
456 Even if it contains doctests, the following block is still considered a normal code-block.
458 ```pycon
459 >>> print("examples")
460 "examples"
461 >>> 2 + 2
462 4
463 ```
465 The blank line before an example is optional.
466 >>> x = 3
467 >>> y = "apple"
468 >>> z = False
469 >>> l = [x, y, z]
470 >>> my_print_list_function(l)
471 3
472 "apple"
473 False
474 """
476 sections, warnings = parse_google(
477 docstring,
478 parent=Function(
479 "func",
480 parameters=Parameters(
481 Parameter("x", annotation="int"),
482 Parameter("y", annotation="int"),
483 ),
484 returns="int",
485 ),
486 trim_doctest_flags=False,
487 )
488 assert len(sections) == 1
489 examples = sections[0]
490 assert len(examples.value) == 9
491 assert examples.value[6][1].startswith(">>> x = 3 + 2 # doctest: +SKIP")
492 assert not warnings
495def test_parse_yields_section(parse_google: ParserType) -> None:
496 """Parse Yields section.
498 Parameters:
499 parse_google: Fixture parser.
500 """
501 docstring = """
502 Yields:
503 x: Floats.
504 (int): Integers.
505 y (int): Same.
506 """
508 sections, warnings = parse_google(docstring)
509 assert len(sections) == 1
510 annotated = sections[0].value[0]
511 assert annotated.name == "x"
512 assert annotated.annotation is None
513 assert annotated.description == "Floats."
514 annotated = sections[0].value[1]
515 assert annotated.name == ""
516 assert annotated.annotation == "int"
517 assert annotated.description == "Integers."
518 annotated = sections[0].value[2]
519 assert annotated.name == "y"
520 assert annotated.annotation == "int"
521 assert annotated.description == "Same."
522 assert len(warnings) == 1
523 assert "'x'" in warnings[0]
526def test_invalid_sections(parse_google: ParserType) -> None:
527 """Warn on invalid sections.
529 Parameters:
530 parse_google: Fixture parser.
531 """
532 docstring = """
533 Parameters:
534 Exceptions:
535 Exceptions:
537 Returns:
538 Note:
540 Important:
541 """
543 sections, warnings = parse_google(docstring)
544 assert len(sections) == 1
545 assert not warnings
548# =============================================================================================
549# Parameters sections
550def test_parse_args_and_kwargs(parse_google: ParserType) -> None:
551 """Parse args and kwargs.
553 Parameters:
554 parse_google: Fixture parser.
555 """
556 docstring = """
557 Parameters:
558 a (str): a parameter.
559 *args (str): args parameters.
560 **kwargs (str): kwargs parameters.
561 """
563 sections, warnings = parse_google(docstring)
564 assert len(sections) == 1
565 expected_parameters = {"a": "a parameter.", "*args": "args parameters.", "**kwargs": "kwargs parameters."}
566 for parameter in sections[0].value:
567 assert parameter.name in expected_parameters
568 assert expected_parameters[parameter.name] == parameter.description
569 assert not warnings
572def test_parse_args_kwargs_keyword_only(parse_google: ParserType) -> None:
573 """Parse args and kwargs.
575 Parameters:
576 parse_google: Fixture parser.
577 """
578 docstring = """
579 Parameters:
580 a (str): a parameter.
581 *args (str): args parameters.
583 Keyword Args:
584 **kwargs (str): kwargs parameters.
585 """
587 sections, warnings = parse_google(docstring)
588 assert len(sections) == 2
589 expected_parameters = {"a": "a parameter.", "*args": "args parameters."}
590 for parameter in sections[0].value:
591 assert parameter.name in expected_parameters
592 assert expected_parameters[parameter.name] == parameter.description
594 expected_parameters = {"**kwargs": "kwargs parameters."}
595 for kwarg in sections[1].value:
596 assert kwarg.name in expected_parameters
597 assert expected_parameters[kwarg.name] == kwarg.description
599 assert not warnings
602def test_parse_types_in_docstring(parse_google: ParserType) -> None:
603 """Parse types in docstring.
605 Parameters:
606 parse_google: Fixture parser.
607 """
608 docstring = """
609 Parameters:
610 x (int): X value.
612 Keyword Args:
613 y (int): Y value.
615 Returns:
616 s (int): Sum X + Y + Z.
617 """
619 sections, warnings = parse_google(
620 docstring,
621 parent=Function(
622 "func",
623 parameters=Parameters(
624 Parameter("x"),
625 Parameter("y"),
626 ),
627 ),
628 )
629 assert len(sections) == 3
630 assert not warnings
632 assert sections[0].kind is DocstringSectionKind.parameters
633 assert sections[1].kind is DocstringSectionKind.other_parameters
634 assert sections[2].kind is DocstringSectionKind.returns
636 (argx,) = sections[0].value
637 (argy,) = sections[1].value
638 (returns,) = sections[2].value
640 assert argx.name == "x"
641 assert argx.annotation.name == "int"
642 assert argx.annotation.canonical_path == "int"
643 assert argx.description == "X value."
644 assert argx.value is None
646 assert argy.name == "y"
647 assert argy.annotation.name == "int"
648 assert argy.annotation.canonical_path == "int"
649 assert argy.description == "Y value."
650 assert argy.value is None
652 assert returns.annotation.name == "int"
653 assert returns.annotation.canonical_path == "int"
654 assert returns.description == "Sum X + Y + Z."
657def test_parse_optional_type_in_docstring(parse_google: ParserType) -> None:
658 """Parse optional types in docstring.
660 Parameters:
661 parse_google: Fixture parser.
662 """
663 docstring = """
664 Parameters:
665 x (int): X value.
666 y (int, optional): Y value.
668 Keyword Args:
669 z (int, optional): Z value.
670 """
672 sections, warnings = parse_google(
673 docstring,
674 parent=Function(
675 "func",
676 parameters=Parameters(
677 Parameter("x", default="1"),
678 Parameter("y", default="None"),
679 Parameter("z", default="None"),
680 ),
681 ),
682 )
683 assert len(sections) == 2
684 assert not warnings
686 assert sections[0].kind is DocstringSectionKind.parameters
687 assert sections[1].kind is DocstringSectionKind.other_parameters
689 argx, argy = sections[0].value
690 (argz,) = sections[1].value
692 assert argx.name == "x"
693 assert argx.annotation.name == "int"
694 assert argx.annotation.canonical_path == "int"
695 assert argx.description == "X value."
696 assert argx.value == "1"
698 assert argy.name == "y"
699 assert argy.annotation.name == "int"
700 assert argy.annotation.canonical_path == "int"
701 assert argy.description == "Y value."
702 assert argy.value == "None"
704 assert argz.name == "z"
705 assert argz.annotation.name == "int"
706 assert argz.annotation.canonical_path == "int"
707 assert argz.description == "Z value."
708 assert argz.value == "None"
711def test_prefer_docstring_types_over_annotations(parse_google: ParserType) -> None:
712 """Prefer the docstring type over the annotation.
714 Parameters:
715 parse_google: Fixture parser.
716 """
717 docstring = """
718 Parameters:
719 x (str): X value.
721 Keyword Args:
722 y (str): Y value.
724 Returns:
725 (str): Sum X + Y + Z.
726 """
728 sections, warnings = parse_google(
729 docstring,
730 parent=Function(
731 "func",
732 parameters=Parameters(
733 Parameter("x", annotation="int"),
734 Parameter("y", annotation="int"),
735 ),
736 returns="int",
737 ),
738 )
739 assert len(sections) == 3
740 assert not warnings
742 assert sections[0].kind is DocstringSectionKind.parameters
743 assert sections[1].kind is DocstringSectionKind.other_parameters
744 assert sections[2].kind is DocstringSectionKind.returns
746 (argx,) = sections[0].value
747 (argy,) = sections[1].value
748 (returns,) = sections[2].value
750 assert argx.name == "x"
751 assert argx.annotation.name == "str"
752 assert argx.annotation.canonical_path == "str"
753 assert argx.description == "X value."
755 assert argy.name == "y"
756 assert argy.annotation.name == "str"
757 assert argy.annotation.canonical_path == "str"
758 assert argy.description == "Y value."
760 assert returns.annotation.name == "str"
761 assert returns.annotation.canonical_path == "str"
762 assert returns.description == "Sum X + Y + Z."
765def test_parameter_line_without_colon(parse_google: ParserType) -> None:
766 """Warn when missing colon.
768 Parameters:
769 parse_google: Fixture parser.
770 """
771 docstring = """
772 Parameters:
773 x is an integer.
774 """
776 sections, warnings = parse_google(docstring)
777 assert len(sections) == 0 # empty sections are discarded
778 assert len(warnings) == 1
779 assert "pair" in warnings[0]
782def test_parameter_line_without_colon_keyword_only(parse_google: ParserType) -> None:
783 """Warn when missing colon.
785 Parameters:
786 parse_google: Fixture parser.
787 """
788 docstring = """
789 Keyword Args:
790 x is an integer.
791 """
793 sections, warnings = parse_google(docstring)
794 assert len(sections) == 0 # empty sections are discarded
795 assert len(warnings) == 1
796 assert "pair" in warnings[0]
799def test_warn_about_unknown_parameters(parse_google: ParserType) -> None:
800 """Warn about unknown parameters in "Parameters" sections.
802 Parameters:
803 parse_google: Fixture parser.
804 """
805 docstring = """
806 Parameters:
807 x (int): Integer.
808 y (int): Integer.
809 """
811 _, warnings = parse_google(
812 docstring,
813 parent=Function(
814 "func",
815 parameters=Parameters(
816 Parameter("a"),
817 Parameter("y"),
818 ),
819 ),
820 )
821 assert len(warnings) == 1
822 assert "'x' does not appear in the function signature" in warnings[0]
825def test_never_warn_about_unknown_other_parameters(parse_google: ParserType) -> None:
826 """Never warn about unknown parameters in "Other parameters" sections.
828 Parameters:
829 parse_google: Fixture parser.
830 """
831 docstring = """
832 Other Parameters:
833 x (int): Integer.
834 z (int): Integer.
835 """
837 _, warnings = parse_google(
838 docstring,
839 parent=Function(
840 "func",
841 parameters=Parameters(
842 Parameter("a"),
843 Parameter("y"),
844 ),
845 ),
846 )
847 assert not warnings
850def test_unknown_params_scan_doesnt_crash_without_parameters(parse_google: ParserType) -> None:
851 """Assert we don't crash when parsing parameters sections and parent object does not have parameters.
853 Parameters:
854 parse_google: Fixture parser.
855 """
856 docstring = """
857 Parameters:
858 this (str): This.
859 that (str): That.
860 """
862 _, warnings = parse_google(docstring, parent=Module("mod"))
863 assert not warnings
866def test_class_uses_init_parameters(parse_google: ParserType) -> None:
867 """Assert we use the `__init__` parameters when parsing classes' parameters sections.
869 Parameters:
870 parse_google: Fixture parser.
871 """
872 docstring = """
873 Parameters:
874 x: X value.
875 """
876 parent = Class("c")
877 parent["__init__"] = Function("__init__", parameters=Parameters(Parameter("x", annotation="int")))
878 sections, warnings = parse_google(docstring, parent=parent)
879 assert not warnings
880 argx = sections[0].value[0]
881 assert argx.name == "x"
882 assert argx.annotation == "int"
883 assert argx.description == "X value."
886# TODO: possible feature
887# def test_missing_parameter(parse_google: ParserType) -> None:
888# """Warn on missing parameter in docstring.
889#
890# Parameters:
891# parse_google: Fixture parser.
892# """
893# docstring = """
894# Parameters:
895# x: Integer.
896# """
897# assert not warnings
900# =============================================================================================
901# Attributes sections
902def test_retrieve_attributes_annotation_from_parent(parse_google: ParserType) -> None:
903 """Retrieve the annotations of attributes from the parent object.
905 Parameters:
906 parse_google: Fixture parser.
907 """
908 docstring = """
909 Summary.
911 Attributes:
912 a: Whatever.
913 b: Whatever.
914 """
915 parent = Class("cls")
916 parent["a"] = Attribute("a", annotation=ExprName("int"))
917 parent["b"] = Attribute("b", annotation=ExprName("str"))
918 sections, _ = parse_google(docstring, parent=parent)
919 attributes = sections[1].value
920 assert attributes[0].name == "a"
921 assert attributes[0].annotation.name == "int"
922 assert attributes[1].name == "b"
923 assert attributes[1].annotation.name == "str"
926# =============================================================================================
927# Yields sections
928def test_parse_yields_section_with_return_annotation(parse_google: ParserType) -> None:
929 """Parse Yields section with a return annotation in the parent function.
931 Parameters:
932 parse_google: Fixture parser.
933 """
934 docstring = """
935 Yields:
936 Integers.
937 """
939 function = Function("func", returns="Iterator[int]")
940 sections, warnings = parse_google(docstring, function)
941 assert len(sections) == 1
942 annotated = sections[0].value[0]
943 assert annotated.annotation == "Iterator[int]"
944 assert annotated.description == "Integers."
945 assert not warnings
948@pytest.mark.parametrize(
949 "return_annotation",
950 [
951 "Iterator[tuple[int, float]]",
952 "Generator[tuple[int, float], ..., ...]",
953 ],
954)
955def test_parse_yields_tuple_in_iterator_or_generator(parse_google: ParserType, return_annotation: str) -> None:
956 """Parse Yields annotations in Iterator or Generator types.
958 Parameters:
959 parse_google: Fixture parser.
960 return_annotation: Parametrized return annotation as a string.
961 """
962 docstring = """
963 Summary.
965 Yields:
966 a: Whatever.
967 b: Whatever.
968 """
969 sections, _ = parse_google(
970 docstring,
971 parent=Function(
972 "func",
973 returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))),
974 ),
975 )
976 yields = sections[1].value
977 assert yields[0].name == "a"
978 assert yields[0].annotation.name == "int"
979 assert yields[1].name == "b"
980 assert yields[1].annotation.name == "float"
983@pytest.mark.parametrize(
984 "return_annotation",
985 [
986 "Iterator[int]",
987 "Generator[int, None, None]",
988 ],
989)
990def test_extract_yielded_type_with_single_return_item(parse_google: ParserType, return_annotation: str) -> None:
991 """Extract main type annotation from Iterator or Generator.
993 Parameters:
994 parse_google: Fixture parser.
995 return_annotation: Parametrized return annotation as a string.
996 """
997 docstring = """
998 Summary.
1000 Yields:
1001 A number.
1002 """
1003 sections, _ = parse_google(
1004 docstring,
1005 parent=Function(
1006 "func",
1007 returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))),
1008 ),
1009 )
1010 yields = sections[1].value
1011 assert yields[0].annotation.name == "int"
1014def test_yield_section_in_property(parse_google: ParserType) -> None:
1015 """No warnings when parsing Yields section in a property.
1017 Parameters:
1018 parse_google: Fixture parser.
1019 """
1020 docstring = """
1021 Summary.
1023 Yields:
1024 A number.
1025 """
1026 sections, warnings = parse_google(
1027 docstring,
1028 parent=Attribute(
1029 "prop",
1030 annotation=parse_docstring_annotation("Iterator[int]", Docstring("d", parent=Attribute("a"))),
1031 ),
1032 )
1033 assert not warnings
1034 yields = sections[1].value
1035 assert yields[0].annotation.name == "int"
1038# =============================================================================================
1039# Receives sections
1040def test_parse_receives_tuple_in_generator(parse_google: ParserType) -> None:
1041 """Parse Receives annotations in Generator type.
1043 Parameters:
1044 parse_google: Fixture parser.
1045 """
1046 docstring = """
1047 Summary.
1049 Receives:
1050 a: Whatever.
1051 b: Whatever.
1052 """
1053 sections, _ = parse_google(
1054 docstring,
1055 parent=Function(
1056 "func",
1057 returns=parse_docstring_annotation(
1058 "Generator[..., tuple[int, float], ...]",
1059 Docstring("d", parent=Function("f")),
1060 ),
1061 ),
1062 )
1063 receives = sections[1].value
1064 assert receives[0].name == "a"
1065 assert receives[0].annotation.name == "int"
1066 assert receives[1].name == "b"
1067 assert receives[1].annotation.name == "float"
1070@pytest.mark.parametrize(
1071 "return_annotation",
1072 [
1073 "Generator[int, float, None]",
1074 ],
1075)
1076def test_extract_received_type_with_single_return_item(parse_google: ParserType, return_annotation: str) -> None:
1077 """Extract main type annotation from Iterator or Generator.
1079 Parameters:
1080 parse_google: Fixture parser.
1081 return_annotation: Parametrized return annotation as a string.
1082 """
1083 docstring = """
1084 Summary.
1086 Receives:
1087 A floating point number.
1088 """
1089 sections, _ = parse_google(
1090 docstring,
1091 parent=Function(
1092 "func",
1093 returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))),
1094 ),
1095 )
1096 receives = sections[1].value
1097 assert receives[0].annotation.name == "float"
1100# =============================================================================================
1101# Returns sections
1102def test_parse_returns_tuple_in_generator(parse_google: ParserType) -> None:
1103 """Parse Returns annotations in Generator type.
1105 Parameters:
1106 parse_google: Fixture parser.
1107 """
1108 docstring = """
1109 Summary.
1111 Returns:
1112 a: Whatever.
1113 b: Whatever.
1114 """
1115 sections, _ = parse_google(
1116 docstring,
1117 parent=Function(
1118 "func",
1119 returns=parse_docstring_annotation(
1120 "Generator[..., ..., tuple[int, float]]",
1121 Docstring("d", parent=Function("f")),
1122 ),
1123 ),
1124 )
1125 returns = sections[1].value
1126 assert returns[0].name == "a"
1127 assert returns[0].annotation.name == "int"
1128 assert returns[1].name == "b"
1129 assert returns[1].annotation.name == "float"
1132# =============================================================================================
1133# Parser special features
1134def test_parse_admonitions(parse_google: ParserType) -> None:
1135 """Parse admonitions.
1137 Parameters:
1138 parse_google: Fixture parser.
1139 """
1140 docstring = """
1141 Important note:
1142 Hello.
1144 Note: With title.
1145 Hello again.
1147 Something:
1148 Something.
1149 """
1151 sections, warnings = parse_google(docstring)
1152 assert len(sections) == 3
1153 assert not warnings
1154 assert sections[0].title == "Important note"
1155 assert sections[0].value.kind == "important-note"
1156 assert sections[0].value.contents == "Hello."
1157 assert sections[1].title == "With title."
1158 assert sections[1].value.kind == "note"
1159 assert sections[1].value.contents == "Hello again."
1160 assert sections[2].title == "Something"
1161 assert sections[2].value.kind == "something"
1162 assert sections[2].value.contents == "Something."
1165@pytest.mark.parametrize(
1166 "docstring",
1167 [
1168 """
1169 ******************************
1170 This looks like an admonition:
1171 ******************************
1172 """,
1173 """
1174 Warning: this line also looks
1175 like an admonition.
1176 """,
1177 """
1178 Matching but not an admonition:
1182 - Multiple empty lines above.
1183 """,
1184 """Last line:""",
1185 ],
1186)
1187def test_handle_false_admonitions_correctly(parse_google: ParserType, docstring: str) -> None:
1188 """Correctly handle lines that look like admonitions.
1190 Parameters:
1191 parse_google: Fixture parser.
1192 docstring: The docstring to parse (parametrized).
1193 """
1194 sections, warnings = parse_google(docstring)
1195 assert len(sections) == 1
1196 assert sections[0].kind is DocstringSectionKind.text
1197 assert len(sections[0].value.splitlines()) == len(inspect.cleandoc(docstring).splitlines())
1198 assert not warnings
1201def test_dont_insert_admonition_before_current_section(parse_google: ParserType) -> None:
1202 """Check that admonitions are inserted at the right place.
1204 Parameters:
1205 parse_google: Fixture parser.
1206 """
1207 docstring = """
1208 Summary.
1210 Short description.
1212 Info:
1213 Something useful.
1214 """
1215 sections, _ = parse_google(docstring)
1216 assert len(sections) == 2
1217 assert sections[0].kind is DocstringSectionKind.text
1218 assert sections[1].kind is DocstringSectionKind.admonition
1221@pytest.mark.parametrize(
1222 "docstring",
1223 [
1224 "",
1225 "\n",
1226 "\n\n",
1227 "Summary.",
1228 "Summary.\n\n\n",
1229 "Summary.\n\nParagraph.",
1230 "Summary\non two lines.",
1231 "Summary\non two lines.\n\nParagraph.",
1232 ],
1233)
1234def test_ignore_init_summary(parse_google: ParserType, docstring: str) -> None:
1235 """Correctly ignore summary in `__init__` methods' docstrings.
1237 Parameters:
1238 parse_google: Fixture parser.
1239 docstring: The docstring to parse_google (parametrized).
1240 """
1241 sections, _ = parse_google(docstring, parent=Function("__init__", parent=Class("C")), ignore_init_summary=True)
1242 for section in sections:
1243 assert "Summary" not in section.value
1245 if docstring.strip():
1246 sections, _ = parse_google(docstring, parent=Function("__init__", parent=Module("M")), ignore_init_summary=True)
1247 assert "Summary" in sections[0].value
1248 sections, _ = parse_google(docstring, parent=Function("f", parent=Class("C")), ignore_init_summary=True)
1249 assert "Summary" in sections[0].value
1250 sections, _ = parse_google(docstring, ignore_init_summary=True)
1251 assert "Summary" in sections[0].value
1254@pytest.mark.parametrize(
1255 "docstring",
1256 [
1257 """
1258 Examples:
1259 Base case 1. We want to skip the following test.
1260 >>> 1 + 1 == 3 # doctest: +SKIP
1261 True
1262 """,
1263 r"""
1264 Examples:
1265 Base case 2. We have a blankline test.
1266 >>> print("a\n\nb")
1267 a
1268 <BLANKLINE>
1269 b
1270 """,
1271 ],
1272)
1273def test_trim_doctest_flags_basic_example(parse_google: ParserType, docstring: str) -> None:
1274 """Correctly parse simple example docstrings when `trim_doctest_flags` option is turned on.
1276 Parameters:
1277 parse_google: Fixture parser.
1278 docstring: The docstring to parse (parametrized).
1279 """
1280 sections, warnings = parse_google(docstring, trim_doctest_flags=True)
1281 assert len(sections) == 1
1282 assert len(sections[0].value) == 2
1283 assert not warnings
1285 # verify that doctest flags have indeed been trimmed
1286 example_str = sections[0].value[1][1]
1287 assert "# doctest: +SKIP" not in example_str
1288 assert "<BLANKLINE>" not in example_str
1291def test_trim_doctest_flags_multi_example(parse_google: ParserType) -> None:
1292 """Correctly parse multiline example docstrings when `trim_doctest_flags` option is turned on.
1294 Parameters:
1295 parse_google: Fixture parser.
1296 """
1297 docstring = r"""
1298 Examples:
1299 Test multiline example blocks.
1300 We want to skip the following test.
1301 >>> 1 + 1 == 3 # doctest: +SKIP
1302 True
1304 And then a few more examples here:
1305 >>> print("a\n\nb")
1306 a
1307 <BLANKLINE>
1308 b
1309 >>> 1 + 1 == 2 # doctest: +SKIP
1310 >>> print(list(range(1, 100))) # doctest: +ELLIPSIS
1311 [1, 2, ..., 98, 99]
1312 """
1313 sections, warnings = parse_google(docstring, trim_doctest_flags=True)
1314 assert len(sections) == 1
1315 assert len(sections[0].value) == 4
1316 assert not warnings
1318 # verify that doctest flags have indeed been trimmed
1319 example_str = sections[0].value[1][1]
1320 assert "# doctest: +SKIP" not in example_str
1321 example_str = sections[0].value[3][1]
1322 assert "<BLANKLINE>" not in example_str
1323 assert "\n>>> print(list(range(1, 100)))\n" in example_str
1326def test_single_line_with_trailing_whitespace(parse_google: ParserType) -> None:
1327 """Don't crash on single line docstrings with trailing whitespace.
1329 Parameters:
1330 parse_google: Fixture parser.
1331 """
1332 docstring = "a: b\n "
1333 sections, warnings = parse_google(docstring, trim_doctest_flags=True)
1334 assert len(sections) == 1
1335 assert sections[0].kind is DocstringSectionKind.text
1336 assert not warnings
1339@pytest.mark.parametrize(
1340 ("returns_multiple_items", "return_annotation", "expected"),
1341 [
1342 (
1343 False,
1344 None,
1345 [DocstringReturn("", description="XXXXXXX\n YYYYYYY\nZZZZZZZ", annotation=None)],
1346 ),
1347 (
1348 False,
1349 "tuple[int, int]",
1350 [DocstringReturn("", description="XXXXXXX\n YYYYYYY\nZZZZZZZ", annotation="tuple[int, int]")],
1351 ),
1352 (
1353 True,
1354 None,
1355 [
1356 DocstringReturn("", description="XXXXXXX\nYYYYYYY", annotation=None),
1357 DocstringReturn("", description="ZZZZZZZ", annotation=None),
1358 ],
1359 ),
1360 (
1361 True,
1362 "tuple[int,int]",
1363 [
1364 DocstringReturn("", description="XXXXXXX\nYYYYYYY", annotation="int"),
1365 DocstringReturn("", description="ZZZZZZZ", annotation="int"),
1366 ],
1367 ),
1368 ],
1369)
1370def test_parse_returns_multiple_items(
1371 parse_google: ParserType,
1372 returns_multiple_items: bool,
1373 return_annotation: str,
1374 expected: list[DocstringReturn],
1375) -> None:
1376 """Parse Returns section with and without multiple items.
1378 Parameters:
1379 parse_google: Fixture parser.
1380 returns_multiple_items: Whether the `Returns` section has multiple items.
1381 return_annotation: The return annotation of the function to parse.
1382 expected: The expected value of the parsed Returns section.
1383 """
1384 parent = (
1385 Function("func", returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))))
1386 if return_annotation is not None
1387 else None
1388 )
1389 docstring = """
1390 Returns:
1391 XXXXXXX
1392 YYYYYYY
1393 ZZZZZZZ
1394 """
1395 sections, _ = parse_google(
1396 docstring,
1397 returns_multiple_items=returns_multiple_items,
1398 parent=parent,
1399 )
1401 assert len(sections) == 1
1402 assert len(sections[0].value) == len(expected)
1404 for annotated, expected_ in zip(sections[0].value, expected):
1405 assert annotated.name == expected_.name
1406 assert str(annotated.annotation) == str(expected_.annotation)
1407 assert annotated.description == expected_.description
1410def test_avoid_false_positive_sections(parse_google: ParserType) -> None:
1411 """Avoid false positive when parsing sections.
1413 Parameters:
1414 parse_google: Fixture parser.
1415 """
1416 docstring = """
1417 Summary.
1418 Modules:
1419 Not a modules section.
1420 No blank line before title:
1421 Not an admonition.
1423 Blank line after title:
1425 Not an admonition.
1427 Modules:
1429 Not a modules section.
1430 Modules:
1432 Not a modules section.
1433 No blank line before and blank line after:
1435 Not an admonition.
1437 Classes:
1439 - Text.
1440 """
1441 sections, warnings = parse_google(docstring)
1442 assert len(sections) == 1
1443 assert "Classes" in sections[0].value
1444 assert "Text" in sections[0].value
1445 assert len(warnings) == 6
1446 assert warnings == [
1447 "Possible section skipped, reasons: Missing blank line above section",
1448 "Possible admonition skipped, reasons: Missing blank line above admonition",
1449 "Possible admonition skipped, reasons: Extraneous blank line below admonition title",
1450 "Possible section skipped, reasons: Extraneous blank line below section title",
1451 "Possible section skipped, reasons: Missing blank line above section; Extraneous blank line below section title",
1452 "Possible admonition skipped, reasons: Missing blank line above admonition; Extraneous blank line below admonition title",
1453 ]
1456def test_type_in_returns_without_parentheses(parse_google: ParserType) -> None:
1457 """Assert we can parse the return type without parentheses.
1459 Parameters:
1460 parse_google: Fixture parser.
1461 """
1462 docstring = """
1463 Summary.
1465 Returns:
1466 int: Description
1467 on several lines.
1468 """
1469 sections, warnings = parse_google(docstring, returns_named_value=False)
1470 assert len(sections) == 2
1471 assert not warnings
1472 retval = sections[1].value[0]
1473 assert retval.name == ""
1474 assert retval.annotation == "int"
1475 assert retval.description == "Description\non several lines."
1477 docstring = """
1478 Summary.
1480 Returns:
1481 Description
1482 on several lines.
1483 """
1484 sections, warnings = parse_google(docstring, returns_named_value=False)
1485 assert len(sections) == 2
1486 assert len(warnings) == 1
1487 retval = sections[1].value[0]
1488 assert retval.name == ""
1489 assert retval.annotation is None
1490 assert retval.description == "Description\non several lines."
1493def test_reading_property_type_in_summary(parse_google: ParserType) -> None:
1494 """Assert we can parse the return type of properties in their summary.
1496 Parameters:
1497 parse_google: Fixture parser.
1498 """
1499 docstring = "str: Description of the property."
1500 parent = Attribute("prop")
1501 parent.labels.add("property")
1502 sections, warnings = parse_google(docstring, returns_type_in_property_summary=True, parent=parent)
1503 assert len(sections) == 2
1504 assert sections[0].kind is DocstringSectionKind.text
1505 assert sections[1].kind is DocstringSectionKind.returns
1506 retval = sections[1].value[0]
1507 assert retval.name == ""
1508 assert retval.annotation.name == "str"
1509 assert retval.description == ""