Coverage for tests/test_parsers/test_docstrings/test_restructured_text.py: 97.17%

Shortcuts on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

371 statements  

1"""Tests for [the `parsers.docstrings.google` module][pytkdocs.parsers.docstrings.google].""" 

2 

3import inspect 

4from textwrap import dedent 

5 

6import pytest 

7 

8from pytkdocs.loader import Loader 

9from pytkdocs.objects import Object 

10from pytkdocs.parsers.docstrings.base import AnnotatedObject, Attribute, Parameter, Section, empty 

11from pytkdocs.parsers.docstrings.restructured_text import RestructuredText 

12from pytkdocs.serializer import serialize_attribute 

13 

14 

15class DummyObject: 

16 def __init__(self, signature, return_type): 

17 self.path = "o" 

18 self.signature = signature 

19 self.type = return_type 

20 

21 

22SOME_NAME = "foo" 

23SOME_TEXT = "descriptive test text" 

24SOME_EXTRA_TEXT = "more test text" 

25SOME_EXCEPTION_NAME = "SomeException" 

26SOME_OTHER_EXCEPTION_NAME = "SomeOtherException" 

27 

28 

29def dedent_strip(text: str) -> str: 

30 return dedent(text).strip() 

31 

32 

33def parse(obj, strip_docstring=True): 

34 """Helper to parse a docstring.""" 

35 return parse_detailed(inspect.getdoc(obj), inspect.signature(obj), strip_docstring=strip_docstring) 

36 

37 

38def parse_detailed(docstring, signature=None, return_type=inspect.Signature.empty, strip_docstring=True): 

39 """Helper to parse a docstring.""" 

40 docstring = dedent_strip(docstring) if strip_docstring else dedent(docstring) 

41 

42 return RestructuredText().parse(docstring, {"obj": DummyObject(signature, return_type)}) 

43 

44 

45def assert_parameter_equal(actual: Parameter, expected: Parameter) -> None: 

46 assert actual.name == expected.name 

47 assert_annotated_obj_equal(actual, expected) 

48 assert actual.kind == expected.kind 

49 assert actual.default == expected.default 

50 

51 

52def assert_attribute_equal(actual: Attribute, expected: Attribute) -> None: 

53 assert actual.name == expected.name 

54 assert_annotated_obj_equal(actual, expected) 

55 

56 

57def assert_annotated_obj_equal(actual: AnnotatedObject, expected: AnnotatedObject) -> None: 

58 assert actual.annotation == expected.annotation 

59 assert actual.description == expected.description 

60 

61 

62def get_rst_object_documentation(dotted_fixture_subpath) -> Object: 

63 return Loader(docstring_style="restructured-text").get_object_documentation( 

64 f"tests.fixtures.parsing.restructured_text.{dotted_fixture_subpath}" 

65 ) 

66 

67 

68@pytest.mark.parametrize( 

69 "docstring", 

70 [ 

71 "One line docstring description", 

72 """ 

73 Multiple line docstring description. 

74  

75 With more text. 

76 """, 

77 ], 

78) 

79def test_parse__description_only_docstring__single_markdown_section(docstring): 

80 sections, errors = parse_detailed(docstring) 

81 

82 assert len(sections) == 1 

83 assert sections[0].type == Section.Type.MARKDOWN 

84 assert sections[0].value == dedent_strip(docstring) 

85 assert not errors 

86 

87 

88def test_parse__no_description__single_markdown_section(): 

89 sections, errors = parse_detailed("") 

90 

91 assert len(sections) == 1 

92 assert sections[0].type == Section.Type.MARKDOWN 

93 assert sections[0].value == "" 

94 assert not errors 

95 

96 

97def test_parse__multiple_blank_lines_before_description__single_markdown_section(): 

98 sections, errors = parse_detailed( 

99 """ 

100  

101  

102 Now text""", 

103 strip_docstring=False, 

104 ) 

105 

106 assert len(sections) == 1 

107 assert sections[0].type == Section.Type.MARKDOWN 

108 assert sections[0].value == "Now text" 

109 assert not errors 

110 

111 

112def test_parse__description_with_initial_newline__single_markdown_section(): 

