Coverage for tests/test_docstrings/test_sphinx.py: 100.00%

352 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-08-15 16:47 +0200

1"""Tests for the [Sphinx-style parser][griffe.docstrings.sphinx].""" 

2 

3from __future__ import annotations 

4 

5import inspect 

6from typing import TYPE_CHECKING, Any 

7 

8import pytest 

9 

10from griffe import ( 

11 Attribute, 

12 Class, 

13 DocstringAttribute, 

14 DocstringParameter, 

15 DocstringRaise, 

16 DocstringReturn, 

17 DocstringSectionKind, 

18 Function, 

19 Module, 

20 Parameter, 

21 Parameters, 

22) 

23 

24if TYPE_CHECKING: 

25 from tests.test_docstrings.helpers import ParserType 

26 

27SOME_NAME = "foo" 

28SOME_TEXT = "descriptive test text" 

29SOME_EXTRA_TEXT = "more test text" 

30SOME_EXCEPTION_NAME = "SomeException" 

31SOME_OTHER_EXCEPTION_NAME = "SomeOtherException" 

32 

33 

34@pytest.mark.parametrize( 

35 "docstring", 

36 [ 

37 "One line docstring description", 

38 """ 

39 Multiple line docstring description. 

40 

41 With more text. 

42 """, 

43 ], 

44) 

45def test_parse__description_only_docstring__single_markdown_section(parse_sphinx: ParserType, docstring: str) -> None: 

46 """Parse a single or multiline docstring. 

47 

48 Parameters: 

49 parse_sphinx: Fixture parser. 

50 docstring: A parametrized docstring. 

51 """ 

52 sections, warnings = parse_sphinx(docstring) 

53 

54 assert len(sections) == 1 

55 assert sections[0].kind is DocstringSectionKind.text 

56 assert sections[0].value == inspect.cleandoc(docstring) 

57 assert not warnings 

58 

59 

60def test_parse__no_description__single_markdown_section(parse_sphinx: ParserType) -> None: 

61 """Parse an empty docstring. 

62 

63 Parameters: 

64 parse_sphinx: Fixture parser. 

65 """ 

66 sections, warnings = parse_sphinx("") 

67 

68 assert len(sections) == 1 

69 assert sections[0].kind is DocstringSectionKind.text 

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

71 assert not warnings 

72 

73 

74def test_parse__multiple_blank_lines_before_description__single_markdown_section(parse_sphinx: ParserType) -> None: 

75 """Parse a docstring with initial blank lines. 

76 

77 Parameters: 

78 parse_sphinx: Fixture parser. 

79 """ 

80 sections, warnings = parse_sphinx( 

81 """ 

82 

83 

84 Now text""", 

85 ) 

86 

87 assert len(sections) == 1 

88 assert sections[0].kind is DocstringSectionKind.text 

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

90 assert not warnings 

91 

92 

93def test_parse__param_field__param_section(parse_sphinx: ParserType) -> None: 

94 """Parse a parameter section. 

95 

96 Parameters: 

97 parse_sphinx: Fixture parser. 

98 """ 

99 sections, _ = parse_sphinx( 

100 f""" 

101 Docstring with one line param. 

102 

103 :param {SOME_NAME}: {SOME_TEXT} 

104 """, 

105 ) 

106 assert len(sections) == 2 

107 assert sections[1].kind is DocstringSectionKind.parameters 

108 actual = sections[1].value[0] 

109 expected = DocstringParameter(SOME_NAME, description=SOME_TEXT) 

110 assert isinstance(actual, type(expected)) 

111 assert actual.as_dict() == expected.as_dict() 

112 

113 

114def test_parse__only_param_field__empty_markdown(parse_sphinx: ParserType) -> None: 

115 """Parse only a parameter section. 

116 

117 Parameters: 

118 parse_sphinx: Fixture parser. 

119 """ 

120 sections, _ = parse_sphinx(":param foo: text") 

121 assert len(sections) == 2 

122 assert sections[0].kind is DocstringSectionKind.text 

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

124 

125 

126@pytest.mark.parametrize( 

127 "param_directive_name", 

128 [ 

129 "param", 

130 "parameter", 

131 "arg", 

132 "arguments", 

133 "key", 

134 "keyword", 

135 ], 

136) 

137def test_parse__all_param_names__param_section(parse_sphinx: ParserType, param_directive_name: str) -> None: 

138 """Parse all parameters directives. 

139 

140 Parameters: 

141 parse_sphinx: Fixture parser. 

142 param_directive_name: A parametrized directive name. 

143 """ 

144 sections, _ = parse_sphinx( 

145 f""" 

146 Docstring with one line param. 

147 

148 :{param_directive_name} {SOME_NAME}: {SOME_TEXT} 

149 """, 

150 ) 

