Run lutris-wrapper with the current Python interpreter

MonitoredCommand previously launched lutris-wrapper via its
`#!/usr/bin/env python3` shebang and worked around the resulting
ambiguity by exporting PYTHONPATH=":".join(sys.path) on the subprocess
env. The original author flagged the PYTHONPATH line as suspect
("not clear why this needs to be added"); the actual reason was that
in scenarios where the shebang picks up a different Python than the
one running Lutris (an AppImage with a bundled interpreter, a venv
launched from outside, etc.), the wrapper couldn't import lutris's
modules without that hint.

Two problems with that workaround:

1. When the shebang resolves to a host Python with a different minor
   version, the inherited PYTHONPATH points at our stdlib, and host
   Python crashes during site initialization with a `_sre` MAGIC
   mismatch (a .pth file does `import importlib`, finds ours via
   PYTHONPATH, ours doesn't match the host interpreter's compiled
   constants).
2. PYTHONPATH propagates to every subprocess the wrapper then spawns,
   poisoning Wine, umu-launcher, RetroArch, every emulator runner —
   none of which need Lutris's sys.path and many of which break in
   subtle ways when handed an inappropriate one.

Invoke the wrapper as [sys.executable, WRAPPER_SCRIPT, ...] so the
interpreter is unambiguous, and drop the PYTHONPATH export. sys.path
is naturally inherited by the wrapper since it's running the same
Python, and host subprocesses get a clean environment.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Daniel Johnson
2026-06-07 10:27:07 -04:00
parent 3da2d31a74
commit 8789d2bbba

View File

@@ -97,8 +97,17 @@ class MonitoredCommand:
def get_wrapper_command(self) -> list[str]:
"""Return launch arguments for the wrapper script"""
# Invoke the wrapper via the current Python explicitly rather than
# relying on its `#!/usr/bin/env python3` shebang. The shebang
# resolves through PATH at exec time, which silently picks up a
# different interpreter in distribution scenarios where the host
# Python doesn't match the one running Lutris (AppImage, venv
# launched from outside, etc.) — and a wrong-version Python
# crashes hard on Lutris's own stdlib via the inherited
# PYTHONPATH.
wrapper_command = (
[
sys.executable,
WRAPPER_SCRIPT,
self._title,
str(len(self.include_processes)),
@@ -133,9 +142,6 @@ class MonitoredCommand:
"""Process the user provided environment variables for use as self.env"""
env = copy(user_env) if user_env else {}
# not clear why this needs to be added, the path is already added in
# the wrappper script.
env["PYTHONPATH"] = ":".join(sys.path)
# Drop bad values of environment keys, those will confuse the Python
# interpreter.
game_uuid = str(uuid.uuid4())