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

571 statements  

« prev     ^ index     » next       coverage.py v7.10.2, created at 2025-08-11 13:44 +0200

1"""Tests for the [Google-style parser][griffe.docstrings.google].""" 

2 

3from __future__ import annotations 

4 

5import inspect 

6from typing import TYPE_CHECKING 

7 

8import pytest 

9 

10from griffe import ( 

11 Attribute, 

12 Class, 

13 Docstring, 

14 DocstringReceive, 

15 DocstringReturn, 

16 DocstringSectionKind, 

17 DocstringYield, 

18 ExprName, 

19 Function, 

20 Module, 

21 Parameter, 

22 Parameters, 

23 TypeParameter, 

24 TypeParameterKind, 

25 TypeParameters, 

26 parse_docstring_annotation, 

27) 

28 

29if TYPE_CHECKING: 

30 from tests.test_docstrings.helpers import ParserType 

31 

32 

33# ============================================================================================= 

34# Markup flow (multilines, indentation, etc.) 

35def test_simple_docstring(parse_google: ParserType) -> None: 

36 """Parse a simple docstring. 

37 

38 Parameters: 

39 parse_google: Fixture parser. 

40 """ 

41 sections, warnings = parse_google("A simple docstring.") 

42 assert len(sections) == 1 

43 assert not warnings 

44 

45 

46def test_multiline_docstring(parse_google: ParserType) -> None: 

47 """Parse a multi-line docstring. 

48 

49 Parameters: 

50 parse_google: Fixture parser. 

51 """ 

52 sections, warnings = parse_google( 

53 """ 

54 A somewhat longer docstring. 

55 

56 Blablablabla. 

57 """, 

58 ) 

59 assert len(sections) == 1 

60 assert not warnings 

61 

62 

63def test_parse_partially_indented_lines(parse_google: ParserType) -> None: 

64 """Properly handle partially indented lines. 

65 

66 Parameters: 

67 parse_google: Fixture parser. 

68 """ 

69 docstring = """ 

70 The available formats are: 

71 - JSON 

72 

73 The unavailable formats are: 

74 - YAML 

75 """ 

76 sections, warnings = parse_google(docstring) 

77 assert len(sections) == 2 

78 assert sections[0].kind is DocstringSectionKind.admonition 

79 assert sections[1].kind is DocstringSectionKind.admonition 

80 assert not warnings 

81 

82 

83def test_multiple_lines_in_sections_items(parse_google: ParserType) -> None: 

84 """Parse multi-line item description. 

85 

86 Parameters: 

87 parse_google: Fixture parser. 

88 """ 

89 docstring = """ 

90 Parameters: 

91 p (int): This parameter 

92 has a description 

93 spawning on multiple lines. 

94 

95 It even has blank lines in it. 

96 Some of these lines 

97 are indented for no reason. 

98 q (int): 

99 What if the first line is blank? 

100 """ 

101 

102 sections, warnings = parse_google(docstring) 

103 assert len(sections) == 1 

104 assert len(sections[0].value) == 2 

105 assert warnings 

106 for warning in warnings: 

107 assert "should be 4 * 2 = 8 spaces, not" in warning 

108 

109 

110def test_code_blocks(parse_google: ParserType) -> None: 

111 """Parse code blocks. 

112 

113 Parameters: 

114 parse_google: Fixture parser. 

115 """ 

116 docstring = """ 

117 This docstring contains a code block! 

118 

119 ```python 

120 print("hello") 

121 ``` 

122 """ 

123 

124 sections, warnings = parse_google(docstring) 

125 assert len(sections) == 1 

126 assert not warnings 

127 

128 

129def test_indented_code_block(parse_google: ParserType) -> None: 

130 """Parse indented code blocks. 

131 

132 Parameters: 

133 parse_google: Fixture parser. 

134 """ 

135 docstring = """ 

136 This docstring contains a docstring in a code block o_O! 

137 

138 \"\"\" 

139 This docstring is contained in another docstring O_o! 

140 

141 Parameters: 

142 s: A string. 

143 \"\"\" 

144 """ 

145 

146 sections, warnings = parse_google(docstring) 

147 assert len(sections) == 1 

148 assert not warnings 

149 

150 

151def test_different_indentation(parse_google: ParserType) -> None: 

152 """Parse different indentations, warn on confusing indentation. 

153 

154 Parameters: 

155 parse_google: Fixture parser. 

156 """ 

157 docstring = """ 

158 Raises: 

159 StartAt5: this section's items starts with 5 spaces of indentation. 

160 Well indented continuation line. 

161 Badly indented continuation line (will trigger a warning). 

162 

163 Empty lines are preserved, as well as extra-indentation (this line is a code block). 

164 AnyOtherLine: ...starting with exactly 5 spaces is a new item. 

165 AnyLine: ...indented with less than 5 spaces signifies the end of the section. 

166 """ 

167 

168 sections, warnings = parse_google(docstring) 

169 assert len(sections) == 2 

170 assert len(sections[0].value) == 2 

171 assert sections[0].value[0].description == ( 

172 "this section's items starts with 5 spaces of indentation.\n" 

173 "Well indented continuation line.\n" 

174 "Badly indented continuation line (will trigger a warning).\n" 

175 "\n" 

176 " Empty lines are preserved, as well as extra-indentation (this line is a code block)." 

177 ) 

178 assert sections[1].value == " AnyLine: ...indented with less than 5 spaces signifies the end of the section." 

179 assert len(warnings) == 1 

180 assert "should be 5 * 2 = 10 spaces, not 6" in warnings[0] 

181 

182 

183def test_empty_indented_lines_in_section_with_items(parse_google: ParserType) -> None: 

184 """In sections with items, don't treat lines with just indentation as items. 

185 

186 Parameters: 

187 parse_google: Fixture parser. 

188 """ 

189 docstring = "Returns:\n only_item: Description.\n \n \n\nSomething." 

190 sections, _ = parse_google(docstring) 

191 assert len(sections) == 2 

192 assert len(sections[0].value) == 1 

193 

194 

195@pytest.mark.parametrize( 

196 "section", 

197 [ 

198 "Attributes", 

199 "Other Parameters", 

200 "Parameters", 

201 "Type Parameters", 

202 "Raises", 

203 "Receives", 

204 "Returns", 

205 "Warns", 

206 "Type aliases", 

207 "Yields", 

208 ], 

209) 

210def test_starting_item_description_on_new_line(parse_google: ParserType, section: str) -> None: 

211 """In sections with items, allow starting item descriptions on a new (indented) line. 

212 

213 Parameters: 

214 parse_google: Fixture parser. 

215 section: A parametrized section name. 

216 """ 

217 docstring = f"\n{section}:\n only_item:\n Description." 

218 sections, _ = parse_google(docstring) 

219 assert len(sections) == 1 

220 assert len(sections[0].value) == 1 

221 assert sections[0].value[0].description.strip() == "Description." 

222 

223 

224# ============================================================================================= 

225# Annotations 

226def test_parse_without_parent(parse_google: ParserType) -> None: 

227 """Parse a docstring without a parent function. 

228 

229 Parameters: 

230 parse_google: Fixture parser. 

231 """ 

232 sections, warnings = parse_google( 

233 """ 

234 Parameters: 

235 void: SEGFAULT. 

236 niet: SEGFAULT. 

237 nada: SEGFAULT. 

238 rien: SEGFAULT. 

239 

240 Keyword Args: 

241 keywd: SEGFAULT. 

242 

243 Exceptions: 

244 GlobalError: when nothing works as expected. 

245 

246 Returns: 

247 Itself. 

248 """, 

249 ) 

250 

251 assert len(sections) == 4 

