Performance Scan - 2026-08-12
Automated scan of src/apm_cli/ for algorithmic performance anti-patterns.
2 finding(s) identified.
Findings
[E] Heavy Top-Level Import -- src/apm_cli/commands/update.py:62-68
- Current: O(startup) --
from git.exc import GitCommandError and from ..deps.github_downloader import GitHubPackageDownloader are loaded at module level; since cli.py eagerly imports all command modules, gitpython and the full download machinery load on every apm invocation regardless of which sub-command is run (e.g. apm list, apm doctor).
- Proposed: O(0) at startup -- import cost deferred to invocation time by moving these imports inside the
update callback function body.
- Fix: Move
from git.exc import GitCommandError, from ..deps.github_downloader import GitHubPackageDownloader, and the revision_pins imports inside the update() function body so they are only imported when apm update is actually invoked.
[B] Double Linear Scan on List -- src/apm_cli/models/dependency/reference.py:1155-1156, 1322-1323, 1397-1398, 1503-1504
- Current: O(2n) per occurrence -- the pattern
if "_git" in parts: git_idx = parts.index("_git") performs two sequential O(n) list scans (one membership check, one index lookup) where a single scan suffices. Appears four times across parse methods; called once per dependency string parsed at install/update time.
- Proposed: O(n) with a single scan -- use
try: git_idx = parts.index("_git") ... except ValueError: pass or precompute the index once.
- Fix: Replace each
if "_git" in parts: idx = parts.index("_git") pair with try: idx = parts.index("_git"); parts = parts[:idx] + parts[idx+1:] except ValueError: pass to eliminate the redundant membership scan.
Scan coverage
- src/apm_cli/ (430 files scanned)
- Patterns checked: A (quadratic loops), B (linear scan in loop),
C (unconditional expensive ops), D (redundant config parsing),
E (heavy top-level imports), F (sequential independent I/O)
Notes:
- Pattern A: No confirmed O(n^2) loops found; context_optimizer.py uses pattern/result caching; security/executables.py uses frozenset for O(1) membership tests.
- Pattern C: http_cache.py already implements fast-path skip before full directory scan in _enforce_size_cap().
- Pattern D: os.getenv calls in registry/operations.py loop over different variable names per iteration (not the same key fetched twice).
- Pattern F: install/phases/download.py already uses ThreadPoolExecutor for parallel downloads; sequential subprocess loops in validation.py are fallback probes (later iterations depend on prior failures).
Generated by Daily Performance Scanner · 190.9 AIC · ⌖ 6.3 AIC · ⊞ 7.3K · ◷
Performance Scan - 2026-08-12
Automated scan of src/apm_cli/ for algorithmic performance anti-patterns.
2 finding(s) identified.
Findings
[E] Heavy Top-Level Import -- src/apm_cli/commands/update.py:62-68
from git.exc import GitCommandErrorandfrom ..deps.github_downloader import GitHubPackageDownloaderare loaded at module level; since cli.py eagerly imports all command modules, gitpython and the full download machinery load on everyapminvocation regardless of which sub-command is run (e.g.apm list,apm doctor).updatecallback function body.from git.exc import GitCommandError,from ..deps.github_downloader import GitHubPackageDownloader, and therevision_pinsimports inside theupdate()function body so they are only imported whenapm updateis actually invoked.[B] Double Linear Scan on List -- src/apm_cli/models/dependency/reference.py:1155-1156, 1322-1323, 1397-1398, 1503-1504
if "_git" in parts: git_idx = parts.index("_git")performs two sequential O(n) list scans (one membership check, one index lookup) where a single scan suffices. Appears four times across parse methods; called once per dependency string parsed at install/update time.try: git_idx = parts.index("_git") ... except ValueError: passor precompute the index once.if "_git" in parts: idx = parts.index("_git")pair withtry: idx = parts.index("_git"); parts = parts[:idx] + parts[idx+1:] except ValueError: passto eliminate the redundant membership scan.Scan coverage
C (unconditional expensive ops), D (redundant config parsing),
E (heavy top-level imports), F (sequential independent I/O)
Notes: