Skip to content

Commit c2ab6cf

Browse files
Merge branch 'master' into master
2 parents 8bac623 + 20c0fec commit c2ab6cf

30 files changed

+2213
-94
lines changed

.github/workflows/io-test.yml

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,23 @@
11
name: NeoIoTest
22

33
on:
4-
pull_request:
5-
branches: [master]
6-
types: [synchronize, opened, reopened, ready_for_review]
7-
8-
# run checks on any change of master, including merge of PRs
9-
push:
10-
branches: [master]
4+
workflow_call:
5+
inputs:
6+
os:
7+
required: true
8+
type: string
119

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

1614
jobs:
1715
build-and-test:
18-
name: Test on (${{ matrix.os }})
19-
runs-on: ${{ matrix.os }}
16+
name: Test on (${{ inputs.os }})
17+
runs-on: ${{ inputs.os }}
2018
strategy:
2119
fail-fast: true
2220
matrix:
23-
# "macos-latest", "windows-latest"
24-
os: ["ubuntu-latest", ]
2521
python-version: ['3.9', ]
2622
defaults:
2723
# by default run in bash mode (required for conda usage)
@@ -50,7 +46,7 @@ jobs:
5046
key: ${{ runner.os }}-datasets-${{ steps.ephy_testing_data.outputs.dataset_hash }}
5147
restore-keys: ${{ runner.os }}-datasets-
5248

53-
- uses: conda-incubator/setup-miniconda@v2
49+
- uses: conda-incubator/setup-miniconda@v2.2.0
5450
with:
5551
activate-environment: neo-test-env
5652
python-version: ${{ matrix.python-version }}
@@ -99,6 +95,13 @@ jobs:
9995
run: |
10096
pip install --no-dependencies -e .
10197
98+
- name: Install wine
99+
run: |
100+
sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list
101+
sudo dpkg --add-architecture i386
102+
sudo apt-get update -qq
103+
sudo apt-get install -yqq --allow-downgrades libc6:i386 libgcc-s1:i386 libstdc++6:i386 wine
104+
102105
- name: Test with pytest
103106
run: |
104107
# only neo.rawio and neo.io
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: NeoIoTest-manual-trigger
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
os:
7+
description: 'The operating system to run the tests on'
8+
required: True
9+
default: 'ubuntu-latest'
10+
type: choice
11+
options:
12+
- macos-latest
13+
- windows-latest
14+
15+
jobs:
16+
call-iotests:
17+
uses: ./.github/workflows/io-test.yml
18+
with:
19+
os: ${{ inputs.os }}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: NeoIoTest-automatic-trigger
2+
3+
on:
4+
pull_request:
5+
branches: [master]
6+
types: [synchronize, opened, reopened, ready_for_review]
7+
8+
# run checks on any change of master, including merge of PRs
9+
push:
10+
branches: [master]
11+
12+
jobs:
13+
call-iotests:
14+
uses: ./.github/workflows/io-test.yml
15+
with:
16+
os: ubuntu-latest

doc/source/authors.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ and may not be the current affiliation of a contributor.
6262
* Elodie Legouée [21]
6363
* Heberto Mayorquin [24]
6464
* Thomas Perret [25]
65+
* Kyle Johnsen [26, 27]
6566

6667
1. Centre de Recherche en Neuroscience de Lyon, CNRS UMR5292 - INSERM U1028 - Universite Claude Bernard Lyon 1
6768
2. Unité de Neuroscience, Information et Complexité, CNRS UPR 3293, Gif-sur-Yvette, France
@@ -88,6 +89,8 @@ and may not be the current affiliation of a contributor.
8889
23. Bio Engineering Laboratory, DBSSE, ETH, Basel, Switzerland
8990
24. CatalystNeuro
9091
25. Institut des Sciences Cognitives Marc Jeannerod, CNRS UMR5229, Lyon, France
92+
26. Georgia Institute of Technology
93+
27. Emory University
9194

9295
If we've somehow missed you off the list we're very sorry - please let us know.
9396

neo/core/container.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,39 @@ def size(self):
327327
return {name: len(getattr(self, name))
328328
for name in self._child_containers}
329329

