Coverage for tests/test_parsers/test_docstrings/test_restructured_text.py: 97.04%
372 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-09 18:24 +0100
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-09 18:24 +0100
1"""Tests for [the `parsers.docstrings.google` module][pytkdocs.parsers.docstrings.google]."""
3import inspect
4from textwrap import dedent
5from typing import Any, Optional
7import pytest
9from pytkdocs.loader import Loader
10from pytkdocs.objects import Object
11from pytkdocs.parsers.docstrings.base import AnnotatedObject, Attribute, Parameter, Section, empty
12from pytkdocs.parsers.docstrings.restructured_text import RestructuredText
13from pytkdocs.serializer import serialize_attribute
16class DummyObject:
17 def __init__(self, signature, return_type): # noqa: D107, ANN001
18 self.path = "o"
19 self.signature = signature
20 self.type = return_type
23SOME_NAME = "foo"
24SOME_TEXT = "descriptive test text"
25SOME_EXTRA_TEXT = "more test text"
26SOME_EXCEPTION_NAME = "SomeException"
27SOME_OTHER_EXCEPTION_NAME = "SomeOtherException"
30def dedent_strip(text: str) -> str:
31 return dedent(text).strip()
34def parse(obj: Any, *, strip_docstring: bool = True) -> tuple[list[Section], list[str]]:
35 """Helper to parse a docstring."""
36 return parse_detailed(inspect.getdoc(obj) or "", inspect.signature(obj), strip_docstring=strip_docstring)
39def parse_detailed(
40 docstring: str,
41 signature: Optional[inspect.Signature] = None,
42 return_type: Any = inspect.Signature.empty,
43 *,
44 strip_docstring: bool = True,
45) -> tuple[list[Section], list[str]]:
46 """Helper to parse a docstring."""
47 docstring = dedent_strip(docstring) if strip_docstring else dedent(docstring)
49 return RestructuredText().parse(docstring, {"obj": DummyObject(signature, return_type)})
52def assert_parameter_equal(actual: Parameter, expected: Parameter) -> None:
53 assert actual.name == expected.name
54 assert_annotated_obj_equal(actual, expected)
55 assert actual.kind == expected.kind
56 assert actual.default == expected.default
59def assert_attribute_equal(actual: Attribute, expected: Attribute) -> None:
60 assert actual.name == expected.name
61 assert_annotated_obj_equal(actual, expected)
64def assert_annotated_obj_equal(actual: AnnotatedObject, expected: AnnotatedObject) -> None:
65 assert actual.annotation == expected.annotation
66 assert actual.description == expected.description
69def get_rst_object_documentation(dotted_fixture_subpath: str) -> Object:
70 return Loader(docstring_style="restructured-text").get_object_documentation(
71 f"tests.fixtures.parsing.restructured_text.{dotted_fixture_subpath}",
72 )
75@pytest.mark.parametrize(
76 "docstring",
77 [
78 "One line docstring description",
79 """
80 Multiple line docstring description.
82 With more text.
83 """,
84 ],
85)
86def test_parse__description_only_docstring__single_markdown_section(docstring: str) -> None:
87 sections, errors = parse_detailed(docstring)
89 assert len(sections) == 1
90 assert sections[0].type == Section.Type.MARKDOWN
91 assert sections[0].value == dedent_strip(docstring)
92 assert not errors
95def test_parse__no_description__single_markdown_section() -> None:
96 sections, errors = parse_detailed("")
98 assert len(sections) == 1
99 assert sections[0].type == Section.Type.MARKDOWN
100 assert sections[0].value == ""
101 assert not errors
104def test_parse__multiple_blank_lines_before_description__single_markdown_section() -> None:
105 sections, errors = parse_detailed(
106 """
109 Now text""", # noqa: W293
110 strip_docstring=False,
111 )
113 assert len(sections) == 1
114 assert sections[0].type == Section.Type.MARKDOWN
115 assert sections[0].value == "Now text"
116 assert not errors
119def test_parse__description_with_initial_newline__single_markdown_section() -> None:
120 docstring = """
121 With initial newline
122 """
123 sections, errors = parse_detailed(docstring, strip_docstring=False)
125 assert len(sections) == 1
126 assert sections[0].type == Section.Type.MARKDOWN
127 assert sections[0].value == dedent_strip(docstring)
128 assert not errors
131def test_parse__param_field__param_section() -> None:
132 """Parse a simple docstring."""
133 sections, errors = parse_detailed(
134 f"""
135 Docstring with one line param.
137 :param {SOME_NAME}: {SOME_TEXT}
138 """,
139 )
140 assert len(sections) == 2
141 assert sections[1].type == Section.Type.PARAMETERS
142 assert_parameter_equal(
143 sections[1].value[0],
144 Parameter(SOME_NAME, annotation=empty, description=SOME_TEXT, kind=empty),
145 )
148def test_parse__only_param_field__empty_markdown() -> None:
149 sections, errors = parse_detailed(":param foo: text")
150 assert len(sections) == 2
151 assert sections[0].type == Section.Type.MARKDOWN
152 assert sections[0].value == ""
155@pytest.mark.parametrize(
156 "param_directive_name",
157 [
158 "param",
159 "parameter",
160 "arg",
161 "argument",
162 "key",
163 "keyword",
164 ],
165)
166def test_parse__all_param_names__param_section(param_directive_name: str) -> None:
167 sections, errors = parse_detailed(
168 f"""
169 Docstring with one line param.
171 :{param_directive_name} {SOME_NAME}: {SOME_TEXT}
172 """,
173 )
174 assert len(sections) == 2
175 assert sections[1].type == Section.Type.PARAMETERS
176 assert_parameter_equal(
177 sections[1].value[0],
178 Parameter(SOME_NAME, annotation=empty, description=SOME_TEXT, kind=empty),
179 )
182@pytest.mark.parametrize(
183 "docstring",
184 [
185 f"""
186 Docstring with param with continuation, no indent.
188 :param {SOME_NAME}: {SOME_TEXT}
189 {SOME_EXTRA_TEXT}
190 """,
191 f"""
192 Docstring with param with continuation, with indent.
194 :param {SOME_NAME}: {SOME_TEXT}
195 {SOME_EXTRA_TEXT}
196 """,
197 ],
198)
199def test_parse__param_field_multi_line__param_section(docstring: str) -> None:
200 """Parse a simple docstring."""
201 sections, errors = parse_detailed(docstring)
202 assert len(sections) == 2
203 assert sections[1].type == Section.Type.PARAMETERS
204 assert_parameter_equal(
205 sections[1].value[0],
206 Parameter(SOME_NAME, annotation=empty, description=f"{SOME_TEXT} {SOME_EXTRA_TEXT}", kind=empty),
207 )
210def test_parse__param_field_for_function__param_section_with_kind() -> None:
211 """Parse a simple docstring."""
213 def f(foo): # noqa: ANN202, ANN001
214 """Docstring with line continuation.
216 :param foo: descriptive test text
217 """
219 sections, errors = parse(f)
220 assert len(sections) == 2
221 assert sections[1].type == Section.Type.PARAMETERS
222 assert_parameter_equal(
223 sections[1].value[0],
224 Parameter(SOME_NAME, annotation=empty, description=SOME_TEXT, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD),
225 )
228def test_parse__param_field_docs_type__param_section_with_type() -> None:
229 """Parse a simple docstring."""
231 def f(foo): # noqa: ANN202, ANN001
232 """Docstring with line continuation.
234 :param str foo: descriptive test text
235 """
237 sections, errors = parse(f)
238 assert len(sections) == 2
239 assert sections[1].type == Section.Type.PARAMETERS
240 assert_parameter_equal(
241 sections[1].value[0],
242 Parameter(SOME_NAME, annotation="str", description=SOME_TEXT, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD),
243 )
246def test_parse__param_field_type_field__param_section_with_type() -> None:
247 """Parse a simple docstring."""
249 def f(foo): # noqa: ANN202, ANN001
250 """Docstring with line continuation.
252 :param foo: descriptive test text
253 :type foo: str
254 """
256 sections, errors = parse(f)
257 assert len(sections) == 2
258 assert sections[1].type == Section.Type.PARAMETERS
259 assert_parameter_equal(
260 sections[1].value[0],
261 Parameter(SOME_NAME, annotation="str", description=SOME_TEXT, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD),
262 )
265def test_parse__param_field_type_field_first__param_section_with_type() -> None:
266 """Parse a simple docstring."""
268 def f(foo): # noqa: ANN202, ANN001
269 """Docstring with line continuation.
271 :type foo: str
272 :param foo: descriptive test text
273 """
275 sections, errors = parse(f)
276 assert len(sections) == 2
277 assert sections[1].type == Section.Type.PARAMETERS
278 assert_parameter_equal(
279 sections[1].value[0],
280 Parameter(SOME_NAME, annotation="str", description=SOME_TEXT, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD),
281 )
284def test_parse__param_field_type_field_or_none__param_section_with_optional() -> None:
285 """Parse a simple docstring."""
287 def f(foo): # noqa: ANN202, ANN001
288 """Docstring with line continuation.
290 :param foo: descriptive test text
291 :type foo: str or None
292 """
294 sections, errors = parse(f)
295 assert len(sections) == 2
296 assert sections[1].type == Section.Type.PARAMETERS
297 assert_parameter_equal(
298 sections[1].value[0],
299 Parameter(
300 SOME_NAME,
301 annotation="Optional[str]",
302 description=SOME_TEXT,
303 kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
304 ),
305 )
308def test_parse__param_field_type_none_or_field__param_section_with_optional() -> None:
309 """Parse a simple docstring."""
311 def f(foo): # noqa: ANN202, ANN001
312 """Docstring with line continuation.
314 :param foo: descriptive test text
315 :type foo: None or str
316 """
318 sections, errors = parse(f)
319 assert len(sections) == 2
320 assert sections[1].type == Section.Type.PARAMETERS
321 assert_parameter_equal(
322 sections[1].value[0],
323 Parameter(
324 SOME_NAME,
325 annotation="Optional[str]",
326 description=SOME_TEXT,
327 kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
328 ),
329 )
332def test_parse__param_field_type_field_or_int__param_section_with_union() -> None:
333 """Parse a simple docstring."""
335 def f(foo): # noqa: ANN202, ANN001
336 """Docstring with line continuation.
338 :param foo: descriptive test text
339 :type foo: str or int
340 """
342 sections, errors = parse(f)
343 assert len(sections) == 2
344 assert sections[1].type == Section.Type.PARAMETERS
345 assert_parameter_equal(
346 sections[1].value[0],
347 Parameter(
348 SOME_NAME,
349 annotation="Union[str,int]",
350 description=SOME_TEXT,
351 kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
352 ),
353 )
356def test_parse__param_field_type_multiple__param_section_with_union() -> None:
357 """Parse a simple docstring."""
359 def f(foo): # noqa: ANN202, ANN001
360 """Docstring with line continuation.
362 :param foo: descriptive test text
363 :type foo: str or int or float
364 """
366 sections, errors = parse(f)
367 assert len(sections) == 2
368 assert sections[1].type == Section.Type.PARAMETERS
369 assert_parameter_equal(
370 sections[1].value[0],
371 Parameter(
372 SOME_NAME,
373 annotation="Union[str,int,float]",
374 description=SOME_TEXT,
375 kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
376 ),
377 )
380def test_parse__param_field_annotate_type__param_section_with_type() -> None:
381 """Parse a simple docstring."""
383 def f(foo: str): # noqa: ANN202
384 """Docstring with line continuation.
386 :param foo: descriptive test text
387 """
389 sections, errors = parse(f)
390 assert len(sections) == 2
391 assert sections[1].type == Section.Type.PARAMETERS
392 assert_parameter_equal(
393 sections[1].value[0],
394 Parameter(SOME_NAME, annotation=str, description=SOME_TEXT, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD),
395 )
398def test_parse__param_field_no_matching_param__result_from_docstring() -> None:
399 """Parse a simple docstring."""
401 def f(foo: str): # noqa: ANN202
402 """Docstring with line continuation.
404 :param other: descriptive test text
405 """
407 sections, errors = parse(f)
408 assert len(sections) == 2
409 assert sections[1].type == Section.Type.PARAMETERS
410 assert_parameter_equal(
411 sections[1].value[0],
412 Parameter("other", annotation=empty, description=SOME_TEXT, kind=empty),
413 )
416def test_parse__param_field_with_default__result_from_docstring() -> None:
417 """Parse a simple docstring."""
419 def f(foo=""): # noqa: ANN202, ANN001
420 """Docstring with line continuation.
422 :param foo: descriptive test text
423 """
425 sections, errors = parse(f)
426 assert len(sections) == 2
427 assert sections[1].type == Section.Type.PARAMETERS
428 assert_parameter_equal(
429 sections[1].value[0],
430 Parameter(
431 "foo",
432 annotation=empty,
433 description=SOME_TEXT,
434 default="",
435 kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
436 ),
437 )
440def test_parse__param_field_no_matching_param__error_message() -> None:
441 """Parse a simple docstring."""
443 def f(foo: str): # noqa: ANN202
444 """Docstring with line continuation.
446 :param other: descriptive test text
447 """
449 sections, errors = parse(f)
450 assert "No matching parameter for 'other'" in errors[0]
453def test_parse__invalid_param_field_only_initial_marker__error_message() -> None:
454 """Parse a simple docstring."""
456 def f(foo: str): # noqa: ANN202
457 """Docstring with line continuation.
459 :param foo descriptive test text
460 """
462 sections, errors = parse(f)
463 assert "Failed to get ':directive: value' pair" in errors[0]
466def test_parse__invalid_param_field_wrong_part_count__error_message() -> None:
467 """Parse a simple docstring."""
469 def f(foo: str): # noqa: ANN202
470 """Docstring with line continuation.
472 :param: descriptive test text
473 """
475 sections, errors = parse(f)
476 assert "Failed to parse field directive" in errors[0]
479def test_parse__param_twice__error_message() -> None:
480 """Parse a simple docstring."""
482 def f(foo: str): # noqa: ANN202
483 """Docstring with line continuation.
485 :param foo: descriptive test text
486 :param foo: descriptive test text again
487 """
489 sections, errors = parse(f)
490 assert "Duplicate parameter entry for 'foo'" in errors[0]
493def test_parse__param_type_twice_doc__error_message() -> None:
494 """Parse a simple docstring."""
496 def f(foo): # noqa: ANN202, ANN001
497 """Docstring with line continuation.
499 :param str foo: descriptive test text
500 :type foo: str
501 """
503 sections, errors = parse(f)
504 assert "Duplicate parameter information for 'foo'" in errors[0]
507def test_parse__param_type_twice_type_directive_first__error_message() -> None:
508 """Parse a simple docstring."""
510 def f(foo): # noqa: ANN202, ANN001
511 """Docstring with line continuation.
513 :type foo: str
514 :param str foo: descriptive test text
515 """
517 sections, errors = parse(f)
518 assert "Duplicate parameter information for 'foo'" in errors[0]
521def test_parse__param_type_twice_annotated__error_message() -> None:
522 """Parse a simple docstring."""
524 def f(foo: str): # noqa: ANN202
525 """Docstring with line continuation.
527 :param str foo: descriptive test text
528 :type foo: str
529 """
531 sections, errors = parse(f)
532 assert "Duplicate parameter information for 'foo'" in errors[0]
535def test_parse__param_type_no_type__error_message() -> None:
536 """Parse a simple docstring."""
538 def f(foo: str): # noqa: ANN202
539 """Docstring with line continuation.
541 :param str foo: descriptive test text
542 :type str
543 """
545 sections, errors = parse(f)
546 assert "Failed to get ':directive: value' pair from" in errors[0]
549def test_parse__param_type_no_name__error_message() -> None:
550 """Parse a simple docstring."""
552 def f(foo: str): # noqa: ANN202
553 """Docstring with line continuation.
555 :param str foo: descriptive test text
556 :type: str
557 """
559 sections, errors = parse(f)
560 assert "Failed to get parameter name from" in errors[0]
563@pytest.mark.parametrize(
564 "docstring",
565 [
566 f"""
567 Docstring with param with continuation, no indent.
569 :var {SOME_NAME}: {SOME_TEXT}
570 {SOME_EXTRA_TEXT}
571 """,
572 f"""
573 Docstring with param with continuation, with indent.
575 :var {SOME_NAME}: {SOME_TEXT}
576 {SOME_EXTRA_TEXT}
577 """,
578 ],
579)
580def test_parse__attribute_field_multi_line__param_section(docstring: str) -> None:
581 """Parse a simple docstring."""
582 sections, errors = parse_detailed(docstring)
583 assert len(sections) == 2
584 assert sections[1].type == Section.Type.ATTRIBUTES
585 assert_attribute_equal(
586 sections[1].value[0],
587 Attribute(SOME_NAME, annotation=empty, description=f"{SOME_TEXT} {SOME_EXTRA_TEXT}"),
588 )
591@pytest.mark.parametrize(
592 "attribute_directive_name",
593 [
594 "var",
595 "ivar",
596 "cvar",
597 ],
598)
599def test_parse__all_attribute_names__param_section(attribute_directive_name: str) -> None:
600 sections, errors = parse_detailed(
601 f"""
602 Docstring with one line attribute.
604 :{attribute_directive_name} {SOME_NAME}: {SOME_TEXT}
605 """,
606 )
607 assert len(sections) == 2
608 assert sections[1].type == Section.Type.ATTRIBUTES
609 assert_attribute_equal(
610 sections[1].value[0],
611 Attribute(SOME_NAME, annotation=empty, description=SOME_TEXT),
612 )
615def test_parse__class_attributes__attributes_section() -> None:
616 class Foo:
617 """Class docstring with attributes.
619 :var foo: descriptive test text
620 """
622 sections, errors = parse(Foo)
623 assert len(sections) == 2
624 assert sections[1].type == Section.Type.ATTRIBUTES
625 assert_attribute_equal(
626 sections[1].value[0],
627 Attribute(SOME_NAME, annotation=empty, description=SOME_TEXT),
628 )
631def test_parse__class_attributes_with_type__annotation_in_attributes_section() -> None:
632 class Foo:
633 """Class docstring with attributes.
635 :vartype foo: str
636 :var foo: descriptive test text
637 """
639 sections, errors = parse(Foo)
640 assert len(sections) == 2
641 assert sections[1].type == Section.Type.ATTRIBUTES
642 assert_attribute_equal(
643 sections[1].value[0],
644 Attribute(SOME_NAME, annotation="str", description=SOME_TEXT),
645 )
648def test_parse__attribute_invalid_directive___error() -> None:
649 class Foo:
650 """Class docstring with attributes.
652 :var descriptive test text
653 """
655 sections, errors = parse(Foo)
656 assert "Failed to get ':directive: value' pair from" in errors[0]
659def test_parse__attribute_no_name__error() -> None:
660 class Foo:
661 """Class docstring with attributes.
663 :var: descriptive test text
664 """
666 sections, errors = parse(Foo)
667 assert "Failed to parse field directive from" in errors[0]
670def test_parse__attribute_duplicate__error() -> None:
671 class Foo:
672 """Class docstring with attributes.
674 :var foo: descriptive test text
675 :var foo: descriptive test text
676 """
678 sections, errors = parse(Foo)
679 assert "Duplicate attribute entry for 'foo'" in errors[0]
682def test_parse__class_attributes_type_invalid__error() -> None:
683 class Foo:
684 """Class docstring with attributes.
686 :vartype str
687 :var foo: descriptive test text
688 """
690 sections, errors = parse(Foo)
691 assert "Failed to get ':directive: value' pair from " in errors[0]
694def test_parse__class_attributes_type_no_name__error() -> None:
695 class Foo:
696 """Class docstring with attributes.
698 :vartype: str
699 :var foo: descriptive test text
700 """
702 sections, errors = parse(Foo)
703 assert "Failed to get attribute name from" in errors[0]
706def test_parse__return_directive__return_section_no_type() -> None:
707 def f(foo: str): # noqa: ANN202
708 """Function with only return directive.
710 :return: descriptive test text
711 """
712 return foo
714 sections, errors = parse(f)
715 assert len(sections) == 2
716 assert sections[1].type == Section.Type.RETURN
717 assert_annotated_obj_equal(
718 sections[1].value,
719 AnnotatedObject(annotation=empty, description=SOME_TEXT),
720 )
723def test_parse__return_directive_rtype__return_section_with_type() -> None:
724 def f(foo: str): # noqa: ANN202
725 """Function with only return & rtype directive.
727 :return: descriptive test text
728 :rtype: str
729 """
730 return foo
732 sections, errors = parse(f)
733 assert len(sections) == 2
734 assert sections[1].type == Section.Type.RETURN
735 assert_annotated_obj_equal(
736 sections[1].value,
737 AnnotatedObject(annotation="str", description=SOME_TEXT),
738 )
741def test_parse__return_directive_rtype_first__return_section_with_type() -> None:
742 def f(foo: str): # noqa: ANN202
743 """Function with only return & rtype directive.
745 :rtype: str
746 :return: descriptive test text
747 """
748 return foo
750 sections, errors = parse(f)
751 assert len(sections) == 2
752 assert sections[1].type == Section.Type.RETURN
753 assert_annotated_obj_equal(
754 sections[1].value,
755 AnnotatedObject(annotation="str", description=SOME_TEXT),
756 )
759def test_parse__return_directive_annotation__return_section_with_type() -> None:
760 def f(foo: str) -> str:
761 """Function with return directive, rtype directive, & annotation.
763 :return: descriptive test text
764 """
765 return foo
767 sections, errors = parse(f)
768 assert len(sections) == 2
769 assert sections[1].type == Section.Type.RETURN
770 assert_annotated_obj_equal(
771 sections[1].value,
772 AnnotatedObject(annotation=str, description=SOME_TEXT),
773 )
776def test_parse__return_directive_annotation__return_section_with_type_error() -> None:
777 def f(foo: str) -> str:
778 """Function with return directive, rtype directive, & annotation.
780 :return: descriptive test text
781 :rtype: str
782 """
783 return foo
785 sections, errors = parse(f)
786 assert len(sections) == 2
787 assert sections[1].type == Section.Type.RETURN
788 assert_annotated_obj_equal(
789 sections[1].value,
790 AnnotatedObject(annotation=str, description=SOME_TEXT),
791 )
792 assert "Duplicate type information for return" in errors[0]
795def test_parse__return_invalid__error() -> None:
796 def f(foo: str): # noqa: ANN202
797 """Function with only return directive.
799 :return descriptive test text
800 """
801 return foo
803 sections, errors = parse(f)
804 assert "Failed to get ':directive: value' pair from " in errors[0]
807def test_parse__rtype_invalid__error() -> None:
808 def f(foo: str): # noqa: ANN202
809 """Function with only return directive.
811 :rtype str
812 """
813 return foo
815 sections, errors = parse(f)
816 assert "Failed to get ':directive: value' pair from " in errors[0]
819def test_parse__raises_directive__exception_section() -> None:
820 def f(foo: str): # noqa: ANN202
821 """Function with only return directive.
823 :raise SomeException: descriptive test text
824 """
825 return foo
827 sections, errors = parse(f)
828 assert len(sections) == 2
829 assert sections[1].type == Section.Type.EXCEPTIONS
830 assert_annotated_obj_equal(
831 sections[1].value[0],
832 AnnotatedObject(annotation=SOME_EXCEPTION_NAME, description=SOME_TEXT),
833 )
836def test_parse__multiple_raises_directive__exception_section_with_two() -> None:
837 def f(foo: str): # noqa: ANN202
838 """Function with only return directive.
840 :raise SomeException: descriptive test text
841 :raise SomeOtherException: descriptive test text
842 """
843 return foo
845 sections, errors = parse(f)
846 assert len(sections) == 2
847 assert sections[1].type == Section.Type.EXCEPTIONS
848 assert_annotated_obj_equal(
849 sections[1].value[0],
850 AnnotatedObject(annotation=SOME_EXCEPTION_NAME, description=SOME_TEXT),
851 )
852 assert_annotated_obj_equal(
853 sections[1].value[1],
854 AnnotatedObject(annotation=SOME_OTHER_EXCEPTION_NAME, description=SOME_TEXT),
855 )
858@pytest.mark.parametrize(
859 "attribute_directive_name",
860 [
861 "raises",
862 "raise",
863 "except",
864 "exception",
865 ],
866)
867def test_parse__all_exception_names__param_section(attribute_directive_name: str) -> None:
868 sections, errors = parse_detailed(
869 f"""
870 Docstring with one line attribute.
872 :{attribute_directive_name} {SOME_EXCEPTION_NAME}: {SOME_TEXT}
873 """,
874 )
875 assert len(sections) == 2
876 assert sections[1].type == Section.Type.EXCEPTIONS
877 assert_annotated_obj_equal(
878 sections[1].value[0],
879 AnnotatedObject(annotation=SOME_EXCEPTION_NAME, description=SOME_TEXT),
880 )
883def test_parse__raise_invalid__error() -> None:
884 def f(foo: str): # noqa: ANN202
885 """Function with only return directive.
887 :raise descriptive test text
888 """
889 return foo
891 sections, errors = parse(f)
892 assert "Failed to get ':directive: value' pair from " in errors[0]
895def test_parse__raise_no_name__error() -> None:
896 def f(foo: str): # noqa: ANN202
897 """Function with only return directive.
899 :raise: descriptive test text
900 """
901 return foo
903 sections, errors = parse(f)
904 assert "Failed to parse exception directive from" in errors[0]
907# -------------------------------
908# Fixture tests
909# -------------------------------
912def test_parse_module_attributes_section__expected_attributes_section() -> None:
913 """Parse attributes section in modules."""
914 obj = get_rst_object_documentation("docstring_attributes_section")
915 assert len(obj.docstring_sections) == 2
916 attr_section = obj.docstring_sections[1]
917 assert attr_section.type == Section.Type.ATTRIBUTES
918 assert len(attr_section.value) == 5
919 expected = [
920 {"name": "A", "annotation": "int", "description": "Alpha."},
921 # type annotation takes preference over docstring
922 {"name": "B", "annotation": "str", "description": "Beta."},
923 {"name": "C", "annotation": "bool", "description": "Gamma."},
924 {"name": "D", "annotation": "", "description": "Delta."},
925 {"name": "E", "annotation": "float", "description": "Epsilon."},
926 ]
927 assert [serialize_attribute(attr) for attr in attr_section.value] == expected
930def test_parse_module_attributes_section__expected_docstring_errors() -> None:
931 """Parse attributes section in modules."""
932 obj = get_rst_object_documentation("docstring_attributes_section")
933 assert len(obj.docstring_errors) == 1
934 assert "Duplicate attribute information for 'B'" in obj.docstring_errors[0]
937def test_property_docstring__expected_description() -> None:
938 """Parse a property docstring."""
939 class_ = get_rst_object_documentation("class_docstrings:NotDefinedYet")
940 prop = class_.attributes[0]
941 sections = prop.docstring_sections
942 assert len(sections) == 2
943 assert sections[0].type == Section.Type.MARKDOWN
944 assert (
945 sections[0].value
946 == "This property returns `self`.\n\nIt's fun because you can call it like `obj.ha.ha.ha.ha.ha.ha...`."
947 )
950def test_property_docstring__expected_return() -> None:
951 """Parse a property docstring."""
952 class_ = get_rst_object_documentation("class_docstrings:NotDefinedYet")
953 prop = class_.attributes[0]
954 sections = prop.docstring_sections
955 assert len(sections) == 2
956 assert sections[1].type == Section.Type.RETURN
957 assert_annotated_obj_equal(sections[1].value, AnnotatedObject("NotDefinedYet", "self!"))
960def test_property_class_init__expected_description() -> None:
961 class_ = get_rst_object_documentation("class_docstrings:ClassInitFunction")
962 init = class_.methods[0]
963 sections = init.docstring_sections
964 assert len(sections) == 2
965 assert sections[0].type == Section.Type.MARKDOWN
966 assert sections[0].value == "Initialize instance."
969def test_class_init__expected_param() -> None:
970 class_ = get_rst_object_documentation("class_docstrings:ClassInitFunction")
971 init = class_.methods[0]
972 sections = init.docstring_sections
973 assert len(sections) == 2
974 assert sections[1].type == Section.Type.PARAMETERS
975 param_section = sections[1]
976 assert_parameter_equal(
977 param_section.value[0],
978 Parameter("value", str, "Value to store", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD),
979 )
980 assert_parameter_equal(
981 param_section.value[1],
982 Parameter("other", "int", "Other value with default", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, default=1),
983 )
986def test_member_function___expected_param() -> None:
987 class_ = get_rst_object_documentation("class_docstrings:ClassWithFunction")
988 init = class_.methods[0]
989 sections = init.docstring_sections
990 assert len(sections) == 3
991 param_section = sections[1]
992 assert param_section.type == Section.Type.PARAMETERS
993 assert_parameter_equal(
994 param_section.value[0],
995 Parameter("value", str, "Value to store", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD),
996 )
997 assert_parameter_equal(
998 param_section.value[1],
999 Parameter("other", "int", "Other value with default", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, default=1),
1000 )
1003def test_member_function___expected_return() -> None:
1004 class_ = get_rst_object_documentation("class_docstrings:ClassWithFunction")
1005 init = class_.methods[0]
1006 sections = init.docstring_sections
1007 assert len(sections) == 3
1008 assert sections[2].type == Section.Type.RETURN
1009 assert_annotated_obj_equal(sections[2].value, AnnotatedObject(str, "Concatenated result"))
1012def test_property_docstring__no_errors() -> None:
1013 """Parse a property docstring."""
1014 class_ = get_rst_object_documentation("class_docstrings:NotDefinedYet")
1015 prop = class_.attributes[0]
1016 assert not prop.docstring_errors