Coverage for tests/test_parsers/test_docstrings/test_google.py: 94.01%

305 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-09 18:24 +0100

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

2 

3import inspect 

4from collections.abc import Iterator 

5from textwrap import dedent 

6from typing import Any, Optional 

7 

8from pytkdocs.loader import Loader 

9from pytkdocs.parsers.docstrings.base import Section 

10from pytkdocs.parsers.docstrings.google import Google 

11from pytkdocs.serializer import serialize_attribute 

12 

13 

14class DummyObject: 

15 path = "o" 

16 

17 

18def parse( 

19 docstring: str, 

20 signature: Optional[inspect.Signature] = None, 

21 return_type: Any = inspect.Signature.empty, 

22 *, 

23 admonitions: bool = True, 

24 trim_doctest: bool = False, 

25) -> tuple[list[Section], list[str]]: 

26 """Helper to parse a doctring.""" 

27 parser = Google(replace_admonitions=admonitions, trim_doctest_flags=trim_doctest) 

28 

29 return parser.parse( 

30 dedent(docstring).strip(), 

31 context={"obj": DummyObject(), "signature": signature, "type": return_type}, 

32 ) 

33 

34 

35def test_simple_docstring() -> None: 

36 """Parse a simple docstring.""" 

37 sections, errors = parse("A simple docstring.") 

38 assert len(sections) == 1 

39 assert not errors 

40 

41 

42def test_multi_line_docstring() -> None: 

43 """Parse a multi-line docstring.""" 

44 sections, errors = parse( 

45 """ 

46 A somewhat longer docstring. 

47 

48 Blablablabla. 

49 """, 

50 ) 

51 assert len(sections) == 1 

52 assert not errors 

53 

54 

55def test_sections_without_signature() -> None: 

56 """Parse a docstring without a signature.""" 

57 sections, errors = parse( 

58 """ 

59 Sections without signature. 

60 

61 Parameters: 

62 void: SEGFAULT. 

63 niet: SEGFAULT. 

64 nada: SEGFAULT. 

65 rien: SEGFAULT. 

66 

67 Keyword Args: 

68 keywd: SEGFAULT. 

69 

70 Exceptions: 

71 GlobalError: when nothing works as expected. 

72 

73 Returns: 

74 Itself. 

75 """, 

76 ) 

77 

78 assert len(sections) == 5 

79 assert len(errors) == 6 # missing annotations for params and return 

80 for error in errors[:-1]: 

81 assert "param" in error 

82 assert "return" in errors[-1] 

83 

84 

85def test_property_docstring() -> None: 

86 """Parse a property docstring.""" 

87 class_ = Loader().get_object_documentation("tests.fixtures.parsing.docstrings.NotDefinedYet") 

88 prop = class_.attributes[0] 

89 sections, errors = prop.docstring_sections, prop.docstring_errors 

90 assert len(sections) == 2 

91 assert not errors 

92 

93 

94def test_function_without_annotations() -> None: 

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

96 

97 def f(x, y, *, z): # noqa: ANN001, ANN202 

98 """This function has no annotations. 

99 

100 Parameters: 

101 x: X value. 

102 y: Y value. 

103 

104 Keyword Args: 

105 z: Z value. 

106 

107 Returns: 

108 Sum X + Y + Z. 

109 """ 

110 return x + y + z 

111 

112 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

113 assert len(sections) == 4 

114 assert len(errors) == 1 

115 assert "No return type/annotation in" in errors[0] 

116 

117 

118def test_function_with_annotations() -> None: 

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

120 

121 def f(x: int, y: int, *, z: int) -> int: # noqa: ARG001 

122 """This function has annotations. 

123 

124 Parameters: 

125 x: X value. 

126 y: Y value. 

127 

128 Keyword Arguments: 

129 z: Z value. 

130 

131 Returns: 

132 Sum X + Y. 

133 """ 

134 return x + y 

135 

136 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

137 assert len(sections) == 4 

138 assert not errors 

139 

140 

141def test_function_with_examples_trim_doctest() -> None: 

142 """Parse example docstring with trim_doctest_flags option.""" 

143 

144 def f(x: int) -> int: 

