Skip to content

Commit 1a249e0

Browse files
committed
fix: resolve all SonarQube issues and security hotspots
1 parent f5b11cd commit 1a249e0

10 files changed

Lines changed: 120 additions & 82 deletions

File tree

scripts/migrate_tests_to_yaml.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,8 @@ def extract_dict_value(node: ast.expr) -> dict[str, str]:
4949

5050

5151
def _set_option(case: dict, key: str, value: ast.expr) -> None:
52-
if isinstance(value, ast.Constant):
53-
if value.value is not None:
54-
case.setdefault("options", {})[key] = value.value
52+
if isinstance(value, ast.Constant) and value.value is not None:
53+
case.setdefault("options", {})[key] = value.value
5554

5655

5756
def _process_keyword(case: dict, kw: ast.keyword) -> None:

src/treemapper/cli.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ def _validate_budget(budget: int | None) -> None:
3434

3535

3636
def _validate_alpha(alpha: float) -> None:
37-
is_valid = 0 < alpha < 1
38-
if not is_valid:
37+
if not (0 < alpha < 1):
3938
_exit_error(f"--alpha must be between 0 and 1 (exclusive), got {alpha}")
4039

4140

src/treemapper/diffctx/config/patterns.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636
"java_class": re.compile(r"^\s*(?:public\s+)?(?:abstract\s+)?(?:final\s+)?class\s+([A-Z]\w*)", re.MULTILINE),
3737
"java_interface": re.compile(r"^\s*(?:public\s+)?interface\s+([A-Z]\w*)", re.MULTILINE),
3838
"java_enum": re.compile(r"^\s*(?:public\s+)?enum\s+([A-Z]\w*)", re.MULTILINE),
39-
"java_method": re.compile(r"^\s*(?:public|private|protected)?\s*(?:static\s+)?[\w<>\[\]]+\s+([a-z]\w*)\s*\(", re.MULTILINE),
39+
"java_method": re.compile(
40+
r"^\s{0,20}(?:(?:public|private|protected)\s+)?(?:static\s+)?[\w<>\[\]]{1,100}\s+([a-z]\w*)\s*\(", re.MULTILINE
41+
),
4042
"csharp_class": re.compile(r"^\s*(?:public\s+)?(?:partial\s+)?(?:abstract\s+)?class\s+([A-Z]\w*)", re.MULTILINE),
4143
"csharp_interface": re.compile(r"^\s*(?:public\s+)?interface\s+(I[A-Z]\w*)", re.MULTILINE),
4244
"c_function": re.compile(r"^(?:static\s+)?(?:\w+\s+)+(\w+)\s*\([^)]*\)\s*\{", re.MULTILINE),
@@ -68,5 +70,5 @@
6870
DOC_PATTERNS = {
6971
"citation": re.compile(r"\[@([a-zA-Z0-9_:-]+)\]"),
7072
"md_internal_link": re.compile(r"\[([^\]]+)\]\(#([^)]+)\)"),
71-
"md_heading": re.compile(r"^#{1,6}\s+([^\n]+)$", re.MULTILINE),
73+
"md_heading": re.compile(r"^#{1,6}\s+([^\n]{1,1000})$", re.MULTILINE),
7274
}

src/treemapper/diffctx/edges/base.py

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,26 @@ def __init__(self, fragments: list[Fragment], repo_root: Path | None = None):
6565
_MIN_REF_LENGTH_FOR_PATH_MATCH = 3
6666

6767