252 assert len(warnings) == 6 # Missing annotations for parameters and return. 

253 for warning in warnings[:-1]: 

254 assert "parameter" in warning 

255 assert "return" in warnings[-1] 

256 

257 

258def test_parse_without_annotations(parse_google: ParserType) -> None: 

259 """Parse a function docstring without signature annotations. 

260 

261 Parameters: 

262 parse_google: Fixture parser. 

263 """ 

264 docstring = """ 

265 Parameters: 

266 x: X value. 

267 

268 Keyword Args: 

269 y: Y value. 

270 

271 Returns: 

272 Sum X + Y + Z. 

273 """ 

274 

275 sections, warnings = parse_google( 

276 docstring, 

277 parent=Function( 

278 "func", 

279 parameters=Parameters( 

280 Parameter("x"), 

281 Parameter("y"), 

282 ), 

283 ), 

284 ) 

285 assert len(sections) == 3 

286 assert len(warnings) == 3 

287 for warning in warnings[:-1]: 

288 assert "parameter" in warning 

289 assert "return" in warnings[-1] 

290 

291 

292def test_parse_with_annotations(parse_google: ParserType) -> None: 

293 """Parse a function docstring with signature annotations. 

294 

295 Parameters: 

296 parse_google: Fixture parser. 

297 """ 

298 docstring = """ 

299 Parameters: 

300 x: X value. 

301 

302 Keyword Parameters: 

303 y: Y value. 

304 

305 Returns: 

306 Sum X + Y. 

307 """ 

308 

309 sections, warnings = parse_google( 

310 docstring, 

311 parent=Function( 

312 "func", 

313 parameters=Parameters( 

314 Parameter("x", annotation="int"), 

315 Parameter("y", annotation="int"), 

316 ), 

317 returns="int", 

318 ), 

319 ) 

320 assert len(sections) == 3 

321 assert not warnings 

322 

323 

324# ============================================================================================= 

325# Sections 

326def test_parse_attributes_section(parse_google: ParserType) -> None: 

327 """Parse Attributes sections. 

328 

329 Parameters: 

330 parse_google: Fixture parser. 

331 """ 

332 docstring = """ 

333 Attributes: 

334 hey: Hey. 

335 ho: Ho. 

336 """ 

337 

338 sections, warnings = parse_google(docstring) 

339 assert len(sections) == 1 

340 assert not warnings 

341 

342 

343def test_parse_functions_section(parse_google: ParserType) -> None: 

344 """Parse Functions/Methods sections. 

345 

346 Parameters: 

347 parse_google: Fixture parser. 

348 """ 

349 docstring = """ 

350 Functions: 

351 f(a, b=2): Hello. 

352 g: Hi. 

353 

354 Methods: 

355 f(a, b=2): Hello. 

356 g: Hi. 

357 """ 

358 

359 sections, warnings = parse_google(docstring) 

360 assert len(sections) == 2 

361 for section in sections: 

362 assert section.kind is DocstringSectionKind.functions 

363 func_f = section.value[0] 

364 assert func_f.name == "f" 

365 assert func_f.signature == "f(a, b=2)" 

366 assert func_f.description == "Hello." 

367 func_g = section.value[1] 

368 assert func_g.name == "g" 

369 assert func_g.signature is None 

370 assert func_g.description == "Hi." 

371 assert not warnings 

372 

373 

374def test_parse_classes_section(parse_google: ParserType) -> None: 

375 """Parse Classes sections. 

376 

377 Parameters: 

378 parse_google: Fixture parser. 

379 """ 

380 docstring = """ 

381 Classes: 

382 C(a, b=2): Hello. 

383 D: Hi. 

384 """ 

385 

386 sections, warnings = parse_google(docstring) 

387 assert len(sections) == 1 

388 assert sections[0].kind is DocstringSectionKind.classes 

389 class_c = sections[0].value[0] 

390 assert class_c.name == "C" 

391 assert class_c.signature == "C(a, b=2)" 

392 assert class_c.description == "Hello." 

393 class_d = sections[0].value[1] 

394 assert class_d.name == "D" 

395 assert class_d.signature is None 

396 assert class_d.description == "Hi." 

397 assert not warnings 

398 

399 

400def test_parse_type_aliases_section(parse_google: ParserType) -> None: 

401 """Parse Type Aliases sections. 

402 

403 Parameters: 

404 parse_google: Fixture parser. 

405 """ 

406 docstring = """ 

407 Type Aliases: 

408 TC: Hello. 

409 TD: Hi. 

410 """ 

411 

412 sections, warnings = parse_google(docstring) 

413 assert len(sections) == 1 

414 assert sections[0].kind is DocstringSectionKind.type_aliases 

415 type_alias_c = sections[0].value[0] 

416 assert type_alias_c.name == "TC" 

417 assert type_alias_c.description == "Hello." 

418 type_alias_d = sections[0].value[1] 

419 assert type_alias_d.name == "TD" 

420 assert type_alias_d.description == "Hi." 

421 assert not warnings 

422 

423 

424def test_parse_modules_section(parse_google: ParserType) -> None: 

425 """Parse Modules sections. 

426 

427 Parameters: 

428 parse_google: Fixture parser. 

429 """ 

430 docstring = """ 

431 Modules: 

432 m: Hello. 

433 n: Hi. 

434 """ 

435 

436 sections, warnings = parse_google(docstring) 

437 assert len(sections) == 1 

438 assert sections[0].kind is DocstringSectionKind.modules 

439 module_m = sections[0].value[0] 

440 assert module_m.name == "m" 

441 assert module_m.description == "Hello." 

442 module_n = sections[0].value[1] 

443 assert module_n.name == "n" 

444 assert module_n.description == "Hi." 

445 assert not warnings 

446 

447 

448def test_parse_examples_sections(parse_google: ParserType) -> None: 

449 """Parse a function docstring with examples. 

450 

451 Parameters: 

452 parse_google: Fixture parser. 

453 """ 

454 docstring = """ 

455 Examples: 

456 Some examples that will create a unified code block: 

457 

458 >>> 2 + 2 == 5 

459 False 

460 >>> print("examples") 

461 "examples" 

462 

463 This is just a random comment in the examples section. 

464 

465 These examples will generate two different code blocks. Note the blank line. 

466 

467 >>> print("I'm in the first code block!") 

468 "I'm in the first code block!" 

469 

470 >>> print("I'm in other code block!") 

471 "I'm in other code block!" 

472 

473 We also can write multiline examples: 

474 

475 >>> x = 3 + 2 # doctest: +SKIP 

476 >>> y = x + 10 

477 >>> y 

478 15 

479 

480 This is just a typical Python code block: 

481 

482 ```python 

483 print("examples") 

484 return 2 + 2 

485 ``` 

486 

487 Even if it contains doctests, the following block is still considered a normal code-block. 

488 

489 ```pycon 

490 >>> print("examples") 

491 "examples" 

492 >>> 2 + 2 

493 4 

494 ``` 

495 

496 The blank line before an example is optional. 

497 >>> x = 3 

498 >>> y = "apple" 

499 >>> z = False 

500 >>> l = [x, y, z] 

501 >>> my_print_list_function(l) 

502 3 

503 "apple" 

504 False 

505 """ 

506 

507 sections, warnings = parse_google( 

508 docstring, 

509 parent=Function( 

510 "func", 

511 parameters=Parameters( 

512 Parameter("x", annotation="int"), 

513 Parameter("y", annotation="int"), 

514 ), 

515 returns="int", 

516 ), 

517 trim_doctest_flags=False, 

518 ) 

519 assert len(sections) == 1 

520 examples = sections[0] 

521 assert len(examples.value) == 9 