113 docstring = """ 

114 With initial newline 

115 """ 

116 sections, errors = parse_detailed(docstring, strip_docstring=False) 

117 

118 assert len(sections) == 1 

119 assert sections[0].type == Section.Type.MARKDOWN 

120 assert sections[0].value == dedent_strip(docstring) 

121 assert not errors 

122 

123 

124def test_parse__param_field__param_section(): 

125 """Parse a simple docstring.""" 

126 sections, errors = parse_detailed( 

127 f""" 

128 Docstring with one line param. 

129 

130 :param {SOME_NAME}: {SOME_TEXT} 

131 """ 

132 ) 

133 assert len(sections) == 2 

134 assert sections[1].type == Section.Type.PARAMETERS 

135 assert_parameter_equal( 

136 sections[1].value[0], Parameter(SOME_NAME, annotation=empty, description=SOME_TEXT, kind=empty) 

137 ) 

138 

139 

140def test_parse__only_param_field__empty_markdown(): 

141 sections, errors = parse_detailed(":param foo: text") 

142 assert len(sections) == 2 

143 assert sections[0].type == Section.Type.MARKDOWN 

144 assert sections[0].value == "" 

145 

146 

147@pytest.mark.parametrize( 

148 "param_directive_name", 

149 [ 

150 "param", 

151 "parameter", 

152 "arg", 

153 "argument", 

154 "key", 

155 "keyword", 

156 ], 

157) 

158def test_parse__all_param_names__param_section(param_directive_name): 

159 sections, errors = parse_detailed( 

160 f""" 

161 Docstring with one line param. 

162 

163 :{param_directive_name} {SOME_NAME}: {SOME_TEXT} 

164 """ 

165 ) 

166 assert len(sections) == 2 

167 assert sections[1].type == Section.Type.PARAMETERS 

168 assert_parameter_equal( 

169 sections[1].value[0], Parameter(SOME_NAME, annotation=empty, description=SOME_TEXT, kind=empty) 

170 ) 

171 

172 

173@pytest.mark.parametrize( 

174 "docstring", 

175 [ 

176 f""" 

177 Docstring with param with continuation, no indent. 

178 

179 :param {SOME_NAME}: {SOME_TEXT} 

180 {SOME_EXTRA_TEXT} 

181 """, 

182 f""" 

183 Docstring with param with continuation, with indent. 

184 

185 :param {SOME_NAME}: {SOME_TEXT} 

186 {SOME_EXTRA_TEXT} 

187 """, 

188 ], 

189) 

190def test_parse__param_field_multi_line__param_section(docstring): 

191 """Parse a simple docstring.""" 

192 sections, errors = parse_detailed(docstring) 

193 assert len(sections) == 2 

194 assert sections[1].type == Section.Type.PARAMETERS 

195 assert_parameter_equal( 

196 sections[1].value[0], 

197 Parameter(SOME_NAME, annotation=empty, description=f"{SOME_TEXT} {SOME_EXTRA_TEXT}", kind=empty), 

198 ) 

199 

200 

201def test_parse__param_field_for_function__param_section_with_kind(): 

202 """Parse a simple docstring.""" 

203 

204 def f(foo): 

205 """ 

206 Docstring with line continuation. 

207 

208 :param foo: descriptive test text 

209 """ 

210 

211 sections, errors = parse(f) 

212 assert len(sections) == 2 

213 assert sections[1].type == Section.Type.PARAMETERS 

214 assert_parameter_equal( 

215 sections[1].value[0], 

216 Parameter(SOME_NAME, annotation=empty, description=SOME_TEXT, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD), 

217 ) 

218 

219 

220def test_parse__param_field_docs_type__param_section_with_type(): 

221 """Parse a simple docstring.""" 

222 

223 def f(foo): 

224 """ 

225 Docstring with line continuation. 

226 

227 :param str foo: descriptive test text 

228 """ 

229 

230 sections, errors = parse(f) 

231 assert len(sections) == 2 

232 assert sections[1].type == Section.Type.PARAMETERS 

233 assert_parameter_equal( 

234 sections[1].value[0], 

235 Parameter(SOME_NAME, annotation="str", description=SOME_TEXT, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD), 

236 ) 

