1
1
"""A linter that checks test docstrings for the arrange/act/assert structure."""
2
2
3
+ from __future__ import annotations
4
+
3
5
import argparse
4
6
import ast
5
7
import re
8
+ import sys
6
9
from functools import wraps
7
10
from pathlib import Path
8
- from typing import Callable , Iterable , List , NamedTuple , Optional , Tuple , Type
11
+ from typing import Callable , Iterator , NamedTuple
12
+
13
+ # Can't cover both paths of a conditional import
14
+ if sys .version_info < (3 , 10 ):
15
+ from typing_extensions import ParamSpec # pragma: nocover
16
+ else :
17
+ from typing import ParamSpec # pragma: nocover
9
18
10
19
from flake8 .options .manager import OptionManager
11
20
@@ -71,12 +80,15 @@ class Section(NamedTuple):
71
80
index_ : int
72
81
name : str
73
82
description : str
74
- next_section_name : Optional [str ]
83
+ next_section_name : str | None
84
+
85
+
86
+ AIMPPParamSpec = ParamSpec ("AIMPPParamSpec" )
75
87
76
88
77
89
def _append_invalid_msg_prefix_postfix (
78
- func : Callable [..., Optional [ str ] ]
79
- ) -> Callable [..., Optional [ str ] ]:
90
+ func : Callable [AIMPPParamSpec , str | None ]
91
+ ) -> Callable [AIMPPParamSpec , str | None ]:
80
92
"""Add the code prefix and invalid message postfix to the return value.
81
93
82
94
Args:
@@ -87,7 +99,7 @@ def _append_invalid_msg_prefix_postfix(
87
99
"""
88
100
89
101
@wraps (func )
90
- def wrapper (* args , ** kwargs ) :
102
+ def wrapper (* args : AIMPPParamSpec . args , ** kwargs : AIMPPParamSpec . kwargs ) -> str | None :
91
103
"""Wrap the function."""
92
104
if (return_value := func (* args , ** kwargs )) is None :
93
105
return None
@@ -98,7 +110,7 @@ def wrapper(*args, **kwargs):
98
110
99
111
def _section_start_problem_message (
100
112
line : str , section : Section , col_offset : int , section_prefix : str
101
- ) -> Optional [ str ] :
113
+ ) -> str | None :
102
114
"""Check the first line of a section.
103
115
104
116
Args:
@@ -136,7 +148,7 @@ def _section_start_problem_message(
136
148
return None
137
149
138
150
139
- def _next_section_start (line : str , next_section_name : Optional [ str ] , section_prefix : str ) -> bool :
151
+ def _next_section_start (line : str , next_section_name : str | None , section_prefix : str ) -> bool :
140
152
"""Detect whether the line is the start of the next section.
141
153
142
154
The next section is defined to be either that the line starts with the next section name after
@@ -165,11 +177,11 @@ def _next_section_start(line: str, next_section_name: Optional[str], section_pre
165
177
166
178
def _remaining_description_problem_message (
167
179
section : Section ,
168
- docstring_lines : List [str ],
180
+ docstring_lines : list [str ],
169
181
section_prefix : str ,
170
182
description_prefix : str ,
171
183
indent_size : int ,
172
- ) -> Tuple [ Optional [ str ] , int ]:
184
+ ) -> tuple [ str | None , int ]:
173
185
"""Check the remaining description of a section after the first line.
174
186
175
187
Args:
@@ -213,7 +225,7 @@ def _remaining_description_problem_message(
213
225
@_append_invalid_msg_prefix_postfix
214
226
def _docstring_problem_message (
215
227
docstring : str , col_offset : int , docs_pattern : DocsPattern , indent_size : int
216
- ) -> Optional [ str ] :
228
+ ) -> str | None :
217
229
"""Get the problem message for a docstring.
218
230
219
231
Args:
@@ -296,7 +308,7 @@ class Visitor(ast.NodeVisitor):
296
308
problems: All the problems that were encountered.
297
309
"""
298
310
299
- problems : List [Problem ]
311
+ problems : list [Problem ]
300
312
_test_docs_pattern : DocsPattern
301
313
_test_function_pattern : str
302
314
_indent_size : int
@@ -456,7 +468,7 @@ def parse_options(cls, options: argparse.Namespace) -> None: # pragma: nocover
456
468
getattr (options , _cli_arg_name_to_attr (INDENT_SIZE_ARN_NAME ), None ) or cls ._indent_size
457
469
)
458
470
459
- def run (self ) -> Iterable [ Tuple [int , int , str , Type ["Plugin" ]]]:
471
+ def run (self ) -> Iterator [ tuple [int , int , str , type ["Plugin" ]]]:
460
472
"""Lint a file.
461
473
462
474
Yields:
0 commit comments