Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Improve CIs, example and code coverage #273

Merged
merged 12 commits into from
May 30, 2024
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
44 changes: 44 additions & 0 deletions .github/actions/get-testing-dataset/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: "Get MNE-LSL testing dataset"
description: "A composite action to get MNE-LSL testing dataset from cache or remote."
inputs:
sample:
description: "If True, retrieve the sample dataset."
required: false
default: "false"
testing:
description: "If True, retrieve the testing dataset."
required: false
default: "true"

runs:
using: "composite"
steps:
- name: Get dataset version file
shell: bash
run: |
curl https://raw.githubusercontent.com/mscheltienne/mne-lsl-datasets/main/version.txt -o mne_lsl_dataset_version.txt
- name: Cache testing dataset
if: ${{ inputs.testing == 'true' }}
id: cache_testing
uses: actions/cache@v4
with:
key: mne-lsl-testing-${{ runner.os }}-${{ hashFiles('mne_lsl_dataset_version.txt') }}
path: ~/mne_data/MNE-LSL-data/testing
- name: Download testing dataset
if: ${{ inputs.testing == 'true' && steps.cache_testing.outputs.cache-hit != 'true' }}
shell: bash
run: python -c "import mne_lsl; mne_lsl.datasets.testing.data_path()"
- name: Cache sample dataset
if: ${{ inputs.sample == 'true' }}
id: cache_sample
uses: actions/cache@v4
with:
key: mne-lsl-sample-${{ runner.os }}-${{ hashFiles('mne_lsl_dataset_version.txt') }}
path: ~/mne_data/MNE-LSL-data/sample
- name: Download sample dataset
if: ${{ inputs.sample == 'true' && steps.cache_sample.outputs.cache-hit != 'true' }}
shell: bash
run: python -c "import mne_lsl; mne_lsl.datasets.sample.data_path()"
- name: Remove dataset version file
shell: bash
run: rm mne_lsl_dataset_version.txt
37 changes: 37 additions & 0 deletions .github/actions/install-system-dependencies/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: "Install system dependencies"
description: "A composite action to install liblsl and system dependencies on different operating systems."

