Skip to content

Commit e30e104

Browse files
committed
fix one-dir mode in neuralynx
1 parent 72ec76a commit e30e104

File tree

2 files changed

+69
-19
lines changed

2 files changed

+69
-19
lines changed

neo/rawio/neuralynxrawio/neuralynxrawio.py

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import numpy as np
5757
import os
5858
import pathlib
59+
from pathlib import Path
5960
import copy
6061
import warnings
6162
from collections import namedtuple, OrderedDict
@@ -151,15 +152,15 @@ def __init__(
151152

152153
if filename is not None:
153154
include_filenames = [filename]
154-
warnings.warn("`filename` is deprecated and will be removed. Please use `include_filenames` instead")
155+
warnings.warn("`filename` is deprecated and will be removed in v1.0. Please use `include_filenames` instead")
155156

156157
if exclude_filename is not None:
157158
if isinstance(exclude_filename, str):
158159
exclude_filenames = [exclude_filename]
159160
else:
160161
exclude_filenames = exclude_filename
161162
warnings.warn(
162-
"`exclude_filename` is deprecated and will be removed. Please use `exclude_filenames` instead"
163+
"`exclude_filename` is deprecated and will be removed in v1.0. Please use `exclude_filenames` instead"
163164
)
164165

165166
if include_filenames is None:
@@ -214,30 +215,42 @@ def _parse_header(self):
214215
unit_annotations = []
215216
event_annotations = []
216217

217-
if self.rawmode == "one-dir":
218-
filenames = sorted(os.listdir(self.dirname))
219-
else:
220-
filenames = self.include_filenames
218+
# 1) Get file paths based on mode and validate existence for multiple-files mode
219+
if self.rawmode == "multiple-files":
220+
# For multiple-files mode, validate that all explicitly provided files exist
221+
file_paths = []
222+
for filename in self.include_filenames:
223+
full_path = Path(self.dirname) / filename
224+
if not full_path.is_file():
225+
raise ValueError(
226+
f"Provided Filename is not a file: "
227+
f"{full_path}. If you want to provide a "
228+
f"directory use the `dirname` keyword"
229+
)
230+
file_paths.append(full_path)
231+
else: # one-dir mode
232+
# For one-dir mode, get all files from directory
233+
dir_path = Path(self.dirname)
234+
file_paths = [item for item in sorted(dir_path.iterdir()) if item.is_file()]
221235

222-
filenames = [f for f in filenames if f not in self.exclude_filenames]
223-
full_filenames = [os.path.join(self.dirname, f) for f in filenames]
236+
# 2) Filter by exclude filenames
237+
file_paths = [fp for fp in file_paths if fp.name not in self.exclude_filenames]
224238

225-
for filename in full_filenames:
226-
if not os.path.isfile(filename):
227-
raise ValueError(
228-
f"Provided Filename is not a file: "
229-
f"{filename}. If you want to provide a "
230-
f"directory use the `dirname` keyword"
231-
)
239+
# 3) Filter to keep only files with correct extensions
240+
# Note: suffix[1:] removes the leading dot from file extension (e.g., ".ncs" -> "ncs")
241+
valid_file_paths = [
242+
fp for fp in file_paths
243+
if fp.suffix[1:].lower() in self.extensions
244+
]
245+
246+
# Convert back to strings for backwards compatibility with existing code
247+
full_filenames = [str(fp) for fp in valid_file_paths]
232248

233249
stream_props = {} # {(sampling_rate, n_samples, t_start): {stream_id: [filenames]}
234250

235251
for filename in full_filenames:
236252
_, ext = os.path.splitext(filename)
237-
ext = ext[1:] # remove dot
238-
ext = ext.lower() # make lower case for comparisons
239-
if ext not in self.extensions:
240-
continue
253+
ext = ext[1:].lower() # remove dot and make lower case
241254

242255
# Skip Ncs files with only header. Other empty file types
243256
# will have an empty dataset constructed later.

neo/test/rawiotest/test_neuralynxrawio.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,43 @@ def test_exclude_filenames(self):
175175
self.assertEqual(len(rawio.header["spike_channels"]), 8)
176176
self.assertEqual(len(rawio.header["event_channels"]), 0)
177177

178+
def test_directory_in_data_folder(self):
179+
"""
180+
Test that directories inside the data folder are properly ignored
181+
and don't cause errors during parsing.
182+
"""
183+
import tempfile
184+
import shutil
185+
186+
# Use existing test data directory
187+
dname = self.get_local_path("neuralynx/Cheetah_v5.6.3/original_data/")
188+
189+
# Create a temporary copy to avoid modifying test data
190+
with tempfile.TemporaryDirectory() as temp_dir:
191+
temp_data_dir = os.path.join(temp_dir, "test_data")
192+
shutil.copytree(dname, temp_data_dir)
193+
194+
# Create a subdirectory inside the test data
195+
test_subdir = os.path.join(temp_data_dir, "raw fscv data with all recorded ch")
196+
os.makedirs(test_subdir, exist_ok=True)
197+
198+
# Create some files in the subdirectory to make it more realistic
199+
with open(os.path.join(test_subdir, "some_file.txt"), "w") as f:
200+
f.write("test file content")
201+
202+
# This should not raise an error despite the directory presence
203+
rawio = NeuralynxRawIO(dirname=temp_data_dir)
204+
rawio.parse_header()
205+
206+
# Verify that the reader still works correctly
207+
self.assertEqual(rawio._nb_segment, 2)
208+
self.assertEqual(len(rawio.ncs_filenames), 2)
209+
self.assertEqual(len(rawio.nev_filenames), 1)
210+
sigHdrs = rawio.header["signal_channels"]
211+
self.assertEqual(sigHdrs.size, 2)
212+
self.assertEqual(len(rawio.header["spike_channels"]), 8)
213+
self.assertEqual(len(rawio.header["event_channels"]), 2)
214+
178215

179216
class TestNcsRecordingType(BaseTestRawIO, unittest.TestCase):
180217
"""

0 commit comments

Comments
 (0)