145 r"""Test function. 

146 

147 Example: 

148 We want to skip the following test. 

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

150 True 

151 

152 And then a few more examples here: 

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

154 a 

155 <BLANKLINE> 

156 b 

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

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

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

160 """ 

161 return x 

162 

163 sections, errors = parse( 

164 inspect.getdoc(f), # type: ignore[arg-type] 

165 inspect.signature(f), 

166 trim_doctest=True, 

167 ) 

168 assert len(sections) == 2 

169 assert len(sections[1].value) == 4 

170 assert not errors 

171 

172 # Verify that doctest flags have indeed been trimmed 

173 example_str = sections[1].value[1][1] 

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

175 example_str = sections[1].value[3][1] 

176 assert "<BLANKLINE>" not in example_str 

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

178 

179 

180def test_function_with_examples() -> None: 

181 """Parse a function docstring with examples.""" 

182 

183 def f(x: int, y: int) -> int: 

184 """This function has annotations. 

185 

186 Examples: 

187 Some examples that will create an unified code block: 

188 

189 >>> 2 + 2 == 5 

190 False 

191 >>> print("examples") 

192 "examples" 

193 

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

195 

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

197 

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

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

200 

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

202 "I'm in other code block!" 

203 

204 We also can write multiline examples: 

205 

206 >>> x = 3 + 2 

207 >>> y = x + 10 

208 >>> y 

209 15 

210 

211 This is just a typical Python code block: 

212 

213 ```python 

214 print("examples") 

215 return 2 + 2 

216 ``` 

217 

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

219 

220 ```python 

221 >>> print("examples") 

222 "examples" 

223 >>> 2 + 2 

224 4 

225 ``` 

226 

227 The blank line before an example is optional. 

228 >>> x = 3 

229 >>> y = "apple" 

230 >>> z = False 

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

232 >>> my_print_list_function(l) 

233 3 

234 "apple" 

235 False 

236 """ 

237 return x + y 

238 

239 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

240 assert len(sections) == 2 

241 assert len(sections[1].value) == 9 

242 assert not errors 

243 

244 

245def test_types_in_docstring() -> None: 

246 """Parse types in docstring.""" 

247 

248 def f(x, y, *, z): # noqa: ANN001, ANN202 

249 """The types are written in the docstring. 

250 

251 Parameters: 

252 x (int): X value. 

253 y (int): Y value. 

254 

255 Keyword Args: 

256 z (int): Z value. 

257 

258 Returns: 

259 int: Sum X + Y + Z. 

260 """ 

261 return x + y + z 

262 

263 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

264 assert len(sections) == 4 

265 assert not errors 

266 

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

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

269 assert sections[2].type == Section.Type.KEYWORD_ARGS 

270 assert sections[3].type == Section.Type.RETURN 

271 

272 x, y = sections[1].value 

273 (z,) = sections[2].value 

274 r = sections[3].value 

275 

276 assert x.name == "x" 

277 assert x.annotation == "int" 

278 assert x.description == "X value." 

279 assert x.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD 

280 assert x.default is inspect.Signature.empty 

281 

282 assert y.name == "y" 

283 assert y.annotation == "int" 

284 assert y.description == "Y value." 

285 assert y.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD 

286 assert y.default is inspect.Signature.empty 

287 

288 assert z.name == "z" 

289 assert z.annotation == "int" 

290 assert z.description == "Z value." 

291 assert z.kind is inspect.Parameter.KEYWORD_ONLY 

292 assert z.default is inspect.Signature.empty 

293 

294 assert r.annotation == "int" 

295 assert r.description == "Sum X + Y + Z." 

296 

297 

298def test_types_and_optional_in_docstring() -> None: 

299 """Parse optional types in docstring.""" 

300 

301 def f(x=1, y=None, *, z=None): # noqa: ANN001, ANN202 

302 """The types are written in the docstring. 

303 

304 Parameters: 

305 x (int): X value. 

306 y (int, optional): Y value. 

307 

308 Keyword Args: 

309 z (int, optional): Z value. 

310 

311 Returns: 

312 int: Sum X + Y + Z. 

313 """ 

314 return x + (y or 1) + (z or 1) 

315 

316 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

317 assert len(sections) == 4 

318 assert not errors 

319 

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

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

