2727import shlex
2828import sys
2929from collections .abc import Iterator
30+ from functools import cache
3031from pathlib import Path
3132from typing import overload
3233
4142CONVERSION_ARGUMENTS = REPO_ROOT / "scripts" / "conversion" / "arguments.py"
4243SETUP_CONVERSION = REPO_ROOT / "scripts" / "conversion" / "setup_conversion.py"
4344SETUP_INFERENCE = REPO_ROOT / "scripts" / "inference" / "setup_inference.py"
45+ PublishedCommands = tuple [tuple [str , tuple [str , ...]], ...]
4446
4547
46- def _declared_options (source_path : Path , * , append_only : bool = False , function : str | None = None ) -> set [str ]:
48+ @cache
49+ def _declared_options (source_path : Path , * , append_only : bool = False , function : str | None = None ) -> frozenset [str ]:
4750 """Read parser options, including called argument helpers in Bridge source."""
4851 options : set [str ] = set ()
4952 tree = ast .parse (source_path .read_text (encoding = "utf-8" ))
@@ -77,7 +80,15 @@ def _declared_options(source_path: Path, *, append_only: bool = False, function:
7780 if isinstance (argument , ast .Constant ) and isinstance (argument .value , str ):
7881 if argument .value .startswith ("-" ):
7982 options .add (argument .value )
80- return options
83+ return frozenset (options )
84+
85+
86+ @pytest .fixture (autouse = True )
87+ def _clear_declared_options_cache () -> Iterator [None ]:
88+ # Reuse parser results within a check, but never across tests that edit source.
89+ _declared_options .cache_clear ()
90+ yield
91+ _declared_options .cache_clear ()
8192
8293
8394def _inference_tasks () -> dict [str , Path ]:
@@ -128,15 +139,21 @@ def _published_commands() -> Iterator[tuple[str, list[str]]]:
128139 yield f"{ card .parent .name } { location } " , tokens
129140
130141
142+ @pytest .fixture (scope = "module" )
143+ def published_commands () -> PublishedCommands :
144+ """Parse every shipped command once, with immutable argv shared by the checks."""
145+ return tuple ((leaf , tuple (tokens )) for leaf , tokens in _published_commands ())
146+
147+
131148@overload
132- def _flag_value (tokens : list [str ], flag : str , default : int ) -> int : ...
149+ def _flag_value (tokens : tuple [str , ... ], flag : str , default : int ) -> int : ...
133150
134151
135152@overload
136- def _flag_value (tokens : list [str ], flag : str , default : None ) -> int | None : ...
153+ def _flag_value (tokens : tuple [str , ... ], flag : str , default : None ) -> int | None : ...
137154
138155
139- def _flag_value (tokens : list [str ], flag : str , default : int | None ) -> int | None :
156+ def _flag_value (tokens : tuple [str , ... ], flag : str , default : int | None ) -> int | None :
140157 """Return the last integer value passed to a flag, or the launcher default."""
141158 value = default
142159 for index , token in enumerate (tokens [:- 1 ]):
@@ -145,17 +162,19 @@ def _flag_value(tokens: list[str], flag: str, default: int | None) -> int | None
145162 return value
146163
147164
148- def _selected_task (tokens : list [str ]) -> str :
165+ def _selected_task (tokens : tuple [str , ... ]) -> str :
149166 task = "text-generation"
150167 for index , token in enumerate (tokens [:- 1 ]):
151168 if token == "--task" :
152169 task = tokens [index + 1 ]
153170 return task
154171
155172
156- def test_published_gpu_conversion_commands_decompose_over_the_published_world_size () -> None :
173+ def test_published_gpu_conversion_commands_decompose_over_the_published_world_size (
174+ published_commands : PublishedCommands ,
175+ ) -> None :
157176 offenders = []
158- for leaf , tokens in _published_commands () :
177+ for leaf , tokens in published_commands :
159178 if "convert.sh" not in tokens [0 ]:
160179 continue
161180 device = "gpu"
@@ -178,12 +197,12 @@ def test_published_gpu_conversion_commands_decompose_over_the_published_world_si
178197 assert not offenders , "setup_conversion.py refuses these published commands: " + "; " .join (offenders )
179198
180199
181- def test_published_inference_commands_carry_no_conversion_launcher_flag () -> None :
200+ def test_published_inference_commands_carry_no_conversion_launcher_flag (published_commands : PublishedCommands ) -> None :
182201 launcher_options = _declared_options (SETUP_INFERENCE )
183202 conversion_options = _declared_options (CONVERSION_ARGUMENTS ) | _declared_options (SETUP_CONVERSION )
184203 tasks = _inference_tasks ()
185204 offenders = []
186- for leaf , tokens in _published_commands () :
205+ for leaf , tokens in published_commands :
187206 if "infer.sh" not in tokens [0 ]:
188207 continue
189208 accepted = launcher_options | _declared_options (tasks [_selected_task (tokens )])
@@ -196,12 +215,12 @@ def test_published_inference_commands_carry_no_conversion_launcher_flag() -> Non
196215 assert not offenders , "these published inference commands die in the container: " + "; " .join (offenders )
197216
198217
199- def test_published_card_commands_do_not_repeat_a_flag () -> None :
218+ def test_published_card_commands_do_not_repeat_a_flag (published_commands : PublishedCommands ) -> None :
200219 repeatable = _declared_options (CONVERSION_ARGUMENTS , append_only = True ) | _declared_options (
201220 SETUP_INFERENCE , append_only = True
202221 )
203222 offenders = []
204- for leaf , tokens in _published_commands () :
223+ for leaf , tokens in published_commands :
205224 flags = [token .split ("=" , 1 )[0 ] for token in tokens [1 :] if token .startswith ("--" )]
206225 repeated = sorted ({flag for flag in flags if flags .count (flag ) > 1 and flag not in repeatable })
207226 if repeated :
@@ -252,10 +271,7 @@ def test_declared_options_follow_only_called_helpers(tmp_path: Path, monkeypatch
252271 ),
253272 ],
254273)
255- def test_published_command_checks_reject_original_errors (
256- command : str , check_name : str , message : str , monkeypatch : pytest .MonkeyPatch
257- ) -> None :
274+ def test_published_command_checks_reject_original_errors (command : str , check_name : str , message : str ) -> None :
258275 module = sys .modules [__name__ ]
259- monkeypatch .setattr (module , "_published_commands" , lambda : iter ([("regression" , shlex .split (command ))]))
260276 with pytest .raises (AssertionError , match = message ):
261- getattr (module , check_name )()
277+ getattr (module , check_name )((( "regression" , tuple ( shlex . split ( command ))),) )
0 commit comments