Skip to content

Commit 8f08083

Browse files
committed
Merge branch 'main' of github.com:SpikeInterface/spikeinterface into use-times-in-get-durations
2 parents 7f37dff + dbd433d commit 8f08083

45 files changed

Lines changed: 823 additions & 406 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/spikeinterface/benchmark/benchmark_base.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from spikeinterface.core import SortingAnalyzer
1313

14-
from spikeinterface import load_extractor, create_sorting_analyzer, load_sorting_analyzer
14+
from spikeinterface import load, create_sorting_analyzer, load_sorting_analyzer
1515
from spikeinterface.widgets import get_some_colors
1616

1717

@@ -150,13 +150,13 @@ def scan_folder(self):
150150
analyzer = load_sorting_analyzer(folder)
151151
self.analyzers[key] = analyzer
152152
# the sorting is in memory here we take the saved one because comparisons need to pickle it later
153-
sorting = load_extractor(analyzer.folder / "sorting")
153+
sorting = load(analyzer.folder / "sorting")
154154
self.datasets[key] = analyzer.recording, sorting
155155

156156
# for rec_file in (self.folder / "datasets" / "recordings").glob("*.pickle"):
157157
# key = rec_file.stem
158-
# rec = load_extractor(rec_file)
159-
# gt_sorting = load_extractor(self.folder / f"datasets" / "gt_sortings" / key)
158+
# rec = load(rec_file)
159+
# gt_sorting = load(self.folder / f"datasets" / "gt_sortings" / key)
160160
# self.datasets[key] = (rec, gt_sorting)
161161

162162
with open(self.folder / "cases.pickle", "rb") as f:
@@ -428,7 +428,7 @@ def load_folder(cls, folder):
428428
elif format == "sorting":
429429
from spikeinterface.core import load_extractor
430430

431-
result[k] = load_extractor(folder / k)
431+
result[k] = load(folder / k)
432432
elif format == "Motion":
433433
from spikeinterface.sortingcomponents.motion import Motion
434434

src/spikeinterface/comparison/multicomparisons.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import numpy as np
99

10-
from spikeinterface.core import load_extractor, BaseSorting, BaseSortingSegment
10+
from spikeinterface.core import load, BaseSorting, BaseSortingSegment
1111
from spikeinterface.core.core_tools import define_function_from_class
1212
from .basecomparison import BaseMultiComparison, MixinSpikeTrainComparison, MixinTemplateComparison
1313
from .paircomparisons import SymmetricSortingComparison, TemplateComparison
@@ -230,7 +230,7 @@ def load_from_folder(folder_path):
230230
with (folder_path / "sortings.json").open() as f:
231231
dict_sortings = json.load(f)
232232
name_list = list(dict_sortings.keys())
233-
sorting_list = [load_extractor(v, base_folder=folder_path) for v in dict_sortings.values()]
233+
sorting_list = [load(v, base_folder=folder_path) for v in dict_sortings.values()]
234234
mcmp = MultiSortingComparison(sorting_list=sorting_list, name_list=list(name_list), do_matching=False, **kwargs)
235235
filename = str(folder_path / "multicomparison.gpickle")
236236
with open(filename, "rb") as f:

src/spikeinterface/comparison/tests/test_hybrid.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22
import shutil
33
from pathlib import Path
4-
from spikeinterface.core import extract_waveforms, load_waveforms, load_extractor
4+
from spikeinterface.core import extract_waveforms, load_waveforms, load
55
from spikeinterface.core.testing import check_recordings_equal
66
from spikeinterface.comparison import (
77
create_hybrid_units_recording,
@@ -52,7 +52,7 @@ def test_hybrid_units_recording(setup_module):
5252
)
5353

5454
# Check dumpability
55-
saved_loaded = load_extractor(hybrid_units_recording.to_dict())
55+
saved_loaded = load(hybrid_units_recording.to_dict())
5656
check_recordings_equal(hybrid_units_recording, saved_loaded, return_scaled=False)
5757

5858
saved_1job = hybrid_units_recording.save(folder=cache_folder / "units_1job")
@@ -81,7 +81,7 @@ def test_hybrid_spikes_recording(setup_module):
8181
)
8282