522 assert examples.value[6][1].startswith(">>> x = 3 + 2 # doctest: +SKIP") 

523 assert not warnings 

524 

525 

526def test_parse_yields_section(parse_google: ParserType) -> None: 

527 """Parse Yields section. 

528 

529 Parameters: 

530 parse_google: Fixture parser. 

531 """ 

532 docstring = """ 

533 Yields: 

534 x: Floats. 

535 (int): Integers. 

536 y (int): Same. 

537 """ 

538 

539 sections, warnings = parse_google(docstring) 

540 assert len(sections) == 1 

541 annotated = sections[0].value[0] 

542 assert annotated.name == "x" 

543 assert annotated.annotation is None 

544 assert annotated.description == "Floats." 

545 annotated = sections[0].value[1] 

546 assert annotated.name == "" 

547 assert annotated.annotation == "int" 

548 assert annotated.description == "Integers." 

549 annotated = sections[0].value[2] 

550 assert annotated.name == "y" 

551 assert annotated.annotation == "int" 

552 assert annotated.description == "Same." 

553 assert len(warnings) == 1 

554 assert "'x'" in warnings[0] 

555 

556 

557def test_invalid_sections(parse_google: ParserType) -> None: 

558 """Warn on invalid sections. 

559 

560 Parameters: 

561 parse_google: Fixture parser. 

562 """ 

563 docstring = """ 

564 Parameters: 

565 Exceptions: 

566 Exceptions: 

567 

568 Returns: 

569 Note: 

570 

571 Important: 

572 """ 

573 

574 sections, warnings = parse_google(docstring) 

575 assert len(sections) == 1 

576 assert not warnings 

577 

578 

579# ============================================================================================= 

580# Parameters sections 

581def test_parse_args_and_kwargs(parse_google: ParserType) -> None: 

582 """Parse args and kwargs. 

583 

584 Parameters: 

585 parse_google: Fixture parser. 

586 """ 

587 docstring = """ 

588 Parameters: 

589 a (str): a parameter. 

590 *args (str): args parameters. 

591 **kwargs (str): kwargs parameters. 

592 """ 

593 

594 sections, warnings = parse_google(docstring) 

595 assert len(sections) == 1 

596 expected_parameters = {"a": "a parameter.", "*args": "args parameters.", "**kwargs": "kwargs parameters."} 

597 for parameter in sections[0].value: 

598 assert parameter.name in expected_parameters 

599 assert expected_parameters[parameter.name] == parameter.description 

600 assert not warnings 

601 

602 

603def test_parse_args_kwargs_keyword_only(parse_google: ParserType) -> None: 

604 """Parse args and kwargs. 

605 

606 Parameters: 

607 parse_google: Fixture parser. 

608 """ 

609 docstring = """ 

610 Parameters: 

611 a (str): a parameter. 

612 *args (str): args parameters. 

613 

614 Keyword Args: 

615 **kwargs (str): kwargs parameters. 

616 """ 

617 

618 sections, warnings = parse_google(docstring) 

619 assert len(sections) == 2 

620 expected_parameters = {"a": "a parameter.", "*args": "args parameters."} 

621 for parameter in sections[0].value: 

622 assert parameter.name in expected_parameters 

623 assert expected_parameters[parameter.name] == parameter.description 

624 

625 expected_parameters = {"**kwargs": "kwargs parameters."} 

626 for kwarg in sections[1].value: 

627 assert kwarg.name in expected_parameters 

628 assert expected_parameters[kwarg.name] == kwarg.description 

629 

630 assert not warnings 

631 

632 

633def test_parse_types_in_docstring(parse_google: ParserType) -> None: 

634 """Parse types in docstring. 

635 

636 Parameters: 

637 parse_google: Fixture parser. 

638 """ 

639 docstring = """ 

640 Parameters: 

641 x (int): X value. 

642 

643 Keyword Args: 

644 y (int): Y value. 

645 

646 Returns: 

647 s (int): Sum X + Y + Z. 

648 """ 

649 

650 sections, warnings = parse_google( 

651 docstring, 

652 parent=Function( 

653 "func", 

654 parameters=Parameters( 

655 Parameter("x"), 

656 Parameter("y"), 

657 ), 

658 ), 

659 ) 

660 assert len(sections) == 3 

661 assert not warnings 

662 

663 assert sections[0].kind is DocstringSectionKind.parameters 

664 assert sections[1].kind is DocstringSectionKind.other_parameters 

665 assert sections[2].kind is DocstringSectionKind.returns 

666 

667 (argx,) = sections[0].value 

668 (argy,) = sections[1].value 

669 (returns,) = sections[2].value 

670 

671 assert argx.name == "x" 

672 assert argx.annotation.name == "int" 

673 assert argx.annotation.canonical_path == "int" 

674 assert argx.description == "X value." 

675 assert argx.value is None 

676 

677 assert argy.name == "y" 

678 assert argy.annotation.name == "int" 

679 assert argy.annotation.canonical_path == "int" 

680 assert argy.description == "Y value." 

681 assert argy.value is None 

682 

683 assert returns.annotation.name == "int" 

684 assert returns.annotation.canonical_path == "int" 

685 assert returns.description == "Sum X + Y + Z." 

686 

687 

688def test_parse_optional_type_in_docstring(parse_google: ParserType) -> None: 

689 """Parse optional types in docstring. 

690 

691 Parameters: 

692 parse_google: Fixture parser. 

693 """ 

694 docstring = """ 

695 Parameters: 

696 x (int): X value. 

697 y (int, optional): Y value. 

698 

699 Keyword Args: 

700 z (int, optional): Z value. 

701 """ 

702 

703 sections, warnings = parse_google( 

704 docstring, 

705 parent=Function( 

706 "func", 

707 parameters=Parameters( 

708 Parameter("x", default="1"), 

709 Parameter("y", default="None"), 

710 Parameter("z", default="None"), 

711 ), 

712 ), 

713 ) 

714 assert len(sections) == 2 

715 assert not warnings 

716 

717 assert sections[0].kind is DocstringSectionKind.parameters 

718 assert sections[1].kind is DocstringSectionKind.other_parameters 

719 

720 argx, argy = sections[0].value 

721 (argz,) = sections[1].value 

722 

723 assert argx.name == "x" 

724 assert argx.annotation.name == "int" 

725 assert argx.annotation.canonical_path == "int" 

726 assert argx.description == "X value." 

727 assert argx.value == "1" 

728 

729 assert argy.name == "y" 

730 assert argy.annotation.name == "int" 

731 assert argy.annotation.canonical_path == "int" 

732 assert argy.description == "Y value." 

733 assert argy.value == "None" 

734 

735 assert argz.name == "z" 

736 assert argz.annotation.name == "int" 

737 assert argz.annotation.canonical_path == "int" 

738 assert argz.description == "Z value." 

739 assert argz.value == "None" 

740 

741 

742def test_prefer_docstring_types_over_annotations(parse_google: ParserType) -> None: 

743 """Prefer the docstring type over the annotation. 

744 

745 Parameters: 

746 parse_google: Fixture parser. 

747 """ 

748 docstring = """ 

749 Parameters: 

750 x (str): X value. 

751 

752 Keyword Args: 

753 y (str): Y value. 

754 

755 Returns: 

756 (str): Sum X + Y + Z. 

757 """ 

758 

759 sections, warnings = parse_google( 

760 docstring, 

761 parent=Function( 

762 "func", 

763 parameters=Parameters( 

764 Parameter("x", annotation="int"), 

765 Parameter("y", annotation="int"), 

766 ), 

767 returns="int", 

768 ), 

769 ) 

770 assert len(sections) == 3 