237 

238 

239def test_parse__param_field_type_field__param_section_with_type(): 

240 """Parse a simple docstring.""" 

241 

242 def f(foo): 

243 """ 

244 Docstring with line continuation. 

245 

246 :param foo: descriptive test text 

247 :type foo: str 

248 """ 

249 

250 sections, errors = parse(f) 

251 assert len(sections) == 2 

252 assert sections[1].type == Section.Type.PARAMETERS 

253 assert_parameter_equal( 

254 sections[1].value[0], 

255 Parameter(SOME_NAME, annotation="str", description=SOME_TEXT, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD), 

256 ) 

257 

258 

259def test_parse__param_field_type_field_first__param_section_with_type(): 

260 """Parse a simple docstring.""" 

261 

262 def f(foo): 

263 """ 

264 Docstring with line continuation. 

265 

266 :type foo: str 

267 :param foo: descriptive test text 

268 """ 

269 

270 sections, errors = parse(f) 

271 assert len(sections) == 2 

272 assert sections[1].type == Section.Type.PARAMETERS 

273 assert_parameter_equal( 

274 sections[1].value[0], 

275 Parameter(SOME_NAME, annotation="str", description=SOME_TEXT, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD), 

276 ) 

277 

278 

279def test_parse__param_field_type_field_or_none__param_section_with_optional(): 

280 """Parse a simple docstring.""" 

281 

282 def f(foo): 

283 """ 

284 Docstring with line continuation. 

285 

286 :param foo: descriptive test text 

287 :type foo: str or None 

288 """ 

289 

290 sections, errors = parse(f) 

291 assert len(sections) == 2 

292 assert sections[1].type == Section.Type.PARAMETERS 

293 assert_parameter_equal( 

294 sections[1].value[0], 

295 Parameter( 

296 SOME_NAME, annotation="Optional[str]", description=SOME_TEXT, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD 

297 ), 

298 ) 

299 

300 

301def test_parse__param_field_type_none_or_field__param_section_with_optional(): 

302 """Parse a simple docstring.""" 

303 

304 def f(foo): 

305 """ 

306 Docstring with line continuation. 

307 

308 :param foo: descriptive test text 

309 :type foo: None or str 

310 """ 

311 

312 sections, errors = parse(f) 

313 assert len(sections) == 2 

314 assert sections[1].type == Section.Type.PARAMETERS 

315 assert_parameter_equal( 

316 sections[1].value[0], 

317 Parameter( 

318 SOME_NAME, annotation="Optional[str]", description=SOME_TEXT, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD 

319 ), 

320 ) 

321 

322 

323def test_parse__param_field_type_field_or_int__param_section_with_union(): 

324 """Parse a simple docstring.""" 

325 

326 def f(foo): 

327 """ 

328 Docstring with line continuation. 

329 

330 :param foo: descriptive test text 

331 :type foo: str or int 

332 """ 

333 

334 sections, errors = parse(f) 

335 assert len(sections) == 2 

336 assert sections[1].type == Section.Type.PARAMETERS 

337 assert_parameter_equal( 

338 sections[1].value[0], 

339 Parameter( 

340 SOME_NAME, annotation="Union[str,int]", description=SOME_TEXT, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD 

341 ), 

342 ) 

343 

344 

345def test_parse__param_field_type_multiple__param_section_with_union(): 

346 """Parse a simple docstring.""" 

347 

348 def f(foo): 

349 """ 

350 Docstring with line continuation. 

351 

352 :param foo: descriptive test text 

353 :type foo: str or int or float 

354 """ 

355 

356 sections, errors = parse(f) 

357 assert len(sections) == 2 

358 assert sections[1].type == Section.Type.PARAMETERS 

359 assert_parameter_equal( 

360 sections[1].value[0], 

361 Parameter( 

362 SOME_NAME, 

363 annotation="Union[str,int,float]", 

364 description=SOME_TEXT, 

365 kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, 

366 ), 

367 ) 

368 

369 

370def test_parse__param_field_annotate_type__param_section_with_type(): 

371 """Parse a simple docstring.""" 

372 

373 def f(foo: str): 