68+
def _candidate_rel_path(candidate: Path, repo_root: Path | None) -> str:
69+
if not repo_root:
70+
return candidate.name.lower()
71+
try:
72+
return str(candidate.relative_to(repo_root)).lower()
73+
except ValueError:
74+
return candidate.name.lower()
75+
76+
77+
def _matches_any_ref(candidate_name: str, candidate_rel: str, refs: set[str]) -> bool:
78+
for ref in refs:
79+
ref_name = ref.split("/")[-1].lower()
80+
if candidate_name == ref_name:
81+
return True
82+
ref_lower = ref.lower()
83+
if len(ref_lower) >= _MIN_REF_LENGTH_FOR_PATH_MATCH and ref_lower in candidate_rel:
84+
return True
85+
return False
86+
87+
6888
def discover_files_by_refs(
6989
refs: set[str],
7090
changed_files: list[Path],
@@ -81,24 +101,9 @@ def discover_files_by_refs(
81101
if candidate in changed_set:
82102
continue
83103
candidate_name = candidate.name.lower()
84-
85-
if repo_root:
86-
try:
87-
candidate_rel = str(candidate.relative_to(repo_root)).lower()
88-
except ValueError:
89-
candidate_rel = candidate_name
90-
else:
91-
candidate_rel = candidate_name
92-
93-
for ref in refs:
94-
ref_lower = ref.lower()
95-
ref_name = ref.split("/")[-1].lower()
96-
if candidate_name == ref_name:
97-
discovered.append(candidate)
98-
break
99-
if len(ref_lower) >= _MIN_REF_LENGTH_FOR_PATH_MATCH and ref_lower in candidate_rel:
100-
discovered.append(candidate)
101-
break
104+
candidate_rel = _candidate_rel_path(candidate, repo_root)
105+
if _matches_any_ref(candidate_name, candidate_rel, refs):
106+
discovered.append(candidate)
102107

103108
return discovered
104109

src/treemapper/diffctx/edges/config/cicd.py

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,31 @@
2020
_JENKINS_SH_RE = re.compile(r"sh\s*(?:\(['\"]|['\"])(.+?)['\"]\)?", re.MULTILINE | re.DOTALL)
2121
_JENKINS_SCRIPT_RE = re.compile(r"script\s*\{([^}]+)\}", re.MULTILINE | re.DOTALL)
2222

23-
_SCRIPT_CALL_RE = re.compile(
24-
r"(?:bash|sh|python|python3|node|npm|yarn|pnpm|make|go|cargo|dotnet|mvn|gradle|pytest|ruff|mypy|black|isort|flake8)\s+([^\s;&|]+)"
23+
_SCRIPT_CALL_TOOLS = frozenset(
24+
{
25+
"bash",
26+
"sh",
27+
"python",
28+
"python3",
29+
"node",
30+
"npm",
31+
"yarn",
32+
"pnpm",
33+
"make",
34+
"go",
35+
"cargo",
36+
"dotnet",
37+
"mvn",
38+
"gradle",
39+
"pytest",
40+
"ruff",
41+
"mypy",
42+
"black",
43+
"isort",
44+
"flake8",
45+
}
2546
)
47+
_SCRIPT_CALL_RE = re.compile(r"(?:" + "|".join(sorted(_SCRIPT_CALL_TOOLS, key=len, reverse=True)) + r")\s+([^\s;&|]+)")
2648
_PKG_MANAGER_SUBCOMMANDS = frozenset(
2749
{
2850
"run",
@@ -162,6 +184,27 @@ def _extract_jenkins_refs(content: str) -> set[str]:
162184
return refs
163185

164186

187+
def _parse_tox_deps(deps: str, refs: set[str]) -> None:
188+
for line in deps.splitlines():
189+
line = line.strip()
190+
if not line or line.startswith("#") or line.startswith("-r"):
191+
continue
192+
refs.add(line.split()[0] if line.split() else line)
193+
194+
195+
def _parse_tox_commands(commands: str, refs: set[str]) -> None:
196+
for line in commands.splitlines():
197+
line = line.strip()
198+
if not line or line.startswith("#"):
199+
continue
200+
for p in line.split():
201+
p = p.strip("'\"")
202+
p = re.sub(r"\{[^}]+\}", "", p)
203+
if p and not p.startswith("-"):
204+
refs.add(p)
205+
refs.update(_extract_script_refs(line))
206+
207+
165208
def _extract_tox_refs(content: str) -> set[str]:
166209
refs: set[str] = set()
167210
parser = configparser.ConfigParser()
@@ -173,28 +216,8 @@ def _extract_tox_refs(content: str) -> set[str]:
173216
for section in parser.sections():
174217
if not section.lower().startswith("testenv"):
175218
continue
176-
177-
deps = parser.get(section, "deps", fallback="")
178-
commands = parser.get(section, "commands", fallback="")
179-
180-
for line in deps.splitlines():
181-
line = line.strip()
182-
if not line or line.startswith("#") or line.startswith("-r"):
183-
continue
184-
refs.add(line.split()[0] if line.split() else line)
185-
186-
for line in commands.splitlines():
187-
line = line.strip()
188-
if not line or line.startswith("#"):
189-
continue
190-
parts = line.split()
191-
for p in parts:
192-
p = p.strip("'\"")
193-
p = re.sub(r"\{[^}]+\}", "", p)
194-
if not p or p.startswith("-"):
195-
continue
196-
refs.add(p)
197-
refs.update(_extract_script_refs(line))
219+
_parse_tox_deps(parser.get(section, "deps", fallback=""), refs)
220+
_parse_tox_commands(parser.get(section, "commands", fallback=""), refs)
198221

199222
return refs
200223

src/treemapper/diffctx/edges/semantic/dotnet.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313

1414
_CS_USING_RE = re.compile(r"^\s*using\s+(?:static\s+)?([A-Z][a-zA-Z0-9_.]*);", re.MULTILINE)
1515
_CS_NAMESPACE_RE = re.compile(r"^\s*namespace\s+([A-Z][a-zA-Z0-9_.]*)", re.MULTILINE)
16+
_CS_ACCESS = "public|private|protected|internal"
17+
_CS_MODIFIERS = "static|sealed|abstract|partial"
18+
_CS_TYPE_KW = "class|interface|struct|record|enum"
1619
_CS_CLASS_RE = re.compile(
17-
r"^\s{0,20}(?:(?:public|private|protected|internal)\s{1,10})?(?:(?:static|sealed|abstract|partial)\s{1,10})?(?:class|interface|struct|record|enum)\s+([A-Z]\w{0,100})",
20+
rf"^\s{{0,20}}(?:(?:{_CS_ACCESS})\s{{1,10}})?(?:(?:{_CS_MODIFIERS})\s{{1,10}})?(?:{_CS_TYPE_KW})\s+([A-Z]\w{{0,100}})",
1821
re.MULTILINE,
1922
)
2023
_CS_INHERIT_RE = re.compile(r"(?:class|struct|record)\s+\w+[^:\n]{0,200}:\s*([A-Z]\w*(?:,\s*[A-Z]\w*)*)")

src/treemapper/diffctx/edges/semantic/python.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,29 @@
1414
_SYMBOL_REF_WEIGHT = 0.95
1515
_TYPE_REF_WEIGHT = 0.60
1616

17-
_PY_IMPORT_RE = re.compile(r"(?:from\s+(\.{0,3}[\w.]*)\s+import|import\s+([\w.]+))")
17+
_PY_IMPORT_RE = re.compile(r"(?:from\s+(\.{0,3}[\w.]{0,200})\s+import|import\s+([\w.]{1,200}))")
1818

1919

2020
def _is_python_file(path: Path) -> bool:
2121
return path.suffix.lower() in _PYTHON_EXTS
2222

2323

24+
def _count_leading_dots(s: str) -> int:
25+
return len(s) - len(s.lstrip("."))
26+
27+
28+
def _strip_source_prefix(parts: list[str]) -> list[str]:
29+
for i, part in enumerate(parts):
30+
if part in ("src", "lib", "packages"):
31+
return parts[i + 1 :]
32+
return parts
33+
34+
2435
def _resolve_relative_import(imported: str, source_path: Path, repo_root: Path | None = None) -> str | None:
2536
if not imported.startswith("."):
2637
return imported
2738

28-
dots = 0
29-
for c in imported:
30-
if c == ".":
31-
dots += 1
32-
else:
33-
break
34-
39+
dots = _count_leading_dots(imported)
3540
relative_module = imported[dots:]
3641

3742
if repo_root and source_path.is_absolute():
@@ -40,12 +45,7 @@ def _resolve_relative_import(imported: str, source_path: Path, repo_root: Path |
4045
except ValueError:
4146
pass
4247

43-
parent_parts = list(source_path.parent.parts)
44-
45-
for i, part in enumerate(parent_parts):
46-
if part in ("src", "lib", "packages"):
47-
parent_parts = parent_parts[i + 1 :]
48-
break
48+
parent_parts = _strip_source_prefix(list(source_path.parent.parts))
4949

5050
if parent_parts and parent_parts[-1] == "__pycache__":
5151
parent_parts = parent_parts[:-1]

tests/conftest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ def _run(args):
114114
main()
115115
return True
116116
except SystemExit as e:
117-
return e.code == 0
117+
if e.code == 0:
118+
return True
119+
raise
118120

119121
return _run
120122

tests/framework/loader.py

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -95,28 +95,33 @@ def load_test_cases(yaml_path: Path) -> list[YamlTestCase]:
9595
return []
9696

9797

98+
def _is_triplet_part(yaml_file: Path, seen: set[Path]) -> bool:
99+
if yaml_file in seen:
100+
return True
101+
return yaml_file.stem.endswith(("_after", "_diffctx"))
102+
103+
98104
def load_test_cases_from_dir(cases_dir: Path, pattern: str = "**/*.yaml") -> list[YamlTestCase]:
99-
cases = []
105+
cases: list[YamlTestCase] = []
100106
seen_triplet_bases: set[Path] = set()
101107

102108
for yaml_file in sorted(cases_dir.glob(pattern)):
103-
if yaml_file.stem.endswith("_before"):
104-
triplet_case = _load_triplet(yaml_file)
105-
if triplet_case:
106-
seen_triplet_bases.add(yaml_file)
107-
base_name = re.sub(r"_before$", "", yaml_file.stem)
108-
seen_triplet_bases.add(yaml_file.parent / f"{base_name}_after.yaml")
109-
seen_triplet_bases.add(yaml_file.parent / f"{base_name}_diffctx.yaml")
110-
cases.append(triplet_case)
109+
if not yaml_file.stem.endswith("_before"):
110+
continue
111+
triplet_case = _load_triplet(yaml_file)
112+
if triplet_case:
113+
seen_triplet_bases.add(yaml_file)
114+
base_name = re.sub(r"_before$", "", yaml_file.stem)
115+
seen_triplet_bases.add(yaml_file.parent / f"{base_name}_after.yaml")
116+
seen_triplet_bases.add(yaml_file.parent / f"{base_name}_diffctx.yaml")
117+
cases.append(triplet_case)
111118

112119
for yaml_file in sorted(cases_dir.glob(pattern)):
113-
if yaml_file in seen_triplet_bases:
114-
continue
115-
if yaml_file.stem.endswith(("_after", "_diffctx")):
116-
continue
117-
cases.extend(load_test_cases(yaml_file))
120+
if not _is_triplet_part(yaml_file, seen_triplet_bases):
121+
cases.extend(load_test_cases(yaml_file))
118122

119-
for yaml_file in sorted(cases_dir.glob(pattern.replace(".yaml", ".yml"))):
123+
yml_pattern = pattern.replace(".yaml", ".yml")
124+
for yaml_file in sorted(cases_dir.glob(yml_pattern)):
120125
if yaml_file in seen_triplet_bases:
121126
continue
122127
if yaml_file.stem.endswith(("_before", "_after", "_diffctx")):

tests/framework/runner.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@ def verify_assertions(self, context: dict, case: YamlTestCase) -> None:
6666

6767
for pattern in case.must_include:
6868
assert pattern in all_content, (
69-
f"[{case.name}] Expected '{pattern}' in context, but not found.\n" f"Fragment paths: {fragment_paths}"
69+
f"[{case.name}] Expected '{pattern}' in context, but not found.\n" + f"Fragment paths: {fragment_paths}"
7070
)
7171

7272
for file_path in case.must_include_files:
7373
assert any(file_path in p for p in fragment_paths), (
74-
f"[{case.name}] Expected file '{file_path}' in fragments, but not found.\n" f"Fragment paths: {fragment_paths}"
74+
f"[{case.name}] Expected file '{file_path}' in fragments, but not found.\n" + f"Fragment paths: {fragment_paths}"
7575
)
7676

7777
for content_block in case.must_include_content:

0 commit comments

Comments
 (0)