vscode_optimizer: split _optimize.py 498 -> 100 + 5 modules
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Python tests / test (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run

The last python file over the 250-line cap. Now that it sits at 100% line
and branch coverage, split along its natural seams:

  _types.py     48  the three dataclasses, so nothing imports a sibling
                    merely to name a type (which would have made a cycle)
  _render.py    69  the ANSI palette and the printers
  _config_io.py 104 variant discovery, JSONC parsing, reads and writes
  _hardware.py  106 _run and the four probes
  _plan.py      156 the pure hardware -> proposed-changes functions
  _optimize.py  100 the CLI entry: _apply_variant and main

Definitions were moved by line range rather than retyped. A throwaway
ast.dump identity check against HEAD confirms all 26 top-level definitions
are unchanged - formatting-blind, logic-sensitive.

The first attempt raised NameError: name '_BO' is not defined at runtime;
_BO was used by main() but missing from the _render import list. Rather than
fix that one failure alone, an AST scan over all six modules for names used
but not bound confirmed nothing else was missing.

Test imports now name the owning module; no re-export barrels. One patch
target genuinely moved - the guard-rail test patches _config_io.Path, since
that is the namespace resolving the name when _discover_variants is called
directly rather than through main().

Verified: same 98 tests pass with every module at 100% line and branch;
python3 -m python_pkg.vscode_optimizer --dry-run reproduces the same
18-change plan and writes nothing; repo-wide pytest 1846 passed at 100%;
over-cap files 36 -> 35.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
This commit is contained in:
Krzysztof Rudnicki 2026-08-17 13:15:50 +02:00
parent 9d12c03ccc
commit 377a352d48
14 changed files with 640 additions and 466 deletions

View File

@ -0,0 +1,18 @@
{
"title": "Split _optimize.py 498 -> six modules under the cap",
"objective": "python_pkg/vscode_optimizer/_optimize.py was the last python file over the 250-line cap at 498 lines. Now that it sits at 100% line and branch coverage, split it along its natural seams into shared types, hardware probes, the pure planning layer, filesystem I/O, terminal rendering, and the CLI entry. Code moves verbatim; the tests predate the seam and are re-pointed, not rewritten.",
"acceptance_criteria": [
"Every resulting module is under 250 lines; the over-cap total falls 36 -> 35",
"All 26 top-level definitions are AST-identical to the pre-split file, proved by a throwaway ast.dump comparison against HEAD",
"The same 98 tests pass, with every module at 100% line and branch coverage",
"python3 -m python_pkg.vscode_optimizer --dry-run reproduces the same 18-change plan and writes nothing",
"Import sites name the owning module - no re-export barrels, no sys.path manipulation",
"No suppression of any kind: no noqa, no type: ignore, no lowered threshold"
],
"out_of_scope": [
"Fixing the _VENDOR_KW 'ati' substring bug, still pinned by a strict xfail and reported to the user",
"Any behaviour change: this commit is a pure relocation of definitions",
"The three transcribe_* files and the 29 over-cap shell scripts"
],
"verifier": "A throwaway ast.dump identity check against HEAD for the verbatim claim; pytest python_pkg/vscode_optimizer/ --cov --cov-branch for tests and coverage; python3 -m python_pkg.vscode_optimizer --dry-run for the behaviour claim; bash ~/utils/scripts/check_file_length.sh --all for the cap; pre-commit run --files for lint."
}

View File

@ -0,0 +1,61 @@
{
"intent": "Split the 498-line _optimize.py into six modules, each well under the 250-line cap, moving every definition verbatim and leaving behaviour unchanged.",
"scope": [
"python_pkg/vscode_optimizer/_optimize.py 498 -> 100 (CLI entry: _apply_variant, main)",
"New: _types.py 48, _render.py 69, _config_io.py 104, _hardware.py 106, _plan.py 156",
"Re-pointed imports and patch targets in the six existing test modules",
"Non-goal: any behaviour change, including the known _VENDOR_KW bug"
],
"changes": [
"Extracted the three dataclasses into _types.py so no module imports a sibling merely to name a type, which is what would have created a cycle.",
"Extracted _run and the four probes into _hardware.py, the pure planning functions into _plan.py, variant discovery plus JSONC parsing and all file reads/writes into _config_io.py, and the ANSI palette plus the printers into _render.py.",
"Definitions were moved with sed over line ranges rather than retyped, so no character could drift; the ast identity check below is what proves it.",
"Test imports now name the owning module (from ... import _plan as opt, and so on). No re-export barrels were added.",
"One patch target moved: test_main.py's guard-rail test patches _config_io.Path and calls _config_io._discover_variants, because that is the namespace resolving the name once it is called directly rather than through main(). The three patches inside main() correctly still target _optimize, whose namespace resolves them."
],
"verification": [
{
"command": "python3 verify_split.py HEAD python_pkg/vscode_optimizer/_optimize.py <six modules>",
"result": "pass",
"evidence": "IDENTICAL: all 26 top-level definitions moved verbatim. Each def/class was normalised through ast.dump(ast.parse(ast.unparse(node))) and compared to the pre-split file at HEAD, so the check is formatting-blind but logic-sensitive."
},
{
"command": "AST scan for names used but not bound in each of the six modules",
"result": "pass",
"evidence": "Zero unbound names. Run after the first split attempt raised NameError: name '_BO' is not defined at _optimize.py:87 - _BO was used by main() but missing from the _render import list. Scanning all six modules at once was what confirmed nothing else was missing, rather than fixing one failure at a time."
},
{
"command": "pytest python_pkg/vscode_optimizer/ --cov=python_pkg/vscode_optimizer --cov-branch --cov-report=term-missing",
"result": "pass",
"evidence": "98 passed, 1 xfailed - the same count as before the split, which is the discriminating check that no test file fell outside the runner's glob. Per-module: _types 26/0, _render 45/0, _plan 56/0, _optimize 58/0, _hardware 65/0, _config_io 66/0 statements missing, all 100% line and branch."
},
{
"command": "python3 -m python_pkg.vscode_optimizer --dry-run",
"result": "pass",
"evidence": "Same output as the pre-split run: Ryzen 9 7900X3D / RTX 3090 / NVME detected, 18 change(s) proposed, '(dry-run) No files modified.' Every unit test stubs subprocess, so only this real run proves the six-module import graph resolves. Re-run after ruff moved _Hw/_Variant into TYPE_CHECKING, since that rewrite is a known source of runtime-only failures."
},
{
"command": "bash ~/utils/scripts/check_file_length.sh --all",
"result": "pass",
"evidence": "35 files over cap, down from 36. Largest new module is _plan.py at 156; largest test file is test_main.py at 241. No file in this package is now over the cap."
},
{
"command": "pre-commit run --files <changed files>",
"result": "pass",
"evidence": "All hooks pass, including ruff, ruff format, mypy, pylint, bandit and the pytest coverage gate. Ruff moved two annotation-only imports into TYPE_CHECKING; the suite and the real run were both repeated afterwards."
},
{
"command": "pytest (whole repo, memory-capped)",
"result": "pass",
"evidence": "1846 passed at 100% total coverage, confirming the split did not disturb the repo-wide gate."
}
],
"risks": [
"A six-way split multiplies import edges. Mitigated by keeping _types.py dependency-free and _plan.py/_render.py importing nothing from siblings except types, so the graph is a tree rather than a web; the real run confirms it resolves.",
"Patch targets that moved could silently stop patching, letting a test call the real function. Mitigated: the suite was run after re-pointing and the count is unchanged at 98, and the one target that genuinely moved failed loudly with AttributeError rather than passing vacuously."
],
"rollback": [
"git revert the commit; _optimize.py returns to its 498-line form and the five new modules disappear.",
"After rollback, confirm python3 -m python_pkg.vscode_optimizer --dry-run still prints the 18-change plan and that pytest python_pkg/vscode_optimizer/ is still 98 passed at 100%."
]
}

View File

@ -0,0 +1,104 @@
"""Find VS Code installations and read and write their configuration.
Settings are JSON with Comments, which the stdlib cannot parse, so this
module carries a small JSONC reader. Every write is preceded by a backup.
"""
from __future__ import annotations
from datetime import datetime, timezone
import json
from pathlib import Path
import re
import shutil
from python_pkg.vscode_optimizer._types import _Opt, _Variant
def _discover_variants() -> list[_Variant]:
"""Find all installed VS Code variants."""
cfg = Path.home() / ".config"
cands = [
("VS Code (stable)", "Code", "code-flags.conf", "code"),
(
"VS Code Insiders",
"Code - Insiders",
"code-insiders-flags.conf",
"code-insiders",
),
("VSCodium", "VSCodium", "vscodium-flags.conf", "codium"),
]
found: list[_Variant] = []
for name, dir_name, flags_name, binary in cands:
sp = cfg / dir_name / "User" / "settings.json"
fp = cfg / flags_name
if sp.exists() or shutil.which(binary):
found.append(_Variant(name, sp, fp, binary))
return found
def _parse_jsonc(text: str) -> dict[str, object]:
"""Parse JSON with Comments (JSONC) used by VS Code."""
out: list[str] = []
i, n = 0, len(text)
while i < n:
ch = text[i]
if ch == '"':
j = i + 1
while j < n:
if text[j] == "\\":
j += 2
continue
if text[j] == '"':
j += 1
break
j += 1
out.append(text[i:j])
i = j
elif ch == "/" and i + 1 < n and text[i + 1] == "/":
while i < n and text[i] != "\n":
i += 1
elif ch == "/" and i + 1 < n and text[i + 1] == "*":
end = text.find("*/", i + 2)
i = end + 2 if end != -1 else n
else:
out.append(ch)
i += 1
cleaned = re.sub(r",(\s*[}\]])", r"\1", "".join(out))
if not cleaned.strip():
return {}
parsed: dict[str, object] = json.loads(cleaned)
return parsed
def _backup(path: Path) -> Path | None:
if not path.exists():
return None
ts = datetime.now(tz=timezone.utc).strftime("%Y%m%dT%H%M%SZ")
dst = path.with_suffix(f".{ts}.bak")
shutil.copy2(path, dst)
return dst
def _read_settings(path: Path) -> dict[str, object]:
return _parse_jsonc(path.read_text()) if path.exists() else {}
def _write_settings(path: Path, current: dict[str, object], opts: list[_Opt]) -> None:
merged = {**current, **{o.key: o.value for o in opts}}
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(merged, indent=4, ensure_ascii=False) + "\n")
def _read_flags(path: Path) -> list[str]:
if not path.exists():
return []
return [
ln.strip()
for ln in path.read_text().splitlines()
if ln.strip() and not ln.strip().startswith("#")
]
def _write_flags(path: Path, flags: list[str]) -> None:
path.write_text("\n".join(flags) + "\n")

