Skip to content
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ba649a6
refactor: remove load_form/save_to folder_metadata in save/load opera…
alejoe91 Jul 14, 2026
85923db
fix: add test for parallel write of timestamps
alejoe91 Jul 14, 2026
3365158
test: extend serialization tests to timestamps
alejoe91 Jul 14, 2026
b0707f9
test: extend serialization tests to binary/zarr_parallel
alejoe91 Jul 14, 2026
37491c5
test: fix serialization tests
alejoe91 Jul 14, 2026
92100d3
test: fix serialization timestamps check
alejoe91 Jul 14, 2026
1078fde
fix: proper folder_metadata cleanup
alejoe91 Jul 14, 2026
bafbd35
remove print
alejoe91 Jul 14, 2026
bb5e50a
fix: proper folder_metadata cleanup 2
alejoe91 Jul 14, 2026
681243e
Merge branch 'main' of github.com:SpikeInterface/spikeinterface into …
alejoe91 Jul 14, 2026
7e4e7ab
Merge branch 'main' of github.com:SpikeInterface/spikeinterface into …
alejoe91 Jul 14, 2026
0ebfd2b
Merge branch 'main' of github.com:SpikeInterface/spikeinterface into …
alejoe91 Jul 14, 2026
f1e2391
feat: add load times arg to zarr recording
alejoe91 Jul 14, 2026
a70d572
fix: propagate all zarr kwargs
alejoe91 Jul 14, 2026
5be192e
Remove prints
alejoe91 Jul 14, 2026
e62712c
fix: make times handling lazy in frame_slice
alejoe91 Jul 14, 2026
d166d51
fix: make time vector handlign fully lazy
alejoe91 Jul 14, 2026
05542ca
Apply suggestion from @alejoe91
alejoe91 Jul 20, 2026
450a762
fix: explicitly save probegroup in save_to_folder
alejoe91 Jul 20, 2026
fb2e9f9
fix: propagate time_kwargs to dict only if in __reduce__ and time_inf…
alejoe91 Jul 20, 2026
499767f
fix: only test fork/forkserver on linux
alejoe91 Jul 20, 2026
b9c29a0
fix: conflicts
alejoe91 Jul 22, 2026
56797ed
refactor: improve logic for frameslice time handling and add has_any_…
alejoe91 Jul 22, 2026
75d0bb2
Apply suggestion from @alejoe91
alejoe91 Jul 22, 2026
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
50 changes: 39 additions & 11 deletions .github/scripts/serialization/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@
"json": ".json",
"pickle": ".pkl",
"binary": "_binary",
"binary_parallel": "_binary_parallel",
"numpy_folder": "_numpy_folder",
"zarr": ".zarr",
"zarr_parallel": "_parallel.zarr",
}

DEFAULT_DURATION = 2.0 # seconds, for recordings and sortings

# --- json (recipe) entries: moved class + a second class -------------------------

Expand All @@ -46,7 +49,12 @@ def _build_noise_generator_recording():
except ImportError:
from spikeinterface.core.generate import NoiseGeneratorRecording

return NoiseGeneratorRecording(num_channels=4, sampling_frequency=30000.0, durations=[1.0, 1.5], seed=0)
return NoiseGeneratorRecording(
num_channels=4,
sampling_frequency=30000.0,
durations=[DEFAULT_DURATION, DEFAULT_DURATION + 0.5],
seed=0,
)


def _check_noise_generator_recording(rec):
Expand All @@ -59,7 +67,7 @@ def _check_noise_generator_recording(rec):
def _build_mock_recording():
from spikeinterface.core import generate_recording

return generate_recording(num_channels=4, durations=[1.0], sampling_frequency=30000.0, seed=0)
return generate_recording(num_channels=4, durations=[DEFAULT_DURATION], sampling_frequency=30000.0, seed=0)


def _check_mock_recording(rec):
Expand All @@ -77,7 +85,7 @@ def _build_recording_with_properties():
import numpy as np
from spikeinterface.core import generate_recording