322 assert sections[2].type == Section.Type.KEYWORD_ARGS 

323 

324 x, y = sections[1].value 

325 (z,) = sections[2].value 

326 

327 assert x.name == "x" 

328 assert x.annotation == "int" 

329 assert x.description == "X value." 

330 assert x.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD 

331 assert x.default == 1 

332 

333 assert y.name == "y" 

334 assert y.annotation == "int" 

335 assert y.description == "Y value." 

336 assert y.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD 

337 assert y.default is None 

338 

339 assert z.name == "z" 

340 assert z.annotation == "int" 

341 assert z.description == "Z value." 

342 assert z.kind is inspect.Parameter.KEYWORD_ONLY 

343 assert z.default is None 

344 

345 

346def test_types_in_signature_and_docstring() -> None: 

347 """Parse types in both signature and docstring. Should prefer the docstring type.""" 

348 

349 def f(x: int, y: int, *, z: int) -> int: 

350 """The types are written both in the signature and in the docstring. 

351 

352 Parameters: 

353 x (str): X value. 

354 y (str): Y value. 

355 

356 Keyword Args: 

357 z (str): Z value. 

358 

359 Returns: 

360 str: Sum X + Y + Z. 

361 """ 

362 return x + y + z 

363 

364 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

365 assert len(sections) == 4 

366 assert not errors 

367 

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

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

370 assert sections[2].type == Section.Type.KEYWORD_ARGS 

371 assert sections[3].type == Section.Type.RETURN 

372 

373 x, y = sections[1].value 

374 (z,) = sections[2].value 

375 r = sections[3].value 

376 

377 assert x.name == "x" 

378 assert x.annotation == "str" 

379 assert x.description == "X value." 

380 assert x.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD 

381 assert x.default is inspect.Signature.empty 

382 

383 assert y.name == "y" 

384 assert y.annotation == "str" 

385 assert y.description == "Y value." 

386 assert y.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD 

387 assert y.default is inspect.Signature.empty 

388 

389 assert z.name == "z" 

390 assert z.annotation == "str" 

391 assert z.description == "Z value." 

392 assert z.kind is inspect.Parameter.KEYWORD_ONLY 

393 assert z.default is inspect.Signature.empty 

394 

395 assert r.annotation == "str" 

396 assert r.description == "Sum X + Y + Z." 

397 

398 

399def test_close_sections() -> None: 

400 """Parse sections without blank lines in between.""" 

401 

402 def f(x, y, z): # noqa: ANN202, ANN001 

403 """Parameters: 

404 x: X. 

405 Parameters: 

406 y: Y. 

407 

408 Parameters: 

409 z: Z. 

410 Exceptions: 

411 Error2: error. 

412 Exceptions: 

413 Error1: error. 

414 

415 Returns: 

416 1. 

417 

418 Returns: 

419 2. 

420 """ # noqa: D205 

421 return x + y + z 

422 

423 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

424 assert len(sections) == 7 

425 assert len(errors) == 2 # no return type annotations 

426 

427 

428def test_code_blocks() -> None: 

429 """Parse code blocks.""" 

430 

431 def f(s): # noqa: ANN001, ANN202 

432 """This docstring contains a docstring in a code block o_O! 

433 

434 ```python 

435 ''' 

436 This docstring is contained in another docstring O_o! 

437 

438 Parameters: 

439 s: A string. 

440 ''' 

441 ``` 

442 """ 

443 return s 

444 

445 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

446 assert len(sections) == 1 

447 assert not errors 

448 

449 

450def test_indented_code_block() -> None: 

451 """Parse indented code blocks.""" 

452 

453 def f(s): # noqa: ANN001, ANN202 

454 ''' 

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

456 

457 """ 

458 This docstring is contained in another docstring O_o! 

459 

460 Parameters: 

461 s: A string. 

462 """ 

463 ''' # noqa: D212 

464 return s 

465 

466 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

467 assert len(sections) == 1 

468 assert not errors 

469 

470 

471def test_extra_parameter() -> None: 

472 """Warn on extra parameter in docstring.""" 

473 

474 def f(x): # noqa: ANN202, ANN001 

475 """ 

476 Parameters: 

477 x: Integer. 

478 y: Integer. 

479 """ # noqa: D205, D212 