151 assert len(sections) == 2 

152 assert sections[1].kind is DocstringSectionKind.parameters 

153 actual = sections[1].value[0] 

154 expected = DocstringParameter(SOME_NAME, description=SOME_TEXT) 

155 assert isinstance(actual, type(expected)) 

156 assert actual.as_dict() == expected.as_dict() 

157 

158 

159@pytest.mark.parametrize( 

160 "docstring", 

161 [ 

162 f""" 

163 Docstring with param with continuation, no indent. 

164 

165 :param {SOME_NAME}: {SOME_TEXT} 

166 {SOME_EXTRA_TEXT} 

167 """, 

168 f""" 

169 Docstring with param with continuation, with indent. 

170 

171 :param {SOME_NAME}: {SOME_TEXT} 

172 {SOME_EXTRA_TEXT} 

173 """, 

174 ], 

175) 

176def test_parse__param_field_multi_line__param_section(parse_sphinx: ParserType, docstring: str) -> None: 

177 """Parse multiline directives. 

178 

179 Parameters: 

180 parse_sphinx: Fixture parser. 

181 docstring: A parametrized docstring. 

182 """ 

183 sections, _ = parse_sphinx(docstring) 

184 assert len(sections) == 2 

185 assert sections[1].kind is DocstringSectionKind.parameters 

186 actual = sections[1].value[0] 

187 expected = DocstringParameter(SOME_NAME, description=f"{SOME_TEXT} {SOME_EXTRA_TEXT}") 

188 assert isinstance(actual, type(expected)) 

189 assert actual.as_dict() == expected.as_dict() 

190 

191 

192def test_parse__param_field_for_function__param_section_with_kind(parse_sphinx: ParserType) -> None: 

193 """Parse parameters. 

194 

195 Parameters: 

196 parse_sphinx: Fixture parser. 

197 """ 

198 docstring = f""" 

199 Docstring with line continuation. 

200 

201 :param foo: {SOME_TEXT} 

202 """ 

203 

204 sections, _ = parse_sphinx(docstring) 

205 assert len(sections) == 2 

206 assert sections[1].kind is DocstringSectionKind.parameters 

207 actual = sections[1].value[0] 

208 expected = DocstringParameter(SOME_NAME, description=SOME_TEXT) 

209 assert isinstance(actual, type(expected)) 

210 assert actual.as_dict() == expected.as_dict() 

211 

212 

213def test_parse__param_field_docs_type__param_section_with_type(parse_sphinx: ParserType) -> None: 

214 """Parse parameters with types. 

215 

216 Parameters: 

217 parse_sphinx: Fixture parser. 

218 """ 

219 docstring = f""" 

220 Docstring with line continuation. 

221 

222 :param str foo: {SOME_TEXT} 

223 """ 

224 

225 sections, _ = parse_sphinx(docstring) 

226 assert len(sections) == 2 

227 assert sections[1].kind is DocstringSectionKind.parameters 

228 actual = sections[1].value[0] 

229 expected = DocstringParameter(SOME_NAME, annotation="str", description=SOME_TEXT) 

230 assert isinstance(actual, type(expected)) 

231 assert actual.as_dict() == expected.as_dict() 

232 

233 

234def test_parse__param_field_type_field__param_section_with_type(parse_sphinx: ParserType) -> None: 

235 """Parse parameters with separated types. 

236 

237 Parameters: 

238 parse_sphinx: Fixture parser. 

239 """ 

240 docstring = f""" 

241 Docstring with line continuation. 

242 

243 :param foo: {SOME_TEXT} 

244 :type foo: str 

245 """ 

246 

247 sections, _ = parse_sphinx(docstring) 

248 assert len(sections) == 2 

249 assert sections[1].kind is DocstringSectionKind.parameters 

250 actual = sections[1].value[0] 

251 expected = DocstringParameter(SOME_NAME, annotation="str", description=SOME_TEXT) 

252 assert isinstance(actual, type(expected)) 

253 assert actual.as_dict() == expected.as_dict() 

254 

255 

256def test_parse__param_field_type_field_first__param_section_with_type(parse_sphinx: ParserType) -> None: 

257 """Parse parameters with separated types first. 

258 

259 Parameters: 

260 parse_sphinx: Fixture parser. 

261 """ 

262 docstring = f""" 

263 Docstring with line continuation. 

264 

265 :type foo: str 

266 :param foo: {SOME_TEXT} 

267 """ 

268 

269 sections, _ = parse_sphinx(docstring) 

270 assert len(sections) == 2 

271 assert sections[1].kind is DocstringSectionKind.parameters 

272 actual = sections[1].value[0] 

273 expected = DocstringParameter(SOME_NAME, annotation="str", description=SOME_TEXT) 