374 """ 

375 Docstring with line continuation. 

376 

377 :param foo: descriptive test text 

378 """ 

379 

380 sections, errors = parse(f) 

381 assert len(sections) == 2 

382 assert sections[1].type == Section.Type.PARAMETERS 

383 assert_parameter_equal( 

384 sections[1].value[0], 

385 Parameter(SOME_NAME, annotation=str, description=SOME_TEXT, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD), 

386 ) 

387 

388 

389def test_parse__param_field_no_matching_param__result_from_docstring(): 

390 """Parse a simple docstring.""" 

391 

392 def f(foo: str): 

393 """ 

394 Docstring with line continuation. 

395 

396 :param other: descriptive test text 

397 """ 

398 

399 sections, errors = parse(f) 

400 assert len(sections) == 2 

401 assert sections[1].type == Section.Type.PARAMETERS 

402 assert_parameter_equal( 

403 sections[1].value[0], 

404 Parameter("other", annotation=empty, description=SOME_TEXT, kind=empty), 

405 ) 

406 

407 

408def test_parse__param_field_with_default__result_from_docstring(): 

409 """Parse a simple docstring.""" 

410 

411 def f(foo=""): 

412 """ 

413 Docstring with line continuation. 

414 

415 :param foo: descriptive test text 

416 """ 

417 

418 sections, errors = parse(f) 

419 assert len(sections) == 2 

420 assert sections[1].type == Section.Type.PARAMETERS 

421 assert_parameter_equal( 

422 sections[1].value[0], 

423 Parameter( 

424 "foo", annotation=empty, description=SOME_TEXT, default="", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD 

425 ), 

426 ) 

427 

428 

429def test_parse__param_field_no_matching_param__error_message(): 

430 """Parse a simple docstring.""" 

431 

432 def f(foo: str): 

433 """ 

434 Docstring with line continuation. 

435 

436 :param other: descriptive test text 

437 """ 

438 

439 sections, errors = parse(f) 

440 assert "No matching parameter for 'other'" in errors[0] 

441 

442 

443def test_parse__invalid_param_field_only_initial_marker__error_message(): 

444 """Parse a simple docstring.""" 

445 

446 def f(foo: str): 

447 """ 

448 Docstring with line continuation. 

449 

450 :param foo descriptive test text 

451 """ 

452 

453 sections, errors = parse(f) 

454 assert "Failed to get ':directive: value' pair" in errors[0] 

455 

456 

457def test_parse__invalid_param_field_wrong_part_count__error_message(): 

458 """Parse a simple docstring.""" 

459 

460 def f(foo: str): 

461 """ 

462 Docstring with line continuation. 

463 

464 :param: descriptive test text 

465 """ 

466 

467 sections, errors = parse(f) 

468 assert "Failed to parse field directive" in errors[0] 

469 

470 

471def test_parse__param_twice__error_message(): 

472 """Parse a simple docstring.""" 

473 

474 def f(foo: str): 

475 """ 

476 Docstring with line continuation. 

477 

478 :param foo: descriptive test text 

479 :param foo: descriptive test text again 

480 """ 

481 

482 sections, errors = parse(f) 

483 assert "Duplicate parameter entry for 'foo'" in errors[0] 

484 

485 

486def test_parse__param_type_twice_doc__error_message(): 

487 """Parse a simple docstring.""" 

488 

489 def f(foo): 

490 """ 

491 Docstring with line continuation. 

492 

493 :param str foo: descriptive test text 

494 :type foo: str 

495 """ 

496 

497 sections, errors = parse(f) 

498 assert "Duplicate parameter information for 'foo'" in errors[0] 

499 

500 

501def test_parse__param_type_twice_type_directive_first__error_message(): 

502 """Parse a simple docstring.""" 

503 

504 def f(foo): 

505 """ 

506 Docstring with line continuation. 

507 

508 :type foo: str 

509 :param str foo: descriptive test text 

510 """ 

511 

512 sections, errors = parse(f) 

513 assert "Duplicate parameter information for 'foo'" in errors[0] 

514 

515 

516def test_parse__param_type_twice_annotated__error_message(): 

517 """Parse a simple docstring.""" 

518 