480 return x 

481 

482 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

483 assert len(sections) == 1 

484 assert len(errors) == 1 

485 assert "No type" in errors[0] 

486 

487 

488def test_missing_parameter() -> None: 

489 """Don't warn on missing parameter in docstring.""" 

490 

491 # FIXME: could warn 

492 def f(x, y): # noqa: ANN202, ANN001 

493 """ 

494 Parameters: 

495 x: Integer. 

496 """ # noqa: D205, D212 

497 return x + y 

498 

499 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

500 assert len(sections) == 1 

501 assert not errors 

502 

503 

504def test_param_line_without_colon() -> None: 

505 """Warn when missing colon.""" 

506 

507 def f(x: int): # noqa: ANN202 

508 """ 

509 Parameters: 

510 x is an integer. 

511 """ # noqa: D205, D212 

512 return x 

513 

514 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

515 assert not sections # getting x fails, so the section is empty and discarded 

516 assert len(errors) == 2 

517 assert "pair" in errors[0] 

518 assert "Empty" in errors[1] 

519 

520 

521def test_param_line_without_colon_keyword_only() -> None: 

522 """Warn when missing colon.""" 

523 

524 def f(*, x: int): # noqa: ANN202 

525 """ 

526 Keyword Args: 

527 x is an integer. 

528 """ # noqa: D205, D212 

529 return x 

530 

531 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

532 assert not sections # getting x fails, so the section is empty and discarded 

533 assert len(errors) == 2 

534 assert "pair" in errors[0] 

535 assert "Empty" in errors[1] 

536 

537 

538def test_admonitions() -> None: 

539 """Parse admonitions.""" 

540 

541 def f() -> None: 

542 """Note: 

543 Hello. 

544 

545 Note: With title. 

546 Hello again. 

547 

548 Something: 

549 Something. 

550 """ # noqa: D205 

551 

552 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

553 assert len(sections) == 1 

554 assert not errors 

555 

556 

557def test_invalid_sections() -> None: 

558 """Warn on invalid (empty) sections.""" 

559 

560 def f() -> None: 

561 """Parameters: 

562 Exceptions: 

563 Exceptions: 

564 

565 Returns: 

566 Note: 

567 

568 Important: 

569 """ # noqa: D205, D415, D414 

570 

571 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

572 assert len(sections) == 1 

573 for error in errors[:3]: 

574 assert "Empty" in error 

575 assert "Empty return section at line" in errors[3] 

576 assert "Empty" in errors[-1] 

577 

578 

579def test_multiple_lines_in_sections_items() -> None: 

580 """Parse multi-line item description.""" 

581 

582 def f(p: str, q: str): # noqa: ANN202 

583 """Hi. 

584 

585 Arguments: 

586 p: This argument 

587 has a description 

588 spawning on multiple lines. 

589 

590 It even has blank lines in it. 

591 Some of these lines 

592 are indented for no reason. 

593 q: 

594 What if the first line is blank? 

595 """ 

596 return p + q 

597 

598 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

599 assert len(sections) == 2 

600 assert len(sections[1].value) == 2 

601 assert errors 

602 for error in errors: 

603 assert "should be 4 * 2 = 8 spaces, not" in error 

604 

605 

606def test_parse_args_kwargs() -> None: 

607 """Parse args and kwargs.""" 

608 

609 def f(a, *args, **kwargs): # noqa: ANN202, ANN001, ANN002, ARG001, ANN003 

610 """ 

611 Arguments: 

612 a: a parameter. 

613 *args: args parameters. 

614 **kwargs: kwargs parameters. 

615 """ # noqa: D205, D212 

616 return 1 

617 

618 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

619 assert len(sections) == 1 

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

621 for param in sections[0].value: 

622 assert param.name in expected_parameters 

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

624 assert not errors 

625 

626 

627def test_parse_args_kwargs_keyword_only() -> None: 

628 """Parse args and kwargs.""" 

629 

630 def f(a, *args, **kwargs) -> int: # noqa: ANN002, ANN001, ARG001, ANN003 

631 """Arguments: 

632 a: a parameter. 

633 *args: args parameters. 

634 

635 Keyword Args: 

636 **kwargs: kwargs parameters. 

637 """ # noqa: D205 