771 assert not warnings 

772 

773 assert sections[0].kind is DocstringSectionKind.parameters 

774 assert sections[1].kind is DocstringSectionKind.other_parameters 

775 assert sections[2].kind is DocstringSectionKind.returns 

776 

777 (argx,) = sections[0].value 

778 (argy,) = sections[1].value 

779 (returns,) = sections[2].value 

780 

781 assert argx.name == "x" 

782 assert argx.annotation.name == "str" 

783 assert argx.annotation.canonical_path == "str" 

784 assert argx.description == "X value." 

785 

786 assert argy.name == "y" 

787 assert argy.annotation.name == "str" 

788 assert argy.annotation.canonical_path == "str" 

789 assert argy.description == "Y value." 

790 

791 assert returns.annotation.name == "str" 

792 assert returns.annotation.canonical_path == "str" 

793 assert returns.description == "Sum X + Y + Z." 

794 

795 

796def test_parameter_line_without_colon(parse_google: ParserType) -> None: 

797 """Warn when missing colon. 

798 

799 Parameters: 

800 parse_google: Fixture parser. 

801 """ 

802 docstring = """ 

803 Parameters: 

804 x is an integer. 

805 """ 

806 

807 sections, warnings = parse_google(docstring) 

808 assert len(sections) == 0 # Empty sections are discarded. 

809 assert len(warnings) == 1 

810 assert "pair" in warnings[0] 

811 

812 

813def test_parameter_line_without_colon_keyword_only(parse_google: ParserType) -> None: 

814 """Warn when missing colon. 

815 

816 Parameters: 

817 parse_google: Fixture parser. 

818 """ 

819 docstring = """ 

820 Keyword Args: 

821 x is an integer. 

822 """ 

823 

824 sections, warnings = parse_google(docstring) 

825 assert len(sections) == 0 # Empty sections are discarded. 

826 assert len(warnings) == 1 

827 assert "pair" in warnings[0] 

828 

829 

830def test_warn_about_unknown_parameters(parse_google: ParserType) -> None: 

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

832 

833 Parameters: 

834 parse_google: Fixture parser. 

835 """ 

836 docstring = """ 

837 Parameters: 

838 x (int): Integer. 

839 y (int): Integer. 

840 """ 

841 

842 _, warnings = parse_google( 

843 docstring, 

844 parent=Function( 

845 "func", 

846 parameters=Parameters( 

847 Parameter("a"), 

848 Parameter("y"), 

849 ), 

850 ), 

851 ) 

852 assert len(warnings) == 1 

853 assert "'x' does not appear in the function signature" in warnings[0] 

854 

855 

856def test_never_warn_about_unknown_other_parameters(parse_google: ParserType) -> None: 

857 """Never warn about unknown parameters in "Other parameters" sections. 

858 

859 Parameters: 

860 parse_google: Fixture parser. 

861 """ 

862 docstring = """ 

863 Other Parameters: 

864 x (int): Integer. 

865 z (int): Integer. 

866 """ 

867 

868 _, warnings = parse_google( 

869 docstring, 

870 parent=Function( 

871 "func", 

872 parameters=Parameters( 

873 Parameter("a"), 

874 Parameter("y"), 

875 ), 

876 ), 

877 ) 

878 assert not warnings 

879 

880 

881def test_unknown_params_scan_doesnt_crash_without_parameters(parse_google: ParserType) -> None: 

882 """Assert we don't crash when parsing parameters sections and parent object does not have parameters. 

883 

884 Parameters: 

885 parse_google: Fixture parser. 

886 """ 

887 docstring = """ 

888 Parameters: 

889 this (str): This. 

890 that (str): That. 

891 """ 

892 

893 _, warnings = parse_google(docstring, parent=Module("mod")) 

894 assert not warnings 

895 

896 

897def test_class_uses_init_parameters(parse_google: ParserType) -> None: 

898 """Assert we use the `__init__` parameters when parsing classes' parameters sections. 

899 

900 Parameters: 

901 parse_google: Fixture parser. 

902 """ 

903 docstring = """ 

904 Parameters: 

905 x: X value. 

906 """ 

907 parent = Class("c") 

908 parent["__init__"] = Function("__init__", parameters=Parameters(Parameter("x", annotation="int"))) 

909 sections, warnings = parse_google(docstring, parent=parent) 

910 assert not warnings 

911 argx = sections[0].value[0] 

912 assert argx.name == "x" 

913 assert argx.annotation == "int" 

914 assert argx.description == "X value." 

915 

916 

917# TODO: possible feature 

918# def test_missing_parameter(parse_google: ParserType) -> None: 

919# """Warn on missing parameter in docstring. 

920# 

921# Parameters: 

922# parse_google: Fixture parser. 

923# """ 

924# docstring = """ 

925# Parameters: 

926# x: Integer. 

927# """ 

928# assert not warnings 

929 

930 

931# ============================================================================================= 

932# Type parameters sections 

933def test_parse_type_var_tuples_and_param_specs(parse_google: ParserType) -> None: 

934 """Parse type variable tuples and parameter specifications. 

935 

936 Parameters: 

937 parse_google: Fixture parser. 

938 """ 

939 docstring = """ 

940 Type Parameters: 

941 T: A type parameter. 

942 C (str, (int, float)): A constrained type parameter. 

943 D complex: A bounded type parameter. 

944 """ 

945 

946 sections, warnings = parse_google(docstring) 

947 assert len(sections) == 1 

948 expected_type_parameters = { 

949 "T": ("A type parameter.", None), 

950 "C": ("A constrained type parameter.", "str, (int, float)"), 

951 "D": ("A bounded type parameter.", "complex"), 

952 } 

953 for type_parameter in sections[0].value: 

954 assert type_parameter.name in expected_type_parameters 

955 assert expected_type_parameters[type_parameter.name][0] == type_parameter.description 

956 assert expected_type_parameters[type_parameter.name][1] == type_parameter.annotation 

957 assert not warnings 

958 

959 

960def test_prefer_docstring_bounds_over_annotations(parse_google: ParserType) -> None: 

961 """Prefer the docstring bound over the annotation. 

962 

963 Parameters: 

964 parse_google: Fixture parser. 

965 """ 

966 docstring = """ 

967 Type Parameters: 

968 X (str): X type. 

969 Y str, int: Y type. 

970 """ 

971 

972 sections, warnings = parse_google( 

973 docstring, 

974 parent=Function( 

975 "func", 

976 type_parameters=TypeParameters( 

977 TypeParameter("X", kind=TypeParameterKind.type_var, constraints=["complex"]), 

978 TypeParameter("Y", kind=TypeParameterKind.type_var, bound="int"), 

979 ), 

980 ), 

981 ) 

982 assert len(sections) == 1 

983 assert not warnings 

984 

985 assert sections[0].kind is DocstringSectionKind.type_parameters 

986 

987 (x, y) = sections[0].value 

988 

989 assert x.name == "X" 

990 assert str(x.bound) == "str" 

991 assert x.constraints is None 

992 

993 assert y.name == "Y" 

994 assert y.bound is None 

995 assert [str(constraint) for constraint in y.constraints] == ["str", "int"] 

996 

997 

998def test_type_parameter_line_without_colon(parse_google: ParserType) -> None: 

999 """Warn when missing colon. 

1000 

1001 Parameters: 

1002 parse_google: Fixture parser. 

1003 """ 

1004 docstring = """ 

1005 Type Parameters: 

1006 X is an integer type. 

1007 """ 

1008 

1009 sections, warnings = parse_google(docstring) 

1010 assert len(sections) == 0 # empty sections are discarded 

1011 assert len(warnings) == 1 