519 def f(foo: str): 

520 """ 

521 Docstring with line continuation. 

522 

523 :param str foo: descriptive test text 

524 :type foo: str 

525 """ 

526 

527 sections, errors = parse(f) 

528 assert "Duplicate parameter information for 'foo'" in errors[0] 

529 

530 

531def test_parse__param_type_no_type__error_message(): 

532 """Parse a simple docstring.""" 

533 

534 def f(foo: str): 

535 """ 

536 Docstring with line continuation. 

537 

538 :param str foo: descriptive test text 

539 :type str 

540 """ 

541 

542 sections, errors = parse(f) 

543 assert "Failed to get ':directive: value' pair from" in errors[0] 

544 

545 

546def test_parse__param_type_no_name__error_message(): 

547 """Parse a simple docstring.""" 

548 

549 def f(foo: str): 

550 """ 

551 Docstring with line continuation. 

552 

553 :param str foo: descriptive test text 

554 :type: str 

555 """ 

556 

557 sections, errors = parse(f) 

558 assert "Failed to get parameter name from" in errors[0] 

559 

560 

561@pytest.mark.parametrize( 

562 "docstring", 

563 [ 

564 f""" 

565 Docstring with param with continuation, no indent. 

566 

567 :var {SOME_NAME}: {SOME_TEXT} 

568 {SOME_EXTRA_TEXT} 

569 """, 

570 f""" 

571 Docstring with param with continuation, with indent. 

572 

573 :var {SOME_NAME}: {SOME_TEXT} 

574 {SOME_EXTRA_TEXT} 

575 """, 

576 ], 

577) 

578def test_parse__attribute_field_multi_line__param_section(docstring): 

579 """Parse a simple docstring.""" 

580 sections, errors = parse_detailed(docstring) 

581 assert len(sections) == 2 

582 assert sections[1].type == Section.Type.ATTRIBUTES 

583 assert_attribute_equal( 

584 sections[1].value[0], 

585 Attribute(SOME_NAME, annotation=empty, description=f"{SOME_TEXT} {SOME_EXTRA_TEXT}"), 

586 ) 

587 

588 

589@pytest.mark.parametrize( 

590 "attribute_directive_name", 

591 [ 

592 "var", 

593 "ivar", 

594 "cvar", 

595 ], 

596) 

597def test_parse__all_attribute_names__param_section(attribute_directive_name): 

598 sections, errors = parse_detailed( 

599 f""" 

600 Docstring with one line attribute. 

601 

602 :{attribute_directive_name} {SOME_NAME}: {SOME_TEXT} 

603 """ 

604 ) 

605 assert len(sections) == 2 

606 assert sections[1].type == Section.Type.ATTRIBUTES 

607 assert_attribute_equal( 

608 sections[1].value[0], 

609 Attribute(SOME_NAME, annotation=empty, description=SOME_TEXT), 

610 ) 

611 

612 

613def test_parse__class_attributes__attributes_section(): 

614 class Foo: 

615 """ 

616 Class docstring with attributes 

617 

618 :var foo: descriptive test text 

619 """ 

620 

621 sections, errors = parse(Foo) 

622 assert len(sections) == 2 

623 assert sections[1].type == Section.Type.ATTRIBUTES 

624 assert_attribute_equal( 

625 sections[1].value[0], 

626 Attribute(SOME_NAME, annotation=empty, description=SOME_TEXT), 

627 ) 

628 

629 

630def test_parse__class_attributes_with_type__annotation_in_attributes_section(): 

631 class Foo: 

632 """ 

633 Class docstring with attributes 

634 

635 :vartype foo: str 

636 :var foo: descriptive test text 

637 """ 

638 

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 ) 

646 

647 

648def test_parse__attribute_invalid_directive___error(): 

649 class Foo: 

650 """ 

651 Class docstring with attributes 

652 

653 :var descriptive test text 

654 """ 

655 

656 sections, errors = parse(Foo) 

657 assert "Failed to get ':directive: value' pair from" in errors[0] 

658 

659 

660def test_parse__attribute_no_name__error(): 

661 class Foo: 

662 """ 

663 Class docstring with attributes 

664 

665 :var: descriptive test text 

666 """ 