274 assert isinstance(actual, type(expected)) 

275 assert actual.as_dict() == expected.as_dict() 

276 

277 

278@pytest.mark.parametrize("union", ["str or None", "None or str", "str or int", "str or int or float"]) 

279def test_parse__param_field_type_field_or_none__param_section_with_optional( 

280 parse_sphinx: ParserType, 

281 union: str, 

282) -> None: 

283 """Parse parameters with separated union types. 

284 

285 Parameters: 

286 parse_sphinx: Fixture parser. 

287 union: A parametrized union type. 

288 """ 

289 docstring = f""" 

290 Docstring with line continuation. 

291 

292 :param foo: {SOME_TEXT} 

293 :type foo: {union} 

294 """ 

295 

296 sections, _ = parse_sphinx(docstring) 

297 assert len(sections) == 2 

298 assert sections[1].kind is DocstringSectionKind.parameters 

299 actual = sections[1].value[0] 

300 expected = DocstringParameter(SOME_NAME, annotation=union.replace(" or ", " | "), description=SOME_TEXT) 

301 assert isinstance(actual, type(expected)) 

302 assert actual.as_dict() == expected.as_dict() 

303 

304 

305def test_parse__param_field_annotate_type__param_section_with_type(parse_sphinx: ParserType) -> None: 

306 """Parse a simple docstring. 

307 

308 Parameters: 

309 parse_sphinx: Fixture parser. 

310 """ 

311 docstring = f""" 

312 Docstring with line continuation. 

313 

314 :param foo: {SOME_TEXT} 

315 """ 

316 

317 sections, warnings = parse_sphinx( 

318 docstring, 

319 parent=Function("func", parameters=Parameters(Parameter("foo", annotation="str", kind=None))), 

320 ) 

321 assert len(sections) == 2 

322 assert sections[1].kind is DocstringSectionKind.parameters 

323 actual = sections[1].value[0] 

324 expected = DocstringParameter(SOME_NAME, annotation="str", description=SOME_TEXT) 

325 assert isinstance(actual, type(expected)) 

326 assert actual.as_dict() == expected.as_dict() 

327 assert not warnings 

328 

329 

330def test_parse__param_field_no_matching_param__result_from_docstring(parse_sphinx: ParserType) -> None: 

331 """Parse a simple docstring. 

332 

333 Parameters: 

334 parse_sphinx: Fixture parser. 

335 """ 

336 docstring = f""" 

337 Docstring with line continuation. 

338 

339 :param other: {SOME_TEXT} 

340 """ 

341 

342 sections, _ = parse_sphinx(docstring) 

343 assert len(sections) == 2 

344 assert sections[1].kind is DocstringSectionKind.parameters 

345 actual = sections[1].value[0] 

346 expected = DocstringParameter("other", description=SOME_TEXT) 

347 assert isinstance(actual, type(expected)) 

348 assert actual.as_dict() == expected.as_dict() 

349 

350 

351def test_parse__param_field_with_default__result_from_docstring(parse_sphinx: ParserType) -> None: 

352 """Parse a simple docstring. 

353 

354 Parameters: 

355 parse_sphinx: Fixture parser. 

356 """ 

357 docstring = f""" 

358 Docstring with line continuation. 

359 

360 :param foo: {SOME_TEXT} 

361 """ 

362 

363 sections, warnings = parse_sphinx( 

364 docstring, 

365 parent=Function("func", parameters=Parameters(Parameter("foo", kind=None, default=repr("")))), 

366 ) 

367 assert len(sections) == 2 

368 assert sections[1].kind is DocstringSectionKind.parameters 

369 actual = sections[1].value[0] 

370 expected = DocstringParameter("foo", description=SOME_TEXT, value=repr("")) 

371 assert isinstance(actual, type(expected)) 

372 assert actual.as_dict() == expected.as_dict() 

373 assert not warnings 

374 

375 

376def test_parse__param_field_no_matching_param__error_message(parse_sphinx: ParserType) -> None: 

377 """Parse a simple docstring. 

378 

379 Parameters: 

380 parse_sphinx: Fixture parser. 

381 """ 

382 docstring = f""" 

383 Docstring with line continuation. 

384 

385 :param other: {SOME_TEXT} 

386 """ 

387 

388 _, warnings = parse_sphinx(docstring) 

389 assert "No matching parameter for 'other'" in warnings[0] 

390 

391 

392def test_parse__invalid_param_field_only_initial_marker__error_message(parse_sphinx: ParserType) -> None: 

393 """Parse a simple docstring. 

394 

395 Parameters: 

396 parse_sphinx: Fixture parser. 

397 """ 

398 docstring = f""" 

399 Docstring with line continuation. 

400 

401 :param foo {SOME_TEXT} 

402 """ 