1012 assert "pair" in warnings[0] 

1013 

1014 

1015def test_warn_about_unknown_type_parameters(parse_google: ParserType) -> None: 

1016 """Warn about unknown type parameters in "Type Parameters" sections. 

1017 

1018 Parameters: 

1019 parse_google: Fixture parser. 

1020 """ 

1021 docstring = """ 

1022 Type Parameters: 

1023 X (int): Integer. 

1024 Y (int): Integer. 

1025 """ 

1026 

1027 _, warnings = parse_google( 

1028 docstring, 

1029 parent=Function( 

1030 "func", 

1031 type_parameters=TypeParameters( 

1032 TypeParameter("A", kind=TypeParameterKind.type_var), 

1033 TypeParameter("Y", kind=TypeParameterKind.type_var), 

1034 ), 

1035 ), 

1036 ) 

1037 assert len(warnings) == 1 

1038 assert "'X' does not appear in the function signature" in warnings[0] 

1039 

1040 

1041def test_unknown_type_params_scan_doesnt_crash_without_type_parameters(parse_google: ParserType) -> None: 

1042 """Assert we don't crash when parsing type parameters sections and parent object does not have type parameters. 

1043 

1044 Parameters: 

1045 parse_google: Fixture parser. 

1046 """ 

1047 docstring = """ 

1048 TypeParameters: 

1049 This (str): This. 

1050 That: That. 

1051 """ 

1052 

1053 _, warnings = parse_google(docstring, parent=Module("mod")) 

1054 assert not warnings 

1055 

1056 

1057# ============================================================================================= 

1058# Attributes sections 

1059def test_retrieve_attributes_annotation_from_parent(parse_google: ParserType) -> None: 

1060 """Retrieve the annotations of attributes from the parent object. 

1061 

1062 Parameters: 

1063 parse_google: Fixture parser. 

1064 """ 

1065 docstring = """ 

1066 Summary. 

1067 

1068 Attributes: 

1069 a: Whatever. 

1070 b: Whatever. 

1071 """ 

1072 parent = Class("cls") 

1073 parent["a"] = Attribute("a", annotation=ExprName("int")) 

1074 parent["b"] = Attribute("b", annotation=ExprName("str")) 

1075 sections, _ = parse_google(docstring, parent=parent) 

1076 attributes = sections[1].value 

1077 assert attributes[0].name == "a" 

1078 assert attributes[0].annotation.name == "int" 

1079 assert attributes[1].name == "b" 

1080 assert attributes[1].annotation.name == "str" 

1081 

1082 

1083# ============================================================================================= 

1084# Yields sections 

1085def test_parse_yields_section_with_return_annotation(parse_google: ParserType) -> None: 

1086 """Parse Yields section with a return annotation in the parent function. 

1087 

1088 Parameters: 

1089 parse_google: Fixture parser. 

1090 """ 

1091 docstring = """ 

1092 Yields: 

1093 Integers. 

1094 """ 

1095 

1096 function = Function("func", returns="Iterator[int]") 

1097 sections, warnings = parse_google(docstring, function) 

1098 assert len(sections) == 1 

1099 annotated = sections[0].value[0] 

1100 assert annotated.annotation == "Iterator[int]" 

1101 assert annotated.description == "Integers." 

1102 assert not warnings 

1103 

1104 

1105@pytest.mark.parametrize( 

1106 "return_annotation", 

1107 [ 

1108 "Iterator[tuple[int, float]]", 

1109 "Generator[tuple[int, float], ..., ...]", 

1110 ], 

1111) 

1112def test_parse_yields_tuple_in_iterator_or_generator(parse_google: ParserType, return_annotation: str) -> None: 

1113 """Parse Yields annotations in Iterator or Generator types. 

1114 

1115 Parameters: 

1116 parse_google: Fixture parser. 

1117 return_annotation: Parametrized return annotation as a string. 

1118 """ 

1119 docstring = """ 

1120 Summary. 

1121 

1122 Yields: 

1123 a: Whatever. 

1124 b: Whatever. 

1125 """ 

1126 sections, _ = parse_google( 

1127 docstring, 

1128 parent=Function( 

1129 "func", 

1130 returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))), 

1131 ), 

1132 ) 

1133 yields = sections[1].value 

1134 assert yields[0].name == "a" 

1135 assert yields[0].annotation.name == "int" 

1136 assert yields[1].name == "b" 

1137 assert yields[1].annotation.name == "float" 

1138 

1139 

1140@pytest.mark.parametrize( 

1141 "return_annotation", 

1142 [ 

1143 "Iterator[int]", 

1144 "Generator[int, None, None]", 

1145 ], 

1146) 

1147def test_extract_yielded_type_with_single_return_item(parse_google: ParserType, return_annotation: str) -> None: 

1148 """Extract main type annotation from Iterator or Generator. 

1149 

1150 Parameters: 

1151 parse_google: Fixture parser. 

1152 return_annotation: Parametrized return annotation as a string. 

1153 """ 

1154 docstring = """ 

1155 Summary. 

1156 

1157 Yields: 

1158 A number. 

1159 """ 

1160 sections, _ = parse_google( 

1161 docstring, 

1162 parent=Function( 

1163 "func", 

1164 returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))), 

1165 ), 

1166 ) 

1167 yields = sections[1].value 

1168 assert yields[0].annotation.name == "int" 

1169 

1170 

1171def test_yield_section_in_property(parse_google: ParserType) -> None: 

1172 """No warnings when parsing Yields section in a property. 

1173 

1174 Parameters: 

1175 parse_google: Fixture parser. 

1176 """ 

1177 docstring = """ 

1178 Summary. 

1179 

1180 Yields: 

1181 A number. 

1182 """ 

1183 sections, warnings = parse_google( 

1184 docstring, 

1185 parent=Attribute( 

1186 "prop", 

1187 annotation=parse_docstring_annotation("Iterator[int]", Docstring("d", parent=Attribute("a"))), 

1188 ), 

1189 ) 

1190 assert not warnings 

1191 yields = sections[1].value 

1192 assert yields[0].annotation.name == "int" 

1193 

1194 

1195# ============================================================================================= 

1196# Receives sections 

1197def test_parse_receives_tuple_in_generator(parse_google: ParserType) -> None: 

1198 """Parse Receives annotations in Generator type. 

1199 

1200 Parameters: 

1201 parse_google: Fixture parser. 

1202 """ 

1203 docstring = """ 

1204 Summary. 

1205 

1206 Receives: 

1207 a: Whatever. 

1208 b: Whatever. 

1209 """ 

1210 sections, _ = parse_google( 

1211 docstring, 

1212 parent=Function( 

1213 "func", 

1214 returns=parse_docstring_annotation( 

1215 "Generator[..., tuple[int, float], ...]", 

1216 Docstring("d", parent=Function("f")), 

1217 ), 

1218 ), 

1219 ) 

1220 receives = sections[1].value 

1221 assert receives[0].name == "a" 

1222 assert receives[0].annotation.name == "int" 

1223 assert receives[1].name == "b" 

1224 assert receives[1].annotation.name == "float" 

1225 

1226 

1227@pytest.mark.parametrize( 

1228 "return_annotation", 

1229 [ 

1230 "Generator[int, float, None]", 

1231 ], 

1232) 

1233def test_extract_received_type_with_single_return_item(parse_google: ParserType, return_annotation: str) -> None: 

1234 """Extract main type annotation from Iterator or Generator. 

1235 

1236 Parameters: 

1237 parse_google: Fixture parser. 

1238 return_annotation: Parametrized return annotation as a string. 

1239 """ 

1240 docstring = """ 

1241 Summary. 

1242 

1243 Receives: 

1244 A floating point number. 

1245 """ 