667 

668 sections, errors = parse(Foo) 

669 assert "Failed to parse field directive from" in errors[0] 

670 

671 

672def test_parse__attribute_duplicate__error(): 

673 class Foo: 

674 """ 

675 Class docstring with attributes 

676 

677 :var foo: descriptive test text 

678 :var foo: descriptive test text 

679 """ 

680 

681 sections, errors = parse(Foo) 

682 assert "Duplicate attribute entry for 'foo'" in errors[0] 

683 

684 

685def test_parse__class_attributes_type_invalid__error(): 

686 class Foo: 

687 """ 

688 Class docstring with attributes 

689 

690 :vartype str 

691 :var foo: descriptive test text 

692 """ 

693 

694 sections, errors = parse(Foo) 

695 assert "Failed to get ':directive: value' pair from " in errors[0] 

696 

697 

698def test_parse__class_attributes_type_no_name__error(): 

699 class Foo: 

700 """ 

701 Class docstring with attributes 

702 

703 :vartype: str 

704 :var foo: descriptive test text 

705 """ 

706 

707 sections, errors = parse(Foo) 

708 assert "Failed to get attribute name from" in errors[0] 

709 

710 

711def test_parse__return_directive__return_section_no_type(): 

712 def f(foo: str): 

713 """ 

714 Function with only return directive 

715 

716 :return: descriptive test text 

717 """ 

718 return foo 

719 

720 sections, errors = parse(f) 

721 assert len(sections) == 2 

722 assert sections[1].type == Section.Type.RETURN 

723 assert_annotated_obj_equal( 

724 sections[1].value, 

725 AnnotatedObject(annotation=empty, description=SOME_TEXT), 

726 ) 

727 

728 

729def test_parse__return_directive_rtype__return_section_with_type(): 

730 def f(foo: str): 

731 """ 

732 Function with only return & rtype directive 

733 

734 :return: descriptive test text 

735 :rtype: str 

736 """ 

737 return foo 

738 

739 sections, errors = parse(f) 

740 assert len(sections) == 2 

741 assert sections[1].type == Section.Type.RETURN 

742 assert_annotated_obj_equal( 

743 sections[1].value, 

744 AnnotatedObject(annotation="str", description=SOME_TEXT), 

745 ) 

746 

747 

748def test_parse__return_directive_rtype_first__return_section_with_type(): 

749 def f(foo: str): 

750 """ 

751 Function with only return & rtype directive 

752 

753 :rtype: str 

754 :return: descriptive test text 

755 """ 

756 return foo 

757 

758 sections, errors = parse(f) 

759 assert len(sections) == 2 

760 assert sections[1].type == Section.Type.RETURN 

761 assert_annotated_obj_equal( 

762 sections[1].value, 

763 AnnotatedObject(annotation="str", description=SOME_TEXT), 

764 ) 

765 

766 

767def test_parse__return_directive_annotation__return_section_with_type(): 

768 def f(foo: str) -> str: 

769 """ 

770 Function with return directive, rtype directive, & annotation 

771 

772 :return: descriptive test text 

773 """ 

774 return foo 

775 

776 sections, errors = parse(f) 

777 assert len(sections) == 2 

778 assert sections[1].type == Section.Type.RETURN 

779 assert_annotated_obj_equal( 

780 sections[1].value, 

781 AnnotatedObject(annotation=str, description=SOME_TEXT), 

782 ) 

783 

784 

785def test_parse__return_directive_annotation__return_section_with_type_error(): 

786 def f(foo: str) -> str: 

787 """ 

788 Function with return directive, rtype directive, & annotation 

789 

790 :return: descriptive test text 

791 :rtype: str 

792 """ 

793 return foo 

794 

795 sections, errors = parse(f) 

796 assert len(sections) == 2 

797 assert sections[1].type == Section.Type.RETURN 

798 assert_annotated_obj_equal( 

799 sections[1].value, 

800 AnnotatedObject(annotation=str, description=SOME_TEXT), 

801 ) 

802 assert "Duplicate type information for return" in errors[0] 

803 

804 

805def test_parse__return_invalid__error(): 

806 def f(foo: str): 

