Skip to content

Commit 992de5d

Browse files
fix: address code review findings
- Remove dead TYPE_CHECKING import in resolver.py - Make async license resolution concurrent (asyncio.gather) - Remove bogus hardcoded SPDX PackageVerificationCode - Fix duplicate comment in CLI scan command - Handle table format + output file in scan command - Simplify _matches_any to use any() (ruff SIM110) - Use re.DOTALL for .csproj regex to handle multiline attrs - Clarify evaluate_policy mutates in-place
1 parent 580301b commit 992de5d

5 files changed

Lines changed: 16 additions & 19 deletions

File tree

src/license_patrol/cli.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,13 @@ def scan_cmd(
9191
console.print("[yellow]No dependencies found.[/yellow]")
9292
raise typer.Exit(0)
9393

94-
# Render output
9594
# Render output
9695
project_name = Path(project_dir).name
9796
if format == "table":
97+
if output:
98+
rendered = render_json(result)
99+
Path(output).write_text(rendered, encoding="utf-8")
100+
console.print(f"[green]Report written to {output} (as JSON)[/green]")
98101
render_table(result, console)
99102
elif format in ("json", "csv", "spdx", "cyclonedx"):
100103
rendered = _render_format(result, format, project_name)

src/license_patrol/ecosystems/nuget.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ def _parse_csproj(self, path: Path) -> list[Dependency]:
6060
"""Parse a .csproj file for PackageReference elements."""
6161
deps = []
6262
content = path.read_text(encoding="utf-8")
63-
# Simple regex parse — avoid heavy XML deps
63+
# Simple regex parse — handles multiline attributes
6464
pattern = r'<PackageReference\s+Include="([^"]+)"\s+Version="([^"]+)"'
65-
for match in re.finditer(pattern, content):
65+
for match in re.finditer(pattern, content, re.DOTALL):
6666
name = match.group(1)
6767
version = match.group(2)
6868
deps.append(

src/license_patrol/policy.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010
def evaluate_policy(dependencies: list[Dependency], policy: Policy) -> list[Dependency]:
11-
"""Evaluate each dependency against the policy and set its verdict."""
11+
"""Evaluate each dependency against the policy and set its verdict (mutates in-place)."""
1212
for dep in dependencies:
1313
# Check overrides first
1414
if dep.name in policy.overrides:
@@ -46,11 +46,6 @@ def _match_license(license_id: str, policy: Policy) -> PolicyVerdict:
4646

4747

4848
def _matches_any(license_id: str, patterns: list[str]) -> bool:
49-
"""Check if a license identifier matches any of the given patterns."""
50-
for pattern in patterns:
51-
if fnmatch.fnmatch(license_id, pattern):
52-
return True
53-
# Also check case-insensitively
54-
if fnmatch.fnmatch(license_id.lower(), pattern.lower()):
55-
return True
56-
return False
49+
"""Check if a license identifier matches any of the given patterns (case-insensitive)."""
50+
lower_id = license_id.lower()
51+
return any(fnmatch.fnmatch(lower_id, p.lower()) for p in patterns)

src/license_patrol/report.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,6 @@ def render_spdx(result: ScanResult, project_name: str = "project") -> str:
126126
f"SPDXID: SPDXRef-Package-{project_name}",
127127
"PackageDownloadLocation: NOASSERTION",
128128
"FilesAnalyzed: false",
129-
"PackageVerificationCode: d6a770ba38583ed4bb4525bd96e50461655d2758"
130-
" (excludes: ./exclude.awk)",
131129
"",
132130
]
133131
)

src/license_patrol/resolver.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,11 @@
33
from __future__ import annotations
44

55
import logging
6-
from typing import TYPE_CHECKING
76

87
import httpx
98

109
from license_patrol.models import Dependency, Ecosystem
1110

12-
if TYPE_CHECKING:
13-
pass
14-
1511
logger = logging.getLogger(__name__)
1612

1713
# Well-known license mappings for common non-SPDX identifiers
@@ -194,14 +190,19 @@ async def resolve_nuget_license(dep: Dependency, client: httpx.AsyncClient) -> N
194190

195191
async def resolve_licenses(dependencies: list[Dependency]) -> list[Dependency]:
196192
"""Resolve licenses for all dependencies that don't already have one."""
193+
import asyncio
194+
197195
to_resolve = [d for d in dependencies if not d.license_id]
198196
if not to_resolve:
199197
return dependencies
200198

201199
async with httpx.AsyncClient(timeout=15.0) as client:
200+
tasks = []
202201
for dep in to_resolve:
203202
resolver = RESOLVERS.get(dep.ecosystem)
204203
if resolver:
205-
await resolver(dep, client)
204+
tasks.append(resolver(dep, client))
205+
if tasks:
206+
await asyncio.gather(*tasks, return_exceptions=True)
206207

207208
return dependencies

0 commit comments

Comments
 (0)