Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
acb5935
test a ruff action
zm711 Mar 9, 2025
f5154cb
oops
zm711 Mar 9, 2025
5ed3ddc
add ruff check
zm711 Mar 9, 2025
0e61521
another try
zm711 Mar 9, 2025
60e84cc
Merge branch 'main' into ruff-test
zm711 Mar 10, 2025
ac7d86b
Merge branch 'main' into ruff-test
zm711 Apr 1, 2025
bf161ed
Merge branch 'main' into ruff-test
zm711 Apr 24, 2025
016d134
Merge branch 'main' into ruff-test
zm711 Jun 3, 2025
fa7431f
Merge branch 'main' into ruff-test
zm711 Jun 12, 2025
760fc27
Merge branch 'main' into ruff-test
zm711 Jul 8, 2025
22ad294
Merge branch 'main' into ruff-test
zm711 Sep 5, 2025
b8dde05
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 5, 2025
0d97305
Merge branch 'main' into ruff-test
zm711 Oct 15, 2026
1b9971e
Merge branch 'main' into ruff-test
alejoe91 Nov 9, 2026
e8e9b0c
fix: core ruff checks
alejoe91 Nov 9, 2026
222b74e
fix: conflicts
alejoe91 Nov 9, 2026
db3d479
fix: extractors ruff checks
alejoe91 Nov 9, 2026
a1fd448
fix: sorters ruff checks
alejoe91 Nov 9, 2026
a7408a3
fix: postprocessing ruff checks
alejoe91 Nov 9, 2026
16ae6cf
fix: exporters ruff checks
alejoe91 Nov 9, 2026
866597f
fix: benchmark ruff checks
alejoe91 Nov 9, 2026
747f5ef
fix: widgets ruff checks
alejoe91 Nov 9, 2026
64df25f
fix: curation ruff checks
alejoe91 Nov 9, 2026
8cd4a5e
fix: comparison ruff checks
alejoe91 Nov 9, 2026
c15ac99
fix: sortingcomponents ruff checks
alejoe91 Nov 9, 2026
b53cf58
lint: all ruff fixed
alejoe91 Nov 9, 2026
3477781
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 9, 2026
f23e7d2
fix: installation tips
alejoe91 Nov 9, 2026
fb2af7f
Merge branch 'ruff-test' of github.com:zm711/spikeinterface into ruff…
alejoe91 Nov 9, 2026
88ae0b8
fix: extractor_classes
alejoe91 Nov 9, 2026
54f5fef
fix: add from spikeinterface.core.basesorting import LEXSORT_UNIT_COM…
alejoe91 Nov 9, 2026
ac3eed5
fix a couple of F821 errors
alejoe91 Nov 9, 2026
7061477
fix: MDA read test
alejoe91 Nov 9, 2026
13fbcbe
fix: curation imputer
alejoe91 Nov 9, 2026
ac0e62f
fix: restore spython and docker imports in tests
alejoe91 Nov 9, 2026
68dc02f
fix: move curation fixtures to conftest.py
alejoe91 Nov 10, 2026
4515305
fix: curation and exporters tests
alejoe91 Nov 10, 2026
433ef55
doc: add instructions (plus some test fixes)
alejoe91 Nov 10, 2026
4b2b633
fix: test linting with manual pre-commit
alejoe91 Nov 10, 2026
bebdeec
fix(test): increase margin_ms in tesT_filter
alejoe91 Nov 10, 2026
c5fc182
fix: conflicts
alejoe91 Nov 10, 2026
0a0f285
fix: remove stray import numba
alejoe91 Nov 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 0 additions & 1 deletion .github/scripts/serialization/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ def _check_recording_with_probe(rec, check_extra_data=None):


def _build_recording_with_interleaved_probes():
import numpy as np
from probeinterface import ProbeGroup, generate_linear_probe
from spikeinterface.core import generate_recording

Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/ruff.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Ruff Linting

on:
pull_request:
types: [synchronize, opened, reopened]
branches:
- main

concurrency: # Cancel previous workflows on the same pull request
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
run:
name: Ruff Linting
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v3
with:
args: check
16 changes: 13 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,18 @@ repos:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 26.10.1
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.0
hooks:
- id: black
- id: ruff-check
args: [--fix]
files: ^src/
# occasional full lint including tests: pre-commit run ruff-check-tests --hook-stage manual --all-files
- id: ruff-check
alias: ruff-check-tests
name: ruff check (including tests)
args: [--fix, --config, "lint.exclude = []"]
files: ^src/
stages: [manual]
- id: ruff-format
files: ^src/
2 changes: 1 addition & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def pytest_collection_modifyitems(config, items):

