%global pypi_name NaviNIBS Name: python-NaviNIBS Version: 0.2.4 Release: 4%{?dist} Summary: Software for neuronavigated non-invasive brain stimulation License: LGPL-3.0-only URL: https://github.com/PrecisionNeuroLab/NaviNIBS Source0: %{pypi_source navinibs %{version} tar.gz} BuildArch: noarch BuildRequires: python3-devel # %%check imports pylsl, which dlopens the native liblsl at import time. # python3-pylsl now pulls liblsl in (fixed in python-pylsl-1.18.2-3; before # that its Requires sat in the preamble and shipped on nothing, which is why # this BuildRequires existed and why it hid the problem from %%check for two # releases). Kept explicit: %%check must not depend on another package's # dependency being right. BuildRequires: liblsl %global _description %{expand: NaviNIBS is comprehensive software for neuronavigated non-invasive brain stimulation (e.g. TMS). It provides real-time head/coil tracking, MRI-based targeting, trajectory planning, and integration with tracking hardware and stimulation devices for image-guided brain stimulation experiments.} %description %_description %package -n python3-%{pypi_name} Summary: %{summary} %description -n python3-%{pypi_name} %_description %prep %autosetup -n navinibs-%{version} # Adapt pyproject for Fedora: (1) relax the conservative <3.14 python cap # (poetry convention, no code reason) so it runs on Fedora 3.14; (2) relax # poetry's exact/caret dep pins to bare names (Fedora ships one version each); # (3) declare the package dir explicitly — the project name "NaviNIBS" is # mixed-case but poetry-core 2.x normalizes it to "navinibs" for module # auto-discovery and then can't find the NaviNIBS/ dir, so pin it. %{python3} - <<'EOF' import re p="pyproject.toml"; s=open(p).read() s=re.sub(r'">=3\.12,<3\.\d+"', '">=3.12,<3.16"', s) s=s.replace("[tool.poetry.dependencies]", '[tool.poetry]\npackages = [{ include = "NaviNIBS" }]\n\n[tool.poetry.dependencies]', 1) s=re.sub(r"(?s)(\[tool\.poetry\.dependencies\]).*?(?=\n\[)", lambda m: re.sub(r'(?m)^(?!python\s*=)([A-Za-z0-9_.-]+)(\s*=\s*)"[^"]*"', r'\1 = "*"', m.group(0)), s) open(p,"w").write(s) EOF # Pin the Qt binding. Fedora's python3-pyqtgraph hard-Requires python3-PyQt5 # (`rpm -q --whatrequires python3-PyQt5` -> python3-pyqtgraph), so any NaviNIBS # install has BOTH PyQt5 and PySide6 present. qtpy then selects PyQt5 and # pyqtgraph selects PySide6, and NaviNIBS/util/GUI/Dock.py:34 mixes the two in # one class statement: # # class AltTContainer(QtWidgets.QTabWidget, pgdc.TContainer): # TypeError: metaclass conflict: the metaclass of a derived class must be a # (non-strict) subclass of the metaclasses of all its bases # # measured on the installed system as # qtpy API: PyQt5 QTabWidget metaclass: # pyqtgraph QT_LIB: PySide6 TContainer metaclass: # # THIS is what the "PySide6 6.9 + Python 3.14 metaclass conflict" in 0.2.4-1's # %%check exclusions actually was. It is not a PySide6 or a Python 3.14 bug: # f44 now ships PySide6 6.11.2 and the conflict is unchanged, and it is # order-dependent (importing NaviNIBS.util.GUI.Dock on its own succeeds, but # reaching it through NaviNIBS.Navigator.GUI.ViewPanels.__init__ fails) which a # real incompatibility would not be. Setting both variables makes the two # libraries agree regardless of import order; setdefault so a user can still # override. NaviNIBS/__init__.py runs before every NaviNIBS submodule and before # the addon loader, so this one place covers /usr/bin/navinibs, library use, and # every addon. # # Verified after the change: NavigatorGUI, every ViewPanel and util.GUI.Dock # import, and both packaged addons resolve all of their entry-point classes # through NaviNIBS' own Addon loader -- 3 of 3, where it was 2 of 3 before. %{python3} - <<'EOF' p = "NaviNIBS/__init__.py" s = open(p).read() s = ("import os\n" "os.environ.setdefault('QT_API', 'pyside6')\n" "os.environ.setdefault('PYQTGRAPH_QT_LIB', 'PySide6')\n") + s open(p, "w").write(s) EOF grep -q "PYQTGRAPH_QT_LIB" NaviNIBS/__init__.py || exit 1 %generate_buildrequires %pyproject_buildrequires %build %pyproject_wheel %install %pyproject_install # f43's pyproject-rpm-macros reject -l when the wheel metadata carries no PEP 639 # License-File, which f43's poetry-core does not write ("No License-File (PEP # 639) in upstream metadata found", COPR 11106328). %%files lists COPYING and # COPYING.LESSER itself, so -L loses nothing there; f44+ keep -l unchanged. %if 0%{?fedora} && 0%{?fedora} < 44 %pyproject_save_files -L %{pypi_name} %else %pyproject_save_files -l %{pypi_name} %endif %check # Import-check the package, minus modules that can't be imported in the build # root. We filter the generated module list directly (the %%pyproject_check_import # -e option is not reliably forwarded by the macro on f44). Excluded: # - util.test*: pytest-based test modules shipped inside the package; pytest is # a test-group dependency, not a runtime one. # # NavigatorGUI, Navigator.GUI.ViewPanels* and util.GUI.Dock were excluded here # from 0.2.4-1 to -2, attributed to a "PySide6 6.9 + Python 3.14 metaclass # conflict ... a real upstream incompatibility". That diagnosis was wrong: it # was qtpy and pyqtgraph binding to two DIFFERENT Qt wrappers in the same # process. See the %%prep note. With QT_API/PYQTGRAPH_QT_LIB pinned they all # import, so the exclusion is gone and the import check now covers the GUI -- # which is the half of this package a user actually looks at. grep -vE 'NaviNIBS\.util\.test' \ %{_pyproject_modules} > %{_pyproject_modules}.filtered mv %{_pyproject_modules}.filtered %{_pyproject_modules} # Guard the filter, and assert the GUI modules are genuinely back in the list: # silently losing them again is exactly how this package spent two releases # not import-testing its own user interface. for m in NaviNIBS.Navigator.GUI.NavigatorGUI NaviNIBS.util.GUI.Dock; do grep -qxF "$m" %{_pyproject_modules} || { echo "ERROR: $m is not in the import list; the filter is wrong." >&2 exit 1 } done n=$(wc -l < %{_pyproject_modules}) echo "import-checking $n modules" test "$n" -ge 90 || { echo "ERROR: only $n modules to check; expected ~100." >&2 exit 1 } %pyproject_check_import %files -n python3-%{pypi_name} -f %{pyproject_files} %license COPYING COPYING.LESSER %doc README.md %{_bindir}/navinibs %changelog * Sat Oct 10 2026 Morgan Hough - 0.2.4-4 - f43: %%pyproject_save_files -L there. f43's macros fail -l because its poetry-core writes no PEP 639 License-File; %%files already lists the licence files explicitly. f44+ unchanged. * Sun Sep 27 2026 Morgan Hough - 0.2.4-3 - Pin the Qt binding in NaviNIBS/__init__.py (QT_API=pyside6, PYQTGRAPH_QT_LIB=PySide6, both via setdefault). Fedora's python3-pyqtgraph hard-Requires python3-PyQt5, so an installed NaviNIBS has both PyQt5 and PySide6; qtpy picked PyQt5 (sip.wrappertype) while pyqtgraph picked PySide6 (Shiboken.ObjectType), and util/GUI/Dock.py:34 derives one class from both: "TypeError: metaclass conflict". - That is the real cause of what 0.2.4-1 recorded as a "PySide6 6.9 + Python 3.14 metaclass conflict ... a real upstream incompatibility". It is neither: f44 now ships PySide6 6.11.2 with the same failure, and the failure is import-order dependent (util.GUI.Dock imports alone, and fails when reached via Navigator.GUI.ViewPanels.__init__), which an interpreter or binding incompatibility would not be. - So %%check no longer excludes NavigatorGUI, Navigator.GUI.ViewPanels* or util.GUI.Dock. It now import-tests ~100 modules including the whole GUI, and asserts those two modules are still in the list so they cannot be dropped again silently. Only util.test* stays excluded (pytest is a test-only dep). - Found while packaging python-NaviNIBS-Simulated-Tools: its SimulatedToolsPanel could not be loaded through NaviNIBS' own Addon loader on an installed f44 system. With this change both packaged addons resolve 3 of 3 entry-point classes, where it was 2 of 3. * Fri Sep 18 2026 Morgan Hough - 0.2.4-2 - Widen upstream's requires-python cap to <3.16 rather than <3.15. f45 now ships Python 3.15.0, and pip refused the build: "Package 'navinibs' requires a different Python: 3.15.0 not in '<3.15,>=3.12'" (10989556). The rewrite is now version-agnostic, so the next interpreter bump does not need another spec edit. %%check still import-tests ~87 modules, which is what would catch a real 3.15 incompatibility. * Sun Jul 26 2026 Morgan Hough - 0.2.4-1 - Initial package (deps of NaviNIBS packaged alongside) - Relax the <3.14 python cap and poetry's exact/caret dep pins for Fedora - Pin packages=[{include=NaviNIBS}] so poetry-core 2.x finds the mixed-case package dir (it normalizes the project name to lowercase for lookup) - BuildRequires liblsl and filter GUI/test modules from the import check (ViewPanels/NavigatorGUI/Dock hit a PySide6-6.9/py3.14 metaclass conflict)