Skip to content

Commit d32188b

Browse files
oruebelCodyCBakerPhDstephprincerly
authored
Fix bad validation check for postion in ElectrodeGroup.__init__ (#1770)
Co-authored-by: Cody Baker <[email protected]> Co-authored-by: Steph Prince <[email protected]> Co-authored-by: Ryan Ly <[email protected]>
1 parent dc98e84 commit d32188b

File tree

8 files changed

+75
-16
lines changed

8 files changed

+75
-16
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
### Performance
66
- Cache global type map to speed import 3X. @sneakers-the-rat [#1931](https://github.com/NeurodataWithoutBorders/pynwb/pull/1931)
77

8+
### Bug fixes
9+
- Fixed bug in how `ElectrodeGroup.__init__` validates its `position` argument. @oruebel [#1770](https://github.com/NeurodataWithoutBorders/pynwb/pull/1770)
10+
811
## PyNWB 2.8.2 (September 9, 2024)
912

1013
### Enhancements and minor changes

docs/source/conf.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,6 @@ def __call__(self, filename):
244244
# html_theme = 'default'
245245
# html_theme = "sphinxdoc"
246246
html_theme = "sphinx_rtd_theme"
247-
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
248247

249248
# Theme options are theme-specific and customize the look and feel of a theme
250249
# further. For a list of options available for each theme, see the
@@ -260,9 +259,6 @@ def __call__(self, filename):
260259
'css/custom.css',
261260
]
262261

263-
# Add any paths that contain custom themes here, relative to this directory.
264-
# html_theme_path = []
265-
266262
# The name for this set of Sphinx documents. If None, it defaults to
267263
# "<project> v<release> documentation".
268264
# html_title = None

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ classifiers = [
3434
]
3535
dependencies = [
3636
"h5py>=2.10",
37-
"hdmf>=3.14.3",
37+
"hdmf>=3.14.5",
3838
"numpy>=1.18",
3939
"pandas>=1.1.5",
4040
"python-dateutil>=2.7.3",

requirements-min.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# minimum versions of package dependencies for installing PyNWB
22
h5py==2.10 # support for selection of datasets with list of indices added in 2.10
3-
hdmf==3.14.3
3+
hdmf==3.14.5
44
numpy==1.18
55
pandas==1.1.5
66
python-dateutil==2.7.3

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# pinned dependencies to reproduce an entire development environment to use PyNWB
22
h5py==3.11.0
3-
hdmf==3.14.3
3+
hdmf==3.14.5
44
numpy==2.1.1; python_version > "3.9" # numpy 2.1+ is not compatible with py3.9
55
numpy==2.0.2; python_version == "3.9"
66
pandas==2.2.2

src/pynwb/ecephys.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import warnings
2+
import numpy as np
23
from collections.abc import Iterable
34

45
from hdmf.common import DynamicTableRegion
@@ -26,13 +27,31 @@ class ElectrodeGroup(NWBContainer):
2627
{'name': 'location', 'type': str, 'doc': 'description of location of this electrode group'},
2728
{'name': 'device', 'type': Device, 'doc': 'the device that was used to record from this electrode group'},
2829
{'name': 'position', 'type': 'array_data',
29-
'doc': 'stereotaxic position of this electrode group (x, y, z)', 'default': None})
30+
'doc': 'Compound dataset with stereotaxic position of this electrode group (x, y, z). '
31+
'The data array must have three elements or the dtype of the '
32+
'array must be ``(float, float, float)``', 'default': None})
3033
def __init__(self, **kwargs):
3134
args_to_set = popargs_to_dict(('description', 'location', 'device', 'position'), kwargs)
3235
super().__init__(**kwargs)
33-
if args_to_set['position'] and len(args_to_set['position']) != 3:
34-
raise ValueError('ElectrodeGroup position argument must have three elements: x, y, z, but received: %s'
35-
% str(args_to_set['position']))
36+
37+
# position is a compound dataset, i.e., this must be a scalar with a
38+
# compound data type of three floats or a list/tuple of three entries
39+
position = args_to_set['position']
40+
if position:
41+
# check position argument is valid
42+
position_dtype_invalid = (
43+
(hasattr(position, 'dtype') and len(position.dtype) != 3) or
44+
(not hasattr(position, 'dtype') and len(position) != 3) or
45+
(len(np.shape(position)) > 1)
46+
)
47+
if position_dtype_invalid:
48+
raise ValueError(f"ElectrodeGroup position argument must have three elements: x, y, z,"
49+
f"but received: {position}")
50+
51+
# convert position to scalar with compound data type if needed
52+
if not hasattr(position, 'dtype'):
53+
args_to_set['position'] = np.array(tuple(position), dtype=[('x', float), ('y', float), ('z', float)])
54+
3655
for key, val in args_to_set.items():
3756
setattr(self, key, val)
3857