try:
rel_path = Path(item.fspath).relative_to(modules_location)
except:
except Exception:
continue

module = rel_path.parts[0]
Expand Down
60 changes: 38 additions & 22 deletions doc/development/development.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,57 +111,73 @@ maintainability of the code, making it easier for contributors to collaborate. T
for the developer we use the following tools and conventions:


Install Black and pre-commit
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Install ruff and pre-commit
^^^^^^^^^^^^^^^^^^^^^^^^^^^

We use the Python formatter Black, with defaults set in the :code:`pyproject.toml`. This allows for
easy local formatting of code.
We use `ruff <https://docs.astral.sh/ruff/>`_ both to format code and to lint it (catch errors such as
undefined names or unused variables). Its settings live in the :code:`[tool.ruff]` section of :code:`pyproject.toml`.
We use pre-commit to run ruff, and a few other checks, automatically before every commit.

To install Black, you can use pip, the Python package installer. Run the following command in your terminal:
You can install both with pip:
Comment thread
alejoe91 marked this conversation as resolved.
Resolved
Comment thread
alejoe91 marked this conversation as resolved.
Resolved

.. code-block:: bash

pip install black
pip install ruff pre-commit
Comment thread
alejoe91 marked this conversation as resolved.
Resolved

This will install Black into your current Python environment.
Then set up the pre-commit hooks for your local repository by running, from the repository root:

In addition to Black, we use pre-commit to manage a suite of code formatting.
Pre-commit helps to automate the process of running these tools (including Black) before every commit,
ensuring that all code is checked for style.
.. code-block:: bash

pre-commit install

You can install pre-commit using pip as well:
Now, each time you make a commit, pre-commit will run :code:`ruff format` and :code:`ruff check --fix` on the changed files
under :code:`src/`. If the hooks make changes or find issues, the commit is stopped so you can review the changes, add them and commit again.

You can also run ruff yourself at any time:

.. code-block:: bash

pip install pre-commit
ruff format src # format the code
ruff check src # report lint errors
ruff check src --fix # report lint errors and apply the safe automatic fixes

A few things to know about the lint configuration:

* Test files (:code:`tests/` folders, :code:`test_*.py` and :code:`conftest.py`) are excluded from linting.
* Unused imports (:code:`F401`) and unused variables (:code:`F841`) are reported but never removed automatically,
because they are sometimes needed: side-effect imports, re-exports, or function calls that read from a file.
Fix them by hand, or keep them and mark the line with :code:`# noqa: F401`.
* Pytest fixtures shared by several test modules belong in a :code:`conftest.py`, so pytest finds them without an import
(an import that ruff would see as unused).

Once pre-commit is installed, you can set up the pre-commit hooks for your local repository.
These hooks are scripts that pre-commit will run prior to each commit. To install the pre-commit hooks,
navigate to your local repository in your terminal and run the following command:
Since tests are not linted on every commit, it is worth checking them every now and then. A manual pre-commit hook lints
everything under :code:`src/`, tests included:

.. code-block:: bash

pre-commit install
pre-commit run ruff-check-tests --hook-stage manual --all-files
Comment thread
chrishalcrow marked this conversation as resolved.
Resolved

or, equivalently, with ruff directly:

.. code-block:: bash

Now, each time you make a commit, pre-commit will automatically run Black and any other configured hooks.
If the hooks make changes or if there are any issues, the commit will be stopped, and you'll be able to review and add the changes.
ruff check src --config 'lint.exclude = []'

If you want Black to omit a line from formatting, you can add the following comment to the end of the line:
If you want ruff to leave a line unformatted, add the following comment to the end of the line:

.. code-block:: python

# fmt: skip

To ignore a block of code you must flank the code with two comments:
To leave a block of code unformatted, put these two comments around it:

.. code-block:: python

# fmt: off
code here
# fmt: on

As described in the `black documentation <https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#code-style>`_.
To silence a single lint error on one line, add :code:`# noqa: <code>` (for example :code:`# noqa: F401`) to the end of that line.


Docstring Conventions
Expand Down Expand Up @@ -202,7 +218,7 @@ This allows users to quickly understand the type of data that should be input in