403 

404 _, warnings = parse_sphinx(docstring) 

405 assert "Failed to get ':directive: value' pair" in warnings[0] 

406 

407 

408def test_parse__invalid_param_field_wrong_part_count__error_message(parse_sphinx: ParserType) -> None: 

409 """Parse a simple docstring. 

410 

411 Parameters: 

412 parse_sphinx: Fixture parser. 

413 """ 

414 docstring = f""" 

415 Docstring with line continuation. 

416 

417 :param: {SOME_TEXT} 

418 """ 

419 

420 _, warnings = parse_sphinx(docstring) 

421 assert "Failed to parse field directive" in warnings[0] 

422 

423 

424def test_parse__param_twice__error_message(parse_sphinx: ParserType) -> None: 

425 """Parse a simple docstring. 

426 

427 Parameters: 

428 parse_sphinx: Fixture parser. 

429 """ 

430 docstring = f""" 

431 Docstring with line continuation. 

432 

433 :param foo: {SOME_TEXT} 

434 :param foo: {SOME_TEXT} again 

435 """ 

436 

437 _, warnings = parse_sphinx( 

438 docstring, 

439 parent=Function("func", parameters=Parameters(Parameter("foo", kind=None))), 

440 ) 

441 assert "Duplicate parameter entry for 'foo'" in warnings[0] 

442 

443 

444def test_parse__param_type_twice_doc__error_message(parse_sphinx: ParserType) -> None: 

445 """Parse a simple docstring. 

446 

447 Parameters: 

448 parse_sphinx: Fixture parser. 

449 """ 

450 docstring = f""" 

451 Docstring with line continuation. 

452 

453 :param str foo: {SOME_TEXT} 

454 :type foo: str 

455 """ 

456 

457 _, warnings = parse_sphinx( 

458 docstring, 

459 parent=Function("func", parameters=Parameters(Parameter("foo", kind=None))), 

460 ) 

461 assert "Duplicate parameter information for 'foo'" in warnings[0] 

462 

463 

464def test_parse__param_type_twice_type_directive_first__error_message(parse_sphinx: ParserType) -> None: 

465 """Parse a simple docstring. 

466 

467 Parameters: 

468 parse_sphinx: Fixture parser. 

469 """ 

470 docstring = f""" 

471 Docstring with line continuation. 

472 

473 :type foo: str 

474 :param str foo: {SOME_TEXT} 

475 """ 

476 

477 _, warnings = parse_sphinx( 

478 docstring, 

479 parent=Function("func", parameters=Parameters(Parameter("foo", kind=None))), 

480 ) 

481 assert "Duplicate parameter information for 'foo'" in warnings[0] 

482 

483 

484def test_parse__param_type_twice_annotated__error_message(parse_sphinx: ParserType) -> None: 

485 """Parse a simple docstring. 

486 

487 Parameters: 

488 parse_sphinx: Fixture parser. 

489 """ 

490 docstring = f""" 

491 Docstring with line continuation. 

492 

493 :param str foo: {SOME_TEXT} 

494 :type foo: str 

495 """ 

496 

497 _, warnings = parse_sphinx( 

498 docstring, 

499 parent=Function("func", parameters=Parameters(Parameter("foo", annotation="str", kind=None))), 

500 ) 

501 assert "Duplicate parameter information for 'foo'" in warnings[0] 

502 

503 

504def test_warn_about_unknown_parameters(parse_sphinx: ParserType) -> None: 

505 """Warn about unknown parameters in "Parameters" sections. 

506 

507 Parameters: 

508 parse_sphinx: Fixture parser. 

509 """ 

510 docstring = """ 

511 

512 :param str a: {SOME_TEXT} 

513 """ 

514 

515 _, warnings = parse_sphinx( 

516 docstring, 

517 parent=Function( 

518 "func", 

519 parameters=Parameters( 

520 Parameter("b"), 

521 ), 

522 ), 

523 ) 

524 assert len(warnings) == 1 

525 assert "Parameter 'a' does not appear in the function signature" in warnings[0] 

526 

527 

528def test_parse__param_type_no_type__error_message(parse_sphinx: ParserType) -> None: 

529 """Parse a simple docstring. 

530 

531 Parameters: 

532 parse_sphinx: Fixture parser. 

533 """ 

534 docstring = f""" 

535 Docstring with line continuation. 

536 

537 :param str foo: {SOME_TEXT} 

538 :type str 

539 """ 

540 

541 _, warnings = parse_sphinx( 

542 docstring, 

543 parent=Function("func", parameters=Parameters(Parameter("foo", annotation="str", kind=None))), 

544 ) 

545 assert "Failed to get ':directive: value' pair from" in warnings[0] 

546 

547 