330+
@property
331+
def _container_lookup(self):
332+
return {
333+
cls_name: getattr(self, container_name)
334+
for cls_name, container_name in zip(self._child_objects, self._child_containers)
335+
}
336+
337+
def _get_container(self, cls):
338+
if hasattr(cls, "proxy_for"):
339+
cls = cls.proxy_for
340+
return self._container_lookup[cls.__name__]
341+
342+
def add(self, *objects):
343+
"""Add a new Neo object to the Container"""
344+
for obj in objects:
345+
if (
346+
obj.__class__.__name__ in self._child_objects
347+
or (
348+
hasattr(obj, "proxy_for")
349+
and obj.proxy_for.__name__ in self._child_objects
350+
)
351+
):
352+
container = self._get_container(obj.__class__)
353+
container.append(obj)
354+
else:
355+
raise TypeError(
356+
f"Cannot add object of type {obj.__class__.__name__} "
357+
f"to a {self.__class__.__name__}, can only add objects of the "
358+
f"following types: {self._child_objects}"
359+
)
360+
361+
362+
330363
def filter(self, targdict=None, data=True, container=False, recursive=True,
331364
objects=None, **kwargs):
332365
"""

neo/core/dataobject.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def _normalize_array_annotations(value, length):
2020
2121
Parameters
2222
----------
23-
value : np.ndarray or list or dict
23+
value : np.ndarray or list or tuple or dict
2424
Value to be checked for consistency.
2525
length : int
2626
Required length of the array annotation.
@@ -48,7 +48,7 @@ def _normalize_array_annotations(value, length):
4848
raise ValueError("Array annotations must not be None")
4949
# If not array annotation, pass on to regular check and make it a list, that is checked again
5050
# This covers array annotations with length 1
51-
elif not isinstance(value, (list, np.ndarray)) or (
51+
elif not isinstance(value, (list, np.ndarray, tuple)) or (
5252
isinstance(value, pq.Quantity) and value.shape == ()):
5353
_check_annotations(value)
5454
value = _normalize_array_annotations(np.array([value]), length)

neo/core/group.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ def __init__(self, objects=None, name=None, description=None, file_origin=None,
7676
self.allowed_types = None
7777
else:
7878
self.allowed_types = tuple(allowed_types)
79+
for type_ in self.allowed_types:
80+
if type_.__name__ not in self._child_objects:
81+
raise TypeError(f"Groups can not contain objects of type {type_.__name__}")
7982

8083
if objects:
8184
self.add(*objects)
@@ -134,26 +137,13 @@ def __init__(self, objects=None, name=None, description=None, file_origin=None,
134137
doc="list of Groups contained in this group"
135138
)
136139

137-
@property
138-
def _container_lookup(self):
139-
return {
140-
cls_name: getattr(self, container_name)
141-
for cls_name, container_name in zip(self._child_objects, self._child_containers)
142-
}
143-
144-
def _get_container(self, cls):
145-
if hasattr(cls, "proxy_for"):
146-
cls = cls.proxy_for
147-
return self._container_lookup[cls.__name__]
148-
149140
def add(self, *objects):
150141
"""Add a new Neo object to the Group"""
151142
for obj in objects:
152143
if self.allowed_types and not isinstance(obj, self.allowed_types):
153144
raise TypeError("This Group can only contain {}, but not {}"
154145
"".format(self.allowed_types, type(obj)))
155-
container = self._get_container(obj.__class__)
156-
container.append(obj)
146+
super().add(*objects)
157147

158148
def walk(self):
159149
"""

neo/io/__init__.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
* :attr:`PhyIO`
5555
* :attr:`PickleIO`
5656
* :attr:`PlexonIO`
57+
* :attr:`Plexon2IO`
5758
* :attr:`RawBinarySignalIO`
5859
* :attr:`RawMCSIO`
5960
* :attr:`Spike2IO`
@@ -226,6 +227,10 @@
226227
227228
.. autoattribute:: extensions
228229
230+
.. autoclass:: neo.io.Plexon2IO
231+
232+
.. autoattribute:: extensions
233+
229234
.. autoclass:: neo.io.RawBinarySignalIO
230235
231236
.. autoattribute:: extensions
@@ -328,6 +333,7 @@
328333
from neo.io.phyio import PhyIO
329334
from neo.io.pickleio import PickleIO
330335
from neo.io.plexonio import PlexonIO
336+
from neo.io.plexon2io import Plexon2IO
331337
from neo.io.rawbinarysignalio import RawBinarySignalIO
332338
from neo.io.rawmcsio import RawMCSIO
333339
from neo.io.spike2io import Spike2IO
@@ -382,6 +388,7 @@
382388
PhyIO,
383389
PickleIO,
384390
PlexonIO,
391+
Plexon2IO,
385392
RawBinarySignalIO,
386393
RawMCSIO,
387394
Spike2IO,
@@ -458,8 +465,23 @@ def list_candidate_ios(file_or_folder, ignore_patterns=['*.ini', 'README.txt', '
458465
# if only file prefix was provided, e.g /mydatafolder/session1-
459466
# to select all files sharing the `session1-` prefix
460467
elif file_or_folder.parent.exists():
461-
filenames = file_or_folder.parent.glob(file_or_folder.name + '*')
462-
468+
filenames = list(file_or_folder.parent.glob(file_or_folder.name + '*'))
469+
# if filenames empty and suffix is provided then non-existent file
470+
# may be written in current dir. So run check for io
471+
if len(filenames)==0 and file_or_folder.suffix:
472+
suffix = file_or_folder.suffix[1:].lower()
473+
if suffix not in io_by_extension:
474+
raise ValueError(f'{suffix} is not a supported format of any IO.')
475+
return io_by_extension[suffix]
476+
477+
# If non-existent file in non-existent dir is given check if this
478+
# structure could be created with an io writing the file
479+
elif file_or_folder.suffix:
480+
suffix = file_or_folder.suffix[1:].lower()
481+
if suffix not in io_by_extension:
482+
raise ValueError(f'{suffix} is not a supported format of any IO.')
483+
return io_by_extension[suffix]
484+
463485
else:
464486
raise ValueError(f'{file_or_folder} does not contain data files of a supported format')
465487

neo/io/plexon2io.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from neo.io.basefromrawio import BaseFromRaw
2+
from neo.rawio.plexon2rawio import Plexon2RawIO
3+
4+
5+
class Plexon2IO(Plexon2RawIO, BaseFromRaw):
6+
"""
7+
Class for reading data from Plexon PL2 files
8+
9+
The IO is based on the Plexon2RawIO, see comments for memory optimization
10+
in neo.rawio.plexon2rawio.Plexon2RawIO
11+
12+
"""
13+
14+
def __init__(self, filename):
15+
Plexon2RawIO.__init__(self, filename=filename)
16+
BaseFromRaw.__init__(self, filename)
17+

neo/io/plexonio.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ class PlexonIO(PlexonRawIO, BaseFromRaw):
1616
_prefered_signal_group_mode = 'group-by-same-units'
1717

1818
def __init__(self, filename):
19-
PlexonRawIO.__init__(self, filename=filename)
19+
PlexonRawIO.__init__(self, filename)
2020
BaseFromRaw.__init__(self, filename)

0 commit comments

Comments
 (0)