Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ on:
workflow_dispatch:

env:
LSL_RELEASE_URL: "https://github.com/sccn/liblsl/releases/download/v1.16.2"
LSL_RELEASE: "1.16.2"
LSL_RELEASE_URL: "https://github.com/sccn/liblsl/releases/download/v1.17.4"
LSL_RELEASE: "1.17.4"

jobs:

Expand All @@ -19,10 +19,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v1
- uses: astral-sh/ruff-action@v1
with:
args: "format --check"
- uses: astral-sh/setup-uv@v4
- run: uv run ruff check
- run: uv run ruff format --check

test:
needs: style
Expand All @@ -37,16 +36,18 @@ jobs:
- uses: actions/checkout@v4
- name: Download liblsl (Windows)
if: matrix.os == 'windows-latest'
shell: bash
run: |
curl -L https://github.com/sccn/liblsl/releases/download/v1.16.2/liblsl-1.16.2-Win_amd64.zip -o liblsl.zip
unzip -oj liblsl.zip bin/lsl* -d src/pylsl/lib/
curl -L ${LSL_RELEASE_URL}/liblsl-${LSL_RELEASE}-Win_amd64.zip -o liblsl.zip
unzip -oj liblsl.zip '*/bin/lsl.dll' -d src/pylsl/lib/
- name: Install liblsl (MacOS)
if: matrix.os == 'macos-latest'
run: brew install labstreaminglayer/tap/lsl
run: |
brew install labstreaminglayer/tap/lsl
echo "PYLSL_LIB=$(brew --prefix lsl)/Frameworks/lsl.framework/Versions/A/lsl" >> $GITHUB_ENV
- name: Install liblsl (Ubuntu)
if: startsWith(matrix.os, 'ubuntu-')
run: |
sudo apt install -y libpugixml-dev
curl -L ${LSL_RELEASE_URL}/liblsl-${LSL_RELEASE}-$(lsb_release -sc)_amd64.deb -o liblsl.deb
sudo apt install ./liblsl.deb
- name: Install uv
Expand Down
155 changes: 125 additions & 30 deletions .github/workflows/publish-to-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,151 @@ name: Build and publish Python 🐍 distributions 📦 to PyPI
on:
release:
types: [published]
pull_request:
branches:
- main
workflow_dispatch:

env:
LSL_RELEASE_URL: "https://github.com/sccn/liblsl/releases/download/"
LSL_RELEASE: "1.16.2"
LSL_RELEASE_URL: "https://github.com/sccn/liblsl/releases/download/v1.17.4"
LSL_RELEASE: "1.17.4"

defaults:
run:
shell: bash

jobs:
deploy:
name: ${{ matrix.config.name }}
# Build pure Python wheel (no bundled library) as fallback
build-pure:
name: Build pure Python wheel
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Needed for setuptools-scm
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Build pure Python wheel
run: uv build
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-pure
path: dist/*

# Build platform-specific wheels with bundled liblsl
build-platform:
name: Build ${{ matrix.config.name }}
runs-on: ${{ matrix.config.os }}
permissions:
id-token: write
strategy:
fail-fast: false
matrix:
config:
- name: "ubuntu-24.04"
os: "ubuntu-latest"
pyarch: "x64"
- name: "windows-x64"
os: "windows-latest"
arch: "amd64"
pyarch: "x64"
- name: "windows-x86"
os: "windows-latest"
arch: "i386"
pyarch: "x86"
- name: "Windows x64"
os: windows-latest
arch: amd64
pyarch: x64
asset: "liblsl-${LSL_RELEASE}-Win_amd64.zip"
extract: "unzip -oj liblsl.zip '*/bin/lsl.dll' -d src/pylsl/lib"
- name: "Windows x86"
os: windows-latest
arch: i386
pyarch: x86
asset: "liblsl-${LSL_RELEASE}-Win_i386.zip"
extract: "unzip -oj liblsl.zip '*/bin/lsl.dll' -d src/pylsl/lib"
- name: "macOS universal"
os: macos-latest
arch: universal
pyarch: x64
asset: "lsl.xcframework.1.17.zip"
extract: |
unzip xcframework.zip
cp lsl.xcframework/macos-arm64_x86_64/lsl.framework/Versions/A/lsl src/pylsl/lib/liblsl.dylib
codesign -s - -f src/pylsl/lib/liblsl.dylib
- name: "Linux x86_64"
os: ubuntu-22.04
arch: x86_64
pyarch: x64
asset: "liblsl-${LSL_RELEASE}-jammy_amd64.deb"
extract: |
sudo apt-get update && sudo apt-get install -y libpugixml1v5
ar x liblsl.deb
tar -xf data.tar.* --wildcards '*/liblsl.so*'
cp ./usr/lib/liblsl.so* src/pylsl/lib/
repair: true
steps:
- uses: actions/checkout@v4
- name: Download liblsl (Windows)
if: matrix.config.os == 'windows-latest'
with:
fetch-depth: 0 # Needed for setuptools-scm