View File

@ -0,0 +1,106 @@
"""Probe the machine's CPU, RAM, GPU, and root disk.
Every probe shells out and degrades to a default rather than raising, so a
missing tool narrows the report instead of failing the run.
"""
from __future__ import annotations
from pathlib import Path
import re
import subprocess
from python_pkg.vscode_optimizer._types import _Hw
_MIB_1024 = 1024
_LSCPU = {
"Model name": "cpu_model",
"CPU(s)": "cpu_logical_cores",
"Core(s) per socket": "cpu_physical_cores",
"CPU max MHz": "cpu_max_mhz",
}
_VENDOR_KW = {"nvidia": "NVIDIA", "amd": "AMD", "ati": "AMD", "intel": "Intel"}
def _run(args: list[str]) -> str:
"""Run *args* and return stdout, or ``""`` on failure."""
try:
proc = subprocess.run(
args,
capture_output=True,
text=True,
timeout=10,
check=False,
)
except (subprocess.TimeoutExpired, FileNotFoundError):
return ""
return proc.stdout.strip()
def _detect_cpu(hw: _Hw) -> None:
for line in _run(["lscpu"]).splitlines():
key, _, val = line.partition(":")
attr = _LSCPU.get(key.strip())
if attr == "cpu_model":
hw.cpu_model = val.strip()
elif attr == "cpu_max_mhz":
hw.cpu_max_mhz = float(val)
elif attr is not None:
setattr(hw, attr, int(val))
def _detect_ram(hw: _Hw) -> None:
try:
meminfo = Path("/proc/meminfo").read_text(encoding="utf-8")
except OSError:
return
m = re.search(r"MemTotal:\s+(\d+)\s+kB", meminfo)
if m:
hw.ram_total_mb = int(m.group(1)) // _MIB_1024
def _detect_gpu(hw: _Hw) -> None:
for line in _run(["lspci"]).splitlines():
low = line.lower()
if "vga" not in low and "3d" not in low:
continue
hw.gpu_model = line.rsplit(":", maxsplit=1)[-1].strip()
for kw, vendor in _VENDOR_KW.items():
if kw in low:
hw.gpu_vendor = vendor
break
if hw.gpu_vendor == "NVIDIA":
vram = _run(
[
"nvidia-smi",
"--query-gpu=memory.total",
"--format=csv,noheader,nounits",
]
)
if vram:
hw.gpu_vram_mb = int(vram.split("\n")[0].strip())
break
def _detect_disk(hw: _Hw) -> None:
root_dev = _run(["findmnt", "-n", "-o", "SOURCE", "/"])
if not root_dev:
return
base = re.sub(r"p?\d+$", "", Path(root_dev).name)
rotational = Path(f"/sys/block/{base}/queue/rotational")
if not rotational.exists():
return
if rotational.read_text(encoding="utf-8").strip() == "1":
hw.disk_type = "hdd"
elif "nvme" in base:
hw.disk_type = "nvme"
else:
hw.disk_type = "ssd"
def _detect_hardware() -> _Hw:
"""Probe CPU, RAM, GPU, and root disk type."""
hw = _Hw()
for fn in (_detect_cpu, _detect_ram, _detect_gpu, _detect_disk):
fn(hw)
return hw

