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

459 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-08-15 16:47 +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 DocstringReturn, 

15 DocstringSectionKind, 

16 ExprName, 

17 Function, 

18 Module, 

19 Parameter, 

20 Parameters, 

21 parse_docstring_annotation, 

22) 

23 

24if TYPE_CHECKING: 

25 from tests.test_docstrings.helpers import ParserType 

26 

27 

28# ============================================================================================= 

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

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

31 """Parse a simple docstring. 

32 

33 Parameters: 

34 parse_google: Fixture parser. 

35 """ 

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

37 assert len(sections) == 1 

38 assert not warnings 

39 

40 

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

42 """Parse a multi-line docstring. 

43 

44 Parameters: 

45 parse_google: Fixture parser. 

46 """ 

47 sections, warnings = parse_google( 

48 """ 

49 A somewhat longer docstring. 

50 

51 Blablablabla. 

52 """, 

53 ) 

54 assert len(sections) == 1 

55 assert not warnings 

56 

57 

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

59 """Properly handle partially indented lines. 

60 

61 Parameters: 

62 parse_google: Fixture parser. 

63 """ 

64 docstring = """ 

65 The available formats are: 

66 - JSON 

67 

68 The unavailable formats are: 

69 - YAML 

70 """ 

71 sections, warnings = parse_google(docstring) 

72 assert len(sections) == 2 

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

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

75 assert not warnings 

76 

77 

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

79 """Parse multi-line item description. 

80 

81 Parameters: 

82 parse_google: Fixture parser. 

83 """ 

84 docstring = """ 

85 Parameters: 

86 p (int): This parameter 

87 has a description 

88 spawning on multiple lines. 

89 

90 It even has blank lines in it. 

91 Some of these lines 

92 are indented for no reason. 

93 q (int): 

94 What if the first line is blank? 

95 """ 

96 

97 sections, warnings = parse_google(docstring) 

98 assert len(sections) == 1 

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

100 assert warnings 

101 for warning in warnings: 

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

103 

104 

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

106 """Parse code blocks. 

107 

108 Parameters: 

109 parse_google: Fixture parser. 

110 """ 

111 docstring = """ 

112 This docstring contains a code block! 

113 

114 ```python 

115 print("hello") 

116 ``` 

117 """ 

118 

119 sections, warnings = parse_google(docstring) 

120 assert len(sections) == 1 

121 assert not warnings 

122 

123 

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

125 """Parse indented code blocks. 

126 

127 Parameters: 

128 parse_google: Fixture parser. 

129 """ 

130 docstring = """ 

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

132 

133 \"\"\" 

134 This docstring is contained in another docstring O_o! 

135 

136 Parameters: 

137 s: A string. 

138 \"\"\" 

139 """ 

140 

141 sections, warnings = parse_google(docstring) 

142 assert len(sections) == 1 

143 assert not warnings 

144 

145 

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

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

148 

149 Parameters: 

150 parse_google: Fixture parser. 

151 """ 

152 docstring = """ 

153 Raises: 

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

155 Well indented continuation line. 

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

157 

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

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

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

161 """ 

162 

163 sections, warnings = parse_google(docstring) 

164 assert len(sections) == 2 

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

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

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

168 "Well indented continuation line.\n" 

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

170 "\n" 

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

172 ) 

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

174 assert len(warnings) == 1 

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

176 

177 

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

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

180 

181 Parameters: 

182 parse_google: Fixture parser. 

183 """ 

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

185 sections, _ = parse_google(docstring) 

186 assert len(sections) == 2 

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

188 

189 

190@pytest.mark.parametrize( 

191 "section", 

192 [ 

193 "Attributes", 

194 "Other Parameters", 

195 "Parameters", 

196 "Raises", 

197 "Receives", 

198 "Returns", 

199 "Warns", 

200 "Yields", 

201 ], 

202) 

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

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

205 

206 Parameters: 

207 parse_google: Fixture parser. 

208 section: A parametrized section name. 

209 """ 

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

211 sections, _ = parse_google(docstring) 

212 assert len(sections) == 1 

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

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

215 

216 

217# ============================================================================================= 

218# Annotations 

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

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

221 

222 Parameters: 

223 parse_google: Fixture parser. 

224 """ 

