Coverage for tests/test_docstrings/test_google.py: 100.00%
515 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 [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 DocstringReceive,
15 DocstringReturn,
16 DocstringSectionKind,
17 DocstringYield,
18 ExprName,
19 Function,
20 Module,
21 Parameter,
22 Parameters,
23 parse_docstring_annotation,
24)
26if TYPE_CHECKING:
27 from tests.test_docstrings.helpers import ParserType
30# =============================================================================================
31# Markup flow (multilines, indentation, etc.)
32def test_simple_docstring(parse_google: ParserType) -> None:
33 """Parse a simple docstring.
35 Parameters:
36 parse_google: Fixture parser.
37 """
38 sections, warnings = parse_google("A simple docstring.")
39 assert len(sections) == 1
40 assert not warnings
43def test_multiline_docstring(parse_google: ParserType) -> None:
44 """Parse a multi-line docstring.
46 Parameters:
47 parse_google: Fixture parser.
48 """
49 sections, warnings = parse_google(
50 """
51 A somewhat longer docstring.
53 Blablablabla.
54 """,
55 )
56 assert len(sections) == 1
57 assert not warnings
60def test_parse_partially_indented_lines(parse_google: ParserType) -> None:
61 """Properly handle partially indented lines.
63 Parameters:
64 parse_google: Fixture parser.
65 """
66 docstring = """
67 The available formats are:
68 - JSON
70 The unavailable formats are:
71 - YAML
72 """
73 sections, warnings = parse_google(docstring)
74 assert len(sections) == 2
75 assert sections[0].kind is DocstringSectionKind.admonition
76 assert sections[1].kind is DocstringSectionKind.admonition
77 assert not warnings
80def test_multiple_lines_in_sections_items(parse_google: ParserType) -> None:
81 """Parse multi-line item description.
83 Parameters:
84 parse_google: Fixture parser.
85 """
86 docstring = """
87 Parameters:
88 p (int): This parameter
89 has a description
90 spawning on multiple lines.
92 It even has blank lines in it.
93 Some of these lines
94 are indented for no reason.
95 q (int):
96 What if the first line is blank?
97 """
99 sections, warnings = parse_google(docstring)
100 assert len(sections) == 1
101 assert len(sections[0].value) == 2
102 assert warnings
103 for warning in warnings:
104 assert "should be 4 * 2 = 8 spaces, not" in warning
107def test_code_blocks(parse_google: ParserType) -> None:
108 """Parse code blocks.
110 Parameters:
111 parse_google: Fixture parser.
112 """
113 docstring = """
114 This docstring contains a code block!
116 ```python
117 print("hello")
118 ```
119 """
121 sections, warnings = parse_google(docstring)
122 assert len(sections) == 1
123 assert not warnings
126def test_indented_code_block(parse_google: ParserType) -> None:
127 """Parse indented code blocks.
129 Parameters:
130 parse_google: Fixture parser.
131 """
132 docstring = """
133 This docstring contains a docstring in a code block o_O!
135 \"\"\"
136 This docstring is contained in another docstring O_o!
138 Parameters:
139 s: A string.
140 \"\"\"
141 """
143 sections, warnings = parse_google(docstring)
144 assert len(sections) == 1
145 assert not warnings
148def test_different_indentation(parse_google: ParserType) -> None:
149 """Parse different indentations, warn on confusing indentation.
151 Parameters:
152 parse_google: Fixture parser.
153 """
154 docstring = """
155 Raises:
156 StartAt5: this section's items starts with 5 spaces of indentation.
157 Well indented continuation line.
158 Badly indented continuation line (will trigger a warning).
160 Empty lines are preserved, as well as extra-indentation (this line is a code block).
161 AnyOtherLine: ...starting with exactly 5 spaces is a new item.
162 AnyLine: ...indented with less than 5 spaces signifies the end of the section.
163 """
165 sections, warnings = parse_google(docstring)
166 assert len(sections) == 2
167 assert len(sections[0].value) == 2
168 assert sections[0].value[0].description == (
169 "this section's items starts with 5 spaces of indentation.\n"
170 "Well indented continuation line.\n"
171 "Badly indented continuation line (will trigger a warning).\n"
172 "\n"
173 " Empty lines are preserved, as well as extra-indentation (this line is a code block)."
174 )
175 assert sections[1].value == " AnyLine: ...indented with less than 5 spaces signifies the end of the section."
176 assert len(warnings) == 1
177 assert "should be 5 * 2 = 10 spaces, not 6" in warnings[0]
180def test_empty_indented_lines_in_section_with_items(parse_google: ParserType) -> None:
181 """In sections with items, don't treat lines with just indentation as items.
183 Parameters:
184 parse_google: Fixture parser.
185 """
186 docstring = "Returns:\n only_item: Description.\n \n \n\nSomething."
187 sections, _ = parse_google(docstring)
188 assert len(sections) == 2
189 assert len(sections[0].value) == 1
192@pytest.mark.parametrize(
193 "section",
194 [
195 "Attributes",
196 "Other Parameters",
197 "Parameters",
198 "Raises",
199 "Receives",
200 "Returns",
201 "Warns",
202 "Yields",
203 ],
204)
205def test_starting_item_description_on_new_line(parse_google: ParserType, section: str) -> None:
206 """In sections with items, allow starting item descriptions on a new (indented) line.
208 Parameters:
209 parse_google: Fixture parser.
210 section: A parametrized section name.
211 """
212 docstring = f"\n{section}:\n only_item:\n Description."
213 sections, _ = parse_google(docstring)
214 assert len(sections) == 1
215 assert len(sections[0].value) == 1
216 assert sections[0].value[0].description.strip() == "Description."
219# =============================================================================================
220# Annotations
221def test_parse_without_parent(parse_google: ParserType) -> None:
222 """Parse a docstring without a parent function.
224 Parameters:
225 parse_google: Fixture parser.
226 """
227 sections, warnings = parse_google(
228 """
229 Parameters:
230 void: SEGFAULT.
231 niet: SEGFAULT.
232 nada: SEGFAULT.
233 rien: SEGFAULT.
235 Keyword Args:
236 keywd: SEGFAULT.
238 Exceptions:
239 GlobalError: when nothing works as expected.
241 Returns:
242 Itself.
243 """,
244 )
246 assert len(sections) == 4
247 assert len(warnings) == 6 # missing annotations for parameters and return
248 for warning in warnings[:-1]:
249 assert "parameter" in warning
250 assert "return" in warnings[-1]
253def test_parse_without_annotations(parse_google: ParserType) -> None:
254 """Parse a function docstring without signature annotations.
256 Parameters:
257 parse_google: Fixture parser.
258 """
259 docstring = """
260 Parameters:
261 x: X value.
263 Keyword Args:
264 y: Y value.
266 Returns:
267 Sum X + Y + Z.
268 """
270 sections, warnings = parse_google(
271 docstring,
272 parent=Function(
273 "func",
274 parameters=Parameters(
275 Parameter("x"),
276 Parameter("y"),
277 ),
278 ),
279 )
280 assert len(sections) == 3
281 assert len(warnings) == 3
282 for warning in warnings[:-1]:
283 assert "parameter" in warning
284 assert "return" in warnings[-1]
287def test_parse_with_annotations(parse_google: ParserType) -> None:
288 """Parse a function docstring with signature annotations.
290 Parameters:
291 parse_google: Fixture parser.
292 """
293 docstring = """
294 Parameters:
295 x: X value.
297 Keyword Parameters:
298 y: Y value.
300 Returns:
301 Sum X + Y.
302 """
304 sections, warnings = parse_google(
305 docstring,
306 parent=Function(
307 "func",
308 parameters=Parameters(
309 Parameter("x", annotation="int"),
310 Parameter("y", annotation="int"),
311 ),
312 returns="int",
313 ),
314 )
315 assert len(sections) == 3
316 assert not warnings
319# =============================================================================================
320# Sections
321def test_parse_attributes_section(parse_google: ParserType) -> None:
322 """Parse Attributes sections.
324 Parameters:
325 parse_google: Fixture parser.
326 """
327 docstring = """
328 Attributes:
329 hey: Hey.
330 ho: Ho.
331 """
333 sections, warnings = parse_google(docstring)
334 assert len(sections) == 1
335 assert not warnings
338def test_parse_functions_section(parse_google: ParserType) -> None:
339 """Parse Functions/Methods sections.
341 Parameters:
342 parse_google: Fixture parser.
343 """
344 docstring = """
345 Functions:
346 f(a, b=2): Hello.
347 g: Hi.
349 Methods:
350 f(a, b=2): Hello.
351 g: Hi.
352 """
354 sections, warnings = parse_google(docstring)
355 assert len(sections) == 2
356 for section in sections:
357 assert section.kind is DocstringSectionKind.functions
358 func_f = section.value[0]
359 assert func_f.name == "f"
360 assert func_f.signature == "f(a, b=2)"
361 assert func_f.description == "Hello."
362 func_g = section.value[1]
363 assert func_g.name == "g"
364 assert func_g.signature is None
365 assert func_g.description == "Hi."
366 assert not warnings
369def test_parse_classes_section(parse_google: ParserType) -> None:
370 """Parse Classes sections.
372 Parameters:
373 parse_google: Fixture parser.
374 """
375 docstring = """
376 Classes:
377 C(a, b=2): Hello.
378 D: Hi.
379 """
381 sections, warnings = parse_google(docstring)
382 assert len(sections) == 1
383 assert sections[0].kind is DocstringSectionKind.classes
384 class_c = sections[0].value[0]
385 assert class_c.name == "C"
386 assert class_c.signature == "C(a, b=2)"
387 assert class_c.description == "Hello."
388 class_d = sections[0].value[1]
389 assert class_d.name == "D"
390 assert class_d.signature is None
391 assert class_d.description == "Hi."
392 assert not warnings
395def test_parse_modules_section(parse_google: ParserType) -> None:
396 """Parse Modules sections.
398 Parameters:
399 parse_google: Fixture parser.
400 """
401 docstring = """
402 Modules:
403 m: Hello.
404 n: Hi.
405 """
407 sections, warnings = parse_google(docstring)
408 assert len(sections) == 1
409 assert sections[0].kind is DocstringSectionKind.modules
410 module_m = sections[0].value[0]
411 assert module_m.name == "m"
412 assert module_m.description == "Hello."
413 module_n = sections[0].value[1]
414 assert module_n.name == "n"
415 assert module_n.description == "Hi."
416 assert not warnings
419def test_parse_examples_sections(parse_google: ParserType) -> None:
420 """Parse a function docstring with examples.
422 Parameters:
423 parse_google: Fixture parser.
424 """
425 docstring = """
426 Examples:
427 Some examples that will create a unified code block:
429 >>> 2 + 2 == 5
430 False
431 >>> print("examples")
432 "examples"
434 This is just a random comment in the examples section.
436 These examples will generate two different code blocks. Note the blank line.
438 >>> print("I'm in the first code block!")
439 "I'm in the first code block!"
441 >>> print("I'm in other code block!")
442 "I'm in other code block!"
444 We also can write multiline examples:
446 >>> x = 3 + 2 # doctest: +SKIP
447 >>> y = x + 10
448 >>> y
449 15
451 This is just a typical Python code block:
453 ```python
454 print("examples")
455 return 2 + 2
456 ```
458 Even if it contains doctests, the following block is still considered a normal code-block.
460 ```pycon
461 >>> print("examples")
462 "examples"
463 >>> 2 + 2
464 4
465 ```
467 The blank line before an example is optional.
468 >>> x = 3
469 >>> y = "apple"
470 >>> z = False
471 >>> l = [x, y, z]
472 >>> my_print_list_function(l)
473 3
474 "apple"
475 False
476 """
478 sections, warnings = parse_google(
479 docstring,
480 parent=Function(
481 "func",
482 parameters=Parameters(
483 Parameter("x", annotation="int"),
484 Parameter("y", annotation="int"),
485 ),
486 returns="int",
487 ),
488 trim_doctest_flags=False,
489 )
490 assert len(sections) == 1
491 examples = sections[0]
492 assert len(examples.value) == 9
493 assert examples.value[6][1].startswith(">>> x = 3 + 2 # doctest: +SKIP")
494 assert not warnings
497def test_parse_yields_section(parse_google: ParserType) -> None:
498 """Parse Yields section.
500 Parameters:
501 parse_google: Fixture parser.
502 """
503 docstring = """
504 Yields:
505 x: Floats.
506 (int): Integers.
507 y (int): Same.
508 """
510 sections, warnings = parse_google(docstring)
511 assert len(sections) == 1
512 annotated = sections[0].value[0]
513 assert annotated.name == "x"
514 assert annotated.annotation is None
515 assert annotated.description == "Floats."
516 annotated = sections[0].value[1]
517 assert annotated.name == ""
518 assert annotated.annotation == "int"
519 assert annotated.description == "Integers."
520 annotated = sections[0].value[2]
521 assert annotated.name == "y"
522 assert annotated.annotation == "int"
523 assert annotated.description == "Same."
524 assert len(warnings) == 1
525 assert "'x'" in warnings[0]
528def test_invalid_sections(parse_google: ParserType) -> None:
529 """Warn on invalid sections.
531 Parameters:
532 parse_google: Fixture parser.
533 """
534 docstring = """
535 Parameters:
536 Exceptions:
537 Exceptions:
539 Returns:
540 Note:
542 Important:
543 """
545 sections, warnings = parse_google(docstring)
546 assert len(sections) == 1
547 assert not warnings
550# =============================================================================================
551# Parameters sections
552def test_parse_args_and_kwargs(parse_google: ParserType) -> None:
553 """Parse args and kwargs.
555 Parameters:
556 parse_google: Fixture parser.
557 """
558 docstring = """
559 Parameters:
560 a (str): a parameter.
561 *args (str): args parameters.
562 **kwargs (str): kwargs parameters.
563 """
565 sections, warnings = parse_google(docstring)
566 assert len(sections) == 1
567 expected_parameters = {"a": "a parameter.", "*args": "args parameters.", "**kwargs": "kwargs parameters."}
568 for parameter in sections[0].value:
569 assert parameter.name in expected_parameters
570 assert expected_parameters[parameter.name] == parameter.description
571 assert not warnings
574def test_parse_args_kwargs_keyword_only(parse_google: ParserType) -> None:
575 """Parse args and kwargs.
577 Parameters:
578 parse_google: Fixture parser.
579 """
580 docstring = """
581 Parameters:
582 a (str): a parameter.
583 *args (str): args parameters.
585 Keyword Args:
586 **kwargs (str): kwargs parameters.
587 """
589 sections, warnings = parse_google(docstring)
590 assert len(sections) == 2
591 expected_parameters = {"a": "a parameter.", "*args": "args parameters."}
592 for parameter in sections[0].value:
593 assert parameter.name in expected_parameters
594 assert expected_parameters[parameter.name] == parameter.description
596 expected_parameters = {"**kwargs": "kwargs parameters."}
597 for kwarg in sections[1].value:
598 assert kwarg.name in expected_parameters
599 assert expected_parameters[kwarg.name] == kwarg.description
601 assert not warnings
604def test_parse_types_in_docstring(parse_google: ParserType) -> None:
605 """Parse types in docstring.
607 Parameters:
608 parse_google: Fixture parser.
609 """
610 docstring = """
611 Parameters:
612 x (int): X value.
614 Keyword Args:
615 y (int): Y value.
617 Returns:
618 s (int): Sum X + Y + Z.
619 """
621 sections, warnings = parse_google(
622 docstring,
623 parent=Function(
624 "func",
625 parameters=Parameters(
626 Parameter("x"),
627 Parameter("y"),
628 ),
629 ),
630 )
631 assert len(sections) == 3
632 assert not warnings
634 assert sections[0].kind is DocstringSectionKind.parameters
635 assert sections[1].kind is DocstringSectionKind.other_parameters
636 assert sections[2].kind is DocstringSectionKind.returns
638 (argx,) = sections[0].value
639 (argy,) = sections[1].value
640 (returns,) = sections[2].value
642 assert argx.name == "x"
643 assert argx.annotation.name == "int"
644 assert argx.annotation.canonical_path == "int"
645 assert argx.description == "X value."
646 assert argx.value is None
648 assert argy.name == "y"
649 assert argy.annotation.name == "int"
650 assert argy.annotation.canonical_path == "int"
651 assert argy.description == "Y value."
652 assert argy.value is None
654 assert returns.annotation.name == "int"
655 assert returns.annotation.canonical_path == "int"
656 assert returns.description == "Sum X + Y + Z."
659def test_parse_optional_type_in_docstring(parse_google: ParserType) -> None:
660 """Parse optional types in docstring.
662 Parameters:
663 parse_google: Fixture parser.
664 """
665 docstring = """
666 Parameters:
667 x (int): X value.
668 y (int, optional): Y value.
670 Keyword Args:
671 z (int, optional): Z value.
672 """
674 sections, warnings = parse_google(
675 docstring,
676 parent=Function(
677 "func",
678 parameters=Parameters(
679 Parameter("x", default="1"),
680 Parameter("y", default="None"),
681 Parameter("z", default="None"),
682 ),
683 ),
684 )
685 assert len(sections) == 2
686 assert not warnings
688 assert sections[0].kind is DocstringSectionKind.parameters
689 assert sections[1].kind is DocstringSectionKind.other_parameters
691 argx, argy = sections[0].value
692 (argz,) = sections[1].value
694 assert argx.name == "x"
695 assert argx.annotation.name == "int"
696 assert argx.annotation.canonical_path == "int"
697 assert argx.description == "X value."
698 assert argx.value == "1"
700 assert argy.name == "y"
701 assert argy.annotation.name == "int"
702 assert argy.annotation.canonical_path == "int"
703 assert argy.description == "Y value."
704 assert argy.value == "None"
706 assert argz.name == "z"
707 assert argz.annotation.name == "int"
708 assert argz.annotation.canonical_path == "int"
709 assert argz.description == "Z value."
710 assert argz.value == "None"
713def test_prefer_docstring_types_over_annotations(parse_google: ParserType) -> None:
714 """Prefer the docstring type over the annotation.
716 Parameters:
717 parse_google: Fixture parser.
718 """
719 docstring = """
720 Parameters:
721 x (str): X value.
723 Keyword Args:
724 y (str): Y value.
726 Returns:
727 (str): Sum X + Y + Z.
728 """
730 sections, warnings = parse_google(
731 docstring,
732 parent=Function(
733 "func",
734 parameters=Parameters(
735 Parameter("x", annotation="int"),
736 Parameter("y", annotation="int"),
737 ),
738 returns="int",
739 ),
740 )
741 assert len(sections) == 3
742 assert not warnings
744 assert sections[0].kind is DocstringSectionKind.parameters
745 assert sections[1].kind is DocstringSectionKind.other_parameters
746 assert sections[2].kind is DocstringSectionKind.returns
748 (argx,) = sections[0].value
749 (argy,) = sections[1].value
750 (returns,) = sections[2].value
752 assert argx.name == "x"
753 assert argx.annotation.name == "str"
754 assert argx.annotation.canonical_path == "str"
755 assert argx.description == "X value."
757 assert argy.name == "y"
758 assert argy.annotation.name == "str"
759 assert argy.annotation.canonical_path == "str"
760 assert argy.description == "Y value."
762 assert returns.annotation.name == "str"
763 assert returns.annotation.canonical_path == "str"
764 assert returns.description == "Sum X + Y + Z."
767def test_parameter_line_without_colon(parse_google: ParserType) -> None:
768 """Warn when missing colon.
770 Parameters:
771 parse_google: Fixture parser.
772 """
773 docstring = """
774 Parameters:
775 x is an integer.
776 """
778 sections, warnings = parse_google(docstring)
779 assert len(sections) == 0 # empty sections are discarded
780 assert len(warnings) == 1
781 assert "pair" in warnings[0]
784def test_parameter_line_without_colon_keyword_only(parse_google: ParserType) -> None:
785 """Warn when missing colon.
787 Parameters:
788 parse_google: Fixture parser.
789 """
790 docstring = """
791 Keyword Args:
792 x is an integer.
793 """
795 sections, warnings = parse_google(docstring)
796 assert len(sections) == 0 # empty sections are discarded
797 assert len(warnings) == 1
798 assert "pair" in warnings[0]
801def test_warn_about_unknown_parameters(parse_google: ParserType) -> None:
802 """Warn about unknown parameters in "Parameters" sections.
804 Parameters:
805 parse_google: Fixture parser.
806 """
807 docstring = """
808 Parameters:
809 x (int): Integer.
810 y (int): Integer.
811 """
813 _, warnings = parse_google(
814 docstring,
815 parent=Function(
816 "func",
817 parameters=Parameters(
818 Parameter("a"),
819 Parameter("y"),
820 ),
821 ),
822 )
823 assert len(warnings) == 1
824 assert "'x' does not appear in the function signature" in warnings[0]
827def test_never_warn_about_unknown_other_parameters(parse_google: ParserType) -> None:
828 """Never warn about unknown parameters in "Other parameters" sections.
830 Parameters:
831 parse_google: Fixture parser.
832 """
833 docstring = """
834 Other Parameters:
835 x (int): Integer.
836 z (int): Integer.
837 """
839 _, warnings = parse_google(
840 docstring,
841 parent=Function(
842 "func",
843 parameters=Parameters(
844 Parameter("a"),
845 Parameter("y"),
846 ),
847 ),
848 )
849 assert not warnings
852def test_unknown_params_scan_doesnt_crash_without_parameters(parse_google: ParserType) -> None:
853 """Assert we don't crash when parsing parameters sections and parent object does not have parameters.
855 Parameters:
856 parse_google: Fixture parser.
857 """
858 docstring = """
859 Parameters:
860 this (str): This.
861 that (str): That.
862 """
864 _, warnings = parse_google(docstring, parent=Module("mod"))
865 assert not warnings
868def test_class_uses_init_parameters(parse_google: ParserType) -> None:
869 """Assert we use the `__init__` parameters when parsing classes' parameters sections.
871 Parameters:
872 parse_google: Fixture parser.
873 """
874 docstring = """
875 Parameters:
876 x: X value.
877 """
878 parent = Class("c")
879 parent["__init__"] = Function("__init__", parameters=Parameters(Parameter("x", annotation="int")))
880 sections, warnings = parse_google(docstring, parent=parent)
881 assert not warnings
882 argx = sections[0].value[0]
883 assert argx.name == "x"
884 assert argx.annotation == "int"
885 assert argx.description == "X value."
888# TODO: possible feature
889# def test_missing_parameter(parse_google: ParserType) -> None:
890# """Warn on missing parameter in docstring.
891#
892# Parameters:
893# parse_google: Fixture parser.
894# """
895# docstring = """
896# Parameters:
897# x: Integer.
898# """
899# assert not warnings
902# =============================================================================================
903# Attributes sections
904def test_retrieve_attributes_annotation_from_parent(parse_google: ParserType) -> None:
905 """Retrieve the annotations of attributes from the parent object.
907 Parameters:
908 parse_google: Fixture parser.
909 """
910 docstring = """
911 Summary.
913 Attributes:
914 a: Whatever.
915 b: Whatever.
916 """
917 parent = Class("cls")
918 parent["a"] = Attribute("a", annotation=ExprName("int"))
919 parent["b"] = Attribute("b", annotation=ExprName("str"))
920 sections, _ = parse_google(docstring, parent=parent)
921 attributes = sections[1].value
922 assert attributes[0].name == "a"
923 assert attributes[0].annotation.name == "int"
924 assert attributes[1].name == "b"
925 assert attributes[1].annotation.name == "str"
928# =============================================================================================
929# Yields sections
930def test_parse_yields_section_with_return_annotation(parse_google: ParserType) -> None:
931 """Parse Yields section with a return annotation in the parent function.
933 Parameters:
934 parse_google: Fixture parser.
935 """
936 docstring = """
937 Yields:
938 Integers.
939 """
941 function = Function("func", returns="Iterator[int]")
942 sections, warnings = parse_google(docstring, function)
943 assert len(sections) == 1
944 annotated = sections[0].value[0]
945 assert annotated.annotation == "Iterator[int]"
946 assert annotated.description == "Integers."
947 assert not warnings
950@pytest.mark.parametrize(
951 "return_annotation",
952 [
953 "Iterator[tuple[int, float]]",
954 "Generator[tuple[int, float], ..., ...]",
955 ],
956)
957def test_parse_yields_tuple_in_iterator_or_generator(parse_google: ParserType, return_annotation: str) -> None:
958 """Parse Yields annotations in Iterator or Generator types.
960 Parameters:
961 parse_google: Fixture parser.
962 return_annotation: Parametrized return annotation as a string.
963 """
964 docstring = """
965 Summary.
967 Yields:
968 a: Whatever.
969 b: Whatever.
970 """
971 sections, _ = parse_google(
972 docstring,
973 parent=Function(
974 "func",
975 returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))),
976 ),
977 )
978 yields = sections[1].value
979 assert yields[0].name == "a"
980 assert yields[0].annotation.name == "int"
981 assert yields[1].name == "b"
982 assert yields[1].annotation.name == "float"
985@pytest.mark.parametrize(
986 "return_annotation",
987 [
988 "Iterator[int]",
989 "Generator[int, None, None]",
990 ],
991)
992def test_extract_yielded_type_with_single_return_item(parse_google: ParserType, return_annotation: str) -> None:
993 """Extract main type annotation from Iterator or Generator.
995 Parameters:
996 parse_google: Fixture parser.
997 return_annotation: Parametrized return annotation as a string.
998 """
999 docstring = """
1000 Summary.
1002 Yields:
1003 A number.
1004 """
1005 sections, _ = parse_google(
1006 docstring,
1007 parent=Function(
1008 "func",
1009 returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))),
1010 ),
1011 )
1012 yields = sections[1].value
1013 assert yields[0].annotation.name == "int"
1016def test_yield_section_in_property(parse_google: ParserType) -> None:
1017 """No warnings when parsing Yields section in a property.
1019 Parameters:
1020 parse_google: Fixture parser.
1021 """
1022 docstring = """
1023 Summary.
1025 Yields:
1026 A number.
1027 """
1028 sections, warnings = parse_google(
1029 docstring,
1030 parent=Attribute(
1031 "prop",
1032 annotation=parse_docstring_annotation("Iterator[int]", Docstring("d", parent=Attribute("a"))),
1033 ),
1034 )
1035 assert not warnings
1036 yields = sections[1].value
1037 assert yields[0].annotation.name == "int"
1040# =============================================================================================
1041# Receives sections
1042def test_parse_receives_tuple_in_generator(parse_google: ParserType) -> None:
1043 """Parse Receives annotations in Generator type.
1045 Parameters:
1046 parse_google: Fixture parser.
1047 """
1048 docstring = """
1049 Summary.
1051 Receives:
1052 a: Whatever.
1053 b: Whatever.
1054 """
1055 sections, _ = parse_google(
1056 docstring,
1057 parent=Function(
1058 "func",
1059 returns=parse_docstring_annotation(
1060 "Generator[..., tuple[int, float], ...]",
1061 Docstring("d", parent=Function("f")),
1062 ),
1063 ),
1064 )
1065 receives = sections[1].value
1066 assert receives[0].name == "a"
1067 assert receives[0].annotation.name == "int"
1068 assert receives[1].name == "b"
1069 assert receives[1].annotation.name == "float"
1072@pytest.mark.parametrize(
1073 "return_annotation",
1074 [
1075 "Generator[int, float, None]",
1076 ],
1077)
1078def test_extract_received_type_with_single_return_item(parse_google: ParserType, return_annotation: str) -> None:
1079 """Extract main type annotation from Iterator or Generator.
1081 Parameters:
1082 parse_google: Fixture parser.
1083 return_annotation: Parametrized return annotation as a string.
1084 """
1085 docstring = """
1086 Summary.
1088 Receives:
1089 A floating point number.
1090 """
1091 sections, _ = parse_google(
1092 docstring,
1093 parent=Function(
1094 "func",
1095 returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))),
1096 ),
1097 )
1098 receives = sections[1].value
1099 assert receives[0].annotation.name == "float"
1102# =============================================================================================
1103# Returns sections
1104def test_parse_returns_tuple_in_generator(parse_google: ParserType) -> None:
1105 """Parse Returns annotations in Generator type.
1107 Parameters:
1108 parse_google: Fixture parser.
1109 """
1110 docstring = """
1111 Summary.
1113 Returns:
1114 a: Whatever.
1115 b: Whatever.
1116 """
1117 sections, _ = parse_google(
1118 docstring,
1119 parent=Function(
1120 "func",
1121 returns=parse_docstring_annotation(
1122 "Generator[..., ..., tuple[int, float]]",
1123 Docstring("d", parent=Function("f")),
1124 ),
1125 ),
1126 )
1127 returns = sections[1].value
1128 assert returns[0].name == "a"
1129 assert returns[0].annotation.name == "int"
1130 assert returns[1].name == "b"
1131 assert returns[1].annotation.name == "float"
1134# =============================================================================================
1135# Parser special features
1136def test_parse_admonitions(parse_google: ParserType) -> None:
1137 """Parse admonitions.
1139 Parameters:
1140 parse_google: Fixture parser.
1141 """
1142 docstring = """
1143 Important note:
1144 Hello.
1146 Note: With title.
1147 Hello again.
1149 Something:
1150 Something.
1151 """
1153 sections, warnings = parse_google(docstring)
1154 assert len(sections) == 3
1155 assert not warnings
1156 assert sections[0].title == "Important note"
1157 assert sections[0].value.kind == "important-note"
1158 assert sections[0].value.contents == "Hello."
1159 assert sections[1].title == "With title."
1160 assert sections[1].value.kind == "note"
1161 assert sections[1].value.contents == "Hello again."
1162 assert sections[2].title == "Something"
1163 assert sections[2].value.kind == "something"
1164 assert sections[2].value.contents == "Something."
1167@pytest.mark.parametrize(
1168 "docstring",
1169 [
1170 """
1171 ******************************
1172 This looks like an admonition:
1173 ******************************
1174 """,
1175 """
1176 Warning: this line also looks
1177 like an admonition.
1178 """,
1179 """
1180 Matching but not an admonition:
1184 - Multiple empty lines above.
1185 """,
1186 """Last line:""",
1187 ],
1188)
1189def test_handle_false_admonitions_correctly(parse_google: ParserType, docstring: str) -> None:
1190 """Correctly handle lines that look like admonitions.
1192 Parameters:
1193 parse_google: Fixture parser.
1194 docstring: The docstring to parse (parametrized).
1195 """
1196 sections, warnings = parse_google(docstring)
1197 assert len(sections) == 1
1198 assert sections[0].kind is DocstringSectionKind.text
1199 assert len(sections[0].value.splitlines()) == len(inspect.cleandoc(docstring).splitlines())
1200 assert not warnings
1203def test_dont_insert_admonition_before_current_section(parse_google: ParserType) -> None:
1204 """Check that admonitions are inserted at the right place.
1206 Parameters:
1207 parse_google: Fixture parser.
1208 """
1209 docstring = """
1210 Summary.
1212 Short description.
1214 Info:
1215 Something useful.
1216 """
1217 sections, _ = parse_google(docstring)
1218 assert len(sections) == 2
1219 assert sections[0].kind is DocstringSectionKind.text
1220 assert sections[1].kind is DocstringSectionKind.admonition
1223@pytest.mark.parametrize(
1224 "docstring",
1225 [
1226 "",
1227 "\n",
1228 "\n\n",
1229 "Summary.",
1230 "Summary.\n\n\n",
1231 "Summary.\n\nParagraph.",
1232 "Summary\non two lines.",
1233 "Summary\non two lines.\n\nParagraph.",
1234 ],
1235)
1236def test_ignore_init_summary(parse_google: ParserType, docstring: str) -> None:
1237 """Correctly ignore summary in `__init__` methods' docstrings.
1239 Parameters:
1240 parse_google: Fixture parser.
1241 docstring: The docstring to parse_google (parametrized).
1242 """
1243 sections, _ = parse_google(docstring, parent=Function("__init__", parent=Class("C")), ignore_init_summary=True)
1244 for section in sections:
1245 assert "Summary" not in section.value
1247 if docstring.strip():
1248 sections, _ = parse_google(docstring, parent=Function("__init__", parent=Module("M")), ignore_init_summary=True)
1249 assert "Summary" in sections[0].value
1250 sections, _ = parse_google(docstring, parent=Function("f", parent=Class("C")), ignore_init_summary=True)
1251 assert "Summary" in sections[0].value
1252 sections, _ = parse_google(docstring, ignore_init_summary=True)
1253 assert "Summary" in sections[0].value
1256@pytest.mark.parametrize(
1257 "docstring",
1258 [
1259 """
1260 Examples:
1261 Base case 1. We want to skip the following test.
1262 >>> 1 + 1 == 3 # doctest: +SKIP
1263 True
1264 """,
1265 r"""
1266 Examples:
1267 Base case 2. We have a blankline test.
1268 >>> print("a\n\nb")
1269 a
1270 <BLANKLINE>
1271 b
1272 """,
1273 ],
1274)
1275def test_trim_doctest_flags_basic_example(parse_google: ParserType, docstring: str) -> None:
1276 """Correctly parse simple example docstrings when `trim_doctest_flags` option is turned on.
1278 Parameters:
1279 parse_google: Fixture parser.
1280 docstring: The docstring to parse (parametrized).
1281 """
1282 sections, warnings = parse_google(docstring, trim_doctest_flags=True)
1283 assert len(sections) == 1
1284 assert len(sections[0].value) == 2
1285 assert not warnings
1287 # verify that doctest flags have indeed been trimmed
1288 example_str = sections[0].value[1][1]
1289 assert "# doctest: +SKIP" not in example_str
1290 assert "<BLANKLINE>" not in example_str
1293def test_trim_doctest_flags_multi_example(parse_google: ParserType) -> None:
1294 """Correctly parse multiline example docstrings when `trim_doctest_flags` option is turned on.
1296 Parameters:
1297 parse_google: Fixture parser.
1298 """
1299 docstring = r"""
1300 Examples:
1301 Test multiline example blocks.
1302 We want to skip the following test.
1303 >>> 1 + 1 == 3 # doctest: +SKIP
1304 True
1306 And then a few more examples here:
1307 >>> print("a\n\nb")
1308 a
1309 <BLANKLINE>
1310 b
1311 >>> 1 + 1 == 2 # doctest: +SKIP
1312 >>> print(list(range(1, 100))) # doctest: +ELLIPSIS
1313 [1, 2, ..., 98, 99]
1314 """
1315 sections, warnings = parse_google(docstring, trim_doctest_flags=True)
1316 assert len(sections) == 1
1317 assert len(sections[0].value) == 4
1318 assert not warnings
1320 # verify that doctest flags have indeed been trimmed
1321 example_str = sections[0].value[1][1]
1322 assert "# doctest: +SKIP" not in example_str
1323 example_str = sections[0].value[3][1]
1324 assert "<BLANKLINE>" not in example_str
1325 assert "\n>>> print(list(range(1, 100)))\n" in example_str
1328def test_single_line_with_trailing_whitespace(parse_google: ParserType) -> None:
1329 """Don't crash on single line docstrings with trailing whitespace.
1331 Parameters:
1332 parse_google: Fixture parser.
1333 """
1334 docstring = "a: b\n "
1335 sections, warnings = parse_google(docstring, trim_doctest_flags=True)
1336 assert len(sections) == 1
1337 assert sections[0].kind is DocstringSectionKind.text
1338 assert not warnings
1341@pytest.mark.parametrize(
1342 ("returns_multiple_items", "return_annotation", "expected"),
1343 [
1344 (
1345 False,
1346 None,
1347 [DocstringReturn("", description="XXXXXXX\n YYYYYYY\nZZZZZZZ", annotation=None)],
1348 ),
1349 (
1350 False,
1351 "tuple[int, int]",
1352 [DocstringReturn("", description="XXXXXXX\n YYYYYYY\nZZZZZZZ", annotation="tuple[int, int]")],
1353 ),
1354 (
1355 True,
1356 None,
1357 [
1358 DocstringReturn("", description="XXXXXXX\nYYYYYYY", annotation=None),
1359 DocstringReturn("", description="ZZZZZZZ", annotation=None),
1360 ],
1361 ),
1362 (
1363 True,
1364 "tuple[int,int]",
1365 [
1366 DocstringReturn("", description="XXXXXXX\nYYYYYYY", annotation="int"),
1367 DocstringReturn("", description="ZZZZZZZ", annotation="int"),
1368 ],
1369 ),
1370 ],
1371)
1372def test_parse_returns_multiple_items(
1373 parse_google: ParserType,
1374 returns_multiple_items: bool,
1375 return_annotation: str,
1376 expected: list[DocstringReturn],
1377) -> None:
1378 """Parse Returns section with and without multiple items.
1380 Parameters:
1381 parse_google: Fixture parser.
1382 returns_multiple_items: Whether the `Returns` section has multiple items.
1383 return_annotation: The return annotation of the function to parse.
1384 expected: The expected value of the parsed Returns section.
1385 """
1386 parent = (
1387 Function("func", returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))))
1388 if return_annotation is not None
1389 else None
1390 )
1391 docstring = """
1392 Returns:
1393 XXXXXXX
1394 YYYYYYY
1395 ZZZZZZZ
1396 """
1397 sections, _ = parse_google(
1398 docstring,
1399 returns_multiple_items=returns_multiple_items,
1400 parent=parent,
1401 )
1403 assert len(sections) == 1
1404 assert len(sections[0].value) == len(expected)
1406 for annotated, expected_ in zip(sections[0].value, expected):
1407 assert annotated.name == expected_.name
1408 assert str(annotated.annotation) == str(expected_.annotation)
1409 assert annotated.description == expected_.description
1412@pytest.mark.parametrize(
1413 ("returns_multiple_items", "return_annotation", "expected"),
1414 [
1415 (
1416 False,
1417 None,
1418 [DocstringYield("", description="XXXXXXX\n YYYYYYY\nZZZZZZZ", annotation=None)],
1419 ),
1420 (
1421 False,
1422 "Iterator[tuple[int, int]]",
1423 [DocstringYield("", description="XXXXXXX\n YYYYYYY\nZZZZZZZ", annotation="tuple[int, int]")],
1424 ),
1425 (
1426 True,
1427 None,
1428 [
1429 DocstringYield("", description="XXXXXXX\nYYYYYYY", annotation=None),
1430 DocstringYield("", description="ZZZZZZZ", annotation=None),
1431 ],
1432 ),
1433 (
1434 True,
1435 "Iterator[tuple[int,int]]",
1436 [
1437 DocstringYield("", description="XXXXXXX\nYYYYYYY", annotation="int"),
1438 DocstringYield("", description="ZZZZZZZ", annotation="int"),
1439 ],
1440 ),
1441 ],
1442)
1443def test_parse_yields_multiple_items(
1444 parse_google: ParserType,
1445 returns_multiple_items: bool,
1446 return_annotation: str,
1447 expected: list[DocstringYield],
1448) -> None:
1449 """Parse Returns section with and without multiple items.
1451 Parameters:
1452 parse_google: Fixture parser.
1453 returns_multiple_items: Whether the `Returns` and `Yields` sections have multiple items.
1454 return_annotation: The return annotation of the function to parse. Usually an `Iterator`.
1455 expected: The expected value of the parsed Yields section.
1456 """
1457 parent = (
1458 Function("func", returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))))
1459 if return_annotation is not None
1460 else None
1461 )
1462 docstring = """
1463 Yields:
1464 XXXXXXX
1465 YYYYYYY
1466 ZZZZZZZ
1467 """
1468 sections, _ = parse_google(
1469 docstring,
1470 returns_multiple_items=returns_multiple_items,
1471 parent=parent,
1472 )
1474 assert len(sections) == 1
1475 assert len(sections[0].value) == len(expected)
1477 for annotated, expected_ in zip(sections[0].value, expected):
1478 assert annotated.name == expected_.name
1479 assert str(annotated.annotation) == str(expected_.annotation)
1480 assert annotated.description == expected_.description
1483@pytest.mark.parametrize(
1484 ("receives_multiple_items", "return_annotation", "expected"),
1485 [
1486 (
1487 False,
1488 None,
1489 [DocstringReceive("", description="XXXXXXX\n YYYYYYY\nZZZZZZZ", annotation=None)],
1490 ),
1491 (
1492 False,
1493 "Generator[..., tuple[int, int], ...]",
1494 [DocstringReceive("", description="XXXXXXX\n YYYYYYY\nZZZZZZZ", annotation="tuple[int, int]")],
1495 ),
1496 (
1497 True,
1498 None,
1499 [
1500 DocstringReceive("", description="XXXXXXX\nYYYYYYY", annotation=None),
1501 DocstringReceive("", description="ZZZZZZZ", annotation=None),
1502 ],
1503 ),
1504 (
1505 True,
1506 "Generator[..., tuple[int, int], ...]",
1507 [
1508 DocstringReceive("", description="XXXXXXX\nYYYYYYY", annotation="int"),
1509 DocstringReceive("", description="ZZZZZZZ", annotation="int"),
1510 ],
1511 ),
1512 ],
1513)
1514def test_parse_receives_multiple_items(
1515 parse_google: ParserType,
1516 receives_multiple_items: bool,
1517 return_annotation: str,
1518 expected: list[DocstringReceive],
1519) -> None:
1520 """Parse Returns section with and without multiple items.
1522 Parameters:
1523 parse_google: Fixture parser.
1524 receives_multiple_items: Whether the `Receives` section has multiple items.
1525 return_annotation: The return annotation of the function to parse. Usually a `Generator`.
1526 expected: The expected value of the parsed Receives section.
1527 """
1528 parent = (
1529 Function("func", returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))))
1530 if return_annotation is not None
1531 else None
1532 )
1533 docstring = """
1534 Receives:
1535 XXXXXXX
1536 YYYYYYY
1537 ZZZZZZZ
1538 """
1539 sections, _ = parse_google(
1540 docstring,
1541 receives_multiple_items=receives_multiple_items,
1542 parent=parent,
1543 )
1545 assert len(sections) == 1
1546 assert len(sections[0].value) == len(expected)
1548 for annotated, expected_ in zip(sections[0].value, expected):
1549 assert annotated.name == expected_.name
1550 assert str(annotated.annotation) == str(expected_.annotation)
1551 assert annotated.description == expected_.description
1554def test_avoid_false_positive_sections(parse_google: ParserType) -> None:
1555 """Avoid false positive when parsing sections.
1557 Parameters:
1558 parse_google: Fixture parser.
1559 """
1560 docstring = """
1561 Summary.
1562 Modules:
1563 Not a modules section.
1564 No blank line before title:
1565 Not an admonition.
1567 Blank line after title:
1569 Not an admonition.
1571 Modules:
1573 Not a modules section.
1574 Modules:
1576 Not a modules section.
1577 No blank line before and blank line after:
1579 Not an admonition.
1581 Classes:
1583 - Text.
1584 """
1585 sections, warnings = parse_google(docstring)
1586 assert len(sections) == 1
1587 assert "Classes" in sections[0].value
1588 assert "Text" in sections[0].value
1589 assert len(warnings) == 6
1590 assert warnings == [
1591 "Possible section skipped, reasons: Missing blank line above section",
1592 "Possible admonition skipped, reasons: Missing blank line above admonition",
1593 "Possible admonition skipped, reasons: Extraneous blank line below admonition title",
1594 "Possible section skipped, reasons: Extraneous blank line below section title",
1595 "Possible section skipped, reasons: Missing blank line above section; Extraneous blank line below section title",
1596 "Possible admonition skipped, reasons: Missing blank line above admonition; Extraneous blank line below admonition title",
1597 ]
1600def test_type_in_returns_without_parentheses(parse_google: ParserType) -> None:
1601 """Assert we can parse the return type without parentheses.
1603 Parameters:
1604 parse_google: Fixture parser.
1605 """
1606 docstring = """
1607 Summary.
1609 Returns:
1610 int: Description
1611 on several lines.
1612 """
1613 sections, warnings = parse_google(docstring, returns_named_value=False)
1614 assert len(sections) == 2
1615 assert not warnings
1616 retval = sections[1].value[0]
1617 assert retval.name == ""
1618 assert retval.annotation == "int"
1619 assert retval.description == "Description\non several lines."
1621 docstring = """
1622 Summary.
1624 Returns:
1625 Description
1626 on several lines.
1627 """
1628 sections, warnings = parse_google(docstring, returns_named_value=False)
1629 assert len(sections) == 2
1630 assert len(warnings) == 1
1631 retval = sections[1].value[0]
1632 assert retval.name == ""
1633 assert retval.annotation is None
1634 assert retval.description == "Description\non several lines."
1637def test_type_in_yields_without_parentheses(parse_google: ParserType) -> None:
1638 """Assert we can parse the return type without parentheses.
1640 Parameters:
1641 parse_google: Fixture parser.
1642 """
1643 docstring = """
1644 Summary.
1646 Yields:
1647 int: Description
1648 on several lines.
1649 """
1650 sections, warnings = parse_google(docstring, returns_named_value=False)
1651 assert len(sections) == 2
1652 assert not warnings
1653 retval = sections[1].value[0]
1654 assert retval.name == ""
1655 assert retval.annotation == "int"
1656 assert retval.description == "Description\non several lines."
1658 docstring = """
1659 Summary.
1661 Yields:
1662 Description
1663 on several lines.
1664 """
1665 sections, warnings = parse_google(docstring, returns_named_value=False)
1666 assert len(sections) == 2
1667 assert len(warnings) == 1
1668 retval = sections[1].value[0]
1669 assert retval.name == ""
1670 assert retval.annotation is None
1671 assert retval.description == "Description\non several lines."
1674def test_type_in_receives_without_parentheses(parse_google: ParserType) -> None:
1675 """Assert we can parse the return type without parentheses.
1677 Parameters:
1678 parse_google: Fixture parser.
1679 """
1680 docstring = """
1681 Summary.
1683 Receives:
1684 int: Description
1685 on several lines.
1686 """
1687 sections, warnings = parse_google(docstring, receives_named_value=False)
1688 assert len(sections) == 2
1689 assert not warnings
1690 retval = sections[1].value[0]
1691 assert retval.name == ""
1692 assert retval.annotation == "int"
1693 assert retval.description == "Description\non several lines."
1695 docstring = """
1696 Summary.
1698 Receives:
1699 Description
1700 on several lines.
1701 """
1702 sections, warnings = parse_google(docstring, receives_named_value=False)
1703 assert len(sections) == 2
1704 assert len(warnings) == 1
1705 retval = sections[1].value[0]
1706 assert retval.name == ""
1707 assert retval.annotation is None
1708 assert retval.description == "Description\non several lines."
1711def test_reading_property_type_in_summary(parse_google: ParserType) -> None:
1712 """Assert we can parse the return type of properties in their summary.
1714 Parameters:
1715 parse_google: Fixture parser.
1716 """
1717 docstring = "str: Description of the property."
1718 parent = Attribute("prop")
1719 parent.labels.add("property")
1720 sections, warnings = parse_google(docstring, returns_type_in_property_summary=True, parent=parent)
1721 assert len(sections) == 2
1722 assert sections[0].kind is DocstringSectionKind.text
1723 assert sections[1].kind is DocstringSectionKind.returns
1724 retval = sections[1].value[0]
1725 assert retval.name == ""
1726 assert retval.annotation.name == "str"
1727 assert retval.description == ""