%global pypi_name pylsl Name: python-%{pypi_name} Version: 1.18.2 Release: 3%{?dist} Summary: Python library for LSL (lab streaming layer) License: MIT URL: https://github.com/labstreaminglayer/pylsl Source0: https://files.pythonhosted.org/packages/source/p/%{pypi_name}/%{pypi_name}-%{version}.tar.gz BuildArch: noarch BuildRequires: python3-devel BuildRequires: pyproject-rpm-macros # setuptools-scm reads PKG-INFO from the sdist; no .git needed. # liblsl is needed at BUILD time too, because %%check now actually imports # pylsl, and pylsl.lib.__init__ dlopens liblsl at import. See %%check. BuildRequires: liblsl >= 1.17.0 %global _description %{expand: pylsl is a Python interface to the Lab Streaming Layer (LSL). LSL is an open-source networked middleware ecosystem for real-time streaming, recording, and analysis of time-series data measured from any sensor or device. This package provides Python bindings around the upstream liblsl C/C++ library; liblsl itself ships separately and is loaded at runtime via ctypes.find_library("lsl").} %description %{_description} %package -n python3-%{pypi_name} Summary: %{summary} # pylsl resolves liblsl with ctypes at IMPORT time, not on first use, so # without this `import pylsl` raises RuntimeError and every consumer dies. # # This Requires was in the PREAMBLE until 1.18.2-3, where it shipped on # nothing: this spec builds only python3-pylsl, the main "python-pylsl" is a # name that never becomes an rpm, and a preamble Requires belongs to the main # package. Exactly PACKAGING-POLICY rule 2. The published # python3-pylsl-1.18.2-2 requires only python(abi) and numpy -- verified with # `dnf repoquery --requires`, not by reading the spec, because reading the # spec is what made it look correct for four months. # # Found from the outside: python3-NaviNIBS-Simulated-Tools and # python3-NaviNIBS-LSL-Output installed cleanly from the COPR and then failed # to import, as does python3-NaviNIBS itself: # RuntimeError: LSL binary library file was not found. Please make sure that # the binary file can be found in the package lib folder # (/usr/lib/python3.14/site-packages/pylsl/lib) or the system search path. # ci/check-pin-ships.sh cannot see this class: its matcher only looks at # macro-guarded pins (`%%{!?foo:UNSET}`), and a plain versioned Requires is # invisible to it. Requires: liblsl >= 1.17.0 %description -n python3-%{pypi_name} %{_description} %prep %autosetup -n %{pypi_name}-%{version} -p1 %generate_buildrequires %pyproject_buildrequires %build %pyproject_wheel %install %pyproject_install %pyproject_save_files -l -L %{pypi_name} %check # This was `:` until 1.18.2-3, skipped on the stated grounds that "the # Requires: liblsl on the binary package ensures runtime correctness". That # premise was false -- the Requires was on no package at all (see the # subpackage comment) -- so the one check that would have caught it was # skipped because of the very bug it would have caught. # # Now it imports for real. pylsl.lib.__init__ runs find_liblsl_libraries() at # import time and raises RuntimeError if the shared library is missing, so # this import IS the test that liblsl is reachable, and it fails in the build # root the moment the BuildRequires above is dropped. # # pylsl.examples.* is excluded, for two separate reasons, both observed in the # first build of this %%check rather than anticipated: # # - They are scripts, not modules: importing them RUNS them. The build log of # the attempt showed %%check opening real LSL outlets and streaming -- # "Created outlet with name BetaGen and type EEG", "sending data at # 16384.0 Hz" -- from inside the build root. A %%check that starts network # services is not an import check. # - pylsl.examples.ReceiveAndPlot additionally fails with # "ModuleNotFoundError: No module named 'pyqtgraph'". Fedora has # python3-pyqtgraph, but pulling a Qt plotting stack in so that one example # script imports is the wrong trade; the example documents what to install. # # The filter keeps pylsl itself plus info/inlet/lib/outlet/resolve/util, which # are the modules that matter and the ones that dlopen liblsl. test -s %{_pyproject_modules} || { echo "ERROR: %{_pyproject_modules} is missing or empty." >&2 exit 1 } grep -vE '^pylsl\.examples' %{_pyproject_modules} > %{_pyproject_modules}.filtered mv %{_pyproject_modules}.filtered %{_pyproject_modules} # pylsl.lib is the one that dlopens liblsl. If the filter above ever removes # it, this %%check stops testing the thing it exists to test. grep -qx 'pylsl\.lib' %{_pyproject_modules} || { echo "ERROR: pylsl.lib is not in the import list; the filter is wrong." >&2 cat %{_pyproject_modules} >&2 exit 1 } echo "import-checking $(wc -l < %{_pyproject_modules}) modules:" cat %{_pyproject_modules} %pyproject_check_import %files -n python3-%{pypi_name} -f %{pyproject_files} %license LICENSE %doc README.md %changelog * Sun Sep 27 2026 Morgan Hough - 1.18.2-3 - Move `Requires: liblsl >= 1.17.0` from the preamble onto python3-pylsl, where it ships. In the preamble it reached nothing: this spec builds only python3-pylsl and the main python-pylsl is never built, so the published python3-pylsl-1.18.2-2 requires only python(abi) and numpy. PACKAGING-POLICY rule 2, found from the outside -- python3-NaviNIBS, and the two new NaviNIBS addons, all install cleanly and then fail on `import pylsl` with "RuntimeError: LSL binary library file was not found". - Make %%check real. It was `:`, skipped on the grounds that the (absent) Requires guaranteed runtime correctness. pylsl dlopens liblsl at import, so %%pyproject_check_import is exactly the right test, and it needs the new BuildRequires: liblsl to pass -- which means the build now fails if either the BuildRequires or the runtime dependency is dropped again. - Exclude pylsl.examples.* from that import check. They are scripts: importing them ran them, and the first build of this %%check opened real LSL outlets and streamed at 16384 Hz from inside the build root. ReceiveAndPlot also needs an undeclared pyqtgraph. pylsl.lib -- the module that dlopens liblsl -- is asserted to still be in the list, so the filter cannot hollow the check out. * Tue Jun 02 2026 Morgan Hough - 1.18.2-2 - Pass -L to %%pyproject_save_files. Upstream pylsl 1.18.2 commented out the pyproject.toml license-file declaration (setuptools bug workaround), so the wheel ships no PEP 639 License-File metadata. -L disables the automatic license-file expectation; LICENSE is shipped via %%license in %%files manually. * Tue Jun 02 2026 Morgan Hough - 1.18.2-1 - Initial package for the mhough/neurofedora COPR. - Pure-Python noarch wheel; liblsl resolved at runtime via ctypes.