There should be a space between each parameter and the colon following it. This is neccessary for using the `numpydoc validator <https://numpydoc.readthedocs.io/en/latest/validation.html>`_.
In the above example we demonstrate two other docstring conventions followed by SpikeInterface. First, that all string arguments should be presented
with double quotes. This is the same stylistic convention followed by Black and enforced by the pre-commit for the repo. Second, when a parameter is a
with double quotes. This is the same stylistic convention enforced by ruff format in the pre-commit for the repo. Second, when a parameter is a
string with a limited number of values (e.g. :code:`mean` and :code:`median`), rather than give the type a value of :code:`str`, please list the possible strings
so that the user knows what the options are.

Expand Down
1 change: 0 additions & 1 deletion doc/images/make_comparison_figures.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
sys.path.append('../../examples/modules/comparison/')
from generate_erroneous_sorting import generate_erroneous_sorting

import spikeinterface.extractors as se
import spikeinterface.comparison as sc
import spikeinterface.widgets as sw

Expand Down
2 changes: 0 additions & 2 deletions examples/forhowto/plot_extract_lfps.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,10 @@
import time
import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path
import pandas as pd
import seaborn as sns

import spikeinterface as si
import spikeinterface.extractors as se
import spikeinterface.preprocessing as spre
import spikeinterface.widgets as sw
from spikeinterface.core import generate_ground_truth_recording
Expand Down
3 changes: 0 additions & 3 deletions examples/get_started/quickstart.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
# The spikeinterface module by itself imports only the spikeinterface.core submodule
# which is not useful for the end user

import spikeinterface

# We need to import one by one different submodules separately (preferred).
# There are several modules:
Expand All @@ -50,9 +49,7 @@

import spikeinterface as si # import core only
import spikeinterface.extractors as se
import spikeinterface.preprocessing as spre
import spikeinterface.sorters as ss
import spikeinterface.postprocessing as spost
import spikeinterface.metrics as sm
import spikeinterface.comparison as sc
import spikeinterface.exporters as sexp
Expand Down
1 change: 0 additions & 1 deletion examples/how_to/auto_label_units.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
# 3. UnitRefine: pre-trained classifiers to label units as noise or SUA/MUA [Jain]_

# %%
import numpy as np

import spikeinterface as si
import spikeinterface.curation as sc
Expand Down
1 change: 0 additions & 1 deletion examples/how_to/benchmark_with_hybrid_recordings.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import spikeinterface.generation as sgen
import spikeinterface.widgets as sw

from spikeinterface.sortingcomponents.motion import estimate_motion

import numpy as np
import matplotlib.pyplot as plt
Expand Down
1 change: 0 additions & 1 deletion examples/how_to/customize_a_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@
# Here's an example of making a unit summary plot.

# +
import matplotlib.pyplot as plt
fig, axs = plt.subplots(ncols=2, nrows=1)

unit_id=8
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
##############################################################################
# Import

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from spikeinterface.comparison import compare_sorter_to_ground_truth
Expand Down
1 change: 0 additions & 1 deletion examples/tutorials/core/plot_1_recording_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

"""

import matplotlib.pyplot as plt

import numpy as np
import spikeinterface.extractors as se
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@
Here is a short example.
"""

import matplotlib.pyplot as plt
import numpy as np


from spikeinterface import NumpyRecording, NumpySorting
from spikeinterface import NumpyRecording
from spikeinterface import append_recordings, concatenate_recordings

##############################################################################
Expand Down
2 changes: 1 addition & 1 deletion examples/tutorials/curation/plot_1_automated_curation.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def calculate_moving_avg(label_df, confidence_label, window_size):
plt.figure(figsize=(6, 6))
plt.plot(p_agreement_sorted, p_agreement_moving_avg, label = 'Moving Average')
plt.axhline(y=1/len(np.unique(predictions)), color='black', linestyle='--', label='Chance')
plt.xlabel('Confidence'); #plt.xlim(0.5, 1)
plt.xlabel('Confidence') #plt.xlim(0.5, 1)
plt.ylabel('Proportion Agreement with Human Label'); plt.ylim(0, 1)
plt.title('Agreement vs Confidence (Moving Average)')
plt.legend(); plt.grid(True); plt.show()
Expand Down
1 change: 0 additions & 1 deletion examples/tutorials/widgets/plot_3_waveforms_gallery.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import matplotlib.pyplot as plt

import spikeinterface as si
import spikeinterface.extractors as se
import spikeinterface.widgets as sw

##############################################################################
Expand Down
2 changes: 1 addition & 1 deletion installation_tips/check_your_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def open_sigui():
folder = f"./analyzer_with_{sorter_name}"
analyzer = si.load_sorting_analyzer(folder)