548def test_parse__param_type_no_name__error_message(parse_sphinx: ParserType) -> None: 

549 """Parse a simple docstring. 

550 

551 Parameters: 

552 parse_sphinx: Fixture parser. 

553 """ 

554 docstring = f""" 

555 Docstring with line continuation. 

556 

557 :param str foo: {SOME_TEXT} 

558 :type: str 

559 """ 

560 

561 _, warnings = parse_sphinx( 

562 docstring, 

563 parent=Function("func", parameters=Parameters(Parameter("foo", annotation="str", kind=None))), 

564 ) 

565 assert "Failed to get parameter name from" in warnings[0] 

566 

567 

568@pytest.mark.parametrize( 

569 "docstring", 

570 [ 

571 f""" 

572 Docstring with param with continuation, no indent. 

573 

574 :var {SOME_NAME}: {SOME_TEXT} 

575 {SOME_EXTRA_TEXT} 

576 """, 

577 f""" 

578 Docstring with param with continuation, with indent. 

579 

580 :var {SOME_NAME}: {SOME_TEXT} 

581 {SOME_EXTRA_TEXT} 

582 """, 

583 ], 

584) 

585def test_parse__attribute_field_multi_line__param_section(parse_sphinx: ParserType, docstring: str) -> None: 

586 """Parse multiline attributes. 

587 

588 Parameters: 

589 parse_sphinx: Fixture parser. 

590 docstring: A parametrized docstring. 

591 """ 

592 sections, warnings = parse_sphinx(docstring) 

593 assert len(sections) == 2 

594 assert sections[1].kind is DocstringSectionKind.attributes 

595 actual = sections[1].value[0] 

596 expected = DocstringAttribute(SOME_NAME, description=f"{SOME_TEXT} {SOME_EXTRA_TEXT}") 

597 assert isinstance(actual, type(expected)) 

598 assert actual.as_dict() == expected.as_dict() 

599 assert not warnings 

600 

601 

602@pytest.mark.parametrize( 

603 "attribute_directive_name", 

604 [ 

605 "var", 

606 "ivar", 

607 "cvar", 

608 ], 

609) 

610def test_parse__all_attribute_names__param_section(parse_sphinx: ParserType, attribute_directive_name: str) -> None: 

611 """Parse all attributes directives. 

612 

613 Parameters: 

614 parse_sphinx: Fixture parser. 

615 attribute_directive_name: A parametrized directive name. 

616 """ 

617 sections, warnings = parse_sphinx( 

618 f""" 

619 Docstring with one line attribute. 

620 

621 :{attribute_directive_name} {SOME_NAME}: {SOME_TEXT} 

622 """, 

623 ) 

624 assert len(sections) == 2 

625 assert sections[1].kind is DocstringSectionKind.attributes 

626 actual = sections[1].value[0] 

627 expected = DocstringAttribute(SOME_NAME, description=SOME_TEXT) 

628 assert isinstance(actual, type(expected)) 

629 assert actual.as_dict() == expected.as_dict() 

630 assert not warnings 

631 

632 

633def test_parse__class_attributes__attributes_section(parse_sphinx: ParserType) -> None: 

634 """Parse class attributes. 

635 

636 Parameters: 

637 parse_sphinx: Fixture parser. 

638 """ 

639 docstring = f""" 

640 Class docstring with attributes 

641 

642 :var foo: {SOME_TEXT} 

643 """ 

644 

645 sections, _ = parse_sphinx(docstring, parent=Class("klass")) 

646 assert len(sections) == 2 

647 assert sections[1].kind is DocstringSectionKind.attributes 

648 actual = sections[1].value[0] 

649 expected = DocstringAttribute(SOME_NAME, description=SOME_TEXT) 

650 assert isinstance(actual, type(expected)) 

651 assert actual.as_dict() == expected.as_dict() 

652 

653 

654def test_parse__class_attributes_with_type__annotation_in_attributes_section(parse_sphinx: ParserType) -> None: 

655 """Parse typed class attributes. 

656 

657 Parameters: 

658 parse_sphinx: Fixture parser. 

659 """ 

660 docstring = f""" 

661 Class docstring with attributes 

662 

663 :vartype foo: str 

664 :var foo: {SOME_TEXT} 

665 """ 

666 

667 sections, _ = parse_sphinx(docstring, parent=Class("klass")) 

668 assert len(sections) == 2 

669 assert sections[1].kind is DocstringSectionKind.attributes 

670 actual = sections[1].value[0] 

671 expected = DocstringAttribute(SOME_NAME, annotation="str", description=SOME_TEXT) 

672 assert isinstance(actual, type(expected)) 

673 assert actual.as_dict() == expected.as_dict() 

674 

675 