1246 sections, _ = parse_google( 

1247 docstring, 

1248 parent=Function( 

1249 "func", 

1250 returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f"))), 

1251 ), 

1252 ) 

1253 receives = sections[1].value 

1254 assert receives[0].annotation.name == "float" 

1255 

1256 

1257# ============================================================================================= 

1258# Returns sections 

1259def test_parse_returns_tuple_in_generator(parse_google: ParserType) -> None: 

1260 """Parse Returns annotations in Generator type. 

1261 

1262 Parameters: 

1263 parse_google: Fixture parser. 

1264 """ 

1265 docstring = """ 

1266 Summary. 

1267 

1268 Returns: 

1269 a: Whatever. 

1270 b: Whatever. 

1271 """ 

1272 sections, _ = parse_google( 

1273 docstring, 

1274 parent=Function( 

1275 "func", 

1276 returns=parse_docstring_annotation( 

1277 "Generator[..., ..., tuple[int, float]]", 

1278 Docstring("d", parent=Function("f")), 

1279 ), 

1280 ), 

1281 ) 

1282 returns = sections[1].value 

1283 assert returns[0].name == "a" 

1284 assert returns[0].annotation.name == "int" 

1285 assert returns[1].name == "b" 

1286 assert returns[1].annotation.name == "float" 

1287 

1288 

1289# ============================================================================================= 

1290# Parser special features 

1291def test_parse_admonitions(parse_google: ParserType) -> None: 

1292 """Parse admonitions. 

1293 

1294 Parameters: 

1295 parse_google: Fixture parser. 

1296 """ 

1297 docstring = """ 

1298 Important note: 

1299 Hello. 

1300 

1301 Note: With title. 

1302 Hello again. 

1303 

1304 Something: 

1305 Something. 

1306 """ 

1307 

1308 sections, warnings = parse_google(docstring) 

1309 assert len(sections) == 3 

1310 assert not warnings 

1311 assert sections[0].title == "Important note" 

1312 assert sections[0].value.kind == "important-note" 

1313 assert sections[0].value.contents == "Hello." 

1314 assert sections[1].title == "With title." 

1315 assert sections[1].value.kind == "note" 

1316 assert sections[1].value.contents == "Hello again." 

1317 assert sections[2].title == "Something" 

1318 assert sections[2].value.kind == "something" 

1319 assert sections[2].value.contents == "Something." 

1320 

1321 

1322@pytest.mark.parametrize( 

1323 "docstring", 

1324 [ 

1325 """ 

1326 ****************************** 

1327 This looks like an admonition: 

1328 ****************************** 

1329 """, 

1330 """ 

1331 Warning: this line also looks 

1332 like an admonition. 

1333 """, 

1334 """ 

1335 Matching but not an admonition: 

1336 

1337 

1338 

1339 - Multiple empty lines above. 

1340 """, 

1341 """Last line:""", 

1342 ], 

1343) 

1344def test_handle_false_admonitions_correctly(parse_google: ParserType, docstring: str) -> None: 

1345 """Correctly handle lines that look like admonitions. 

1346 

1347 Parameters: 

1348 parse_google: Fixture parser. 

1349 docstring: The docstring to parse (parametrized). 

1350 """ 

1351 sections, warnings = parse_google(docstring) 

1352 assert len(sections) == 1 

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

1354 assert len(sections[0].value.splitlines()) == len(inspect.cleandoc(docstring).splitlines()) 

1355 assert not warnings 

1356 

1357 

1358def test_dont_insert_admonition_before_current_section(parse_google: ParserType) -> None: 

1359 """Check that admonitions are inserted at the right place. 

1360 

1361 Parameters: 

1362 parse_google: Fixture parser. 

1363 """ 

1364 docstring = """ 

1365 Summary. 

1366 

1367 Short description. 

1368 

1369 Info: 

1370 Something useful. 

1371 """ 

1372 sections, _ = parse_google(docstring) 

1373 assert len(sections) == 2 

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

1375 assert sections[1].kind is DocstringSectionKind.admonition 

1376 

1377 

1378@pytest.mark.parametrize( 

1379 "docstring", 

1380 [ 

1381 "", 

1382 "\n", 

1383 "\n\n", 

1384 "Summary.", 

1385 "Summary.\n\n\n", 

1386 "Summary.\n\nParagraph.", 

1387 "Summary\non two lines.", 

1388 "Summary\non two lines.\n\nParagraph.", 

1389 ], 

1390) 

1391def test_ignore_init_summary(parse_google: ParserType, docstring: str) -> None: 

1392 """Correctly ignore summary in `__init__` methods' docstrings. 

1393 

1394 Parameters: 

1395 parse_google: Fixture parser. 

1396 docstring: The docstring to parse_google (parametrized). 

1397 """ 

1398 sections, _ = parse_google(docstring, parent=Function("__init__", parent=Class("C")), ignore_init_summary=True) 

1399 for section in sections: 

1400 assert "Summary" not in section.value 

1401 

1402 if docstring.strip(): 

1403 sections, _ = parse_google(docstring, parent=Function("__init__", parent=Module("M")), ignore_init_summary=True) 

1404 assert "Summary" in sections[0].value 

1405 sections, _ = parse_google(docstring, parent=Function("f", parent=Class("C")), ignore_init_summary=True) 

1406 assert "Summary" in sections[0].value 

1407 sections, _ = parse_google(docstring, ignore_init_summary=True) 

1408 assert "Summary" in sections[0].value 

1409 

1410 

1411@pytest.mark.parametrize( 

1412 "docstring", 

1413 [ 

1414 """ 

1415 Examples: 

1416 Base case 1. We want to skip the following test. 

1417 >>> 1 + 1 == 3 # doctest: +SKIP 

1418 True 

1419 """, 

1420 r""" 

1421 Examples: 

1422 Base case 2. We have a blankline test. 

1423 >>> print("a\n\nb") 

1424 a 

1425 <BLANKLINE> 

1426 b 

1427 """, 

1428 ], 

1429) 

1430def test_trim_doctest_flags_basic_example(parse_google: ParserType, docstring: str) -> None: 

1431 """Correctly parse simple example docstrings when `trim_doctest_flags` option is turned on. 

1432 

1433 Parameters: 

1434 parse_google: Fixture parser. 

1435 docstring: The docstring to parse (parametrized). 

1436 """ 

1437 sections, warnings = parse_google(docstring, trim_doctest_flags=True) 

1438 assert len(sections) == 1 

1439 assert len(sections[0].value) == 2 

1440 assert not warnings 

1441 

1442 # Verify that doctest flags have indeed been trimmed. 

1443 example_str = sections[0].value[1][1] 

1444 assert "# doctest: +SKIP" not in example_str 

1445 assert "<BLANKLINE>" not in example_str 

1446 

1447 

1448def test_trim_doctest_flags_multi_example(parse_google: ParserType) -> None: 

1449 """Correctly parse multiline example docstrings when `trim_doctest_flags` option is turned on. 

1450 

1451 Parameters: 

1452 parse_google: Fixture parser. 

1453 """ 

1454 docstring = r""" 

1455 Examples: 

1456 Test multiline example blocks. 

1457 We want to skip the following test. 

1458 >>> 1 + 1 == 3 # doctest: +SKIP 

1459 True 

1460 

1461 And then a few more examples here: 

1462 >>> print("a\n\nb") 

1463 a 

1464 <BLANKLINE> 

1465 b 

1466 >>> 1 + 1 == 2 # doctest: +SKIP 

1467 >>> print(list(range(1, 100))) # doctest: +ELLIPSIS 

1468 [1, 2, ..., 98, 99] 

1469 """ 