rec = generate_recording(num_channels=4, durations=[1.0], sampling_frequency=30000.0, seed=0)
rec = generate_recording(num_channels=4, durations=[DEFAULT_DURATION], sampling_frequency=30000.0, seed=0)
rec.set_property("quality", np.array(["good", "good", "bad", "good"]))
rec.annotate(experimenter="test")
return rec
Expand All @@ -94,7 +102,7 @@ def _build_recording_with_probe():
from probeinterface import generate_linear_probe
from spikeinterface.core import generate_recording

rec = generate_recording(num_channels=8, durations=[1.0], sampling_frequency=30000.0, seed=0)
rec = generate_recording(num_channels=8, durations=[DEFAULT_DURATION], sampling_frequency=30000.0, seed=0)
probe = generate_linear_probe(num_elec=8)
probe.set_device_channel_indices(np.arange(8))
if parse(si_version) <= parse("0.105.0"):
Expand All @@ -104,6 +112,20 @@ def _build_recording_with_probe():
return rec_with_probe


def _build_recording_with_timestamps():
rec = _build_mock_recording()
times = rec.get_times(segment_index=0) + 100
rec.set_times(times, segment_index=0)
return rec


def _check_recording_with_timestamps(rec):
import numpy as np
expected_times = np.arange(int(DEFAULT_DURATION * 30000)) / 30000.0 + 100
times = rec.get_times(segment_index=0)
assert np.allclose(times, expected_times)


def _check_recording_with_probe(rec):
import numpy as np

Expand All @@ -121,7 +143,7 @@ def _build_recording_with_interleaved_probes():
from probeinterface import ProbeGroup, generate_linear_probe
from spikeinterface.core import generate_recording

rec = generate_recording(num_channels=8, durations=[1.0], sampling_frequency=30000.0, seed=0)
rec = generate_recording(num_channels=8, durations=[DEFAULT_DURATION], sampling_frequency=30000.0, seed=0)
probe0 = generate_linear_probe(num_elec=4)
probe1 = generate_linear_probe(num_elec=4)
probe1.move([100.0, 0.0])
Expand Down Expand Up @@ -164,7 +186,7 @@ def _build_preprocessed_chain():
from spikeinterface.core import generate_recording
from spikeinterface.preprocessing import common_reference, scale

rec = generate_recording(num_channels=4, durations=[1.0], sampling_frequency=30000.0, seed=0)
rec = generate_recording(num_channels=4, durations=[DEFAULT_DURATION], sampling_frequency=30000.0, seed=0)
# Two nested scipy-free preprocessing wrappers (scale then common_reference): this
# exercises recursive parent reload without pulling scipy into the environments.
return common_reference(scale(rec, gain=2.0))
Expand All @@ -181,7 +203,7 @@ def _check_preprocessed_chain(rec):
def _build_sorting():
from spikeinterface.core import generate_sorting

return generate_sorting(num_units=5, sampling_frequency=30000.0, durations=[1.0])
return generate_sorting(num_units=5, sampling_frequency=30000.0, durations=[DEFAULT_DURATION])


def _check_sorting(sorting):
Expand All @@ -195,7 +217,7 @@ def _build_sorting_with_properties():
import numpy as np
from spikeinterface.core import generate_sorting