225 sections, warnings = parse_google( 

226 """ 

227 Parameters: 

228 void: SEGFAULT. 

229 niet: SEGFAULT. 

230 nada: SEGFAULT. 

231 rien: SEGFAULT. 

232 

233 Keyword Args: 

234 keywd: SEGFAULT. 

235 

236 Exceptions: 

237 GlobalError: when nothing works as expected. 

238 

239 Returns: 

240 Itself. 

241 """, 

242 ) 

243 

244 assert len(sections) == 4 

245 assert len(warnings) == 6 # missing annotations for parameters and return 

246 for warning in warnings[:-1]: 

247 assert "parameter" in warning 

248 assert "return" in warnings[-1] 

249 

250 

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

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

253 

254 Parameters: 

255 parse_google: Fixture parser. 

256 """ 

257 docstring = """ 

258 Parameters: 

259 x: X value. 

260 

261 Keyword Args: 

262 y: Y value. 

263 

264 Returns: 

265 Sum X + Y + Z. 

266 """ 

267 

268 sections, warnings = parse_google( 

269 docstring, 

270 parent=Function( 

271 "func", 

272 parameters=Parameters( 

273 Parameter("x"), 

274 Parameter("y"), 

275 ), 

276 ), 

277 ) 

278 assert len(sections) == 3 

279 assert len(warnings) == 3 

280 for warning in warnings[:-1]: 

281 assert "parameter" in warning 

282 assert "return" in warnings[-1] 

283 

284 

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

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

287 

288 Parameters: 

289 parse_google: Fixture parser. 

290 """ 

291 docstring = """ 

292 Parameters: 

293 x: X value. 

294 

295 Keyword Parameters: 

296 y: Y value. 

297 

298 Returns: 

299 Sum X + Y. 

300 """ 

301 

302 sections, warnings = parse_google( 

303 docstring, 

304 parent=Function( 

305 "func", 

306 parameters=Parameters( 

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

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

309 ), 

310 returns="int", 

311 ), 

312 ) 

313 assert len(sections) == 3 

314 assert not warnings 

315 

316 

317# ============================================================================================= 

318# Sections 

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

320 """Parse Attributes sections. 

321 

322 Parameters: 

323 parse_google: Fixture parser. 

324 """ 

325 docstring = """ 

326 Attributes: 

327 hey: Hey. 

328 ho: Ho. 

329 """ 

330 

331 sections, warnings = parse_google(docstring) 

332 assert len(sections) == 1 

333 assert not warnings 

334 

335 

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

337 """Parse Functions/Methods sections. 

338 

339 Parameters: 

340 parse_google: Fixture parser. 

341 """ 

342 docstring = """ 

343 Functions: 

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

345 g: Hi. 

346 

347 Methods: 

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

349 g: Hi. 

350 """ 

351 

352 sections, warnings = parse_google(docstring) 

353 assert len(sections) == 2 

354 for section in sections: 

355 assert section.kind is DocstringSectionKind.functions 

356 func_f = section.value[0] 

357 assert func_f.name == "f" 

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

359 assert func_f.description == "Hello." 

360 func_g = section.value[1] 

361 assert func_g.name == "g" 

362 assert func_g.signature is None 

363 assert func_g.description == "Hi." 

364 assert not warnings 

365 

366 

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

368 """Parse Classes sections. 

369 

370 Parameters: 

371 parse_google: Fixture parser. 

372 """ 

373 docstring = """ 

374 Classes: 

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

376 D: Hi. 

377 """ 

378 

379 sections, warnings = parse_google(docstring) 

380 assert len(sections) == 1 

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

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

383 assert class_c.name == "C" 

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

385 assert class_c.description == "Hello." 

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

387 assert class_d.name == "D" 

388 assert class_d.signature is None 

389 assert class_d.description == "Hi." 

390 assert not warnings 

391 

392 

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

394 """Parse Modules sections. 

395 

396 Parameters: 

397 parse_google: Fixture parser. 

398 """ 

399 docstring = """ 

400 Modules: 

401 m: Hello. 

402 n: Hi. 

403 """ 

404 

405 sections, warnings = parse_google(docstring) 

406 assert len(sections) == 1 

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

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

409 assert module_m.name == "m" 

410 assert module_m.description == "Hello." 

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

412 assert module_n.name == "n" 

413 assert module_n.description == "Hi." 

414 assert not warnings 

415 