1470 sections, warnings = parse_google(docstring, trim_doctest_flags=True) 

1471 assert len(sections) == 1 

1472 assert len(sections[0].value) == 4 

1473 assert not warnings 

1474 

1475 # Verify that doctest flags have indeed been trimmed. 

1476 example_str = sections[0].value[1][1] 

1477 assert "# doctest: +SKIP" not in example_str 

1478 example_str = sections[0].value[3][1] 

1479 assert "<BLANKLINE>" not in example_str 

1480 assert "\n>>> print(list(range(1, 100)))\n" in example_str 

1481 

1482 

1483def test_single_line_with_trailing_whitespace(parse_google: ParserType) -> None: 

1484 """Don't crash on single line docstrings with trailing whitespace. 

1485 

1486 Parameters: 

1487 parse_google: Fixture parser. 

1488 """ 

1489 docstring = "a: b\n " 

1490 sections, warnings = parse_google(docstring, trim_doctest_flags=True) 

1491 assert len(sections) == 1 

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

1493 assert not warnings 

1494 

1495 

1496@pytest.mark.parametrize( 

1497 ("returns_multiple_items", "return_annotation", "expected"), 

1498 [ 

1499 ( 

1500 False, 

1501 None, 

1502 [DocstringReturn("", description="XXXXXXX\n YYYYYYY\nZZZZZZZ", annotation=None)], 

1503 ), 

1504 ( 

1505 False, 

1506 "tuple[int, int]", 

1507 [DocstringReturn("", description="XXXXXXX\n YYYYYYY\nZZZZZZZ", annotation="tuple[int, int]")], 

1508 ), 

1509 ( 

1510 True, 

1511 None, 

1512 [ 

1513 DocstringReturn("", description="XXXXXXX\nYYYYYYY", annotation=None), 

1514 DocstringReturn("", description="ZZZZZZZ", annotation=None), 

1515 ], 

1516 ), 

1517 ( 

1518 True, 

1519 "tuple[int,int]", 

1520 [ 

1521 DocstringReturn("", description="XXXXXXX\nYYYYYYY", annotation="int"), 

1522 DocstringReturn("", description="ZZZZZZZ", annotation="int"), 

1523 ], 

1524 ), 

1525 ], 

1526) 

1527def test_parse_returns_multiple_items( 

1528 parse_google: ParserType, 

1529 returns_multiple_items: bool, 

1530 return_annotation: str, 

1531 expected: list[DocstringReturn], 

1532) -> None: 

1533 """Parse Returns section with and without multiple items. 

1534 

1535 Parameters: 

1536 parse_google: Fixture parser. 

1537 returns_multiple_items: Whether the `Returns` section has multiple items. 

1538 return_annotation: The return annotation of the function to parse. 

1539 expected: The expected value of the parsed Returns section. 

1540 """ 