638 return 1 

639 

640 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

641 assert len(sections) == 2 

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

643 for param in sections[0].value: 

644 assert param.name in expected_parameters 

645 assert expected_parameters[param.name] == param.description 

646 

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

648 for param in sections[1].value: 

649 assert param.name in expected_parameters 

650 assert expected_parameters[param.name] == param.description 

651 

652 assert not errors 

653 

654 

655def test_different_indentation() -> None: 

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

657 

658 def f() -> None: 

659 """Hello. 

660 

661 Raises: 

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

663 Well indented continuation line. 

664 Badly indented continuation line (will trigger an error). 

665 

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

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

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

669 """ 

670 

671 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

672 assert len(sections) == 3 

673 assert len(sections[1].value) == 2 

674 assert sections[1].value[0].description == ( 

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

676 "Well indented continuation line.\n" 

677 "Badly indented continuation line (will trigger an error).\n" 

678 "\n" 

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

680 ) 

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

682 assert len(errors) == 1 

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

684 

685 

686def test_parse_module_attributes_section() -> None: 

687 """Parse attributes section in modules.""" 

688 loader = Loader() 

689 obj = loader.get_object_documentation("tests.fixtures.docstring_attributes_section") 

690 assert len(obj.docstring_sections) == 2 

691 assert not obj.docstring_errors 

692 attr_section = obj.docstring_sections[1] 

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

694 assert len(attr_section.value) == 5 

695 expected = [ 

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

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

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

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

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

701 ] 

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

703 

704 

705def test_docstring_with_yield_section() -> None: 

706 """Parse Yields section.""" 

707 

708 def f(): # noqa: ANN202 

709 """A useless range wrapper. 

710 

711 Yields: 

712 int: Integers. 

713 """ 

714 yield from range(10) 

715 

716 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

717 assert len(sections) == 2 

718 annotated = sections[1].value 

719 assert annotated.annotation == "int" 

720 assert annotated.description == "Integers." 

721 assert not errors 

722 

723 

724def test_docstring_with_yield_section_and_return_annotation() -> None: 

725 """Parse Yields section.""" 

726 

727 def f() -> Iterator[int]: 

728 """A useless range wrapper. 

729 

730 Yields: 

731 Integers. 

732 """ 

733 yield from range(10) 

734 

735 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

736 assert len(sections) == 2 

737 annotated = sections[1].value 

738 assert annotated.annotation == Iterator[int] 

739 assert annotated.description == "Integers." 

740 assert not errors 

741 

742 

743def test_keyword_args_no_type() -> None: 

744 """Parse types for keyword arguments.""" 

745 

746 def f(**kwargs) -> None: # noqa: ANN003 

747 """Do nothing. 

748 

749 Keyword Arguments: 

750 a: No type. 

751 """ 

752 

753 sections, errors = parse(inspect.getdoc(f), inspect.signature(f)) # type: ignore[arg-type] 

754 assert len(sections) == 2 

755 kwargs = sections[1].value 

756 assert kwargs[0].name == "a" 

757 assert kwargs[0].annotation is inspect.Parameter.empty 

758 assert kwargs[0].description == "No type." 

759 assert kwargs[0].kind is inspect.Parameter.KEYWORD_ONLY 

760 assert kwargs[0].default is inspect.Parameter.empty 

761 assert len(errors) == 1 

762 assert "No type annotation for parameter" in errors[0] 

763 

764 

765def test_keyword_args_type() -> None: 

766 """Parse types for keyword arguments.""" 

767 

768 def f(**kwargs) -> None: # noqa: ANN003 

769 """Do nothing. 

770 

771 Keyword Arguments: 

772 a (int): Typed. 

773 """ 

774 

775 sections, errors = parse(inspect.getdoc(f) or "", inspect.signature(f)) 

776 assert len(sections) == 2 

777 kwargs = sections[1].value 

778 assert kwargs[0].name == "a" 

779 assert kwargs[0].annotation == "int" 

780 assert kwargs[0].description == "Typed." 

781 assert kwargs[0].kind is inspect.Parameter.KEYWORD_ONLY 

782 assert kwargs[0].default is inspect.Parameter.empty 

783 assert not errors