676def test_parse__attribute_invalid_directive___error(parse_sphinx: ParserType) -> None: 

677 """Warn on invalid attribute directive. 

678 

679 Parameters: 

680 parse_sphinx: Fixture parser. 

681 """ 

682 docstring = f""" 

683 Class docstring with attributes 

684 

685 :var {SOME_TEXT} 

686 """ 

687 

688 _, warnings = parse_sphinx(docstring) 

689 assert "Failed to get ':directive: value' pair from" in warnings[0] 

690 

691 

692def test_parse__attribute_no_name__error(parse_sphinx: ParserType) -> None: 

693 """Warn on invalid attribute directive. 

694 

695 Parameters: 

696 parse_sphinx: Fixture parser. 

697 """ 

698 docstring = f""" 

699 Class docstring with attributes 

700 

701 :var: {SOME_TEXT} 

702 """ 

703 

704 _, warnings = parse_sphinx(docstring) 

705 assert "Failed to parse field directive from" in warnings[0] 

706 

707 

708def test_parse__attribute_duplicate__error(parse_sphinx: ParserType) -> None: 

709 """Warn on duplicate attribute directive. 

710 

711 Parameters: 

712 parse_sphinx: Fixture parser. 

713 """ 

714 docstring = f""" 

715 Class docstring with attributes 

716 

717 :var foo: {SOME_TEXT} 

718 :var foo: {SOME_TEXT} 

719 """ 

720 

721 _, warnings = parse_sphinx(docstring) 

722 assert "Duplicate attribute entry for 'foo'" in warnings[0] 

723 

724 

725def test_parse__class_attributes_type_invalid__error(parse_sphinx: ParserType) -> None: 

726 """Warn on invalid attribute type directive. 

727 

728 Parameters: 

729 parse_sphinx: Fixture parser. 

730 """ 

731 docstring = f""" 

732 Class docstring with attributes 

733 

734 :vartype str 

735 :var foo: {SOME_TEXT} 

736 """ 

737 

738 _, warnings = parse_sphinx(docstring) 

739 assert "Failed to get ':directive: value' pair from " in warnings[0] 

740 

741 

742def test_parse__class_attributes_type_no_name__error(parse_sphinx: ParserType) -> None: 

743 """Warn on invalid attribute directive. 

744 

745 Parameters: 

746 parse_sphinx: Fixture parser. 

747 """ 

748 docstring = f""" 

749 Class docstring with attributes 

750 

751 :vartype: str 

752 :var foo: {SOME_TEXT} 

753 """ 

754 

755 _, warnings = parse_sphinx(docstring) 

756 assert "Failed to get attribute name from" in warnings[0] 

757 

758 

759def test_parse__return_directive__return_section_no_type(parse_sphinx: ParserType) -> None: 

760 """Parse return directives. 

761 

762 Parameters: 

763 parse_sphinx: Fixture parser. 

764 """ 

765 docstring = f""" 

766 Function with only return directive 

767 

768 :return: {SOME_TEXT} 

769 """ 

770 

771 sections, _ = parse_sphinx(docstring) 

772 assert len(sections) == 2 

773 assert sections[1].kind is DocstringSectionKind.returns 

774 actual = sections[1].value[0] 

775 expected = DocstringReturn(name="", annotation=None, description=SOME_TEXT) 

776 assert isinstance(actual, type(expected)) 

777 assert actual.as_dict() == expected.as_dict() 

778 

779 

780def test_parse__return_directive_rtype__return_section_with_type(parse_sphinx: ParserType) -> None: 

781 """Parse typed return directives. 

782 

783 Parameters: 

784 parse_sphinx: Fixture parser. 

785 """ 

786 docstring = f""" 

787 Function with only return & rtype directive 

788 

789 :return: {SOME_TEXT} 

790 :rtype: str 

791 """ 

792 

793 sections, _ = parse_sphinx(docstring) 

794 assert len(sections) == 2 

795 assert sections[1].kind is DocstringSectionKind.returns 

796 actual = sections[1].value[0] 

797 expected = DocstringReturn(name="", annotation="str", description=SOME_TEXT) 

798 assert isinstance(actual, type(expected)) 

799 assert actual.as_dict() == expected.as_dict() 

800 

801 

802def test_parse__return_directive_rtype_first__return_section_with_type(parse_sphinx: ParserType) -> None: 

803 """Parse typed-first return directives. 

804 

805 Parameters: 

806 parse_sphinx: Fixture parser. 

807 """ 

808 docstring = f""" 

809 Function with only return & rtype directive 

810 

811 :rtype: str 

812 :return: {SOME_TEXT} 

813 """ 

814 

815 sections, _ = parse_sphinx(docstring) 

816 assert len(sections) == 2 

817 assert sections[1].kind is DocstringSectionKind.returns 

