Skip to content

Commit 00d409d

Browse files
committed
refactor(version-scanner): consolidate file reading and error handling under _safe_read_file
1 parent 3c0b8fe commit 00d409d

1 file changed

Lines changed: 66 additions & 43 deletions

File tree

scripts/version_scanner/version_scanner.py

Lines changed: 66 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,57 @@
2323
import os
2424
import re
2525
import sys
26-
from typing import Dict, List, Tuple, Any
26+
from typing import Dict, List, Tuple, Any, Optional
2727
import yaml
2828

29+
30+
def _safe_read_file(
31+
file_path: str,
32+
required: bool = True,
33+
description: str = "file",
34+
silent_missing: bool = False
35+
) -> Optional[str]:
36+
"""
37+
Safely reads file content and handles common file errors.
38+
39+
Args:
40+
file_path: Path to the file.
41+
required: If True, exits the program with code 1 on read failure.
42+
If False, prints a warning (or ignores) and returns None.
43+
description: Description of the file type for error logging.
44+
silent_missing: If True, silently ignores FileNotFoundError (returns None).
45+
46+
Returns:
47+
The file content string, or None if reading failed/was ignored.
48+
"""
49+
try:
50+
with open(file_path, 'r', encoding='utf-8') as f:
51+
return f.read()
52+
except FileNotFoundError:
53+
if silent_missing:
54+
return None
55+
if required:
56+
print(f"Error: {description.capitalize()} not found: {file_path}", file=sys.stderr)
57+
sys.exit(1)
58+
else:
59+
print(f"Warning: {description.capitalize()} not found: {file_path}", file=sys.stderr)
60+
return None
61+
except PermissionError:
62+
if required:
63+
print(f"Error: Permission denied reading {description}: {file_path}", file=sys.stderr)
64+
sys.exit(1)
65+
else:
66+
print(f"Warning: Permission denied reading {description}: {file_path}", file=sys.stderr)
67+
return None
68+
except IOError as e:
69+
if required:
70+
print(f"Error reading {description} {file_path}: {e}", file=sys.stderr)
71+
sys.exit(1)
72+
else:
73+
print(f"Warning: Error reading {description} {file_path}: {e}", file=sys.stderr)
74+
return None
75+
76+
2977
class ConfigManager:
3078
"""
3179
Handles loading, validation, and interpolation of the regex configuration rules.
@@ -106,15 +154,9 @@ def _compute_variables(self) -> Dict[str, str]:
106154

107155
def load_config(self) -> List[Dict[str, str]]:
108156
"""Load and resolve rules from config."""
157+
content = _safe_read_file(self.config_path, required=True, description="config file")
109158
try:
110-
with open(self.config_path, 'r', encoding='utf-8') as f:
111-
config = yaml.safe_load(f)
112-
except FileNotFoundError:
113-
print(f"Error: Config file not found: {self.config_path}", file=sys.stderr)
114-
sys.exit(1)
115-
except PermissionError:
116-
print(f"Error: Permission denied reading config file: {self.config_path}", file=sys.stderr)
117-
sys.exit(1)
159+
config = yaml.safe_load(content)
118160
except yaml.YAMLError as e:
119161
print(f"Error parsing config file: {e}", file=sys.stderr)
120162
sys.exit(1)
@@ -330,14 +372,12 @@ def load_ignore_file(file_path: str) -> List[str]:
330372
Read ignore paths from a file.
331373
"""
332374
ignore_dirs = []
333-
try:
334-
with open(file_path, 'r', encoding='utf-8') as f:
335-
for line in f:
336-
line = line.strip()
337-
if line and not line.startswith('#'):
338-
ignore_dirs.append(line)
339-
except FileNotFoundError:
340-
pass
375+
content = _safe_read_file(file_path, required=False, silent_missing=True)
376+
if content:
377+
for line in content.splitlines():
378+
line = line.strip()
379+
if line and not line.startswith('#'):
380+
ignore_dirs.append(line)
341381
return ignore_dirs
342382

343383

@@ -449,21 +489,12 @@ def read_package_file(file_path: str) -> List[str]:
449489
A list of package paths.
450490
"""
451491
packages = []
452-
try:
453-
with open(file_path, 'r', encoding='utf-8') as f:
454-
for line in f:
455-
line = line.strip()
456-
if line and not line.startswith('#'):
457-
packages.append(line)
458-
except FileNotFoundError:
459-
print(f"Error: Package file not found: {file_path}", file=sys.stderr)
460-
sys.exit(1)
461-
except PermissionError:
462-
print(f"Error: Permission denied reading package file: {file_path}", file=sys.stderr)
463-
sys.exit(1)
464-
except IOError as e:
465-
print(f"Error reading package file: {e}", file=sys.stderr)
466-
sys.exit(1)
492+
content = _safe_read_file(file_path, required=True, description="package file")
493+
if content:
494+
for line in content.splitlines():
495+
line = line.strip()
496+
if line and not line.startswith('#'):
497+
packages.append(line)
467498
return packages
468499

469500

@@ -597,18 +628,11 @@ def parse_targets_file(file_path: str) -> List[Tuple[str, str]]:
597628
"""
598629
Parses a YAML targets file into a list of (dependency, version) tuples.
599630
"""
600-
if not os.path.exists(file_path):
601-
print(f"Error: Targets file not found: {file_path}", file=sys.stderr)
602-
sys.exit(1)
603-
631+
content = _safe_read_file(file_path, required=True, description="targets file")
604632
try:
605-
with open(file_path, 'r', encoding='utf-8') as f:
606-
raw_targets = yaml.safe_load(f)
607-
except PermissionError:
608-
print(f"Error: Permission denied reading targets file: {file_path}", file=sys.stderr)
609-
sys.exit(1)
633+
raw_targets = yaml.safe_load(content)
610634
except Exception as e:
611-
print(f"Error reading or parsing targets file {file_path}: {e}", file=sys.stderr)
635+
print(f"Error parsing targets YAML mapping: {e}", file=sys.stderr)
612636
sys.exit(1)
613637

614638
if not isinstance(raw_targets, dict):
@@ -632,7 +656,6 @@ def parse_targets_file(file_path: str) -> List[Tuple[str, str]]:
632656
return targets
633657

634658

635-
636659
def main():
637660
script_dir = os.path.dirname(os.path.abspath(__file__))
638661
default_config = os.path.join(script_dir, "regex_config.yaml")

0 commit comments

Comments
 (0)