416 

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

418 """Parse a function docstring with examples. 

419 

420 Parameters: 

421 parse_google: Fixture parser. 

422 """ 

423 docstring = """ 

424 Examples: 

425 Some examples that will create a unified code block: 

426 

427 >>> 2 + 2 == 5 

428 False 

429 >>> print("examples") 

430 "examples" 

431 

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

433 

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

435 

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

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

438 

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

440 "I'm in other code block!" 

441 

442 We also can write multiline examples: 

443 

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

445 >>> y = x + 10 

446 >>> y 

447 15 

448 

449 This is just a typical Python code block: 

450 

451 ```python 

452 print("examples") 

453 return 2 + 2 

454 ``` 

455 

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

457 

458 ```pycon 

459 >>> print("examples") 

460 "examples" 

461 >>> 2 + 2 

462 4 

463 ``` 

464 

465 The blank line before an example is optional. 

466 >>> x = 3 

467 >>> y = "apple" 

468 >>> z = False 

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

470 >>> my_print_list_function(l) 

471 3 

472 "apple" 

473 False 

474 """ 

475 

476 sections, warnings = parse_google( 

477 docstring, 

478 parent=Function( 

479 "func", 

480 parameters=Parameters( 

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

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

483 ), 

484 returns="int", 

485 ), 

486 trim_doctest_flags=False, 

487 ) 

488 assert len(sections) == 1 

489 examples = sections[0] 

490 assert len(examples.value) == 9 

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

492 assert not warnings 

493 

494 

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

496 """Parse Yields section. 

497 

498 Parameters: 

499 parse_google: Fixture parser. 

500 """ 

501 docstring = """ 

502 Yields: 

503 x: Floats. 

504 (int): Integers. 

505 y (int): Same. 

506 """ 

507 

508 sections, warnings = parse_google(docstring) 

509 assert len(sections) == 1 

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

511 assert annotated.name == "x" 

512 assert annotated.annotation is None 

513 assert annotated.description == "Floats." 

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

515 assert annotated.name == "" 

516 assert annotated.annotation == "int" 

517 assert annotated.description == "Integers." 

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

519 assert annotated.name == "y" 

520 assert annotated.annotation == "int" 

521 assert annotated.description == "Same." 

522 assert len(warnings) == 1 

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

524 

525 

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

527 """Warn on invalid sections. 

528 

529 Parameters: 

530 parse_google: Fixture parser. 

531 """ 

532 docstring = """ 

533 Parameters: 

534 Exceptions: 

535 Exceptions: 

536 

537 Returns: 

538 Note: 

539 

540 Important: 

541 """ 

542 

543 sections, warnings = parse_google(docstring) 

544 assert len(sections) == 1 

545 assert not warnings 

546 

547 

548# ============================================================================================= 

549# Parameters sections 

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

551 """Parse args and kwargs. 

552 

553 Parameters: 

554 parse_google: Fixture parser. 

555 """ 

556 docstring = """ 

557 Parameters: 

558 a (str): a parameter. 

559 *args (str): args parameters. 

560 **kwargs (str): kwargs parameters. 

561 """ 

562 

563 sections, warnings = parse_google(docstring) 

564 assert len(sections) == 1 

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

566 for parameter in sections[0].value: 

567 assert parameter.name in expected_parameters 

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

569 assert not warnings 

570 

571 

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

573 """Parse args and kwargs. 

574 

575 Parameters: 

576 parse_google: Fixture parser. 

577 """ 

578 docstring = """ 

579 Parameters: 

580 a (str): a parameter. 

581 *args (str): args parameters. 

582 

583 Keyword Args: 

584 **kwargs (str): kwargs parameters. 

585 """ 

586 

587 sections, warnings = parse_google(docstring) 

588 assert len(sections) == 2 

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

590 for parameter in sections[0].value: 

591 assert parameter.name in expected_parameters 

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

593 

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

595 for kwarg in sections[1].value: 

596 assert kwarg.name in expected_parameters 

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

598 

599 assert not warnings 

600 

601 

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

603 """Parse types in docstring. 

604 

605 Parameters: 

606 parse_google: Fixture parser. 

607 """ 

608 docstring = """ 

609 Parameters: 

610 x (int): X value. 

611 

612 Keyword Args: 

613 y (int): Y value. 

614 

615 Returns: 

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

617 """ 