807 """ 

808 Function with only return directive 

809 

810 :return descriptive test text 

811 """ 

812 return foo 

813 

814 sections, errors = parse(f) 

815 assert "Failed to get ':directive: value' pair from " in errors[0] 

816 

817 

818def test_parse__rtype_invalid__error(): 

819 def f(foo: str): 

820 """ 

821 Function with only return directive 

822 

823 :rtype str 

824 """ 

825 return foo 

826 

827 sections, errors = parse(f) 

828 assert "Failed to get ':directive: value' pair from " in errors[0] 

829 

830 

831def test_parse__raises_directive__exception_section(): 

832 def f(foo: str): 

833 """ 

834 Function with only return directive 

835 

836 :raise SomeException: descriptive test text 

837 """ 

838 return foo 

839 

840 sections, errors = parse(f) 

841 assert len(sections) == 2 

842 assert sections[1].type == Section.Type.EXCEPTIONS 

843 assert_annotated_obj_equal( 

844 sections[1].value[0], 

845 AnnotatedObject(annotation=SOME_EXCEPTION_NAME, description=SOME_TEXT), 

846 ) 

847 

848 

849def test_parse__multiple_raises_directive__exception_section_with_two(): 

850 def f(foo: str): 

851 """ 

852 Function with only return directive 

853 

854 :raise SomeException: descriptive test text 

855 :raise SomeOtherException: descriptive test text 

856 """ 

857 return foo 

858 

859 sections, errors = parse(f) 

860 assert len(sections) == 2 

861 assert sections[1].type == Section.Type.EXCEPTIONS 

862 assert_annotated_obj_equal( 

863 sections[1].value[0], 

864 AnnotatedObject(annotation=SOME_EXCEPTION_NAME, description=SOME_TEXT), 

865 ) 

866 assert_annotated_obj_equal( 

867 sections[1].value[1], 

868 AnnotatedObject(annotation=SOME_OTHER_EXCEPTION_NAME, description=SOME_TEXT), 

869 ) 

870 

871 

872@pytest.mark.parametrize( 

873 "attribute_directive_name", 

874 [ 

875 "raises", 

876 "raise", 

877 "except", 

878 "exception", 

879 ], 

880) 

881def test_parse__all_exception_names__param_section(attribute_directive_name): 

882 sections, errors = parse_detailed( 

883 f""" 

884 Docstring with one line attribute. 

885 

886 :{attribute_directive_name} {SOME_EXCEPTION_NAME}: {SOME_TEXT} 

887 """ 

888 ) 

889 assert len(sections) == 2 

890 assert sections[1].type == Section.Type.EXCEPTIONS 

891 assert_annotated_obj_equal( 

892 sections[1].value[0], 

893 AnnotatedObject(annotation=SOME_EXCEPTION_NAME, description=SOME_TEXT), 

894 ) 

895 

896 

897def test_parse__raise_invalid__error(): 

898 def f(foo: str): 

899 """ 

900 Function with only return directive 

901 

902 :raise descriptive test text 

903 """ 

904 return foo 

905 

906 sections, errors = parse(f) 

907 assert "Failed to get ':directive: value' pair from " in errors[0] 

908 

909 

910def test_parse__raise_no_name__error(): 

911 def f(foo: str): 

912 """ 

913 Function with only return directive 

914 

915 :raise: descriptive test text 

916 """ 

917 return foo 

918 

919 sections, errors = parse(f) 

920 assert "Failed to parse exception directive from" in errors[0] 

921 

922 

923# ------------------------------- 

924# Fixture tests 

925# ------------------------------- 

926 

927 

928def test_parse_module_attributes_section__expected_attributes_section(): 

929 """Parse attributes section in modules.""" 

930 obj = get_rst_object_documentation("docstring_attributes_section") 

931 assert len(obj.docstring_sections) == 2 

932 attr_section = obj.docstring_sections[1] 

933 assert attr_section.type == Section.Type.ATTRIBUTES 

934 assert len(attr_section.value) == 5 