818 actual = sections[1].value[0] 

819 expected = DocstringReturn(name="", annotation="str", description=SOME_TEXT) 

820 assert isinstance(actual, type(expected)) 

821 assert actual.as_dict() == expected.as_dict() 

822 

823 

824def test_parse__return_directive_annotation__return_section_with_type(parse_sphinx: ParserType) -> None: 

825 """Parse return directives with return annotation. 

826 

827 Parameters: 

828 parse_sphinx: Fixture parser. 

829 """ 

830 docstring = f""" 

831 Function with return directive, rtype directive, & annotation 

832 

833 :return: {SOME_TEXT} 

834 """ 

835 

836 sections, _ = parse_sphinx(docstring, parent=Function("func", returns="str")) 

837 assert len(sections) == 2 

838 assert sections[1].kind is DocstringSectionKind.returns 

839 actual = sections[1].value[0] 

840 expected = DocstringReturn(name="", annotation="str", description=SOME_TEXT) 

841 assert isinstance(actual, type(expected)) 

842 assert actual.as_dict() == expected.as_dict() 

843 

844 

845def test_parse__return_directive_annotation__prefer_return_directive(parse_sphinx: ParserType) -> None: 

846 """Prefer docstring type over return annotation. 

847 

848 Parameters: 

849 parse_sphinx: Fixture parser. 

850 """ 

851 docstring = f""" 

852 Function with return directive, rtype directive, & annotation 

853 

854 :return: {SOME_TEXT} 

855 :rtype: str 

856 """ 

857 

858 sections, _ = parse_sphinx(docstring, parent=Function("func", returns="int")) 

859 assert len(sections) == 2 

860 assert sections[1].kind is DocstringSectionKind.returns 

861 actual = sections[1].value[0] 

862 expected = DocstringReturn(name="", annotation="str", description=SOME_TEXT) 

863 assert isinstance(actual, type(expected)) 

864 assert actual.as_dict() == expected.as_dict() 

865 

866 

867def test_parse__return_invalid__error(parse_sphinx: ParserType) -> None: 

868 """Warn on invalid return directive. 

869 

870 Parameters: 

871 parse_sphinx: Fixture parser. 

872 """ 

873 docstring = f""" 

874 Function with only return directive 

875 

876 :return {SOME_TEXT} 

877 """ 

878 

879 _, warnings = parse_sphinx(docstring) 

880 assert "Failed to get ':directive: value' pair from " in warnings[0] 

881 

882 

883def test_parse__rtype_invalid__error(parse_sphinx: ParserType) -> None: 

884 """Warn on invalid typed return directive. 

885 

886 Parameters: 

887 parse_sphinx: Fixture parser. 

888 """ 

889 docstring = """ 

890 Function with only return directive 

891 

892 :rtype str 

893 """ 

894 

895 _, warnings = parse_sphinx(docstring) 

896 assert "Failed to get ':directive: value' pair from " in warnings[0] 

897 

898 

899def test_parse__raises_directive__exception_section(parse_sphinx: ParserType) -> None: 

900 """Parse raise directives. 

901 

902 Parameters: 

903 parse_sphinx: Fixture parser. 

904 """ 

905 docstring = f""" 

906 Function with only return directive 

907 

908 :raise SomeException: {SOME_TEXT} 

909 """ 

910 

911 sections, _ = parse_sphinx(docstring) 

912 assert len(sections) == 2 

913 assert sections[1].kind is DocstringSectionKind.raises 

914 actual = sections[1].value[0] 

915 expected = DocstringRaise(annotation=SOME_EXCEPTION_NAME, description=SOME_TEXT) 

916 assert isinstance(actual, type(expected)) 

917 assert actual.as_dict() == expected.as_dict() 

918 

919 

920def test_parse__multiple_raises_directive__exception_section_with_two(parse_sphinx: ParserType) -> None: 

921 """Parse multiple raise directives. 

922 

923 Parameters: 

924 parse_sphinx: Fixture parser. 

925 """ 

926 docstring = f""" 

927 Function with only return directive 

928 

929 :raise SomeException: {SOME_TEXT} 

930 :raise SomeOtherException: {SOME_TEXT} 

931 """ 

932 

933 sections, _ = parse_sphinx(docstring) 

934 assert len(sections) == 2 

935 assert sections[1].kind is DocstringSectionKind.raises 

936 actual = sections[1].value[0] 

937 expected = DocstringRaise(annotation=SOME_EXCEPTION_NAME, description=SOME_TEXT) 

938 assert isinstance(actual, type(expected)) 

939 assert actual.as_dict() == expected.as_dict() 

940 actual = sections[1].value[1] 

941 expected = DocstringRaise(annotation=SOME_OTHER_EXCEPTION_NAME, description=SOME_TEXT) 