618 

619 sections, warnings = parse_google( 

620 docstring, 

621 parent=Function( 

622 "func", 

623 parameters=Parameters( 

624 Parameter("x"), 

625 Parameter("y"), 

626 ), 

627 ), 

628 ) 

629 assert len(sections) == 3 

630 assert not warnings 

631 

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

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

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

635 

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

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

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

639 

640 assert argx.name == "x" 

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

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

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

644 assert argx.value is None 

645 

646 assert argy.name == "y" 

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

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

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

650 assert argy.value is None 

651 

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

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

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

655 

656 

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

658 """Parse optional types in docstring. 

659 

660 Parameters: 

661 parse_google: Fixture parser. 

662 """ 

663 docstring = """ 

664 Parameters: 

665 x (int): X value. 

666 y (int, optional): Y value. 

667 

668 Keyword Args: 

669 z (int, optional): Z value. 

670 """ 

671 

672 sections, warnings = parse_google( 

673 docstring, 

674 parent=Function( 

675 "func", 

676 parameters=Parameters( 

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

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

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

680 ), 

681 ), 

682 ) 

683 assert len(sections) == 2 

684 assert not warnings 

685 

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

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

688 

689 argx, argy = sections[0].value 

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

691 

692 assert argx.name == "x" 

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

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

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

696 assert argx.value == "1" 

697 

698 assert argy.name == "y" 

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

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

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

702 assert argy.value == "None" 

703 

704 assert argz.name == "z" 

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

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

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

708 assert argz.value == "None" 

709 

710 

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

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

713 

714 Parameters: 

715 parse_google: Fixture parser. 

716 """ 

717 docstring = """ 

718 Parameters: 

719 x (str): X value. 

720 

721 Keyword Args: 

722 y (str): Y value. 

723 

724 Returns: 

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

726 """ 

727 

728 sections, warnings = parse_google( 

729 docstring, 

730 parent=Function( 

731 "func", 

732 parameters=Parameters( 

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

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

735 ), 

736 returns="int", 

737 ), 

738 ) 

739 assert len(sections) == 3 

740 assert not warnings 

741 

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

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

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

745 

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

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

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

749 

750 assert argx.name == "x" 

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

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

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

754 

755 assert argy.name == "y" 

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

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

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

759 

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

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

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

763 

764 

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

766 """Warn when missing colon. 

767 

768 Parameters: 

769 parse_google: Fixture parser. 

770 """ 

771 docstring = """ 

772 Parameters: 

773 x is an integer. 

774 """ 

775 

776 sections, warnings = parse_google(docstring) 

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

778 assert len(warnings) == 1 

779 assert "pair" in warnings[0] 

780 

781 

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

783 """Warn when missing colon. 

784 

785 Parameters: 

786 parse_google: Fixture parser. 

787 """ 

788 docstring = """ 

789 Keyword Args: 

790 x is an integer. 

791 """ 

792 

793 sections, warnings = parse_google(docstring) 

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

795 assert len(warnings) == 1 

796 assert "pair" in warnings[0] 

797 

798 

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

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

801 

802 Parameters: 

803 parse_google: Fixture parser. 

804 """ 

805 docstring = """ 

806 Parameters: 

807 x (int): Integer. 

808 y (int): Integer. 

809 """ 

810 

811 _, warnings = parse_google( 

812 docstring, 

813 parent=Function( 

814 "func", 

815 parameters=Parameters( 

816 Parameter("a"), 

817 Parameter("y"), 

818 ), 

819 ), 

820 ) 

821 assert len(warnings) == 1 

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

823 

824 

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

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

827 

828 Parameters: 

829 parse_google: Fixture parser. 

830 """ 

831 docstring = """ 

832 Other Parameters: 

833 x (int): Integer. 

834 z (int): Integer. 

835 """ 

836 

837 _, warnings = parse_google( 

838 docstring, 

839 parent=Function( 

840 "func", 

841 parameters=Parameters( 

842 Parameter("a"), 

843 Parameter("y"), 

844 ), 

845 ), 

846 ) 

847 assert not warnings 

848 

849 

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

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

852 

853 Parameters: 

854 parse_google: Fixture parser. 

855 """ 

856 docstring = """ 

857 Parameters: 

858 this (str): This. 

859 that (str): That. 

860 """ 

