Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
JuliaSprenger authored Jul 27, 2023
2 parents 8bac623 + 20c0fec commit c2ab6cf
Show file tree
Hide file tree
Showing 30 changed files with 2,213 additions and 94 deletions.
27 changes: 15 additions & 12 deletions .github/workflows/io-test.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
name: NeoIoTest

on:
pull_request:
branches: [master]
types: [synchronize, opened, reopened, ready_for_review]

# run checks on any change of master, including merge of PRs
push:
branches: [master]
workflow_call:
inputs:
os:
required: true
type: string

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

jobs:
build-and-test:
name: Test on (${{ matrix.os }})
runs-on: ${{ matrix.os }}
name: Test on (${{ inputs.os }})
runs-on: ${{ inputs.os }}
strategy:
fail-fast: true
matrix:
# "macos-latest", "windows-latest"
os: ["ubuntu-latest", ]
python-version: ['3.9', ]
defaults:
# by default run in bash mode (required for conda usage)
Expand Down Expand Up @@ -50,7 +46,7 @@ jobs:
key: ${{ runner.os }}-datasets-${{ steps.ephy_testing_data.outputs.dataset_hash }}
restore-keys: ${{ runner.os }}-datasets-

- uses: conda-incubator/setup-miniconda@v2
- uses: conda-incubator/setup-miniconda@v2.2.0
with:
activate-environment: neo-test-env
python-version: ${{ matrix.python-version }}
Expand Down Expand Up @@ -99,6 +95,13 @@ jobs:
run: |
pip install --no-dependencies -e .
- name: Install wine
run: |
sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list
sudo dpkg --add-architecture i386
sudo apt-get update -qq
sudo apt-get install -yqq --allow-downgrades libc6:i386 libgcc-s1:i386 libstdc++6:i386 wine
- name: Test with pytest
run: |
# only neo.rawio and neo.io
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/io-test_dispatch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: NeoIoTest-manual-trigger

on:
workflow_dispatch:
inputs:
os:
description: 'The operating system to run the tests on'
required: True
default: 'ubuntu-latest'
type: choice
options:
- macos-latest
- windows-latest

jobs:
call-iotests:
uses: ./.github/workflows/io-test.yml
with:
os: ${{ inputs.os }}
16 changes: 16 additions & 0 deletions .github/workflows/io-test_trigger.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: NeoIoTest-automatic-trigger

on:
pull_request:
branches: [master]
types: [synchronize, opened, reopened, ready_for_review]

# run checks on any change of master, including merge of PRs
push:
branches: [master]

jobs:
call-iotests:
uses: ./.github/workflows/io-test.yml
with:
os: ubuntu-latest
3 changes: 3 additions & 0 deletions doc/source/authors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ and may not be the current affiliation of a contributor.
* Elodie Legouée [21]
* Heberto Mayorquin [24]
* Thomas Perret [25]
* Kyle Johnsen [26, 27]

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

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

Expand Down
33 changes: 33 additions & 0 deletions neo/core/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,39 @@ def size(self):
return {name: len(getattr(self, name))
for name in self._child_containers}

@property
def _container_lookup(self):
return {
cls_name: getattr(self, container_name)
for cls_name, container_name in zip(self._child_objects, self._child_containers)
}

def _get_container(self, cls):
if hasattr(cls, "proxy_for"):
cls = cls.proxy_for
return self._container_lookup[cls.__name__]

def add(self, *objects):
"""Add a new Neo object to the Container"""
for obj in objects:
if (
obj.__class__.__name__ in self._child_objects
or (
hasattr(obj, "proxy_for")
and obj.proxy_for.__name__ in self._child_objects
)
):
container = self._get_container(obj.__class__)
container.append(obj)
else:
raise TypeError(
f"Cannot add object of type {obj.__class__.__name__} "
f"to a {self.__class__.__name__}, can only add objects of the "
f"following types: {self._child_objects}"
)