win = run_mainwindow(analyzer, start_app=True)
_ = run_mainwindow(analyzer, start_app=True)


def _clean():
Expand Down
18 changes: 15 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,20 @@ include = ["spikeinterface*"]
namespaces = false
exclude = ["spikeinterface.*.tests"]

[tool.black]
[tool.ruff]
line-length = 120
force-exclude = true
[tool.ruff.lint]
select = ['E4', 'E7', 'E9', 'F']
exclude = ["**/tests/**", "**/test_*.py", "**/conftest.py", "examples/**", "doc/conf.py", "installation_tips/**"]
ignore = ['E741', 'E731', 'F821']
# report but never auto-remove unused imports/variables: they may be side-effect imports, re-exports, or calls that advance state
unfixable = ['F401', 'F841']
[tool.ruff.lint.per-file-ignores]
"**/__init__.py" = ['F401', 'F403']
"src/spikeinterface/full.py" = ['F403']
"src/spikeinterface/core/testing_tools.py" = ['E402', 'F403']
"src/spikeinterface/extractors/extractor_classes.py" = ['F401', 'F403']

[project.urls]
homepage = "https://github.com/SpikeInterface/spikeinterface"
Expand Down Expand Up @@ -232,7 +244,7 @@ docs = [

dev = [
"spikeinterface[docs]",
"black",
"ruff",
"pre-commit",
]

Expand Down Expand Up @@ -342,7 +354,7 @@ test-all = [

# Tools for the pre-commit hooks. uv installs this group by default.
dev = [
"black",
"ruff",
"pre-commit",
]

Expand Down
14 changes: 4 additions & 10 deletions src/spikeinterface/benchmark/benchmark_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@


from spikeinterface.core import SortingAnalyzer, ChannelSparsity, NumpySorting
from spikeinterface.core import SortingAnalyzer, NumpySorting
from spikeinterface.core.job_tools import fix_job_kwargs, split_job_kwargs
from spikeinterface import load, create_sorting_analyzer, load_sorting_analyzer
from spikeinterface.widgets import get_some_colors

Expand Down Expand Up @@ -332,7 +330,6 @@ def run(self, case_keys=None, keep=True, verbose=False, **job_kwargs):

job_keys = []
for key in case_keys:

result_folder = self.folder / "results" / self.key_to_str(key)
sorter_folder = self.folder / "sorters" / self.key_to_str(key)

Expand Down Expand Up @@ -443,9 +440,9 @@ def get_grouped_keys_mapping(self, levels_to_group_by=None, case_keys=None):
keys_mapping = {key: [key] for key in case_keys}
else:
study_levels = self.levels
assert np.all(
[l in study_levels for l in levels_to_group_by]
), f"levels_to_group_by must be in {study_levels}, got {levels_to_group_by}"
assert np.all([l in study_levels for l in levels_to_group_by]), (
f"levels_to_group_by must be in {study_levels}, got {levels_to_group_by}"
)
keys_mapping = {}
for key in case_keys:
new_key = tuple(key[list(study_levels).index(level)] for level in levels_to_group_by)
Expand Down Expand Up @@ -480,7 +477,6 @@ def compute_results(self, case_keys=None, verbose=False, **result_params):
if case_keys is None:
case_keys = list(self.cases.keys())

job_keys = []
for key in case_keys:
if verbose:
print("### Compute result", key, "###")
Expand Down Expand Up @@ -546,7 +542,7 @@ def compute_metrics(self, case_keys=None, metric_names=["snr", "firing_rate"], f
qm_ext = sorting_analyzer.compute("quality_metrics", metric_names=metric_names, **job_kwargs)

# TODO remove this metics CSV file!!!!
metrics = qm_ext.get_data()
# _ = qm_ext.get_data()
# metrics.to_csv(filename, sep="\t", index=True)

def get_metrics(self, key):
Expand Down Expand Up @@ -609,7 +605,6 @@ def get_pairs_by_level(self, level):

pairs = []
for key in self.cases.keys():

case0 = list(key)
case1 = list(key)
case0[level_index] = possible_values[0]
Expand Down Expand Up @@ -702,7 +697,6 @@ def load_folder(cls, folder):

zarr_folder = folder / k
if zarr_folder.exists():

result[k] = Templates.from_zarr(zarr_folder)
elif format == "sorting_analyzer":
analyzer_folder = folder / k
Expand Down
Loading
Loading