861 

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

863 assert not warnings 

864 

865 

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

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

868 

869 Parameters: 

870 parse_google: Fixture parser. 

871 """ 

872 docstring = """ 

873 Parameters: 

874 x: X value. 

875 """ 

876 parent = Class("c") 

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

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

879 assert not warnings 

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

881 assert argx.name == "x" 

882 assert argx.annotation == "int" 

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

884 

885 

886# TODO: possible feature 

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

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

889# 

890# Parameters: 

891# parse_google: Fixture parser. 

892# """ 

893# docstring = """ 

894# Parameters: 

895# x: Integer. 

896# """ 

897# assert not warnings 

898 

899 

900# ============================================================================================= 

901# Attributes sections 

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

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

904 

905 Parameters: 

906 parse_google: Fixture parser. 

907 """ 

908 docstring = """ 

909 Summary. 

910 

911 Attributes: 

912 a: Whatever. 

913 b: Whatever. 

914 """ 

915 parent = Class("cls") 

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

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

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

919 attributes = sections[1].value 

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

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

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

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

924 

925 

926# ============================================================================================= 

927# Yields sections 

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

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

930 

931 Parameters: 

932 parse_google: Fixture parser. 

933 """ 

934 docstring = """ 

935 Yields: 

936 Integers. 

937 """ 

938 

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

940 sections, warnings = parse_google(docstring, function) 

941 assert len(sections) == 1 

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

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

944 assert annotated.description == "Integers." 

945 assert not warnings 

946 

947 

948@pytest.mark.parametrize( 

949 "return_annotation", 

950 [ 

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

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

953 ], 

954) 

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

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

957 

958 Parameters: 

959 parse_google: Fixture parser. 

960 return_annotation: Parametrized return annotation as a string. 

961 """ 

962 docstring = """ 

963 Summary. 

964 

965 Yields: 

966 a: Whatever. 

967 b: Whatever. 

968 """ 

969 sections, _ = parse_google( 

970 docstring, 

971 parent=Function( 

972 "func", 

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

974 ), 

975 ) 

976 yields = sections[1].value 

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

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

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

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

981 

982 

983@pytest.mark.parametrize( 

984 "return_annotation", 

985 [ 

986 "Iterator[int]", 

987 "Generator[int, None, None]", 

988 ], 

989) 

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

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

992 

993 Parameters: 

994 parse_google: Fixture parser. 

995 return_annotation: Parametrized return annotation as a string. 

996 """ 

997 docstring = """ 

998 Summary. 

999 

1000 Yields: 

1001 A number. 

1002 """ 

1003 sections, _ = parse_google( 

1004 docstring, 

1005 parent=Function( 

1006 "func", 

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

1008 ), 

1009 ) 

1010 yields = sections[1].value 

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

1012 

1013 

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

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

1016 

1017 Parameters: 

1018 parse_google: Fixture parser. 

1019 """ 

1020 docstring = """ 

1021 Summary. 

1022 

1023 Yields: 

1024 A number. 

1025 """ 

1026 sections, warnings = parse_google( 

1027 docstring, 

1028 parent=Attribute( 

1029 "prop", 

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

1031 ), 

1032 ) 

1033 assert not warnings 

1034 yields = sections[1].value 

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

1036 

1037 

1038# ============================================================================================= 

1039# Receives sections 

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

1041 """Parse Receives annotations in Generator type. 

1042 

1043 Parameters: 

1044 parse_google: Fixture parser. 

1045 """ 

1046 docstring = """ 

1047 Summary. 

1048 

1049 Receives: 

1050 a: Whatever. 

1051 b: Whatever. 

1052 """ 

1053 sections, _ = parse_google( 

1054 docstring, 

1055 parent=Function( 

1056 "func", 

1057 returns=parse_docstring_annotation( 

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

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

1060 ), 

1061 ), 

1062 ) 

1063 receives = sections[1].value 

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

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

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

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

1068 

1069 

1070@pytest.mark.parametrize( 

1071 "return_annotation", 

1072 [ 

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

1074 ], 

1075) 

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

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

1078 

1079 Parameters: 

1080 parse_google: Fixture parser. 

1081 return_annotation: Parametrized return annotation as a string. 

1082 """ 

1083 docstring = """ 

1084 Summary. 

1085 

1086 Receives: 

1087 A floating point number. 

1088 """ 

