Code cleanup and improvements.

This commit is contained in:
2026-09-03 21:52:06 +02:00
parent ea323e3ab0
commit ecdea827ac
20 changed files with 312 additions and 213 deletions

View File

@@ -924,7 +924,7 @@ def _rewrite_wrapped_file(
output: Path,
patches: list[_BinaryPatch],
) -> tuple[str, int]:
ordered = sorted(patches, key=lambda patch: patch.offset)
ordered = sorted(patches, key=lambda item: item.offset)
previous_end = 0
for patch in ordered:
if patch.offset < previous_end:

View File

@@ -40,19 +40,11 @@ class GameConfig:
virtual_desktop: str
@dataclass(frozen=True)
class SafetyConfig:
allow_live_game_modification: bool
allow_prefix_modification: bool
allow_binary_patch: bool
@dataclass(frozen=True)
class AppConfig:
paths: PathsConfig
proton7: Proton7Config
game: GameConfig
safety: SafetyConfig
source: Path
def to_dict(self) -> dict[str, Any]:
@@ -81,7 +73,6 @@ def load_config(path: str | Path | None = None) -> AppConfig:
paths = _section(data, "paths")
proton7 = _section(data, "proton7")
game = _section(data, "game")
safety = _section(data, "safety")
virtual_desktop = _required_string(game, "virtual_desktop")
if re.fullmatch(r"[1-9][0-9]*x[1-9][0-9]*", virtual_desktop) is None:
raise ConfigError("game.virtual_desktop must look like 1600x900")
@@ -105,15 +96,6 @@ def load_config(path: str | Path | None = None) -> AppConfig:
args=_required_string_tuple(game, "args"),
virtual_desktop=virtual_desktop,
),
safety=SafetyConfig(
allow_live_game_modification=_required_bool(
safety, "allow_live_game_modification"
),
allow_prefix_modification=_required_bool(
safety, "allow_prefix_modification"
),
allow_binary_patch=_required_bool(safety, "allow_binary_patch"),
),
source=config_path,
)
@@ -145,13 +127,6 @@ def _required_string_tuple(section: Mapping[str, Any], key: str) -> tuple[str, .
return tuple(value)
def _required_bool(section: Mapping[str, Any], key: str) -> bool:
value = section.get(key)
if not isinstance(value, bool):
raise ConfigError(f"{key} must be true or false")
return value
def _paths_to_strings(value: Any) -> Any:
if isinstance(value, Path):
return str(value)

View File

@@ -12,7 +12,7 @@ import signal
import subprocess
import sys
import time
from typing import Any, Mapping, Sequence, TextIO
from typing import Any, Mapping, Sequence
from .config import AppConfig, project_root
from .filesystem import sha256_file, verify_known_game_hashes
@@ -190,7 +190,6 @@ def _run_standard_experiment(
_dry_run_commands(
config,
testcase,
result,
logger,
registry_before,
launch_argv,
@@ -1025,7 +1024,6 @@ def _preflight(
def _dry_run_commands(
config: AppConfig,
testcase: Testcase,
result: ResultDirectory,
logger: CommandLogger,
registry_before: DesktopRegistrySnapshot,
launch_argv: list[str],
@@ -1087,8 +1085,6 @@ def _run_and_watch(
process: subprocess.Popen[str] | None = None
process_identity: dict[str, Any] | None = None
failure_state: LogFileState | None = None
failure_analysis: dict[str, Any] | None = None
failure_text = ""
failure_trigger_mtime_ns: int | None = None
current_state: LogFileState | None = None
current_analysis: dict[str, Any] | None = None
@@ -1121,6 +1117,7 @@ def _run_and_watch(
error=str(exc),
)
raise ExperimentError(f"Could not launch experiment: {exc}") from exc
assert process is not None
logger.record_lifecycle(
argv,
state="started",
@@ -1156,8 +1153,6 @@ def _run_and_watch(
current_text = read_current_run_log_text(state, baseline)
if analysis.load_texture_to_material_failed:
failure_state = state
failure_analysis = analysis.to_summary()
failure_text = read_current_run_log_text(state, baseline)
failure_trigger_mtime_ns = state.mtime_ns
detected_at_ns = now_ns
post_failure_deadline_ns = now_ns + int(
@@ -1235,12 +1230,9 @@ def _run_and_watch(
final_states = snapshot_native_logs(config.paths.native_logs)
final_state = final_states.get(str(failure_state.path.absolute()))
if final_state is not None:
failure_state = final_state
failure_text = read_current_run_log_text(final_state, baseline)
failure_analysis = parse_current_run_log(final_state, baseline).to_summary()
current_state = final_state
current_text = failure_text
current_analysis = failure_analysis
current_text = read_current_run_log_text(final_state, baseline)
current_analysis = parse_current_run_log(final_state, baseline).to_summary()
if current_state is not None:
_write_text(result.path / "native.log", current_text)
whispering_vale = (
@@ -1381,8 +1373,13 @@ def _launch_argv(config: AppConfig, testcase: Testcase, result_directory: Path)
def _proton_command(config: AppConfig, *arguments: str) -> list[str]:
shim = Path(__file__).with_name("proton_compat.py")
return [sys.executable, str(shim), str(config.proton7.proton), *arguments]
shim = project_root() / "ptlinux/proton_compat.py"
return [
sys.executable,
str(shim),
str(config.proton7.proton),
*arguments,
]
def _runtime_environment(
@@ -1659,7 +1656,7 @@ def _collect_hashes(config: AppConfig, testcase: Testcase) -> list[dict[str, Any
config.proton7.prefix
/ "drive_c/windows/syswow64/d3dcompiler_43.dll": "native-d3dcompiler-43",
config.proton7.prefix / "drive_c/windows/syswow64/d3d9.dll": "dxvk-d3d9",
Path(__file__).with_name("proton_compat.py"): "proton-python314-shim",
project_root() / "ptlinux/proton_compat.py": "proton-python314-shim",
config.source: "local-config",
testcase.source: "testcase",
}

View File

@@ -11,7 +11,7 @@ from typing import Iterable, Mapping
DEFAULT_NATIVE_LOG_PATTERNS = ("log_*.txt", "*.log")
_SCENE_RE = re.compile(
r"\[S1\]\s+after import\s+(?P<scene>[^:]+):\s*"
r"\[S1]\s+after import\s+(?P<scene>[^:]+):\s*"
r"materials=(?P<materials>\d+)\s+textures=(?P<textures>\d+)",
re.IGNORECASE,
)
@@ -24,14 +24,14 @@ _FAILED_IMPORT_RE = re.compile(
re.IGNORECASE,
)
_MODULE_CODE_SYNC_RE = re.compile(
r"Module Code Sync Error\s*\(\[(?P<left>-?\d+)\]\s*!=\s*"
r"\[(?P<right>-?\d+)\]\)",
r"Module Code Sync Error\s*\(\[(?P<left>-?\d+)]\s*!=\s*"
r"\[(?P<right>-?\d+)]\)",
re.IGNORECASE,
)
_TIMESTAMP_RE = re.compile(
r"^\s*(?:\[)?(?P<timestamp>"
r"^\s*\[?(?P<timestamp>"
r"\d{4}-\d{2}-\d{2}[T ][0-9:.+Z-]+|\d{2}:\d{2}:\d{2}(?:\.\d+)?)"
r"(?:\])?"
r"]?"
)
@@ -90,9 +90,6 @@ class NativeLogAnalysis:
return imports[-1]
return None
def first_event(self, kind: str) -> LogEvent | None:
return next((event for event in self.events if event.kind == kind), None)
def to_summary(self) -> dict[str, object]:
summary: dict[str, object] = {
"scenes": {

View File

@@ -1,32 +0,0 @@
"""Run the retained Proton 7 script safely on Python 3.14.
Proton 7 predates Python's tar extraction-filter API. The known-good v41
runtime used this compatibility shim; keeping it in the harness makes the
exact launch recipe inspectable and reproducible.
"""
from __future__ import annotations
from pathlib import Path
import runpy
import sys
import tarfile
from typing import Sequence
def main(argv: Sequence[str] | None = None) -> None:
arguments = list(sys.argv[1:] if argv is None else argv)
if not arguments:
raise SystemExit("usage: proton_compat.py /path/to/proton [arguments...]")
proton = Path(arguments[0]).expanduser().resolve(strict=True)
proton_directory = str(proton.parent)
if proton_directory not in sys.path:
sys.path.insert(0, proton_directory)
if hasattr(tarfile, "fully_trusted_filter"):
tarfile.TarFile.extraction_filter = staticmethod(tarfile.fully_trusted_filter)
sys.argv = [str(proton), *arguments[1:]]
runpy.run_path(str(proton), run_name="__main__")
if __name__ == "__main__":
main()

View File

@@ -11,7 +11,7 @@ from .filesystem import sha256_file
_VALUE_RE = re.compile(r'^(?P<name>"(?:\\.|[^"\\])*")=(?P<value>.*)$')
_SECTION_RE = re.compile(r"^\[(?P<name>.*)\](?:\s+[0-9]+)?$")
_SECTION_RE = re.compile(r"^\[(?P<name>.*)](?:\s+[0-9]+)?$")
@dataclass(frozen=True)

View File

@@ -29,18 +29,12 @@ class TestDisplay:
@dataclass(frozen=True)
class TestWatch:
require_new_native_log: bool
patterns: tuple[str, ...]
@dataclass(frozen=True)
class TestCapture:
process_maps: bool
cwd: bool
environment: bool
dll_overrides: bool
hashes: bool
dxvk_log: bool
native_log: bool
strace_files: bool
performance_stats: bool
focus_before_seconds: float
@@ -123,16 +117,10 @@ def load_testcase(value: str | Path) -> Testcase:
env=_string_mapping(environment, "env"),
watch=TestWatch(
require_new_native_log=_boolean(watch, "require_new_native_log"),
patterns=_string_tuple(watch, "patterns"),
),
capture=TestCapture(
process_maps=_boolean(capture, "process_maps"),
cwd=_boolean(capture, "cwd"),
environment=_boolean(capture, "environment"),
dll_overrides=_boolean(capture, "dll_overrides"),
hashes=_boolean(capture, "hashes"),
dxvk_log=_boolean(capture, "dxvk_log"),
native_log=_boolean(capture, "native_log"),
strace_files=_boolean(capture, "strace_files"),
performance_stats=_optional_boolean(capture, "performance_stats", False),
focus_before_seconds=_optional_nonnegative_number(