8383
# Check dumpability
84-
saved_loaded = load_extractor(hybrid_spikes_recording.to_dict())
84+
saved_loaded = load(hybrid_spikes_recording.to_dict())
8585
check_recordings_equal(hybrid_spikes_recording, saved_loaded, return_scaled=False)
8686

8787
saved_1job = hybrid_spikes_recording.save(folder=cache_folder / "spikes_1job")

src/spikeinterface/core/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
from .base import load_extractor # , load_extractor_from_dict, load_extractor_from_json, load_extractor_from_pickle
21
from .baserecording import BaseRecording, BaseRecordingSegment
32
from .basesorting import BaseSorting, BaseSortingSegment, SpikeVectorSortingSegment
43
from .baseevent import BaseEvent, BaseEventSegment
54
from .basesnippets import BaseSnippets, BaseSnippetsSegment
65
from .baserecordingsnippets import BaseRecordingSnippets
76

7+
from .loading import load, load_extractor
8+
89
# main extractor from dump and cache
910
from .binaryrecordingextractor import BinaryRecordingExtractor, read_binary
1011
from .npzsortingextractor import NpzSortingExtractor, read_npz_sorting

src/spikeinterface/core/base.py

Lines changed: 9 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
from .globals import get_global_tmp_folder, is_set_global_tmp_folder
1818
from .core_tools import (
19-
check_json,
19+
is_path_remote,
2020
clean_zarr_folder_name,
2121
is_dict_extractor,
2222
SIJsonEncoder,
@@ -673,7 +673,7 @@ def dump_to_json(
673673
) -> None:
674674
"""
675675
Dump recording extractor to json file.
676-
The extractor can be re-loaded with load_extractor(json_file)
676+
The extractor can be re-loaded with load(json_file)
677677
678678
Parameters
679679
----------
@@ -715,7 +715,7 @@ def dump_to_pickle(
715715
):
716716
"""
717717
Dump recording extractor to a pickle file.
718-
The extractor can be re-loaded with load_extractor(pickle_file)
718+
The extractor can be re-loaded with load(pickle_file)
719719
720720
Parameters
721721
----------
@@ -752,7 +752,9 @@ def dump_to_pickle(
752752
file_path.write_bytes(pickle.dumps(dump_dict))
753753

754754
@staticmethod
755-
def load(file_path: Union[str, Path], base_folder: Optional[Union[Path, str, bool]] = None) -> "BaseExtractor":
755+
def load(
756+
file_or_folder_path: Union[str, Path], base_folder: Optional[Union[Path, str, bool]] = None
757+
) -> "BaseExtractor":
756758
"""
757759
Load extractor from file path (.json or .pkl)
758760
@@ -761,62 +763,10 @@ def load(file_path: Union[str, Path], base_folder: Optional[Union[Path, str, boo
761763
* save (...) a folder which contain data + json (or pickle) + metadata.
762764
763765
"""
766+
# use loading.py and keep backward compatibility
767+
from .loading import load
764768

765-
file_path = Path(file_path)
766-
if base_folder is True:
767-
base_folder = file_path.parent
768-
769-
if file_path.is_file():
770-
# standard case based on a file (json or pickle)
771-
if str(file_path).endswith(".json"):
772-
with open(file_path, "r") as f:
773-
d = json.load(f)
774-
elif str(file_path).endswith(".pkl") or str(file_path).endswith(".pickle"):
775-
with open(file_path, "rb") as f:
776-
d = pickle.load(f)
777-
else:
778-
raise ValueError(f"Impossible to load {file_path}")
779-
if "warning" in d:
780-
print("The extractor was not serializable to file")
781-
return None
782-
783-
extractor = BaseExtractor.from_dict(d, base_folder=base_folder)
784-
return extractor
785-
786-
elif file_path.is_dir():
787-
# case from a folder after a calling extractor.save(...)
788-
folder = file_path
789-
file = None
790-
791-
if folder.suffix == ".zarr":
792-
from .zarrextractors import read_zarr
793-
794-
extractor = read_zarr(folder)
795-
else:
796-
# the is spikeinterface<=0.94.0
797-
# a folder came with 'cached.json'
798-
for dump_ext in ("json", "pkl", "pickle"):
799-
f = folder / f"cached.{dump_ext}"
800-
if f.is_file():
801-
file = f
802-
803-
# spikeinterface>=0.95.0
804-
f = folder / f"si_folder.json"
805-
if f.is_file():
806-
file = f
807-
808-
if file is None:
809-
raise ValueError(f"This folder is not a cached folder {file_path}")
810-
extractor = BaseExtractor.load(file, base_folder=folder)
811-
812-
return extractor
813-
814-
else:
815-
error_msg = (
816-
f"{file_path} is not a file or a folder. It should point to either a json, pickle file or a "
817-
"folder that is the result of extractor.save(...)"
818-
)
819-
raise ValueError(error_msg)
769+
return load(file_or_folder_path, base_folder=base_folder)
820770

821771
def __reduce__(self):
822772
"""
@@ -1167,50 +1117,6 @@ def _check_same_version(class_string, version):
11671117
return "unknown"
11681118

11691119

1170-
def load_extractor(file_or_folder_or_dict, base_folder=None) -> BaseExtractor:
1171-
"""
1172-
Instantiate extractor from:
1173-
* a dict
1174-
* a json file
1175-
* a pickle file
1176-
* folder (after save)
1177-
* a zarr folder (after save)
1178-
1179-
Parameters
1180-
----------
1181-
file_or_folder_or_dict : dictionary or folder or file (json, pickle)
1182-
The file path, folder path, or dictionary to load the extractor from
1183-
base_folder : str | Path | bool (optional)
1184-
The base folder to make relative paths absolute.
1185-
If True and file_or_folder_or_dict is a file, the parent folder of the file is used.
1186-
1187-
Returns
1188-
-------
1189-
extractor: Recording or Sorting
1190-
The loaded extractor object
1191-
"""
1192-
if isinstance(file_or_folder_or_dict, dict):
1193-
assert not isinstance(base_folder, bool), "`base_folder` must be a string or Path when loading from dict"
1194-
return BaseExtractor.from_dict(file_or_folder_or_dict, base_folder=base_folder)
1195-
else:
1196-
return BaseExtractor.load(file_or_folder_or_dict, base_folder=base_folder)
1197-
1198-
1199-
def load_extractor_from_dict(d, base_folder=None) -> BaseExtractor:
1200-
warnings.warn("Use load_extractor(..) instead")
1201-
return BaseExtractor.from_dict(d, base_folder=base_folder)
1202-
1203-
1204-
def load_extractor_from_json(json_file, base_folder=None) -> "BaseExtractor":
1205-
warnings.warn("Use load_extractor(..) instead")
1206-
return BaseExtractor.load(json_file, base_folder=base_folder)
1207-
1208-
1209-
def load_extractor_from_pickle(pkl_file, base_folder=None) -> "BaseExtractor":
1210-
warnings.warn("Use load_extractor(..) instead")
1211-
return BaseExtractor.load(pkl_file, base_folder=base_folder)
1212-
1213-
12141120
class BaseSegment:
12151121
def __init__(self):
12161122
self._parent_extractor = None

src/spikeinterface/core/loading.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import warnings
2+
from pathlib import Path
3+
4+
5+
from .base import BaseExtractor
6+
from .core_tools import is_path_remote
7+
8+
9+
def load(file_or_folder_or_dict, base_folder=None) -> BaseExtractor:
10+
"""
11+
General load function to load a SpikeInterface object.
12+
13+
The function can load:
14+
- a `Recording` or `Sorting` object from:
15+
* dictionary
16+
* json file
17+
* pkl file
18+
* binary folder (after `extractor.save(..., format='binary_folder')`)
19+
* zarr folder (after `extractor.save(..., format='zarr')`)
20+
* remote zarr folder
21+
- (TODO) a `SortingAnalyzer` object from :
22+
* binary folder
23+
* zarr folder
24+
* remote zarr folder
25+
* WaveformExtractor folder
26+
27+
Parameters
28+
----------
29+
file_or_folder_or_dict : dictionary or folder or file (json, pickle)
30+
The file path, folder path, or dictionary to load the extractor from
31+
base_folder : str | Path | bool (optional)
32+
The base folder to make relative paths absolute.
33+
If True and file_or_folder_or_dict is a file, the parent folder of the file is used.
34+
35+
Returns
36+
-------
37+
extractor: Recording or Sorting
38+
The loaded extractor object
39+
"""
40+
if isinstance(file_or_folder_or_dict, dict):
41+
assert not isinstance(base_folder, bool), "`base_folder` must be a string or Path when loading from dict"
42+
return BaseExtractor.from_dict(file_or_folder_or_dict, base_folder=base_folder)
43+
else:
44+
file_path = file_or_folder_or_dict
45+
error_msg = (
46+
f"{file_path} is not a file or a folder. It should point to either a json, pickle file or a "
47+
"folder that is the result of extractor.save(...)"
48+
)
49+
if not is_path_remote(file_path):
50+
file_path = Path(file_path)
51+
52+
if base_folder is True:
53+
base_folder = file_path.parent
54+
55+
if file_path.is_file():
56+
# standard case based on a file (json or pickle)
57+
if str(file_path).endswith(".json"):
58+
import json
59+
60+
with open(file_path, "r") as f:
61+
d = json.load(f)
62+
elif str(file_path).endswith(".pkl") or str(file_path).endswith(".pickle"):
63+
import pickle
64+
65+
with open(file_path, "rb") as f:
66+
d = pickle.load(f)
67+
else:
68+
raise ValueError(error_msg)
69+
70+
# this is for back-compatibility since now unserializable objects will not
71+
# be saved to file
72+
if "warning" in d:
73+
print("The extractor was not serializable to file")
74+
return None
75+
76+
extractor = BaseExtractor.from_dict(d, base_folder=base_folder)
77+
78+
elif file_path.is_dir():
79+
# this can be and extractor, SortingAnalyzer, or WaveformExtractor
80+
folder = file_path
81+
file = None
82+
83+
if folder.suffix == ".zarr":
84+
from .zarrextractors import read_zarr
85+
86+
extractor = read_zarr(folder)
87+
else:
88+
# For backward compatibility (v<=0.94) we check for the cached.json/pkl/pickle files
89+
# In later versions (v>0.94) we use the si_folder.json file
90+
for dump_ext in ("json", "pkl", "pickle"):
91+
f = folder / f"cached.{dump_ext}"
92+
if f.is_file():
93+
file = f
94+
95+
f = folder / f"si_folder.json"
96+
if f.is_file():
97+
file = f
98+
99+
if file is None:
100+
raise ValueError(error_msg)
101+
extractor = BaseExtractor.load(file, base_folder=folder)
102+
103+
else:
104+
raise ValueError(error_msg)
105+
else:
106+
# remote case - zarr
107+
if str(file_path).endswith(".zarr"):
108+
from .zarrextractors import read_zarr
109+
110+
extractor = read_zarr(file_path)
111+
else:
112+
raise NotImplementedError(
113+
"Only zarr format is supported for remote files and you should provide a path to a .zarr "
114+
"remote path. You can save to a valid zarr folder using: "
115+
"`extractor.save(folder='path/to/folder', format='zarr')`"
116+
)
117+
118+
return extractor
119+
120+
121+
def load_extractor(file_or_folder_or_dict, base_folder=None) -> BaseExtractor:
122+
warnings.warn(
123+
"load_extractor() is deprecated and will be removed in the future. Please use load() instead.",
124+
DeprecationWarning,
125+
stacklevel=2,
126+
)
127+
return load(file_or_folder_or_dict, base_folder=base_folder)

src/spikeinterface/core/recording_tools.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,9 @@ def _init_memory_worker(recording, arrays, shm_names, shapes, dtype, cast_unsign
247247
# create a local dict per worker
248248
worker_ctx = {}
249249
if isinstance(recording, dict):
250-
from spikeinterface.core import load_extractor
250+
from spikeinterface.core import load
251251

252-
worker_ctx["recording"] = load_extractor(recording)
252+
worker_ctx["recording"] = load(recording)
253253
else:
254254
worker_ctx["recording"] = recording
255255

0 commit comments

Comments
 (0)