1089 sections, _ = parse_google( 

1090 docstring, 

1091 parent=Function( 

1092 "func", 

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

1094 ), 

1095 ) 

1096 receives = sections[1].value 

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

1098 

1099 

1100# ============================================================================================= 

1101# Returns sections 

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

1103 """Parse Returns annotations in Generator type. 

1104 

1105 Parameters: 

1106 parse_google: Fixture parser. 

1107 """ 

1108 docstring = """ 

1109 Summary. 

1110 

1111 Returns: 

1112 a: Whatever. 

1113 b: Whatever. 

1114 """ 

1115 sections, _ = parse_google( 

1116 docstring, 

1117 parent=Function( 

1118 "func", 

1119 returns=parse_docstring_annotation( 

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

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

1122 ), 

1123 ), 

1124 ) 

1125 returns = sections[1].value 

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

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

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

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

1130 

1131 

1132# ============================================================================================= 

1133# Parser special features 

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

1135 """Parse admonitions. 

1136 

1137 Parameters: 

1138 parse_google: Fixture parser. 

1139 """ 

1140 docstring = """ 

1141 Important note: 

1142 Hello. 

1143 

1144 Note: With title. 

1145 Hello again. 

1146 

1147 Something: 

1148 Something. 

1149 """ 

1150 

1151 sections, warnings = parse_google(docstring) 

1152 assert len(sections) == 3 

1153 assert not warnings 

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

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

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

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

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

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

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

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

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

1163 

1164 

1165@pytest.mark.parametrize( 

1166 "docstring", 

1167 [ 

1168 """ 

1169 ****************************** 

1170 This looks like an admonition: 

1171 ****************************** 

1172 """, 

1173 """ 

1174 Warning: this line also looks 

1175 like an admonition. 

1176 """, 

1177 """ 

1178 Matching but not an admonition: 

1179 

1180 

1181 

1182 - Multiple empty lines above. 

1183 """, 

1184 """Last line:""", 

1185 ], 

1186) 

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

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

1189 

1190 Parameters: 

1191 parse_google: Fixture parser. 

1192 docstring: The docstring to parse (parametrized). 

1193 """ 

1194 sections, warnings = parse_google(docstring) 

1195 assert len(sections) == 1 

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

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

1198 assert not warnings 

1199 

1200 

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

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

1203 

1204 Parameters: 

1205 parse_google: Fixture parser. 

1206 """ 

1207 docstring = """ 

1208 Summary. 

1209 

1210 Short description. 

1211 

1212 Info: 

1213 Something useful. 

1214 """ 

1215 sections, _ = parse_google(docstring) 

1216 assert len(sections) == 2 

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

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

1219 

1220 

1221@pytest.mark.parametrize( 

1222 "docstring", 

1223 [ 

1224 "", 

1225 "\n", 

1226 "\n\n", 

1227 "Summary.", 

1228 "Summary.\n\n\n", 

1229 "Summary.\n\nParagraph.", 

1230 "Summary\non two lines.", 

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

1232 ], 

1233) 

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

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

1236 

1237 Parameters: 

1238 parse_google: Fixture parser. 

1239 docstring: The docstring to parse_google (parametrized). 

1240 """ 

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

1242 for section in sections: 

1243 assert "Summary" not in section.value 

1244 

1245 if docstring.strip(): 

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

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

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

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

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

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

1252 

1253 

1254@pytest.mark.parametrize( 

1255 "docstring", 

1256 [ 

1257 """ 

1258 Examples: 

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

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

1261 True 

1262 """, 

1263 r""" 

1264 Examples: 

1265 Base case 2. We have a blankline test. 

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

1267 a 

1268 <BLANKLINE> 

1269 b 

1270 """, 

1271 ], 

1272) 

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

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

1275 

1276 Parameters: 

1277 parse_google: Fixture parser. 

1278 docstring: The docstring to parse (parametrized). 

1279 """ 

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

1281 assert len(sections) == 1 

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

1283 assert not warnings 

1284 

1285 # verify that doctest flags have indeed been trimmed 

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

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

1288 assert "<BLANKLINE>" not in example_str 

1289 

1290 

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

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

1293 

1294 Parameters: 

1295 parse_google: Fixture parser. 

1296 """ 

1297 docstring = r""" 

