Fail usefully on NixOS: probe QtCore, explain the missing-.so error
Build PCM package / build (push) Successful in 6s
Build PCM package / build (push) Successful in 6s
First NixOS attempt: pip wheel PySide6 cannot load libgthread-2.0.so.0 because NixOS has no FHS library paths. Nothing inside the venv can fix that (KiCad installs wheels only) - but the plugin made it worse twice over: - The backend probe imported bare PySide6, whose pure-Python __init__ succeeds even when QtCore's .so cannot load - so matplotlib was promised QtAgg and the error figure died at switch_backend, taking the failure report with it. The probe now imports <binding>.QtCore and falls through to Tk/Agg on a broken Qt. - The user got a raw ImportError traceback. A cannot-open-shared-object failure during the kipy/dialog imports now raises a UserFacingError that names the actual fixes: run KiCad in an FHS environment (steam-run) or enable nix-ld with Qt runtime libraries. The README Linux notes carry the same guidance. 141 passed on the dev stack and the Python 3.9 mac-equivalent stack. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -92,6 +92,14 @@ OS specifics are spelled out per step and in *Platform notes* below.
|
|||||||
On **ARM64 (aarch64)** there are no pyamg wheels —
|
On **ARM64 (aarch64)** there are no pyamg wheels —
|
||||||
`requirements.txt` skips pyamg there and the solver falls back to
|
`requirements.txt` skips pyamg there and the solver falls back to
|
||||||
Jacobi-CG: same results, noticeably slower on large grids.
|
Jacobi-CG: same results, noticeably slower on large grids.
|
||||||
|
**NixOS**: pip's Linux wheels link against standard FHS library
|
||||||
|
paths, which NixOS does not provide — PySide6 fails with
|
||||||
|
`libgthread-2.0.so.0: cannot open shared object file`. The plugin
|
||||||
|
cannot fix this from inside its venv (KiCad installs wheels only):
|
||||||
|
run KiCad inside an FHS environment (e.g. `steam-run kicad`) or
|
||||||
|
enable `programs.nix-ld` with Qt's runtime libraries (glib,
|
||||||
|
fontconfig, freetype, dbus, libGL, libxkbcommon and the X11/xcb
|
||||||
|
set).
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|||||||
@@ -38,10 +38,25 @@ def _fail(message: str, outdir) -> None:
|
|||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
outdir = None
|
outdir = None
|
||||||
|
try:
|
||||||
try:
|
try:
|
||||||
from kipy.errors import ApiError
|
from kipy.errors import ApiError
|
||||||
|
|
||||||
from . import board_io, dialog
|
from . import board_io, dialog
|
||||||
|
except ImportError as e:
|
||||||
|
if "cannot open shared object file" not in str(e):
|
||||||
|
raise
|
||||||
|
# pip's Linux wheels link against FHS system libraries;
|
||||||
|
# on NixOS those paths don't exist and PySide6/pynng die
|
||||||
|
# exactly like this. Nothing inside the venv can fix it.
|
||||||
|
raise UserFacingError(
|
||||||
|
f"A compiled dependency cannot load its system "
|
||||||
|
f"libraries: {e}\nThe plugin venv is built from pip "
|
||||||
|
f"wheels, which expect standard (FHS) library paths. "
|
||||||
|
f"On NixOS, run KiCad inside an FHS environment (e.g. "
|
||||||
|
f"steam-run) or enable programs.nix-ld with Qt's "
|
||||||
|
f"runtime libraries - see README, Platform notes."
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
kicad, board = board_io.connect()
|
kicad, board = board_io.connect()
|
||||||
stackup = board_io.get_stackup_info(board)
|
stackup = board_io.get_stackup_info(board)
|
||||||
|
|||||||
@@ -24,10 +24,16 @@ def _pick_backend():
|
|||||||
dependency and the selection dialog / progress window put a Qt event
|
dependency and the selection dialog / progress window put a Qt event
|
||||||
loop in this process, after which matplotlib refuses TkAgg
|
loop in this process, after which matplotlib refuses TkAgg
|
||||||
("Cannot load backend 'TkAgg' ... as 'qt' is currently running") -
|
("Cannot load backend 'TkAgg' ... as 'qt' is currently running") -
|
||||||
exactly what happened on macOS, whose bundled Python ships tkinter."""
|
exactly what happened on macOS, whose bundled Python ships tkinter.
|
||||||
|
|
||||||
|
The probe must import the binding's native core, not just the
|
||||||
|
package: on NixOS `import PySide6` succeeds (pure __init__) while
|
||||||
|
QtCore's .so cannot find the system libraries pip wheels expect
|
||||||
|
("libgthread-2.0.so.0: cannot open shared object file") - promising
|
||||||
|
QtAgg then kills even the error figure at switch_backend time."""
|
||||||
for qt in ("PySide6", "PyQt6", "PyQt5", "PySide2"):
|
for qt in ("PySide6", "PyQt6", "PyQt5", "PySide2"):
|
||||||
try:
|
try:
|
||||||
__import__(qt)
|
__import__(qt + ".QtCore")
|
||||||
return "QtAgg" if qt in ("PySide6", "PyQt6") else "Qt5Agg"
|
return "QtAgg" if qt in ("PySide6", "PyQt6") else "Qt5Agg"
|
||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -20,6 +20,25 @@ def test_backend_prefers_qt_over_tk():
|
|||||||
assert plots._pick_backend() in ("QtAgg", "Qt5Agg")
|
assert plots._pick_backend() in ("QtAgg", "Qt5Agg")
|
||||||
|
|
||||||
|
|
||||||
|
def test_backend_probe_requires_working_qtcore(monkeypatch):
|
||||||
|
# NixOS: `import PySide6` succeeds (a pure-Python __init__) while
|
||||||
|
# QtCore's .so cannot load the FHS system libraries pip wheels
|
||||||
|
# expect. The probe must import the native core and fall through -
|
||||||
|
# promising QtAgg kills even the error figure at switch_backend
|
||||||
|
# time, and the failure report with it.
|
||||||
|
import builtins
|
||||||
|
real_import = builtins.__import__
|
||||||
|
|
||||||
|
def broken_qt(name, *args, **kwargs):
|
||||||
|
if name.split(".")[0] in ("PySide6", "PyQt6", "PyQt5", "PySide2"):
|
||||||
|
raise ImportError("libgthread-2.0.so.0: cannot open shared "
|
||||||
|
"object file: No such file or directory")
|
||||||
|
return real_import(name, *args, **kwargs)
|
||||||
|
|
||||||
|
monkeypatch.setattr(builtins, "__import__", broken_qt)
|
||||||
|
assert plots._pick_backend() in ("TkAgg", None)
|
||||||
|
|
||||||
|
|
||||||
def test_output_dir_falls_back_when_board_dir_unwritable(
|
def test_output_dir_falls_back_when_board_dir_unwritable(
|
||||||
tmp_path, monkeypatch, capsys):
|
tmp_path, monkeypatch, capsys):
|
||||||
board_dir = tmp_path / "board"
|
board_dir = tmp_path / "board"
|
||||||
|
|||||||
Reference in New Issue
Block a user