runs:
using: "composite"
steps:
- name: Install linux dependencies
if: ${{ runner.os == 'Linux' }}
shell: bash
run: |
sudo apt update
sudo apt install -y binutils libpugixml-dev qtbase5-dev qt5-qmake
- name: Install liblsl (linux)
if: ${{ runner.os == 'Linux' }}
shell: bash
run: |
curl -L https://github.com/sccn/liblsl/releases/download/v1.16.2/liblsl-1.16.2-jammy_amd64.deb -o liblsl-1.16.2-jammy_amd64.deb
sudo apt install -y ./liblsl-1.16.2-jammy_amd64.deb
rm liblsl-1.16.2-jammy_amd64.deb
- name: Install liblsl (macOS)
if: ${{ runner.os == 'macOS' }}
shell: bash
run: |
curl -L https://github.com/sccn/liblsl/releases/download/v1.16.0/liblsl-1.16.0-OSX_arm64.tar.bz2 -o liblsl-1.16.0-OSX_arm64.tar.bz2
tar -xf liblsl-1.16.0-OSX_arm64.tar.bz2
mv lib/liblsl.1.16.0.dylib .
echo "MNE_LSL_LIB=$PWD/liblsl.1.16.0.dylib" >> $GITHUB_ENV
rm -R lib include bin
rm liblsl-1.16.0-OSX_arm64.tar.bz2
- name: Install liblsl (windows)
if: ${{ runner.os == 'Windows' }}
shell: bash
run: |
curl -L https://github.com/sccn/liblsl/releases/download/v1.16.2/liblsl-1.16.2-Win_amd64.zip -o liblsl-1.16.2-Win_amd64.zip
7z x -oliblsl liblsl-1.16.2-Win_amd64.zip
echo "MNE_LSL_LIB=$PWD/liblsl/bin/lsl.dll" >> $GITHUB_ENV
rm liblsl-1.16.2-Win_amd64.zip
22 changes: 10 additions & 12 deletions .github/workflows/doc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,23 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: 3.11
- name: Install linux dependencies
run: |
curl -L https://github.com/sccn/liblsl/releases/download/v1.16.2/liblsl-1.16.2-jammy_amd64.deb -o liblsl-1.16.2-jammy_amd64.deb
sudo apt update
sudo apt install -y libpugixml-dev qtbase5-dev qt5-qmake optipng
sudo apt install -y ./liblsl-1.16.2-jammy_amd64.deb
rm liblsl-1.16.2-jammy_amd64.deb
- name: Install system dependencies
uses: ./.github/actions/install-system-dependencies
- name: Install optipng (for sphinx-gallery)
run: sudo apt install optipng
- name: Install package
run: |
python -m pip install --progress-bar off --upgrade pip setuptools
python -m pip install --progress-bar off .[doc]
- name: Display system information
run: mne_lsl-sys_info --developer
- name: Build doc
uses: nick-fields/retry@v3
- name: Get dataset
uses: ./.github/actions/get-testing-dataset
with:
timeout_minutes: 10
max_attempts: 3
command: make -C doc html
sample: "true"
testing: "false"
- name: Build doc
run: make -C doc html
- name: Prune sphinx environment
run: rm -R ./doc/_build/html/.doctrees
- name: Upload documentation
Expand Down
11 changes: 3 additions & 8 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,9 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: 3.11
- name: Install linux dependencies
run: |
curl -L https://github.com/sccn/liblsl/releases/download/v1.16.2/liblsl-1.16.2-jammy_amd64.deb -o liblsl-1.16.2-jammy_amd64.deb
sudo apt update
sudo apt install -y libpugixml-dev qtbase5-dev qt5-qmake optipng
sudo apt install -y ./liblsl-1.16.2-jammy_amd64.deb
rm liblsl-1.16.2-jammy_amd64.deb
- name: Install dependencies
- name: Install system dependencies
uses: ./.github/actions/install-system-dependencies
- name: Install package
run: |
python -m pip install --progress-bar off --upgrade pip setuptools
python -m pip install --progress-bar off -e .[build,stubs]
Expand Down
55 changes: 15 additions & 40 deletions .github/workflows/pytest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,36 +28,16 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install liblsl (linux)
if: ${{ matrix.os == 'ubuntu' }}
run: |
curl -L https://github.com/sccn/liblsl/releases/download/v1.16.2/liblsl-1.16.2-jammy_amd64.deb -o liblsl-1.16.2-jammy_amd64.deb
sudo apt update
sudo apt install -y binutils libpugixml-dev qtbase5-dev qt5-qmake
sudo apt install -y ./liblsl-1.16.2-jammy_amd64.deb
rm liblsl-1.16.2-jammy_amd64.deb
- name: Install liblsl (macOS)
if: ${{ matrix.os == 'macos' }}
run: |
curl -L https://github.com/sccn/liblsl/releases/download/v1.16.0/liblsl-1.16.0-OSX_arm64.tar.bz2 -o liblsl-1.16.0-OSX_arm64.tar.bz2
tar -xf liblsl-1.16.0-OSX_arm64.tar.bz2
mv lib/liblsl.1.16.0.dylib .
echo "MNE_LSL_LIB=$PWD/liblsl.1.16.0.dylib" >> $GITHUB_ENV
rm -R lib include bin
rm liblsl-1.16.0-OSX_arm64.tar.bz2
- name: Install liblsl (windows)
if: ${{ matrix.os == 'windows' }}
run: |
curl -L https://github.com/sccn/liblsl/releases/download/v1.16.2/liblsl-1.16.2-Win_amd64.zip -o liblsl-1.16.2-Win_amd64.zip
7z x -oliblsl liblsl-1.16.2-Win_amd64.zip
echo "MNE_LSL_LIB=$PWD/liblsl/bin/lsl.dll" >> $GITHUB_ENV
rm liblsl-1.16.2-Win_amd64.zip
- name: Install dependencies
- name: Install system dependencies
uses: ./.github/actions/install-system-dependencies
- name: Install package
run: |
python -m pip install --progress-bar off --upgrade pip setuptools
python -m pip install --progress-bar off .[test]
- name: Display system information
run: mne_lsl-sys_info --developer
- name: Get testing dataset
uses: ./.github/actions/get-testing-dataset
- name: Run pytest
run: pytest mne_lsl --cov=mne_lsl --cov-report=xml --cov-config=pyproject.toml -s
env:
Expand Down Expand Up @@ -90,13 +70,9 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install liblsl & linux dependencies
run: |
curl -L https://github.com/sccn/liblsl/releases/download/v1.16.2/liblsl-1.16.2-jammy_amd64.deb -o liblsl-1.16.2-jammy_amd64.deb
sudo apt update
sudo apt install -y binutils libpugixml-dev qtbase5-dev qt5-qmake
sudo apt install -y ./liblsl-1.16.2-jammy_amd64.deb
- name: Install dependencies
- name: Install system dependencies
uses: ./.github/actions/install-system-dependencies
- name: Install package
run: |
python -m pip install --progress-bar off --upgrade pip setuptools
python -m pip install --progress-bar off .[test]
Expand All @@ -106,6 +82,8 @@ jobs:
python -m pip install --progress-bar off --upgrade --pre --only-binary :all: -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple --timeout=180 numpy scipy
- name: Display system information
run: mne_lsl-sys_info --developer
- name: Get testing dataset
uses: ./.github/actions/get-testing-dataset
- name: Run pytest
run: pytest mne_lsl --cov=mne_lsl --cov-report=xml --cov-config=pyproject.toml -s
env:
Expand Down Expand Up @@ -139,21 +117,18 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install liblsl (linux)
run: |
curl -L https://github.com/sccn/liblsl/releases/download/v1.16.2/liblsl-1.16.2-jammy_amd64.deb -o liblsl-1.16.2-jammy_amd64.deb
sudo apt update
sudo apt install -y binutils libpugixml-dev qtbase5-dev qt5-qmake
sudo apt install -y ./liblsl-1.16.2-jammy_amd64.deb
rm liblsl-1.16.2-jammy_amd64.deb
- name: Install dependencies
- name: Install system dependencies
uses: ./.github/actions/install-system-dependencies
- name: Install package
run: |
python -m pip install --progress-bar off --upgrade pip setuptools
python -m pip install --progress-bar off .[test]
python -m pip uninstall mne -y
python -m pip install --progress-bar off mne==${{ matrix.mne-version }}
- name: Display system information
run: mne_lsl-sys_info --developer
- name: Get testing dataset
uses: ./.github/actions/get-testing-dataset
- name: Run pytest
run: pytest mne_lsl --cov=mne_lsl --cov-report=xml --cov-config=pyproject.toml -s
env:
Expand Down
9 changes: 2 additions & 7 deletions .github/workflows/stubs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,8 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: 3.11
- name: Install linux dependencies
run: |
curl -L https://github.com/sccn/liblsl/releases/download/v1.16.2/liblsl-1.16.2-jammy_amd64.deb -o liblsl-1.16.2-jammy_amd64.deb
sudo apt update
sudo apt install -y libpugixml-dev qtbase5-dev qt5-qmake
sudo apt install -y ./liblsl-1.16.2-jammy_amd64.deb
rm liblsl-1.16.2-jammy_amd64.deb
- name: Install system dependencies
uses: ./.github/actions/install-system-dependencies
- name: Install package
run: |
python -m pip install --progress-bar off --upgrade pip setuptools
Expand Down
Binary file modified doc/_static/tutorials/qrs-detector-performance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ def __init__(
self._stream.notch_filter(100, picks=ch_name)
sleep(bufsize) # prefill an entire buffer
# peak detection settings
self._last_acq_time = None
self._last_peak = None
self._peak_candidates = None
self._peak_candidates_count = None
Expand All @@ -301,6 +302,11 @@ def detect_peaks(self) -> NDArray[np.float64]:
The timestamps of all detected peaks.
"""
data, ts = self._stream.get_data() # we have a single channel in the stream
if self._last_acq_time is None:
self._last_acq_time = ts[-1]
elif self._last_acq_time == ts[-1]:
self._last_acq_time = ts[-1]
return np.array([]) # nothing new to do
data = data.squeeze()
peaks, _ = find_peaks(
data,
Expand Down
2 changes: 1 addition & 1 deletion mne_lsl/datasets/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def data_path() -> Path:
path = (
Path(get_config("MNE_DATA", Path.home())).expanduser()
/ "mne_data"
/ "MNE-LSL"
/ "MNE-LSL-data"
/ "testing"
)
base_url = "https://github.com/mscheltienne/mne-lsl-datasets/raw/main/testing"
Expand Down
6 changes: 3 additions & 3 deletions mne_lsl/player/player_lsl.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ def _stream(self) -> None:
else:
self._outlet.push_chunk(data, timestamp=self._target_timestamp)
self._stream_annotations(start, stop, start_timestamp)
except Exception as exc:
except Exception as exc: # pragma: no cover
logger.error("%s: Stopping due to exception: %s", self._name, exc)
self._del_outlets()
self._reset_variables()
Expand All @@ -296,8 +296,8 @@ def _stream(self) -> None:
sleep(delay)
try:
self._executor.submit(self._stream)
except RuntimeError:
assert self._executor._shutdown # pragma: no cover
except RuntimeError: # pragma: no cover
pass # shutdown

def _stream_annotations(
self, start: int, stop: int, start_timestamp: float
Expand Down
6 changes: 6 additions & 0 deletions mne_lsl/player/tests/test_player_lsl.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ def test_player_context_manager_raw_annotations(raw_annotations):
with Player(
raw_annotations, chunk_size=200, name=name, annotations=False
) as player:
assert player.running
streams = resolve_streams(timeout=2)
assert len(streams) == 1
assert streams[0].name == name
Expand All @@ -122,6 +123,7 @@ def test_player_context_manager_raw_annotations(raw_annotations):
assert len(streams) == 0

with Player(raw_annotations, chunk_size=200, name=name) as player:
assert player.running
streams = resolve_streams(timeout=2)
assert len(streams) == 2
assert any(stream.name == name for stream in streams)
Expand Down Expand Up @@ -336,6 +338,7 @@ def test_player_annotations(raw_annotations, close_io):
name = "Player-test_player_annotations"
annotations = sorted(set(raw_annotations.annotations.description))
player = Player(raw_annotations, chunk_size=200, name=name)
assert f"Player: {name}" in repr(player)
assert player.name == name
assert player.fname == Path(raw_annotations.filenames[0])
streams = resolve_streams(timeout=0.1)
Expand Down Expand Up @@ -410,7 +413,10 @@ def test_player_n_repeat(raw):
player = Player(
raw, chunk_size=200, n_repeat=4, name="Player-test_player_n_repeat-2"
)
assert player.n_repeat == 4
player.start()
assert player.n_repeat == 4
assert player.running
time.sleep((raw.times.size / raw.info["sfreq"]) * 1.8)
assert player._executor is not None
streams = resolve_streams(timeout=2)
Expand Down
2 changes: 1 addition & 1 deletion mne_lsl/stream/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ def get_data(
"The Stream is not connected. Please connect to the stream before "
"retrieving data from the buffer."
)
else:
else: # pragma: no cover
logger.error(
"Something went wrong while retrieving data from a connected "
"stream. Please open an issue on GitHub and provide the error "
Expand Down
14 changes: 7 additions & 7 deletions mne_lsl/stream/stream_lsl.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ def _acquire(self) -> None:
sleep(self._acquisition_delay)
try:
self._executor.submit(self._acquire)
except RuntimeError:
assert self._executor._shutdown # pragma: no cover
except RuntimeError: # pragma: no cover
pass # shutdown
return # interrupt early

# process acquisition window
Expand All @@ -249,8 +249,8 @@ def _acquire(self) -> None:
sleep(self._acquisition_delay)
try:
self._executor.submit(self._acquire)
except RuntimeError:
assert self._executor._shutdown # pragma: no cover
except RuntimeError: # pragma: no cover
pass # shutdown
return # interrupt early
if len(self._added_channels) != 0:
refs = np.zeros(
Expand Down Expand Up @@ -296,7 +296,7 @@ def _acquire(self) -> None:
"argument or consider retrieving new samples more often with "
"Stream.get_data()."
)
except Exception as error:
except Exception as error: # pragma: no cover
logger.exception(error)
self._reset_variables() # disconnects from the stream
if os.getenv("MNE_LSL_RAISE_STREAM_ERRORS", "false").lower() == "true":
Expand All @@ -305,8 +305,8 @@ def _acquire(self) -> None:
try:
sleep(self._acquisition_delay)
self._executor.submit(self._acquire)
except RuntimeError:
assert self._executor._shutdown # pragma: no cover
except RuntimeError: # pragma: no cover
pass # shutdown

def _reset_variables(self) -> None:
"""Reset variables define after connection."""
Expand Down
Loading