445
python_pkg/vscode_optimizer/_optimize.py Executable file → Normal file
View File

@ -1,432 +1,37 @@
#!/usr/bin/env python3
"""Auto-optimize VS Code settings based on detected hardware."""
"""Auto-optimize VS Code settings based on detected hardware.
Run as ``python3 -m python_pkg.vscode_optimizer [--dry-run] [--yes]``.
"""
from __future__ import annotations
import argparse
from dataclasses import dataclass
from datetime import datetime, timezone
import json
from pathlib import Path
import re
import shutil
import subprocess
import sys
from typing import TYPE_CHECKING
_RAM_THRESHOLDS = ((28000, 4096), (14000, 2048), (7000, 1024))
_DEFAULT_MEM = 512
_MIB_1024 = 1024
_MIN_THREADS = 4
_SUBMOD_LIMIT = 30
_LSCPU = {
"Model name": "cpu_model",
"CPU(s)": "cpu_logical_cores",
"Core(s) per socket": "cpu_physical_cores",
"CPU max MHz": "cpu_max_mhz",
}
_VENDOR_KW = {"nvidia": "NVIDIA", "amd": "AMD", "ati": "AMD", "intel": "Intel"}
_WATCHER_EX: dict[str, bool] = dict.fromkeys(
[
"**/.git/objects/**",
"**/.git/subtree-cache/**",
"**/node_modules/**",
"**/.venv/**",
"**/venv/**",
"**/__pycache__/**",
"**/build/**",
"**/.mypy_cache/**",
"**/.ruff_cache/**",
"**/.pytest_cache/**",
"**/dist/**",
"**/*.egg-info/**",
],
True,
from python_pkg.vscode_optimizer._config_io import (
_backup,
_discover_variants,
_read_flags,
_read_settings,
_write_flags,
_write_settings,
)
_SEARCH_EX: dict[str, bool] = dict.fromkeys(
[
"**/node_modules",
"**/build",
"**/.venv",
"**/venv",
"**/__pycache__",
"**/.mypy_cache",
"**/.ruff_cache",
"**/.pytest_cache",
"**/dist",
],
True,
from python_pkg.vscode_optimizer._hardware import _detect_hardware
from python_pkg.vscode_optimizer._plan import _compute_opts, _gpu_flags
from python_pkg.vscode_optimizer._render import (
_BO,
_G,
_R,
_Y,
_hdr,
_out,
_show_hw,
_show_plan,
)
_B = "\033[94m"
_G = "\033[92m"
_Y = "\033[93m"
_C = "\033[96m"
_BO = "\033[1m"
_R = "\033[0m"
@dataclass
class _Hw:
"""Detected system hardware."""
cpu_model: str = "Unknown"
cpu_physical_cores: int = 1
cpu_logical_cores: int = 1
cpu_max_mhz: float = 0.0
ram_total_mb: int = 0
gpu_vendor: str = "Unknown"
gpu_model: str = "Unknown"
gpu_vram_mb: int = 0
disk_type: str = "unknown"
@dataclass
class _Opt:
"""Single proposed change."""
key: str
value: object
reason: str
current: object = None
@dataclass
class _Variant:
"""A VS Code installation."""
name: str
settings: Path
flags: Path
binary: str
def _run(args: list[str]) -> str:
"""Run *args* and return stdout, or ``""`` on failure."""
try:
proc = subprocess.run(
args,
capture_output=True,
text=True,
timeout=10,
check=False,
)
except (subprocess.TimeoutExpired, FileNotFoundError):
return ""
return proc.stdout.strip()
def _detect_cpu(hw: _Hw) -> None:
for line in _run(["lscpu"]).splitlines():
key, _, val = line.partition(":")
attr = _LSCPU.get(key.strip())
if attr == "cpu_model":
hw.cpu_model = val.strip()
elif attr == "cpu_max_mhz":
hw.cpu_max_mhz = float(val)
elif attr is not None:
setattr(hw, attr, int(val))
def _detect_ram(hw: _Hw) -> None:
try:
meminfo = Path("/proc/meminfo").read_text(encoding="utf-8")
except OSError:
return
m = re.search(r"MemTotal:\s+(\d+)\s+kB", meminfo)
if m:
hw.ram_total_mb = int(m.group(1)) // _MIB_1024
def _detect_gpu(hw: _Hw) -> None:
for line in _run(["lspci"]).splitlines():
low = line.lower()
if "vga" not in low and "3d" not in low:
continue
hw.gpu_model = line.rsplit(":", maxsplit=1)[-1].strip()
for kw, vendor in _VENDOR_KW.items():
if kw in low:
hw.gpu_vendor = vendor
break
if hw.gpu_vendor == "NVIDIA":
vram = _run(
[
"nvidia-smi",
"--query-gpu=memory.total",
"--format=csv,noheader,nounits",
]
)
if vram:
hw.gpu_vram_mb = int(vram.split("\n")[0].strip())
break
def _detect_disk(hw: _Hw) -> None:
root_dev = _run(["findmnt", "-n", "-o", "SOURCE", "/"])
if not root_dev:
return
base = re.sub(r"p?\d+$", "", Path(root_dev).name)
rotational = Path(f"/sys/block/{base}/queue/rotational")
if not rotational.exists():
return
if rotational.read_text(encoding="utf-8").strip() == "1":
hw.disk_type = "hdd"
elif "nvme" in base:
hw.disk_type = "nvme"
else:
hw.disk_type = "ssd"
def _detect_hardware() -> _Hw:
"""Probe CPU, RAM, GPU, and root disk type."""
hw = _Hw()
for fn in (_detect_cpu, _detect_ram, _detect_gpu, _detect_disk):
fn(hw)
return hw
def _discover_variants() -> list[_Variant]:
"""Find all installed VS Code variants."""
cfg = Path.home() / ".config"
cands = [
("VS Code (stable)", "Code", "code-flags.conf", "code"),
(
"VS Code Insiders",
"Code - Insiders",
"code-insiders-flags.conf",
"code-insiders",
),
("VSCodium", "VSCodium", "vscodium-flags.conf", "codium"),
]
found: list[_Variant] = []
for name, dir_name, flags_name, binary in cands:
sp = cfg / dir_name / "User" / "settings.json"
fp = cfg / flags_name
if sp.exists() or shutil.which(binary):
found.append(_Variant(name, sp, fp, binary))
return found
def _parse_jsonc(text: str) -> dict[str, object]:
"""Parse JSON with Comments (JSONC) used by VS Code."""
out: list[str] = []
i, n = 0, len(text)
while i < n:
ch = text[i]
if ch == '"':
j = i + 1
while j < n:
if text[j] == "\\":
j += 2
continue
if text[j] == '"':
j += 1
break
j += 1
out.append(text[i:j])
i = j
elif ch == "/" and i + 1 < n and text[i + 1] == "/":
while i < n and text[i] != "\n":
i += 1
elif ch == "/" and i + 1 < n and text[i + 1] == "*":
end = text.find("*/", i + 2)
i = end + 2 if end != -1 else n
else:
out.append(ch)
i += 1
cleaned = re.sub(r",(\s*[}\]])", r"\1", "".join(out))
if not cleaned.strip():
return {}
parsed: dict[str, object] = json.loads(cleaned)
return parsed
def _ideal_mem(ram_mb: int) -> int:
for threshold, value in _RAM_THRESHOLDS:
if ram_mb >= threshold:
return value
return _DEFAULT_MEM
def _dict_merge_opt(
cur_settings: dict[str, object],
key: str,
ideal: dict[str, bool],
reason: str,
) -> _Opt | None:
cur = cur_settings.get(key, {})
if not isinstance(cur, dict):
cur = {}
if all(k in cur for k in ideal):
return None
return _Opt(key, {**cur, **ideal}, reason, cur or None)
def _compute_opts(hw: _Hw, cur: dict[str, object]) -> list[_Opt]:
"""Return optimizations based on hardware and current settings."""
opts: list[_Opt] = []
def _p(key: str, val: object, reason: str) -> None:
if cur.get(key) != val:
opts.append(_Opt(key, val, reason, cur.get(key)))
threads = max(_MIN_THREADS, hw.cpu_physical_cores)
_p(
"search.maxThreads",
threads,
f"{hw.cpu_physical_cores} physical cores - use them for workspace search",
)
mem = _ideal_mem(hw.ram_total_mb)
_p(
"files.maxMemoryForLargeFilesMB",
mem,
f"{hw.ram_total_mb // _MIB_1024} GB RAM - allow up to {mem} MB for large files",
)
if hw.gpu_vendor in ("NVIDIA", "AMD"):
_p(
"terminal.integrated.gpuAcceleration",
"on",
f"{hw.gpu_vendor} GPU - enable GPU-rendered terminal",
)
smooth = True
for key in (
"editor.smoothScrolling",
"workbench.list.smoothScrolling",
"terminal.integrated.smoothScrolling",
):
_p(key, smooth, "Smooth scrolling is free with a dedicated GPU")
no = False
_p("search.followSymlinks", no, "Avoid duplicate results and wasted I/O")
for result in (
_dict_merge_opt(
cur,
"files.watcherExclude",
_WATCHER_EX,
"Exclude build/cache dirs from file watcher",
),
_dict_merge_opt(
cur, "search.exclude", _SEARCH_EX, "Exclude build/cache dirs from search"
),
):
if result:
opts.extend([result])
_p("editor.guides.bracketPairs", "active", "Lightweight visual aid")
_p(
"diffEditor.maxComputationTime",
0,
f"Fast CPU ({hw.cpu_model}) - compute diffs fully",
)
_p("editor.minimap.enabled", no, "Minimap consumes GPU/CPU for little benefit")
cur_sub = cur.get("git.detectSubmodulesLimit")
if cur_sub is None or (isinstance(cur_sub, int) and cur_sub < _SUBMOD_LIMIT):
opts.append(
_Opt(
"git.detectSubmodulesLimit",
_SUBMOD_LIMIT,
"Higher limit is fine with fast CPU",
cur_sub,
)
)
return opts
def _gpu_flags(hw: _Hw) -> list[str]:
"""Return Electron flags appropriate for the detected GPU."""
if hw.gpu_vendor in ("NVIDIA", "AMD"):
base = [
"--enable-gpu-rasterization",
"--enable-zero-copy",
"--ignore-gpu-blocklist",
"--enable-features=CanvasOopRasterization",
]
if hw.gpu_vendor == "NVIDIA":
base.append("--enable-features=VaapiVideoDecodeLinuxGL,VaapiVideoEncoder")
return base
if hw.gpu_vendor == "Intel":
return [
"--enable-gpu-rasterization",
"--ignore-gpu-blocklist",
"--enable-features=VaapiVideoDecodeLinuxGL",
]
return []
def _backup(path: Path) -> Path | None:
if not path.exists():
return None
ts = datetime.now(tz=timezone.utc).strftime("%Y%m%dT%H%M%SZ")
dst = path.with_suffix(f".{ts}.bak")
shutil.copy2(path, dst)
return dst
def _read_settings(path: Path) -> dict[str, object]:
return _parse_jsonc(path.read_text()) if path.exists() else {}
def _write_settings(path: Path, current: dict[str, object], opts: list[_Opt]) -> None:
merged = {**current, **{o.key: o.value for o in opts}}
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(merged, indent=4, ensure_ascii=False) + "\n")
def _read_flags(path: Path) -> list[str]:
if not path.exists():
return []
return [
ln.strip()
for ln in path.read_text().splitlines()
if ln.strip() and not ln.strip().startswith("#")
]
def _write_flags(path: Path, flags: list[str]) -> None:
path.write_text("\n".join(flags) + "\n")
def _out(text: str = "") -> None:
"""Write a line to stdout."""
sys.stdout.write(text + "\n")
def _hdr(text: str) -> None:
_out(f"\n{_BO}{_B}{'─' * 60}{_R}\n{_BO}{_B} {text}{_R}\n{_BO}{_B}{'─' * 60}{_R}")
def _show_hw(hw: _Hw) -> None:
_hdr("Detected Hardware")
_out(f" {_C}CPU{_R} {hw.cpu_model}")
_out(
f" {hw.cpu_physical_cores} cores / {hw.cpu_logical_cores} threads"
f" @ {hw.cpu_max_mhz:.0f} MHz"
)
_out(f" {_C}RAM{_R} {hw.ram_total_mb // _MIB_1024} GB")
gpu = f" {_C}GPU{_R} {hw.gpu_vendor} - {hw.gpu_model}"
if hw.gpu_vram_mb:
gpu += f" ({hw.gpu_vram_mb} MB VRAM)"
_out(gpu)
_out(f" {_C}Disk{_R} {hw.disk_type.upper()}")
def _show_plan(opts: list[_Opt], new_fl: list[str], old_fl: list[str]) -> None:
_hdr("Optimization Plan")
added = [f for f in new_fl if f not in old_fl]
if added:
_out(f"\n {_BO}Electron flags to add:{_R}")
for fl in added:
_out(f" {_G}+ {fl}{_R}")
elif new_fl:
_out(f"\n {_Y}Electron GPU flags already present{_R}")
if opts:
_out(f"\n {_BO}Settings to change:{_R}")
for i, o in enumerate(opts, 1):
c = json.dumps(o.current)[:55] if o.current is not None else "-"
v = json.dumps(o.value)[:55]
_out(f"\n {_BO}{i}. {o.key}{_R}\n {o.reason}")
_out(f" {_Y}{c}{_R} -> {_G}{v}{_R}")
else:
_out(f"\n {_G}All settings already optimized{_R}")
total = len(opts) + len(added)
if total:
_out(f"\n {_BO}{total} change(s) proposed.{_R}")
if TYPE_CHECKING:
from python_pkg.vscode_optimizer._types import _Hw, _Variant
def _apply_variant(

View File

@ -0,0 +1,156 @@
"""Turn detected hardware plus current settings into a list of changes.
Pure functions: nothing here reads the filesystem or shells out. Each
proposal carries the value it replaces, so the plan can be shown as a diff.
"""
from __future__ import annotations
from python_pkg.vscode_optimizer._types import _Hw, _Opt
_RAM_THRESHOLDS = ((28000, 4096), (14000, 2048), (7000, 1024))
_DEFAULT_MEM = 512
_MIB_1024 = 1024
_MIN_THREADS = 4
_SUBMOD_LIMIT = 30
_WATCHER_EX: dict[str, bool] = dict.fromkeys(
[
"**/.git/objects/**",
"**/.git/subtree-cache/**",
"**/node_modules/**",
"**/.venv/**",
"**/venv/**",
"**/__pycache__/**",
"**/build/**",
"**/.mypy_cache/**",
"**/.ruff_cache/**",
"**/.pytest_cache/**",
"**/dist/**",
"**/*.egg-info/**",
],
True,
)
_SEARCH_EX: dict[str, bool] = dict.fromkeys(
[
"**/node_modules",
"**/build",
"**/.venv",
"**/venv",
"**/__pycache__",
"**/.mypy_cache",
"**/.ruff_cache",
"**/.pytest_cache",
"**/dist",
],
True,
)
def _ideal_mem(ram_mb: int) -> int:
for threshold, value in _RAM_THRESHOLDS:
if ram_mb >= threshold:
return value
return _DEFAULT_MEM
def _dict_merge_opt(
cur_settings: dict[str, object],
key: str,
ideal: dict[str, bool],
reason: str,
) -> _Opt | None:
cur = cur_settings.get(key, {})
if not isinstance(cur, dict):
cur = {}
if all(k in cur for k in ideal):
return None
return _Opt(key, {**cur, **ideal}, reason, cur or None)
def _compute_opts(hw: _Hw, cur: dict[str, object]) -> list[_Opt]:
"""Return optimizations based on hardware and current settings."""
opts: list[_Opt] = []
def _p(key: str, val: object, reason: str) -> None:
if cur.get(key) != val:
opts.append(_Opt(key, val, reason, cur.get(key)))
threads = max(_MIN_THREADS, hw.cpu_physical_cores)
_p(
"search.maxThreads",
threads,
f"{hw.cpu_physical_cores} physical cores - use them for workspace search",
)
mem = _ideal_mem(hw.ram_total_mb)
_p(
"files.maxMemoryForLargeFilesMB",
mem,
f"{hw.ram_total_mb // _MIB_1024} GB RAM - allow up to {mem} MB for large files",
)
if hw.gpu_vendor in ("NVIDIA", "AMD"):
_p(
"terminal.integrated.gpuAcceleration",
"on",
f"{hw.gpu_vendor} GPU - enable GPU-rendered terminal",
)
smooth = True
for key in (
"editor.smoothScrolling",
"workbench.list.smoothScrolling",
"terminal.integrated.smoothScrolling",
):
_p(key, smooth, "Smooth scrolling is free with a dedicated GPU")
no = False
_p("search.followSymlinks", no, "Avoid duplicate results and wasted I/O")
for result in (
_dict_merge_opt(
cur,
"files.watcherExclude",
_WATCHER_EX,
"Exclude build/cache dirs from file watcher",
),
_dict_merge_opt(
cur, "search.exclude", _SEARCH_EX, "Exclude build/cache dirs from search"
),
):
if result:
opts.extend([result])
_p("editor.guides.bracketPairs", "active", "Lightweight visual aid")
_p(
"diffEditor.maxComputationTime",
0,
f"Fast CPU ({hw.cpu_model}) - compute diffs fully",
)
_p("editor.minimap.enabled", no, "Minimap consumes GPU/CPU for little benefit")
cur_sub = cur.get("git.detectSubmodulesLimit")
if cur_sub is None or (isinstance(cur_sub, int) and cur_sub < _SUBMOD_LIMIT):
opts.append(
_Opt(
"git.detectSubmodulesLimit",
_SUBMOD_LIMIT,
"Higher limit is fine with fast CPU",
cur_sub,
)
)
return opts
def _gpu_flags(hw: _Hw) -> list[str]:
"""Return Electron flags appropriate for the detected GPU."""
if hw.gpu_vendor in ("NVIDIA", "AMD"):
base = [
"--enable-gpu-rasterization",
"--enable-zero-copy",
"--ignore-gpu-blocklist",
"--enable-features=CanvasOopRasterization",
]
if hw.gpu_vendor == "NVIDIA":
base.append("--enable-features=VaapiVideoDecodeLinuxGL,VaapiVideoEncoder")
return base
if hw.gpu_vendor == "Intel":
return [
"--enable-gpu-rasterization",
"--ignore-gpu-blocklist",
"--enable-features=VaapiVideoDecodeLinuxGL",
]
return []

View File

@ -0,0 +1,69 @@
"""Render the hardware summary and the optimization plan to the terminal.
The only module that owns the ANSI colour codes, so changing the palette
touches one file.
"""
from __future__ import annotations
import json
import sys
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from python_pkg.vscode_optimizer._types import _Hw, _Opt
_MIB_1024 = 1024
_B = "\033[94m"
_G = "\033[92m"
_Y = "\033[93m"
_C = "\033[96m"
_BO = "\033[1m"
_R = "\033[0m"
def _out(text: str = "") -> None:
"""Write a line to stdout."""
sys.stdout.write(text + "\n")
def _hdr(text: str) -> None:
_out(f"\n{_BO}{_B}{'─' * 60}{_R}\n{_BO}{_B} {text}{_R}\n{_BO}{_B}{'─' * 60}{_R}")
def _show_hw(hw: _Hw) -> None:
_hdr("Detected Hardware")
_out(f" {_C}CPU{_R} {hw.cpu_model}")
_out(
f" {hw.cpu_physical_cores} cores / {hw.cpu_logical_cores} threads"
f" @ {hw.cpu_max_mhz:.0f} MHz"
)
_out(f" {_C}RAM{_R} {hw.ram_total_mb // _MIB_1024} GB")
gpu = f" {_C}GPU{_R} {hw.gpu_vendor} - {hw.gpu_model}"
if hw.gpu_vram_mb:
gpu += f" ({hw.gpu_vram_mb} MB VRAM)"
_out(gpu)
_out(f" {_C}Disk{_R} {hw.disk_type.upper()}")
def _show_plan(opts: list[_Opt], new_fl: list[str], old_fl: list[str]) -> None:
_hdr("Optimization Plan")
added = [f for f in new_fl if f not in old_fl]
if added:
_out(f"\n {_BO}Electron flags to add:{_R}")
for fl in added:
_out(f" {_G}+ {fl}{_R}")
elif new_fl:
_out(f"\n {_Y}Electron GPU flags already present{_R}")
if opts:
_out(f"\n {_BO}Settings to change:{_R}")
for i, o in enumerate(opts, 1):
c = json.dumps(o.current)[:55] if o.current is not None else "-"
v = json.dumps(o.value)[:55]
_out(f"\n {_BO}{i}. {o.key}{_R}\n {o.reason}")
_out(f" {_Y}{c}{_R} -> {_G}{v}{_R}")
else:
_out(f"\n {_G}All settings already optimized{_R}")
total = len(opts) + len(added)
if total:
_out(f"\n {_BO}{total} change(s) proposed.{_R}")

View File

@ -0,0 +1,48 @@
"""Detected hardware, proposed changes, and the VS Code installations found.
These three dataclasses are the vocabulary the other modules share, kept
apart from them so nothing has to import a sibling just to name a type.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from pathlib import Path
@dataclass
class _Hw:
"""Detected system hardware."""
cpu_model: str = "Unknown"
cpu_physical_cores: int = 1
cpu_logical_cores: int = 1
cpu_max_mhz: float = 0.0
ram_total_mb: int = 0
gpu_vendor: str = "Unknown"
gpu_model: str = "Unknown"
gpu_vram_mb: int = 0
disk_type: str = "unknown"
@dataclass
class _Opt:
"""Single proposed change."""
key: str
value: object
reason: str
current: object = None
@dataclass
class _Variant:
"""A VS Code installation."""
name: str
settings: Path
flags: Path
binary: str

View File

@ -12,7 +12,8 @@ from typing import TYPE_CHECKING
import pytest
from python_pkg.vscode_optimizer import _optimize as opt
from python_pkg.vscode_optimizer import _hardware as opt
from python_pkg.vscode_optimizer._types import _Hw
from python_pkg.vscode_optimizer.tests.conftest import fake_run as _fake_run
if TYPE_CHECKING:
@ -35,7 +36,7 @@ def test_detect_gpu_reads_nvidia_vram(monkeypatch: pytest.MonkeyPatch) -> None:
"run",
_fake_run({"lspci": _NVIDIA_LINE, "nvidia-smi": "24576\n"}),
)
hw = opt._Hw()
hw = _Hw()
opt._detect_gpu(hw)
@ -49,7 +50,7 @@ def test_detect_gpu_tolerates_nvidia_smi_being_absent(
) -> None:
"""Without nvidia-smi the vendor is still known; VRAM stays unset."""
monkeypatch.setattr(subprocess, "run", _fake_run({"lspci": _NVIDIA_LINE}))
hw = opt._Hw()
hw = _Hw()
opt._detect_gpu(hw)
@ -76,7 +77,7 @@ def test_detect_gpu_maps_each_vendor_keyword(
) -> None:
"""Vendor keywords map to display names; 3D controllers count as GPUs."""
monkeypatch.setattr(subprocess, "run", _fake_run({"lspci": line}))
hw = opt._Hw()
hw = _Hw()
opt._detect_gpu(hw)
@ -100,7 +101,7 @@ def test_detect_gpu_should_recognise_an_intel_igpu(
"""An Intel iGPU ought to be detected as Intel."""
line = "00:02.0 VGA compatible controller: Intel UHD Graphics 630"
monkeypatch.setattr(subprocess, "run", _fake_run({"lspci": line}))
hw = opt._Hw()
hw = _Hw()
opt._detect_gpu(hw)
@ -114,7 +115,7 @@ def test_detect_gpu_leaves_defaults_when_lspci_lists_no_display_device(
monkeypatch.setattr(
subprocess, "run", _fake_run({"lspci": "00:1f.3 Audio device: Intel\n"})
)
hw = opt._Hw()
hw = _Hw()
opt._detect_gpu(hw)
@ -130,7 +131,7 @@ def test_detect_gpu_skips_non_display_devices(
monkeypatch.setattr(
subprocess, "run", _fake_run({"lspci": listing, "nvidia-smi": "24576"})
)
hw = opt._Hw()
hw = _Hw()
opt._detect_gpu(hw)
@ -179,7 +180,7 @@ def test_detect_disk_classifies_the_root_device(
"""Partition suffixes are stripped, then rotational/nvme decide the type."""
monkeypatch.setattr(subprocess, "run", _fake_run({"findmnt": device}))
_disk_paths(monkeypatch, tmp_path, base, rotational)
hw = opt._Hw()
hw = _Hw()
opt._detect_disk(hw)
@ -191,7 +192,7 @@ def test_detect_disk_gives_up_when_findmnt_says_nothing(
) -> None:
"""No root device means the type stays unknown."""
monkeypatch.setattr(subprocess, "run", _fake_run({}))
hw = opt._Hw()
hw = _Hw()
opt._detect_disk(hw)
@ -204,7 +205,7 @@ def test_detect_disk_gives_up_without_a_rotational_flag(
"""A device with no sysfs queue entry (e.g. dm-crypt) stays unknown."""
monkeypatch.setattr(subprocess, "run", _fake_run({"findmnt": "/dev/mapper/root"}))
_disk_paths(monkeypatch, tmp_path, "mapper/root", None)
hw = opt._Hw()
hw = _Hw()
opt._detect_disk(hw)
@ -218,10 +219,10 @@ def test_detect_hardware_runs_every_probe(monkeypatch: pytest.MonkeyPatch) -> No
"""The aggregate calls all four detectors against one _Hw."""
called: list[str] = []
def _recorder(name: str) -> Callable[[opt._Hw], None]:
def _recorder(name: str) -> Callable[[_Hw], None]:
"""Build a probe double that records the name it stands in for."""
def _probe(_hw: opt._Hw) -> None:
def _probe(_hw: _Hw) -> None:
called.append(name)
return _probe
@ -231,5 +232,5 @@ def test_detect_hardware_runs_every_probe(monkeypatch: pytest.MonkeyPatch) -> No
result = opt._detect_hardware()
assert isinstance(result, opt._Hw)
assert isinstance(result, _Hw)
assert called == ["_detect_cpu", "_detect_ram", "_detect_gpu", "_detect_disk"]

View File

@ -12,7 +12,8 @@ from typing import TYPE_CHECKING
import pytest
from python_pkg.vscode_optimizer import _optimize as opt
from python_pkg.vscode_optimizer import _hardware as opt
from python_pkg.vscode_optimizer._types import _Hw
from python_pkg.vscode_optimizer.tests.conftest import FakeProc as _Proc
from python_pkg.vscode_optimizer.tests.conftest import fake_run as _fake_run
@ -64,7 +65,7 @@ def test_detect_cpu_reads_model_cores_and_clock(
) -> None:
"""Each recognised lscpu key lands on its typed _Hw attribute."""
monkeypatch.setattr(subprocess, "run", _fake_run({"lscpu": _LSCPU_OUT}))
hw = opt._Hw()
hw = _Hw()
opt._detect_cpu(hw)
@ -81,7 +82,7 @@ def test_detect_cpu_ignores_unknown_keys(monkeypatch: pytest.MonkeyPatch) -> Non
"run",
_fake_run({"lscpu": "Architecture: x86_64\nBogoMIPS: 9000\n"}),
)
hw = opt._Hw()
hw = _Hw()
opt._detect_cpu(hw)
@ -99,7 +100,7 @@ def test_detect_ram_converts_kb_to_mb(
meminfo = tmp_path / "meminfo"
meminfo.write_text("MemTotal: 32768000 kB\nMemFree: 100 kB\n")
monkeypatch.setattr(opt, "Path", lambda _p: meminfo)
hw = opt._Hw()
hw = _Hw()
opt._detect_ram(hw)
@ -113,7 +114,7 @@ def test_detect_ram_leaves_zero_when_memtotal_is_absent(
meminfo = tmp_path / "meminfo"
meminfo.write_text("SwapTotal: 4096 kB\n")
monkeypatch.setattr(opt, "Path", lambda _p: meminfo)
hw = opt._Hw()
hw = _Hw()
opt._detect_ram(hw)
@ -125,7 +126,7 @@ def test_detect_ram_survives_an_unreadable_procfs(
) -> None:
"""An OSError reading /proc/meminfo leaves the default in place."""
monkeypatch.setattr(opt, "Path", lambda _p: tmp_path / "does-not-exist")
hw = opt._Hw()
hw = _Hw()
opt._detect_ram(hw)

View File

@ -13,7 +13,8 @@ from typing import TYPE_CHECKING
import pytest
from python_pkg.vscode_optimizer import _optimize as opt
from python_pkg.vscode_optimizer import _config_io as opt
from python_pkg.vscode_optimizer._types import _Opt
if TYPE_CHECKING:
from pathlib import Path
@ -183,8 +184,8 @@ def test_write_settings_merges_the_plan_over_the_current_values(
path = tmp_path / "nested" / "settings.json"
current: dict[str, object] = {"editor.fontSize": 14, "a": 1}
opts = [
opt._Opt("a", 2, "reason"),
opt._Opt(key="b", value=True, reason="reason"),
_Opt("a", 2, "reason"),
_Opt(key="b", value=True, reason="reason"),
]
opt._write_settings(path, current, opts)

View File

@ -14,15 +14,17 @@ from typing import TYPE_CHECKING
import pytest
from python_pkg.vscode_optimizer import _config_io as config_io
from python_pkg.vscode_optimizer import _optimize as opt
from python_pkg.vscode_optimizer._types import _Hw, _Variant
if TYPE_CHECKING:
from pathlib import Path
def _variant(tmp_path: Path) -> opt._Variant:
def _variant(tmp_path: Path) -> _Variant:
"""A VS Code variant rooted entirely inside tmp_path."""
return opt._Variant(
return _Variant(
name="Test Code",
settings=tmp_path / "User" / "settings.json",
flags=tmp_path / "code-flags.conf",
@ -30,7 +32,7 @@ def _variant(tmp_path: Path) -> opt._Variant:
)
_HW = opt._Hw(cpu_physical_cores=8, ram_total_mb=32000, gpu_vendor="NVIDIA")
_HW = _Hw(cpu_physical_cores=8, ram_total_mb=32000, gpu_vendor="NVIDIA")
# --------------------------------------------------------------------------- #
@ -232,8 +234,8 @@ def test_the_suite_never_touches_the_real_user_settings(
A regression here would mean some test could rewrite the developer's own
VS Code configuration, which is exactly what --dry-run exists to prevent.
"""
monkeypatch.setattr(opt.Path, "home", classmethod(lambda _cls: tmp_path))
monkeypatch.setattr(config_io.Path, "home", classmethod(lambda _cls: tmp_path))
for variant in opt._discover_variants():
for variant in config_io._discover_variants():
assert tmp_path in variant.settings.parents
assert tmp_path in variant.flags.parents

View File

@ -9,10 +9,11 @@ from __future__ import annotations
import pytest
from python_pkg.vscode_optimizer import _optimize as opt
from python_pkg.vscode_optimizer import _plan as opt
from python_pkg.vscode_optimizer._types import _Hw, _Opt
def _opts_by_key(opts: list[opt._Opt]) -> dict[str, object]:
def _opts_by_key(opts: list[_Opt]) -> dict[str, object]:
"""Index a plan by setting key, for assertions that ignore ordering."""
return {o.key: o.value for o in opts}
@ -91,7 +92,7 @@ def test_dict_merge_opt_reports_no_current_value_when_the_key_is_absent() -> Non
# --------------------------------------------------------------------------- #
def test_compute_opts_scales_threads_and_memory_to_the_hardware() -> None:
"""Physical cores drive search threads; RAM drives the large-file budget."""
hw = opt._Hw(cpu_physical_cores=12, ram_total_mb=32000)
hw = _Hw(cpu_physical_cores=12, ram_total_mb=32000)
plan = _opts_by_key(opt._compute_opts(hw, {}))
@ -101,7 +102,7 @@ def test_compute_opts_scales_threads_and_memory_to_the_hardware() -> None:
def test_compute_opts_keeps_a_floor_under_search_threads() -> None:
"""A dual-core machine still gets the four-thread minimum."""
hw = opt._Hw(cpu_physical_cores=2)
hw = _Hw(cpu_physical_cores=2)
plan = _opts_by_key(opt._compute_opts(hw, {}))
@ -110,7 +111,7 @@ def test_compute_opts_keeps_a_floor_under_search_threads() -> None:
def test_compute_opts_enables_gpu_extras_for_a_dedicated_card() -> None:
"""A discrete GPU turns on terminal acceleration and smooth scrolling."""
hw = opt._Hw(gpu_vendor="NVIDIA")
hw = _Hw(gpu_vendor="NVIDIA")
plan = _opts_by_key(opt._compute_opts(hw, {}))
@ -122,7 +123,7 @@ def test_compute_opts_enables_gpu_extras_for_a_dedicated_card() -> None:
def test_compute_opts_leaves_gpu_extras_alone_without_a_dedicated_card() -> None:
"""Integrated or unknown graphics get no GPU-dependent settings."""
plan = _opts_by_key(opt._compute_opts(opt._Hw(gpu_vendor="Intel"), {}))
plan = _opts_by_key(opt._compute_opts(_Hw(gpu_vendor="Intel"), {}))
assert "terminal.integrated.gpuAcceleration" not in plan
assert "editor.smoothScrolling" not in plan
@ -130,7 +131,7 @@ def test_compute_opts_leaves_gpu_extras_alone_without_a_dedicated_card() -> None
def test_compute_opts_proposes_nothing_already_set() -> None:
"""Settings that already hold the ideal value are not re-proposed."""
hw = opt._Hw(cpu_physical_cores=8, ram_total_mb=32000)
hw = _Hw(cpu_physical_cores=8, ram_total_mb=32000)
current: dict[str, object] = {
"search.maxThreads": 8,
"files.maxMemoryForLargeFilesMB": 4096,
@ -164,7 +165,7 @@ def test_compute_opts_raises_only_a_low_submodule_limit(
{} if current_limit is None else {"git.detectSubmodulesLimit": current_limit}
)
plan = _opts_by_key(opt._compute_opts(opt._Hw(), current))
plan = _opts_by_key(opt._compute_opts(_Hw(), current))
assert ("git.detectSubmodulesLimit" in plan) is proposed
@ -175,7 +176,7 @@ def test_compute_opts_records_the_previous_value_for_display() -> None:
minimap = next(
o
for o in opt._compute_opts(opt._Hw(), current)
for o in opt._compute_opts(_Hw(), current)
if o.key == "editor.minimap.enabled"
)
@ -188,8 +189,8 @@ def test_compute_opts_records_the_previous_value_for_display() -> None:
# --------------------------------------------------------------------------- #
def test_gpu_flags_adds_a_video_decode_flag_only_for_nvidia() -> None:
"""NVIDIA gets the VAAPI extras on top of the shared rasterization flags."""
nvidia = opt._gpu_flags(opt._Hw(gpu_vendor="NVIDIA"))
amd = opt._gpu_flags(opt._Hw(gpu_vendor="AMD"))
nvidia = opt._gpu_flags(_Hw(gpu_vendor="NVIDIA"))
amd = opt._gpu_flags(_Hw(gpu_vendor="AMD"))
assert "--enable-gpu-rasterization" in nvidia
assert "--enable-zero-copy" in nvidia
@ -199,7 +200,7 @@ def test_gpu_flags_adds_a_video_decode_flag_only_for_nvidia() -> None:
def test_gpu_flags_gives_intel_a_reduced_set() -> None:
"""Intel gets rasterization and decode, but not zero-copy."""
flags = opt._gpu_flags(opt._Hw(gpu_vendor="Intel"))
flags = opt._gpu_flags(_Hw(gpu_vendor="Intel"))
assert "--enable-gpu-rasterization" in flags
assert "--enable-zero-copy" not in flags
@ -207,4 +208,4 @@ def test_gpu_flags_gives_intel_a_reduced_set() -> None:
def test_gpu_flags_are_empty_for_an_unknown_gpu() -> None:
"""No flags are forced on hardware the script could not identify."""
assert opt._gpu_flags(opt._Hw()) == []
assert opt._gpu_flags(_Hw()) == []

View File

@ -8,7 +8,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from python_pkg.vscode_optimizer import _optimize as opt
from python_pkg.vscode_optimizer import _render as opt
from python_pkg.vscode_optimizer._types import _Hw, _Opt
if TYPE_CHECKING:
import pytest
@ -18,7 +19,7 @@ def test_show_hw_reports_every_detected_component(
capsys: pytest.CaptureFixture[str],
) -> None:
"""CPU, RAM, GPU and disk each appear, with RAM converted to GB."""
hw = opt._Hw(
hw = _Hw(
cpu_model="AMD Ryzen 9 7900X3D",
cpu_physical_cores=12,
cpu_logical_cores=24,
@ -46,7 +47,7 @@ def test_show_hw_omits_vram_when_it_is_unknown(
capsys: pytest.CaptureFixture[str],
) -> None:
"""A card whose VRAM could not be read shows no empty parentheses."""
opt._show_hw(opt._Hw(gpu_vendor="AMD", gpu_model="Radeon"))
opt._show_hw(_Hw(gpu_vendor="AMD", gpu_model="Radeon"))
assert "VRAM" not in capsys.readouterr().out
@ -56,7 +57,7 @@ def test_show_plan_lists_new_flags_and_settings(
) -> None:
"""Each proposed change is numbered and shows before -> after."""
opts = [
opt._Opt(
_Opt(
key="editor.minimap.enabled",
value=False,
reason="Minimap costs GPU",
@ -101,7 +102,7 @@ def test_show_plan_renders_an_addition_with_no_previous_value(
capsys: pytest.CaptureFixture[str],
) -> None:
"""A setting that was never set shows a dash as its 'before'."""
opt._show_plan([opt._Opt("search.maxThreads", 12, "cores")], [], [])
opt._show_plan([_Opt("search.maxThreads", 12, "cores")], [], [])
assert "-" in capsys.readouterr().out
@ -112,7 +113,7 @@ def test_show_plan_truncates_very_long_values(
"""A large exclude dict is clipped so one setting cannot flood the screen."""
big = {f"**/dir{i}": True for i in range(50)}
opt._show_plan([opt._Opt("files.watcherExclude", big, "exclude", big)], [], [])
opt._show_plan([_Opt("files.watcherExclude", big, "exclude", big)], [], [])
for line in capsys.readouterr().out.splitlines():
assert len(line) < 200