def filter(self, targdict=None, data=True, container=False, recursive=True,
objects=None, **kwargs):
"""
Expand Down
4 changes: 2 additions & 2 deletions neo/core/dataobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def _normalize_array_annotations(value, length):
Parameters
----------
value : np.ndarray or list or dict
value : np.ndarray or list or tuple or dict
Value to be checked for consistency.
length : int
Required length of the array annotation.
Expand Down Expand Up @@ -48,7 +48,7 @@ def _normalize_array_annotations(value, length):
raise ValueError("Array annotations must not be None")
# If not array annotation, pass on to regular check and make it a list, that is checked again
# This covers array annotations with length 1
elif not isinstance(value, (list, np.ndarray)) or (
elif not isinstance(value, (list, np.ndarray, tuple)) or (
isinstance(value, pq.Quantity) and value.shape == ()):
_check_annotations(value)
value = _normalize_array_annotations(np.array([value]), length)
Expand Down
18 changes: 4 additions & 14 deletions neo/core/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ def __init__(self, objects=None, name=None, description=None, file_origin=None,
self.allowed_types = None
else:
self.allowed_types = tuple(allowed_types)
for type_ in self.allowed_types:
if type_.__name__ not in self._child_objects:
raise TypeError(f"Groups can not contain objects of type {type_.__name__}")

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

@property
def _container_lookup(self):
return {
cls_name: getattr(self, container_name)
for cls_name, container_name in zip(self._child_objects, self._child_containers)
}

def _get_container(self, cls):
if hasattr(cls, "proxy_for"):
cls = cls.proxy_for
return self._container_lookup[cls.__name__]

def add(self, *objects):
"""Add a new Neo object to the Group"""
for obj in objects:
if self.allowed_types and not isinstance(obj, self.allowed_types):
raise TypeError("This Group can only contain {}, but not {}"
"".format(self.allowed_types, type(obj)))
container = self._get_container(obj.__class__)
container.append(obj)
super().add(*objects)

def walk(self):
"""
Expand Down
26 changes: 24 additions & 2 deletions neo/io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
* :attr:`PhyIO`
* :attr:`PickleIO`
* :attr:`PlexonIO`
* :attr:`Plexon2IO`
* :attr:`RawBinarySignalIO`
* :attr:`RawMCSIO`
* :attr:`Spike2IO`
Expand Down Expand Up @@ -226,6 +227,10 @@
.. autoattribute:: extensions
.. autoclass:: neo.io.Plexon2IO
.. autoattribute:: extensions
.. autoclass:: neo.io.RawBinarySignalIO
.. autoattribute:: extensions
Expand Down Expand Up @@ -328,6 +333,7 @@
from neo.io.phyio import PhyIO
from neo.io.pickleio import PickleIO
from neo.io.plexonio import PlexonIO
from neo.io.plexon2io import Plexon2IO
from neo.io.rawbinarysignalio import RawBinarySignalIO
from neo.io.rawmcsio import RawMCSIO
from neo.io.spike2io import Spike2IO
Expand Down Expand Up @@ -382,6 +388,7 @@
PhyIO,
PickleIO,
PlexonIO,
Plexon2IO,
RawBinarySignalIO,
RawMCSIO,
Spike2IO,
Expand Down Expand Up @@ -458,8 +465,23 @@ def list_candidate_ios(file_or_folder, ignore_patterns=['*.ini', 'README.txt', '
# if only file prefix was provided, e.g /mydatafolder/session1-
# to select all files sharing the `session1-` prefix
elif file_or_folder.parent.exists():
filenames = file_or_folder.parent.glob(file_or_folder.name + '*')

filenames = list(file_or_folder.parent.glob(file_or_folder.name + '*'))
# if filenames empty and suffix is provided then non-existent file
# may be written in current dir. So run check for io
if len(filenames)==0 and file_or_folder.suffix:
suffix = file_or_folder.suffix[1:].lower()
if suffix not in io_by_extension:
raise ValueError(f'{suffix} is not a supported format of any IO.')
return io_by_extension[suffix]

# If non-existent file in non-existent dir is given check if this
# structure could be created with an io writing the file
elif file_or_folder.suffix:
suffix = file_or_folder.suffix[1:].lower()
if suffix not in io_by_extension:
raise ValueError(f'{suffix} is not a supported format of any IO.')
return io_by_extension[suffix]

else:
raise ValueError(f'{file_or_folder} does not contain data files of a supported format')

Expand Down
17 changes: 17 additions & 0 deletions neo/io/plexon2io.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from neo.io.basefromrawio import BaseFromRaw
from neo.rawio.plexon2rawio import Plexon2RawIO


class Plexon2IO(Plexon2RawIO, BaseFromRaw):
"""
Class for reading data from Plexon PL2 files
The IO is based on the Plexon2RawIO, see comments for memory optimization
in neo.rawio.plexon2rawio.Plexon2RawIO
"""

def __init__(self, filename):
Plexon2RawIO.__init__(self, filename=filename)
BaseFromRaw.__init__(self, filename)

2 changes: 1 addition & 1 deletion neo/io/plexonio.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ class PlexonIO(PlexonRawIO, BaseFromRaw):
_prefered_signal_group_mode = 'group-by-same-units'

def __init__(self, filename):
PlexonRawIO.__init__(self, filename=filename)
PlexonRawIO.__init__(self, filename)
BaseFromRaw.__init__(self, filename)
16 changes: 10 additions & 6 deletions neo/io/proxyobjects.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,21 +187,25 @@ def _time_slice_indices(self, time_slice, strict_slicing=True):
assert self.t_start <= t_start <= self.t_stop, 't_start is outside'
else:
t_start = max(t_start, self.t_start)
# the i_start is necessary ceil
i_start = int(np.ceil((t_start - self.t_start).magnitude * sr.magnitude))
# this needed to get the real t_start of the first sample
# because do not necessary match what is demanded
# the i_start is rounded to the nearest sample
i_start = np.rint((t_start - self.t_start).magnitude * sr.magnitude).astype(np.int64)
# this is needed to get the real t_start of the first sample
# because it does not necessary match what is demanded
sig_t_start = self.t_start + i_start / sr

if t_stop is None:
i_stop = None
else:
t_stop = ensure_second(t_stop)
if strict_slicing:
assert self.t_start <= t_stop <= self.t_stop, 't_stop is outside'
assert self.t_start <= t_stop <= self.t_stop, 't_stop is outside possible time range'
else:
t_stop = min(t_stop, self.t_stop)
i_stop = int((t_stop - self.t_start).magnitude * sr.magnitude)
# calculate duration demanded then round it to nearest sample number
# add this to i_start to get i_stop
delta = (t_stop - t_start) * sr
i_stop = i_start + int(np.rint(delta.simplified.magnitude))

return i_start, i_stop, sig_t_start

def load(self, time_slice=None, strict_slicing=True,
Expand Down
7 changes: 7 additions & 0 deletions neo/rawio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
* :attr:`OpenEphysBinaryRawIO`
* :attr:`PhyRawIO`
* :attr:`PlexonRawIO`
* :attr:`Plexon2RawIO`
* :attr:`RawBinarySignalRawIO`
* :attr:`RawMCSRawIO`
* :attr:`Spike2RawIO`
Expand Down Expand Up @@ -137,6 +138,10 @@
.. autoattribute:: extensions
.. autoclass:: neo.rawio.Plexon2RawIO
.. autoattribute:: extensions
.. autoclass:: neo.rawio.RawBinarySignalRawIO
.. autoattribute:: extensions
Expand Down Expand Up @@ -196,6 +201,7 @@
from neo.rawio.openephysbinaryrawio import OpenEphysBinaryRawIO
from neo.rawio.phyrawio import PhyRawIO
from neo.rawio.plexonrawio import PlexonRawIO
from neo.rawio.plexon2rawio import Plexon2RawIO
from neo.rawio.rawbinarysignalrawio import RawBinarySignalRawIO
from neo.rawio.rawmcsrawio import RawMCSRawIO
from neo.rawio.spike2rawio import Spike2RawIO
Expand Down Expand Up @@ -229,6 +235,7 @@
OpenEphysBinaryRawIO,
PhyRawIO,
PlexonRawIO,
Plexon2RawIO,
RawBinarySignalRawIO,
RawMCSRawIO,
Spike2RawIO,
Expand Down
7 changes: 4 additions & 3 deletions neo/rawio/maxwellrawio.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,10 @@ def _get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop,
if np.array(channel_indexes).size > 1 and np.any(np.diff(channel_indexes) < 0):
# get around h5py constraint that it does not allow datasets
# to be indexed out of order
sorted_channel_indexes = np.sort(channel_indexes)
resorted_indexes = np.array(
[list(channel_indexes).index(ch) for ch in sorted_channel_indexes])
order_f = np.argsort(channel_indexes)
sorted_channel_indexes = channel_indexes[order_f]
# use argsort again on order_f to obtain resorted_indexes
resorted_indexes = np.argsort(order_f)

try:
if resorted_indexes is None:
Expand Down
Loading

0 comments on commit c2ab6cf

Please sign in to comment.