sorting = generate_sorting(num_units=4, sampling_frequency=30000.0, durations=[1.0])
sorting = generate_sorting(num_units=4, sampling_frequency=30000.0, durations=[DEFAULT_DURATION])
sorting.set_property("quality", np.array(["good", "good", "bad", "good"]))
sorting.annotate(experimenter="test")
return sorting
Expand Down Expand Up @@ -224,19 +246,25 @@ def _check_sorting_with_properties(sorting):
"id": "recording_with_properties",
"build": _build_recording_with_properties,
"check": _check_recording_with_properties,
"formats": ["binary", "zarr"],
"formats": ["binary", "zarr", "binary_parallel", "zarr_parallel"],
},
{
"id": "recording_with_timestamps",
"build": _build_recording_with_timestamps,
"check": _check_recording_with_timestamps,
"formats": ["binary", "zarr", "binary_parallel", "zarr_parallel"],
},
{
"id": "recording_with_probe",
"build": _build_recording_with_probe,
"check": _check_recording_with_probe,
"formats": ["binary", "zarr"],
"formats": ["binary", "zarr", "binary_parallel", "zarr_parallel"],
},
{
"id": "recording_with_interleaved_probes",
"build": _build_recording_with_interleaved_probes,
"check": _check_recording_with_interleaved_probes,
"formats": ["binary", "zarr"],
"formats": ["binary", "zarr", "binary_parallel", "zarr_parallel"],
},
{
"id": "preprocessed_chain",
Expand Down
4 changes: 4 additions & 0 deletions .github/scripts/serialization/serialize_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@
obj.dump_to_pickle(dest)
elif fmt == "binary":
obj.save(folder=dest, format="binary", overwrite=True)
elif fmt == "binary_parallel":
obj.save(folder=dest, format="binary", overwrite=True, n_jobs=2)
elif fmt == "numpy_folder":
obj.save(folder=dest, format="numpy_folder", overwrite=True)
elif fmt == "zarr":
obj.save(folder=dest, format="zarr", overwrite=True)
elif fmt == "zarr_parallel":
obj.save(folder=dest, format="zarr", overwrite=True, n_jobs=2)
print(f" wrote {dest.name} ({fmt})")

print(f"Fixtures written to: {out_dir.resolve()}")
22 changes: 7 additions & 15 deletions .github/workflows/cross_version_serialization.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ name: Cross-version serialization
#
# Delivery model: Live generation. Fixtures are regenerated from a real old install each
# run in an isolated uv environment (nothing committed). The version list is computed
# from PyPI (latest patch of each minor at or above MIN_VERSION_TO_TEST): on a pull
# request only the latest released minor is tested; the full matrix runs weekly and on
# manual dispatch.
# from PyPI (latest patch of each minor at or above MIN_VERSION_TO_TEST), and the full
# matrix is tested on every pull request and on manual dispatch.

on:
pull_request:
Expand All @@ -20,11 +19,9 @@ on:
# watches core only; a break introduced in generation/ or preprocessing/ would not
# trigger it.
paths:
- "src/spikeinterface/core/**"
- ".github/scripts/serialization/**"
- ".github/workflows/cross_version_serialization.yml"
schedule:
- cron: "0 4 * * 0" # weekly, Sunday 04:00 UTC
- 'src/spikeinterface/core/**'
- '.github/scripts/serialization/**'
- '.github/workflows/cross_version_serialization.yml'
workflow_dispatch:

concurrency:
Expand All @@ -33,8 +30,8 @@ concurrency:

jobs:
# Compute which released versions the matrix below tests loading from, exposed as the
# job output `list`. Latest patch of each minor at or above the floor on schedule /
# dispatch; only the latest minor on a pull request (keeps per-PR runs cheap).
# job output `list`: the latest patch of each minor at or above the floor, tested on
# every trigger.
versions:
name: Select versions
runs-on: ubuntu-latest
Expand All @@ -46,7 +43,6 @@ jobs:
python-version: "3.10"
- id: set
env:
GITHUB_EVENT_NAME: ${{ github.event_name }}
# Support floor: oldest minor to test. Below this, installs fail on the CI
# Python (numcodecs dependency rot in 0.100/0.101). Raise when 0.102 rots.
MIN_VERSION_TO_TEST: "0.102" # We will change this when making breaking changes (hopefully not too often)
Expand All @@ -67,10 +63,6 @@ jobs:
candidates = [v for v in available if not v.is_prerelease and (v.major, v.minor) >= (floor.major, floor.minor)]
minor_releases = [max(group) for _, group in groupby(candidates, key=lambda v: (v.major, v.minor))]

# on a pull request, test only the latest minor
if os.environ.get('GITHUB_EVENT_NAME') == 'pull_request':
minor_releases = minor_releases[-1:]

print(json.dumps([str(v) for v in minor_releases]))
")

Expand Down
Loading
Loading