- name: Download liblsl
run: |
ASSET="${{ matrix.config.asset }}"
ASSET="${ASSET//\$\{LSL_RELEASE\}/$LSL_RELEASE}"
if [[ "$ASSET" == *xcframework* ]]; then
curl -L "${LSL_RELEASE_URL}/${ASSET}" -o xcframework.zip
elif [[ "$ASSET" == *.zip ]]; then
curl -L "${LSL_RELEASE_URL}/${ASSET}" -o liblsl.zip
elif [[ "$ASSET" == *.deb ]]; then
curl -L "${LSL_RELEASE_URL}/${ASSET}" -o liblsl.deb
else
curl -L "${LSL_RELEASE_URL}/${ASSET}" -o liblsl.tar.gz
fi

- name: Extract liblsl
run: |
curl -L ${LSL_RELEASE_URL}/v${LSL_RELEASE}/liblsl-${LSL_RELEASE}-Win_${{ matrix.config.arch}}.zip -o liblsl.zip
unzip -oj liblsl.zip bin/lsl* -d src/pylsl/lib
- name: Set up Python 3.x
uses: actions/setup-python@v4
mkdir -p src/pylsl/lib
${{ matrix.config.extract }}

- name: List bundled library
run: ls -la src/pylsl/lib/

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
architecture: ${{ matrix.config.pyarch }}

- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Build Package (Linux)
if: matrix.config.os != 'windows-latest'
run: uv build
- name: Build Package (Windows)
if: matrix.config.os == 'windows-latest'

- name: Build wheel
run: uv build --wheel
- name: Publish package distributions to PyPI
run: uv publish

- name: Repair wheel (Linux)
if: matrix.config.repair
run: |
pip install auditwheel patchelf
auditwheel repair dist/*.whl -w dist/
rm dist/*-none-any.whl

- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-${{ matrix.config.os }}-${{ matrix.config.arch }}
path: dist/*.whl

# Publish all wheels to PyPI
publish:
name: Publish to PyPI
needs: [build-pure, build-platform]
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- name: Download all wheels
uses: actions/download-artifact@v4
with:
path: dist
pattern: wheels-*
merge-multiple: true

- name: List wheels
run: ls -la dist/

- name: Install uv
uses: astral-sh/setup-uv@v4

- name: Publish to PyPI
if: github.event_name == 'release'
run: uv publish dist/*
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ examples = [
[dependency-groups]
dev = [
"pytest>=8.3.4",
"ruff>=0.8.2",
"ruff>=0.14",
]

[build-system]
Expand All @@ -65,4 +65,4 @@ license-files = []
version_file = "src/pylsl/__version__.py"

[tool.setuptools.package-data]
pylsl = ["lib/*.dll"]
pylsl = ["lib/*.dll", "lib/*.so*", "lib/*.dylib"]
2 changes: 1 addition & 1 deletion src/pylsl/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def __init__(
)
self.obj = ctypes.c_void_p(self.obj)
if not self.obj:
raise RuntimeError("could not create stream description " "object.")
raise RuntimeError("could not create stream description object.")

def __del__(self):
"""Destroy a previously created StreamInfo object."""
Expand Down
4 changes: 1 addition & 3 deletions src/pylsl/inlet.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ def __init__(
flags. Use `proc_ALL` for all flags. (default proc_none).
"""
if type(info) is list:
raise TypeError(
"description needs to be of type StreamInfo, " "got a list."
)
raise TypeError("description needs to be of type StreamInfo, got a list.")
self.obj = lib.lsl_create_inlet(info.obj, max_buflen, max_chunklen, recover)
self.obj = ctypes.c_void_p(self.obj)
if not self.obj:
Expand Down
4 changes: 2 additions & 2 deletions src/pylsl/lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,14 +257,14 @@ def find_liblsl_libraries(verbose=False):
lib.lsl_pull_chunk_str.restype = ctypes.c_long
lib.lsl_pull_chunk_buf.restype = ctypes.c_long
except Exception:
print("pylsl: chunk transfer functions not available in your liblsl " "version.")
print("pylsl: chunk transfer functions not available in your liblsl version.")
# noinspection PyBroadException
try:
lib.lsl_create_continuous_resolver.restype = ctypes.c_void_p
lib.lsl_create_continuous_resolver_bypred.restype = ctypes.c_void_p
lib.lsl_create_continuous_resolver_byprop.restype = ctypes.c_void_p
except Exception:
print("pylsl: ContinuousResolver not (fully) available in your liblsl " "version.")
print("pylsl: ContinuousResolver not (fully) available in your liblsl version.")


# int64 support on windows and 32bit OSes isn't there yet
Expand Down
7 changes: 2 additions & 5 deletions src/pylsl/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ def deprecated(reason: str = None):
def old_function():
pass
"""

def decorator(func):
message = f"Function '{func.__name__}' is deprecated."
if reason:
Expand All @@ -130,11 +131,7 @@ def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
warnings.simplefilter("always", DeprecationWarning) # ensure it shows up
warnings.warn(
message,
category=DeprecationWarning,
stacklevel=2
)
warnings.warn(message, category=DeprecationWarning, stacklevel=2)
warnings.simplefilter("default", DeprecationWarning)
return func(*args, **kwargs)

Expand Down