diff --git a/scripts/version_scanner/tests/unit/test_version_scanner.py b/scripts/version_scanner/tests/unit/test_version_scanner.py index 16ed7e00bd9e..d35028b20137 100644 --- a/scripts/version_scanner/tests/unit/test_version_scanner.py +++ b/scripts/version_scanner/tests/unit/test_version_scanner.py @@ -148,6 +148,38 @@ def test_scan_file_ignores_pragma(tmp_path): results = scan_file(str(test_file), rules) assert len(results) == 0 + +def test_scan_file_ignores_targeted_pragma(tmp_path): + test_file = tmp_path / "test.py" + test_file.write_text("python_requires = '>=3.7' # version-scanner: ignore-rule=python_requires_check:3.7\n") + + rules = [ + { + "name": "python_requires_check", + "pattern": re.compile(r"python_requires\s*=\s*['\"]>=3\.7['\"]"), + "version": "3.7" + } + ] + + results = scan_file(str(test_file), rules) + assert len(results) == 0 + + +def test_scan_file_does_not_ignore_mismatched_targeted_pragma(tmp_path): + test_file = tmp_path / "test.py" + test_file.write_text("python_requires = '>=3.7' # version-scanner: ignore-rule=python_requires_check:3.8\n") + + rules = [ + { + "name": "python_requires_check", + "pattern": re.compile(r"python_requires\s*=\s*['\"]>=3\.7['\"]"), + "version": "3.7" + } + ] + + results = scan_file(str(test_file), rules) + assert len(results) == 1 + def test_scan_file_ignores_next_line(tmp_path): test_file = tmp_path / "test.py" test_file.write_text("# version-scanner: ignore-next-line\npython_requires = '>=3.7'\n") diff --git a/scripts/version_scanner/version_scanner.py b/scripts/version_scanner/version_scanner.py index 4cfafba9d6df..dab9ed055207 100644 --- a/scripts/version_scanner/version_scanner.py +++ b/scripts/version_scanner/version_scanner.py @@ -214,11 +214,16 @@ def scan_file(file_path: str, compiled_rules: List[Dict[str, re.Pattern]]) -> Li if "version-scanner: ignore-next-line" in line: skip_next = True continue - if "version-scanner: ignore" in line: + if "version-scanner: ignore" in line and "version-scanner: ignore-rule" not in line and "version-scanner: ignore-next-line" not in line: continue for rule in compiled_rules: match = rule["pattern"].search(line) if match: + version = rule.get("version") + if version: + pragma_pattern = rf"version-scanner\s*:\s*ignore-rule\s*=\s*{re.escape(rule['name'])}\s*:\s*{re.escape(version)}" + if re.search(pragma_pattern, line, re.IGNORECASE): + continue results.append({ "rule_name": rule["name"], "line_number": line_num,