942 assert isinstance(actual, type(expected)) 

943 assert actual.as_dict() == expected.as_dict() 

944 

945 

946@pytest.mark.parametrize( 

947 "raise_directive_name", 

948 [ 

949 "raises", 

950 "raise", 

951 "except", 

952 "exception", 

953 ], 

954) 

955def test_parse__all_exception_names__param_section(parse_sphinx: ParserType, raise_directive_name: str) -> None: 

956 """Parse all raise directives. 

957 

958 Parameters: 

959 parse_sphinx: Fixture parser. 

960 raise_directive_name: A parametrized directive name. 

961 """ 

962 sections, _ = parse_sphinx( 

963 f""" 

964 Docstring with one line attribute. 

965 

966 :{raise_directive_name} {SOME_EXCEPTION_NAME}: {SOME_TEXT} 

967 """, 

968 ) 

969 assert len(sections) == 2 

970 assert sections[1].kind is DocstringSectionKind.raises 

971 actual = sections[1].value[0] 

972 expected = DocstringRaise(annotation=SOME_EXCEPTION_NAME, description=SOME_TEXT) 

973 assert isinstance(actual, type(expected)) 

974 assert actual.as_dict() == expected.as_dict() 

975 

976 

977def test_parse__raise_invalid__error(parse_sphinx: ParserType) -> None: 

978 """Warn on invalid raise directives. 

979 

980 Parameters: 

981 parse_sphinx: Fixture parser. 

982 """ 

983 docstring = f""" 

984 Function with only return directive 

985 

986 :raise {SOME_TEXT} 

987 """ 

988 

989 _, warnings = parse_sphinx(docstring) 

990 assert "Failed to get ':directive: value' pair from " in warnings[0] 

991 

992 

993def test_parse__raise_no_name__error(parse_sphinx: ParserType) -> None: 

994 """Warn on invalid raise directives. 

995 

996 Parameters: 

997 parse_sphinx: Fixture parser. 

998 """ 

999 docstring = f""" 

1000 Function with only return directive 

1001 

1002 :raise: {SOME_TEXT} 

1003 """ 

1004 

1005 _, warnings = parse_sphinx(docstring) 

1006 assert "Failed to parse exception directive from" in warnings[0] 

1007 

1008 

1009def test_parse__module_attributes_section__expected_attributes_section(parse_sphinx: ParserType) -> None: 

1010 """Parse attributes section in modules. 

1011 

1012 Parameters: 

1013 parse_sphinx: Fixture parser. 

1014 """ 

1015 docstring = """ 

1016 Let's describe some attributes. 

1017 

1018 :var A: Alpha. 

1019 :vartype B: bytes 

1020 :var B: Beta. 

1021 :var C: Gamma. 

1022 :var D: Delta. 

1023 :var E: Epsilon. 

1024 :vartype E: float 

1025 """ 

1026 

1027 module = Module("mod", filepath=None) 

1028 module["A"] = Attribute("A", annotation="int", value="0") 

1029 module["B"] = Attribute("B", annotation="str", value=repr("ŧ")) 

1030 module["C"] = Attribute("C", annotation="bool", value="True") 

1031 module["D"] = Attribute("D", annotation=None, value="3.0") 

1032 module["E"] = Attribute("E", annotation=None, value="None") 

1033 sections, warnings = parse_sphinx(docstring, parent=module) 

1034 

1035 attr_section = sections[1] 

1036 assert attr_section.kind is DocstringSectionKind.attributes 

1037 assert len(attr_section.value) == 5 

1038 expected_data: list[dict[str, Any]] = [ 

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

1040 {"name": "B", "annotation": "bytes", "description": "Beta."}, 

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

1042 {"name": "D", "annotation": None, "description": "Delta."}, 

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

1044 ] 

1045 for index, expected_kwargs in enumerate(expected_data): 

1046 actual = attr_section.value[index] 

1047 expected = DocstringAttribute(**expected_kwargs) 

1048 assert isinstance(actual, type(expected)) 

1049 assert actual.name == expected.name 

1050 assert actual.as_dict() == expected.as_dict() 

1051 assert not warnings 

1052 

1053 

1054def test_parse__properties_return_type(parse_sphinx: ParserType) -> None: 

1055 """Parse attributes section in modules. 

1056 

1057 Parameters: 

1058 parse_sphinx: Fixture parser. 

1059 """ 

1060 docstring = """ 

1061 Property that returns True for explaining the issue. 

1062 

1063 :return: True 

1064 """ 

1065 prop = Attribute("example", annotation="bool") 

1066 sections, warnings = parse_sphinx(docstring, parent=prop) 

1067 assert not warnings 

1068 assert sections[1].value[0].annotation == "bool"