1541 parent = ( 

1542 Function("func", returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f")))) 

1543 if return_annotation is not None 

1544 else None 

1545 ) 

1546 docstring = """ 

1547 Returns: 

1548 XXXXXXX 

1549 YYYYYYY 

1550 ZZZZZZZ 

1551 """ 

1552 sections, _ = parse_google( 

1553 docstring, 

1554 returns_multiple_items=returns_multiple_items, 

1555 parent=parent, 

1556 ) 

1557 

1558 assert len(sections) == 1 

1559 assert len(sections[0].value) == len(expected) 

1560 

1561 for annotated, expected_ in zip(sections[0].value, expected): 

1562 assert annotated.name == expected_.name 

1563 assert str(annotated.annotation) == str(expected_.annotation) 

1564 assert annotated.description == expected_.description 

1565 

1566 

1567@pytest.mark.parametrize( 

1568 ("returns_multiple_items", "return_annotation", "expected"), 

1569 [ 

1570 ( 

1571 False, 

1572 None, 

1573 [DocstringYield("", description="XXXXXXX\n YYYYYYY\nZZZZZZZ", annotation=None)], 

1574 ), 

1575 ( 

1576 False, 

1577 "Iterator[tuple[int, int]]", 

1578 [DocstringYield("", description="XXXXXXX\n YYYYYYY\nZZZZZZZ", annotation="tuple[int, int]")], 

1579 ), 

1580 ( 

1581 True, 

1582 None, 

1583 [ 

1584 DocstringYield("", description="XXXXXXX\nYYYYYYY", annotation=None), 

1585 DocstringYield("", description="ZZZZZZZ", annotation=None), 

1586 ], 

1587 ), 

1588 ( 

1589 True, 

1590 "Iterator[tuple[int,int]]", 

1591 [ 

1592 DocstringYield("", description="XXXXXXX\nYYYYYYY", annotation="int"), 

1593 DocstringYield("", description="ZZZZZZZ", annotation="int"), 

1594 ], 

1595 ), 

1596 ], 

1597) 

1598def test_parse_yields_multiple_items( 

1599 parse_google: ParserType, 

1600 returns_multiple_items: bool, 

1601 return_annotation: str, 

1602 expected: list[DocstringYield], 

1603) -> None: 

1604 """Parse Returns section with and without multiple items. 

1605 

1606 Parameters: 

1607 parse_google: Fixture parser. 

1608 returns_multiple_items: Whether the `Returns` and `Yields` sections have multiple items. 

1609 return_annotation: The return annotation of the function to parse. Usually an `Iterator`. 

1610 expected: The expected value of the parsed Yields section. 

1611 """ 

1612 parent = ( 

1613 Function("func", returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f")))) 

1614 if return_annotation is not None 

1615 else None 

1616 ) 

1617 docstring = """ 

1618 Yields: 

1619 XXXXXXX 

1620 YYYYYYY 

1621 ZZZZZZZ 

1622 """ 

1623 sections, _ = parse_google( 

1624 docstring, 

1625 returns_multiple_items=returns_multiple_items, 

1626 parent=parent, 

1627 ) 

1628 

1629 assert len(sections) == 1 

1630 assert len(sections[0].value) == len(expected) 

1631 

1632 for annotated, expected_ in zip(sections[0].value, expected): 

1633 assert annotated.name == expected_.name 

1634 assert str(annotated.annotation) == str(expected_.annotation) 

1635 assert annotated.description == expected_.description 

1636 

1637 

1638@pytest.mark.parametrize( 

1639 ("receives_multiple_items", "return_annotation", "expected"), 

1640 [ 

1641 ( 

1642 False, 

1643 None, 

1644 [DocstringReceive("", description="XXXXXXX\n YYYYYYY\nZZZZZZZ", annotation=None)], 

1645 ), 

1646 ( 

1647 False, 

1648 "Generator[..., tuple[int, int], ...]", 

1649 [DocstringReceive("", description="XXXXXXX\n YYYYYYY\nZZZZZZZ", annotation="tuple[int, int]")], 

1650 ), 

1651 ( 

1652 True, 

1653 None, 

1654 [ 

1655 DocstringReceive("", description="XXXXXXX\nYYYYYYY", annotation=None), 

1656 DocstringReceive("", description="ZZZZZZZ", annotation=None), 

1657 ], 

1658 ), 

1659 ( 

1660 True, 

1661 "Generator[..., tuple[int, int], ...]", 

1662 [ 

1663 DocstringReceive("", description="XXXXXXX\nYYYYYYY", annotation="int"), 

1664 DocstringReceive("", description="ZZZZZZZ", annotation="int"), 

1665 ], 

1666 ), 

1667 ], 

1668) 

1669def test_parse_receives_multiple_items( 

1670 parse_google: ParserType, 

1671 receives_multiple_items: bool, 

1672 return_annotation: str, 

1673 expected: list[DocstringReceive], 

1674) -> None: 

1675 """Parse Returns section with and without multiple items. 

1676 

1677 Parameters: 

1678 parse_google: Fixture parser. 

1679 receives_multiple_items: Whether the `Receives` section has multiple items. 

1680 return_annotation: The return annotation of the function to parse. Usually a `Generator`. 

1681 expected: The expected value of the parsed Receives section. 

1682 """ 

1683 parent = ( 

1684 Function("func", returns=parse_docstring_annotation(return_annotation, Docstring("d", parent=Function("f")))) 

1685 if return_annotation is not None 

1686 else None 

1687 ) 

1688 docstring = """ 

1689 Receives: 

1690 XXXXXXX 

1691 YYYYYYY 

1692 ZZZZZZZ 

1693 """ 

1694 sections, _ = parse_google( 

1695 docstring, 

1696 receives_multiple_items=receives_multiple_items, 

1697 parent=parent, 

1698 ) 

1699 

1700 assert len(sections) == 1 

1701 assert len(sections[0].value) == len(expected) 

1702 

1703 for annotated, expected_ in zip(sections[0].value, expected): 

1704 assert annotated.name == expected_.name 

1705 assert str(annotated.annotation) == str(expected_.annotation) 

1706 assert annotated.description == expected_.description 

1707 

1708 

1709def test_avoid_false_positive_sections(parse_google: ParserType) -> None: 

1710 """Avoid false positive when parsing sections. 

1711 

1712 Parameters: 

1713 parse_google: Fixture parser. 

1714 """ 

1715 docstring = """ 

1716 Summary. 

1717 Modules: 

1718 Not a modules section. 

1719 No blank line before title: 

1720 Not an admonition. 

1721 

1722 Blank line after title: 

1723 

1724 Not an admonition. 

1725 

1726 Modules: 

1727 

1728 Not a modules section. 

1729 Modules: 

1730 

1731 Not a modules section. 

1732 No blank line before and blank line after: 

1733 

1734 Not an admonition. 

1735 

1736 Classes: 

1737 

1738 - Text. 

1739 """ 

1740 sections, warnings = parse_google(docstring) 

1741 assert len(sections) == 1 

1742 assert "Classes" in sections[0].value 

1743 assert "Text" in sections[0].value 

1744 assert len(warnings) == 6 

1745 assert warnings == [ 

1746 "Possible section skipped, reasons: Missing blank line above section", 

1747 "Possible admonition skipped, reasons: Missing blank line above admonition", 

1748 "Possible admonition skipped, reasons: Extraneous blank line below admonition title", 

1749 "Possible section skipped, reasons: Extraneous blank line below section title", 

1750 "Possible section skipped, reasons: Missing blank line above section; Extraneous blank line below section title", 

1751 "Possible admonition skipped, reasons: Missing blank line above admonition; Extraneous blank line below admonition title", 

1752 ] 

1753 

1754 

1755def test_type_in_returns_without_parentheses(parse_google: ParserType) -> None: 

1756 """Assert we can parse the return type without parentheses. 

1757 

1758 Parameters: 

1759 parse_google: Fixture parser. 

1760 """ 

1761 docstring = """ 

1762 Summary. 

1763 

1764 Returns: 

1765 int: Description 

1766 on several lines. 

1767 """ 

1768 sections, warnings = parse_google(docstring, returns_named_value=False) 

1769 assert len(sections) == 2 

1770 assert not warnings 

1771 retval = sections[1].value[0] 

1772 assert retval.name == "" 

1773 assert retval.annotation == "int" 

1774 assert retval.description == "Description\non several lines." 

1775 

1776 docstring = """ 

1777 Summary. 

1778 

1779 Returns: 

1780 Description 

1781 on several lines. 

1782 """ 

1783 sections, warnings = parse_google(docstring, returns_named_value=False) 

1784 assert len(sections) == 2 

1785 assert len(warnings) == 1 

1786 retval = sections[1].value[0] 

1787 assert retval.name == "" 

1788 assert retval.annotation is None 

1789 assert retval.description == "Description\non several lines." 

1790 

1791 

1792def test_type_in_yields_without_parentheses(parse_google: ParserType) -> None: 

1793 """Assert we can parse the return type without parentheses. 

1794 

1795 Parameters: 

1796 parse_google: Fixture parser. 

1797 """ 

1798 docstring = """ 

1799 Summary. 

1800 

1801 Yields: 

1802 int: Description 

1803 on several lines. 

1804 """ 

1805 sections, warnings = parse_google(docstring, returns_named_value=False) 

1806 assert len(sections) == 2 

1807 assert not warnings 

1808 retval = sections[1].value[0] 

1809 assert retval.name == "" 

1810 assert retval.annotation == "int" 

1811 assert retval.description == "Description\non several lines." 

1812 

1813 docstring = """ 

1814 Summary. 

1815 

1816 Yields: 

1817 Description 

1818 on several lines. 

1819 """ 

1820 sections, warnings = parse_google(docstring, returns_named_value=False) 

1821 assert len(sections) == 2 

1822 assert len(warnings) == 1 

1823 retval = sections[1].value[0] 

1824 assert retval.name == "" 

1825 assert retval.annotation is None 

1826 assert retval.description == "Description\non several lines." 

1827 

1828 

1829def test_type_in_receives_without_parentheses(parse_google: ParserType) -> None: 

1830 """Assert we can parse the return type without parentheses. 

1831 

1832 Parameters: 

1833 parse_google: Fixture parser. 

1834 """ 

1835 docstring = """ 

1836 Summary. 

1837 

1838 Receives: 

1839 int: Description 

1840 on several lines. 

1841 """ 

1842 sections, warnings = parse_google(docstring, receives_named_value=False) 

1843 assert len(sections) == 2 

1844 assert not warnings 

1845 retval = sections[1].value[0] 

1846 assert retval.name == "" 

1847 assert retval.annotation == "int" 

1848 assert retval.description == "Description\non several lines." 

1849 

1850 docstring = """ 

1851 Summary. 

1852 

1853 Receives: 

1854 Description 

1855 on several lines. 

1856 """ 

1857 sections, warnings = parse_google(docstring, receives_named_value=False) 

1858 assert len(sections) == 2 

1859 assert len(warnings) == 1 

1860 retval = sections[1].value[0] 

1861 assert retval.name == "" 

1862 assert retval.annotation is None 

1863 assert retval.description == "Description\non several lines." 

1864 

1865 

1866def test_reading_property_type_in_summary(parse_google: ParserType) -> None: 

1867 """Assert we can parse the return type of properties in their summary. 

1868 

1869 Parameters: 

1870 parse_google: Fixture parser. 

1871 """ 

1872 docstring = "str: Description of the property." 

1873 parent = Attribute("prop") 

1874 parent.labels.add("property") 

1875 sections, warnings = parse_google(docstring, returns_type_in_property_summary=True, parent=parent) 

1876 assert len(sections) == 2 

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

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

1879 retval = sections[1].value[0] 

1880 assert retval.name == "" 

1881 assert retval.annotation.name == "str" 

1882 assert retval.description == "" 

1883 

1884 

1885# ============================================================================================= 

1886# Warnings 

1887def test_disabled_warnings(parse_google: ParserType) -> None: 

1888 """Assert warnings are disabled. 

1889 

1890 Parameters: 

1891 parse_google: Fixture parser. 

1892 """ 

1893 docstring = """ 

1894 Parameters: 

1895 x: X value. 

1896 """ 

1897 _, warnings = parse_google(docstring, warnings=True) 

1898 assert warnings 

1899 _, warnings = parse_google(docstring, warnings=False) 

1900 assert not warnings