1298 Examples: 

1299 Test multiline example blocks. 

1300 We want to skip the following test. 

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

1302 True 

1303 

1304 And then a few more examples here: 

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

1306 a 

1307 <BLANKLINE> 

1308 b 

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

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

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

1312 """ 

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

1314 assert len(sections) == 1 

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

1316 assert not warnings 

1317 

1318 # verify that doctest flags have indeed been trimmed 

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

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

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

1322 assert "<BLANKLINE>" not in example_str 

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

1324 

1325 

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

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

1328 

1329 Parameters: 

1330 parse_google: Fixture parser. 

1331 """ 

1332 docstring = "a: b\n " 

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

1334 assert len(sections) == 1 

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

1336 assert not warnings 

1337 

1338 

1339@pytest.mark.parametrize( 

1340 ("returns_multiple_items", "return_annotation", "expected"), 

1341 [ 

1342 ( 

1343 False, 

1344 None, 

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

1346 ), 

1347 ( 

1348 False, 

1349 "tuple[int, int]", 

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

1351 ), 

1352 ( 

1353 True, 

1354 None, 

1355 [ 

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

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

1358 ], 

1359 ), 

1360 ( 

1361 True, 

1362 "tuple[int,int]", 

1363 [ 

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

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

1366 ], 

1367 ), 

1368 ], 

1369) 

1370def test_parse_returns_multiple_items( 

1371 parse_google: ParserType, 

1372 returns_multiple_items: bool, 

1373 return_annotation: str, 

1374 expected: list[DocstringReturn], 

1375) -> None: 

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

1377 

1378 Parameters: 

1379 parse_google: Fixture parser. 

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

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

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

1383 """ 

1384 parent = ( 

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

1386 if return_annotation is not None 

1387 else None 

1388 ) 

1389 docstring = """ 

1390 Returns: 

1391 XXXXXXX 

1392 YYYYYYY 

1393 ZZZZZZZ 

1394 """ 

1395 sections, _ = parse_google( 

1396 docstring, 

1397 returns_multiple_items=returns_multiple_items, 

1398 parent=parent, 

1399 ) 

1400 

1401 assert len(sections) == 1 

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

1403 

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

1405 assert annotated.name == expected_.name 

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

1407 assert annotated.description == expected_.description 

1408 

1409 

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

1411 """Avoid false positive when parsing sections. 

1412 

1413 Parameters: 

1414 parse_google: Fixture parser. 

1415 """ 

1416 docstring = """ 

1417 Summary. 

1418 Modules: 

1419 Not a modules section. 

1420 No blank line before title: 

1421 Not an admonition. 

1422 

1423 Blank line after title: 

1424 

1425 Not an admonition. 

1426 

1427 Modules: 

1428 

1429 Not a modules section. 

1430 Modules: 

1431 

1432 Not a modules section. 

1433 No blank line before and blank line after: 

1434 

1435 Not an admonition. 

1436 

1437 Classes: 

1438 

1439 - Text. 

1440 """ 

1441 sections, warnings = parse_google(docstring) 

1442 assert len(sections) == 1 

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

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

1445 assert len(warnings) == 6 

1446 assert warnings == [ 

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

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

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

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

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

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

1453 ] 

1454 

1455 

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

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

1458 

1459 Parameters: 

1460 parse_google: Fixture parser. 

1461 """ 

1462 docstring = """ 

1463 Summary. 

1464 

1465 Returns: 

1466 int: Description 

1467 on several lines. 

1468 """ 

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

1470 assert len(sections) == 2 

1471 assert not warnings 

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

1473 assert retval.name == "" 

1474 assert retval.annotation == "int" 

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

1476 

1477 docstring = """ 

1478 Summary. 

1479 

1480 Returns: 

1481 Description 

1482 on several lines. 

1483 """ 

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

1485 assert len(sections) == 2 

1486 assert len(warnings) == 1 

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

1488 assert retval.name == "" 

1489 assert retval.annotation is None 

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

1491 

1492 

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

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

1495 

1496 Parameters: 

1497 parse_google: Fixture parser. 

1498 """ 

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

1500 parent = Attribute("prop") 

1501 parent.labels.add("property") 

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

1503 assert len(sections) == 2 

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

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

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

1507 assert retval.name == "" 

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

1509 assert retval.description == ""