12
12
from abc import abstractmethod
13
13
from dataclasses import dataclass
14
14
from pathlib import Path
15
- from typing import Any , Iterator , NamedTuple , Pattern , Union
15
+ from typing import Any , Iterator , NamedTuple , NoReturn , Pattern , Union
16
16
from typing_extensions import Final , TypeAlias as _TypeAlias
17
17
18
18
import pytest
@@ -77,11 +77,19 @@ def parse_test_case(case: DataDrivenTestCase) -> None:
77
77
targets : dict [int , list [str ]] = {} # Fine-grained targets (per fine-grained update)
78
78
test_modules : list [str ] = [] # Modules which are deemed "test" (vs "fixture")
79
79
80
+ def _case_fail (msg : str ) -> NoReturn :
81
+ pytest .fail (f"{ case .file } :{ case .line } : { msg } " , pytrace = False )
82
+
80
83
# Process the parsed items. Each item has a header of form [id args],
81
84
# optionally followed by lines of text.
82
85
item = first_item = test_items [0 ]
83
86
test_modules .append ("__main__" )
84
87
for item in test_items [1 :]:
88
+
89
+ def _item_fail (msg : str ) -> NoReturn :
90
+ item_abs_line = case .line + item .line - 2
91
+ pytest .fail (f"{ case .file } :{ item_abs_line } : { msg } " , pytrace = False )
92
+
85
93
if item .id in {"file" , "fixture" , "outfile" , "outfile-re" }:
86
94
# Record an extra file needed for the test case.
87
95
assert item .arg is not None
@@ -132,9 +140,11 @@ def parse_test_case(case: DataDrivenTestCase) -> None:
132
140
# File/directory to delete during a multi-step test case
133
141
assert item .arg is not None
134
142
m = re .match (r"(.*)\.([0-9]+)$" , item .arg )
135
- assert m , f"Invalid delete section: { item .arg } "
143
+ if m is None :
144
+ _item_fail (f"Invalid delete section { item .arg !r} " )
136
145
num = int (m .group (2 ))
137
- assert num >= 2 , f"Can't delete during step { num } "
146
+ if num < 2 :
147
+ _item_fail (f"Can't delete during step { num } " )
138
148
full = join (base_path , m .group (1 ))
139
149
deleted_paths .setdefault (num , set ()).add (full )
140
150
elif re .match (r"out[0-9]*$" , item .id ):
@@ -150,29 +160,18 @@ def parse_test_case(case: DataDrivenTestCase) -> None:
150
160
if arg .startswith ("version" ):
151
161
compare_op = arg [7 :9 ]
152
162
if compare_op not in {">=" , "==" }:
153
- raise ValueError (
154
- "{}, line {}: Only >= and == version checks are currently supported" .format (
155
- case .file , item .line
156
- )
157
- )
163
+ _item_fail ("Only >= and == version checks are currently supported" )
158
164
version_str = arg [9 :]
159
165
try :
160
166
version = tuple (int (x ) for x in version_str .split ("." ))
161
167
except ValueError :
162
- raise ValueError (
163
- '{}, line {}: "{}" is not a valid python version' .format (
164
- case .file , item .line , version_str
165
- )
166
- )
168
+ _item_fail (f"{ version_str !r} is not a valid python version" )
167
169
if compare_op == ">=" :
168
170
version_check = sys .version_info >= version
169
171
elif compare_op == "==" :
170
172
if not 1 < len (version ) < 4 :
171
- raise ValueError (
172
- "{}, line {}: Only minor or patch version checks "
173
- 'are currently supported with "==": "{}"' .format (
174
- case .file , item .line , version_str
175
- )
173
+ _item_fail (
174
+ f'Only minor or patch version checks are currently supported with "==": { version_str !r} '
176
175
)
177
176
version_check = sys .version_info [: len (version )] == version
178
177
if version_check :
@@ -189,15 +188,11 @@ def parse_test_case(case: DataDrivenTestCase) -> None:
189
188
elif item .id == "triggered" and item .arg is None :
190
189
triggered = item .data
191
190
else :
192
- section = item .id + (f" { item .arg } " if item .arg else "" )
193
- raise ValueError (
194
- f"{ case .file } :{ case .line + item .line - 2 } : Invalid section header [{ section } ] in case { case .name !r} "
195
- )
191
+ section_str = item .id + (f" { item .arg } " if item .arg else "" )
192
+ _item_fail (f"Invalid section header [{ section_str } ] in case { case .name !r} " )
196
193
197
194
if out_section_missing :
198
- raise ValueError (
199
- f"{ case .file } :{ case .line } : Required output section not found in case { case .name !r} "
200
- )
195
+ _case_fail (f"Required output section not found in case { case .name !r} " )
201
196
202
197
for passnum in stale_modules .keys ():
203
198
if passnum not in rechecked_modules :
@@ -209,11 +204,7 @@ def parse_test_case(case: DataDrivenTestCase) -> None:
209
204
and passnum in rechecked_modules
210
205
and not stale_modules [passnum ].issubset (rechecked_modules [passnum ])
211
206
):
212
- raise ValueError (
213
- (
214
- "Stale modules after pass {} must be a subset of rechecked modules ({}:{})"
215
- ).format (passnum , case .file , first_item .line )
216
- )
207
+ _case_fail (f"Stale modules after pass { passnum } must be a subset of rechecked modules" )
217
208
218
209
output_inline_start = len (output )
219
210
input = first_item .data
@@ -224,10 +215,7 @@ def parse_test_case(case: DataDrivenTestCase) -> None:
224
215
seen_files = set ()
225
216
for file , _ in files :
226
217
if file in seen_files :
227
- raise ValueError (
228
- f"{ case .file } :{ case .line } : Duplicated filename { file } . Did you include"
229
- " it multiple times?"
230
- )
218
+ _case_fail (f"Duplicated filename { file } . Did you include it multiple times?" )
231
219
232
220
seen_files .add (file )
233
221
@@ -372,12 +360,13 @@ def setup(self) -> None:
372
360
self .steps = [steps .get (num , []) for num in range (2 , max_step + 1 )]
373
361
374
362
def teardown (self ) -> None :
375
- assert self .old_cwd is not None and self .tmpdir is not None , "test was not properly set up"
376
- os .chdir (self .old_cwd )
377
- try :
378
- self .tmpdir .cleanup ()
379
- except OSError :
380
- pass
363
+ if self .old_cwd is not None :
364
+ os .chdir (self .old_cwd )
365
+ if self .tmpdir is not None :
366
+ try :
367
+ self .tmpdir .cleanup ()
368
+ except OSError :
369
+ pass
381
370
self .old_cwd = None
382
371
self .tmpdir = None
383
372
@@ -666,7 +655,7 @@ def split_test_cases(
666
655
for case_id in cases_iter :
667
656
data = next (cases_iter )
668
657
669
- m = _case_name_pattern .match (case_id )
658
+ m = _case_name_pattern .fullmatch (case_id )
670
659
if not m :
671
660
raise RuntimeError (f"Invalid testcase id { case_id !r} " )
672
661
name = m .group ("name" )
0 commit comments