tests/integration/hdf5/test_ecephys.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ def setUpContainer(self):
2626
eg = ElectrodeGroup(name='elec1',
2727
description='a test ElectrodeGroup',
2828
location='a nonexistent place',
29-
device=self.dev1)
29+
device=self.dev1,
30+
position=(1., 2., 3.))
3031
return eg
3132

3233
def addContainer(self, nwbfile):

tests/unit/test_ecephys.py

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,16 +178,34 @@ class ElectrodeGroupConstructor(TestCase):
178178

179179
def test_init(self):
180180
dev1 = Device('dev1')
181-
group = ElectrodeGroup('elec1', 'electrode description', 'electrode location', dev1, (1, 2, 3))
181+
group = ElectrodeGroup(name='elec1',
182+
description='electrode description',
183+
location='electrode location',
184+
device=dev1,
185+
position=(1, 2, 3))
182186
self.assertEqual(group.name, 'elec1')
183187
self.assertEqual(group.description, 'electrode description')
184188
self.assertEqual(group.location, 'electrode location')
185189
self.assertEqual(group.device, dev1)
186-
self.assertEqual(group.position, (1, 2, 3))
190+
self.assertEqual(group.position.tolist(), (1, 2, 3))
191+
192+
def test_init_position_array(self):
193+
position = np.array((1, 2, 3), dtype=np.dtype([('x', float), ('y', float), ('z', float)]))
194+
dev1 = Device('dev1')
195+
group = ElectrodeGroup('elec1', 'electrode description', 'electrode location', dev1,
196+
position)
197+
self.assertEqual(group.name, 'elec1')
198+
self.assertEqual(group.description, 'electrode description')
199+
self.assertEqual(group.location, 'electrode location')
200+
self.assertEqual(group.device, dev1)
201+
self.assertEqual(group.position, position)
187202

188203
def test_init_position_none(self):
189204
dev1 = Device('dev1')
190-
group = ElectrodeGroup('elec1', 'electrode description', 'electrode location', dev1)
205+
group = ElectrodeGroup(name='elec1',
206+
description='electrode description',
207+
location='electrode location',
208+
device=dev1)
191209
self.assertEqual(group.name, 'elec1')
192210
self.assertEqual(group.description, 'electrode description')
193211
self.assertEqual(group.location, 'electrode location')
@@ -197,7 +215,29 @@ def test_init_position_none(self):
197215
def test_init_position_bad(self):
198216
dev1 = Device('dev1')
199217
with self.assertRaises(ValueError):
200-
ElectrodeGroup('elec1', 'electrode description', 'electrode location', dev1, (1, 2))
218+
ElectrodeGroup(name='elec1',
219+
description='electrode description',
220+
location='electrode location',
221+
device=dev1,
222+
position=(1, 2))
223+
with self.assertRaises(ValueError):
224+
ElectrodeGroup(name='elec1',
225+
description='electrode description',
226+
location='electrode location',
227+
device=dev1,
228+
position=[(1, 2), ])
229+
with self.assertRaises(ValueError):
230+
ElectrodeGroup(name='elec1',
231+
description='electrode description',
232+
location='electrode location',
233+
device=dev1,
234+
position=np.array([(1., 2.)], dtype=np.dtype([('x', float), ('y', float)])))
235+
with self.assertRaises(ValueError):
236+
ElectrodeGroup(name='elec1',
237+
description='electrode description',
238+
location='electrode location',
239+
device=dev1,
240+
position=[(1, 2, 3), (4, 5, 6), (7, 8, 9)])
201241

202242

203243
class EventDetectionConstructor(TestCase):

0 commit comments

Comments
 (0)