935 expected = [ 

936 {"name": "A", "annotation": "int", "description": "Alpha."}, 

937 # type annotation takes preference over docstring 

938 {"name": "B", "annotation": "str", "description": "Beta."}, 

939 {"name": "C", "annotation": "bool", "description": "Gamma."}, 

940 {"name": "D", "annotation": "", "description": "Delta."}, 

941 {"name": "E", "annotation": "float", "description": "Epsilon."}, 

942 ] 

943 assert [serialize_attribute(attr) for attr in attr_section.value] == expected 

944 

945 

946def test_parse_module_attributes_section__expected_docstring_errors(): 

947 """Parse attributes section in modules.""" 

948 obj = get_rst_object_documentation("docstring_attributes_section") 

949 assert len(obj.docstring_errors) == 1 

950 assert "Duplicate attribute information for 'B'" in obj.docstring_errors[0] 

951 

952 

953def test_property_docstring__expected_description(): 

954 """Parse a property docstring.""" 

955 class_ = get_rst_object_documentation("class_docstrings:NotDefinedYet") 

956 prop = class_.attributes[0] 

957 sections = prop.docstring_sections 

958 assert len(sections) == 2 

959 assert sections[0].type == Section.Type.MARKDOWN 

960 assert ( 

961 sections[0].value 

962 == "This property returns `self`.\n\nIt's fun because you can call it like `obj.ha.ha.ha.ha.ha.ha...`." 

963 ) 

964 

965 

966def test_property_docstring__expected_return(): 

967 """Parse a property docstring.""" 

968 class_ = get_rst_object_documentation("class_docstrings:NotDefinedYet") 

969 prop = class_.attributes[0] 

970 sections = prop.docstring_sections 

971 assert len(sections) == 2 

972 assert sections[1].type == Section.Type.RETURN 

973 assert_annotated_obj_equal(sections[1].value, AnnotatedObject("NotDefinedYet", "self!")) 

974 

975 

976def test_property_class_init__expected_description(): 

977 class_ = get_rst_object_documentation("class_docstrings:ClassInitFunction") 

978 init = class_.methods[0] 

979 sections = init.docstring_sections 

980 assert len(sections) == 2 

981 assert sections[0].type == Section.Type.MARKDOWN 

982 assert sections[0].value == "Initialize instance." 

983 

984 

985def test_class_init__expected_param(): 

986 class_ = get_rst_object_documentation("class_docstrings:ClassInitFunction") 

987 init = class_.methods[0] 

988 sections = init.docstring_sections 

989 assert len(sections) == 2 

990 assert sections[1].type == Section.Type.PARAMETERS 

991 param_section = sections[1] 

992 assert_parameter_equal( 

993 param_section.value[0], Parameter("value", str, "Value to store", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD) 

994 ) 

995 assert_parameter_equal( 

996 param_section.value[1], 

997 Parameter("other", "int", "Other value with default", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, default=1), 

998 ) 

999 

1000 

1001def test_member_function___expected_param(): 

1002 class_ = get_rst_object_documentation("class_docstrings:ClassWithFunction") 

1003 init = class_.methods[0] 

1004 sections = init.docstring_sections 

1005 assert len(sections) == 3 

1006 param_section = sections[1] 

1007 assert param_section.type == Section.Type.PARAMETERS 

1008 assert_parameter_equal( 

1009 param_section.value[0], Parameter("value", str, "Value to store", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD) 

1010 ) 

1011 assert_parameter_equal( 

1012 param_section.value[1], 

1013 Parameter("other", "int", "Other value with default", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, default=1), 

1014 ) 

1015 

1016 

1017def test_member_function___expected_return(): 

1018 class_ = get_rst_object_documentation("class_docstrings:ClassWithFunction") 

1019 init = class_.methods[0] 

1020 sections = init.docstring_sections 

1021 assert len(sections) == 3 

1022 assert sections[2].type == Section.Type.RETURN 

1023 assert_annotated_obj_equal(sections[2].value, AnnotatedObject(str, "Concatenated result")) 

1024 

1025 

1026def test_property_docstring__no_errors(): 

1027 """Parse a property docstring.""" 

1028 class_ = get_rst_object_documentation("class_docstrings:NotDefinedYet") 

1029 prop = class_.attributes[0] 

1030 assert not prop.docstring_errors