Skip to content

Commit

Permalink
Merge branch 'master' into patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
khl02007 committed Dec 14, 2023
2 parents 30f8e8b + 4ef2801 commit 8e3e421
Show file tree
Hide file tree
Showing 51 changed files with 1,457 additions and 364 deletions.
9 changes: 4 additions & 5 deletions .github/workflows/core-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
paths:
- 'neo/core/**'
- 'pyproject.toml'
- '.github/workflows/*.yml'

# run checks on any change of master, including merge of PRs
push:
Expand All @@ -26,14 +27,12 @@ jobs:
os: ["ubuntu-latest", "windows-latest"]
# "macos-latest",
python-version: ['3.8', '3.9', '3.10', '3.11']
numpy-version: ['1.19.5', '1.20.3', '1.21.6', '1.22.4', '1.23.5', '1.24.1']
numpy-version: ['1.20.3', '1.21.6', '1.22.4', '1.23.5', '1.24.1', '1.25.1']
exclude:
- python-version: '3.10'
numpy-version: '1.19.5'
- python-version: '3.8'
numpy-version: '1.25.1'
- python-version: '3.10'
numpy-version: '1.20.3'
- python-version: '3.11'
numpy-version: '1.19.5'
- python-version: '3.11'
numpy-version: '1.20.3'
- python-version: '3.11'
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/io-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ jobs:
sudo apt-get install -yqq --allow-downgrades libc6:i386 libgcc-s1:i386 libstdc++6:i386 wine
- name: Test with pytest
env:
HDF5_PLUGIN_PATH: ${{ github.workspace }}/hdf5_local_plugin_path
run: |
# only neo.rawio and neo.io
pytest --cov=neo neo/test/rawiotest
Expand Down
5 changes: 2 additions & 3 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
version: 2

build:
os: ubuntu-20.04
os: ubuntu-22.04
tools:
python: "3.8"
python: "3.11"

sphinx:
configuration: doc/source/conf.py
Expand All @@ -14,4 +14,3 @@ python:
path: .
extra_requirements:
- docs

2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
See http://neo.readthedocs.io/en/latest/developers_guide.html
See https://neo.readthedocs.io/en/latest/contributing.html
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
2 changes: 2 additions & 0 deletions doc/source/governance.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ The current maintainers are:
- Samuel Garcia (`@samuelgarcia`_)
- Julia Sprenger (`@JuliaSprenger`_)
- Michael Denker (`@mdenker`_)
- Alessio Buccino (`@alejoe91`_)


.. _`Neo maintainers team`: https://github.com/orgs/NeuralEnsemble/teams/neo-maintainers
Expand All @@ -45,3 +46,4 @@ The current maintainers are:
.. _`@samuelgarcia`: https://github.com/samuelgarcia
.. _`@JuliaSprenger`: https://github.com/JuliaSprenger
.. _`@mdenker`: https://github.com/mdenker
.. _`@alejoe91`: https://github.com/alejoe91
17 changes: 7 additions & 10 deletions neo/core/analogsignal.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,12 +416,9 @@ def _repr_pretty_(self, pp, cycle):
'''
Handle pretty-printing the :class:`AnalogSignal`.
'''
pp.text("{cls} with {channels} channels of length {length}; "
"units {units}; datatype {dtype} ".format(cls=self.__class__.__name__,
channels=self.shape[1],
length=self.shape[0],
units=self.units.dimensionality.string,
dtype=self.dtype))
pp.text(f"{self.__class__.__name__} with {self.shape[1]} channels of length "
f"{self.shape[0]}; units {self.units.dimensionality.string}; datatype "
f"{self.dtype}")
if self._has_repr_pretty_attrs_():
pp.breakable()
self._repr_pretty_attrs_(pp, cycle)
Expand All @@ -431,8 +428,8 @@ def _pp(line):
with pp.group(indent=1):
pp.text(line)

_pp("sampling rate: {}".format(self.sampling_rate))
_pp("time: {} to {}".format(self.t_start, self.t_stop))
_pp(f"sampling rate: {self.sampling_rate}")
_pp(f"time: {self.t_start} to {self.t_stop}")

def time_index(self, t):
"""Return the array index (or indices) corresponding to the time (or times) `t`"""
Expand Down Expand Up @@ -716,9 +713,9 @@ def concatenate(self, *signals, overwrite=False, padding=False):
raise MergeError(f'Signals are not continuous. Can not concatenate signals with gaps. '
f'Please provide a padding value.')
if padding is not False:
logger.warning('Signals will be padded using {}.'.format(padding))
logger.warning(f'Signals will be padded using {padding}.')
if padding is True:
padding = np.NaN * units
padding = np.nan * units
if isinstance(padding, pq.Quantity):
padding = padding.rescale(units).magnitude
else:
Expand Down
24 changes: 11 additions & 13 deletions neo/core/baseneo.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ def _check_annotations(value):
"""
if isinstance(value, np.ndarray):
if not issubclass(value.dtype.type, ALLOWED_ANNOTATION_TYPES):
raise ValueError("Invalid annotation. NumPy arrays with dtype %s"
"are not allowed" % value.dtype.type)
raise ValueError(f"Invalid annotation. NumPy arrays with dtype {value.dtype.type}"
f"are not allowed")
elif isinstance(value, dict):
for element in value.values():
_check_annotations(element)
elif isinstance(value, (list, tuple)):
for element in value:
_check_annotations(element)
elif not isinstance(value, ALLOWED_ANNOTATION_TYPES):
raise ValueError("Invalid annotation. Annotations of type %s are not"
"allowed" % type(value))
raise ValueError(f"Invalid annotation. Annotations of type {type(value)} are not"
f"allowed")


def merge_annotation(a, b):
Expand All @@ -58,8 +58,7 @@ def merge_annotation(a, b):
For strings: concatenate with ';'
Otherwise: fail if the annotations are not equal
"""
assert type(a) == type(b), 'type({}) {} != type({}) {}'.format(a, type(a),
b, type(b))
assert type(a) == type(b), f'type({a})) {type(a)} != type({b}) {type(b)}'
if isinstance(a, dict):
return merge_annotations(a, b)
elif isinstance(a, np.ndarray): # concatenate b to a
Expand All @@ -72,7 +71,7 @@ def merge_annotation(a, b):
else:
return a + ";" + b
else:
assert a == b, '{} != {}'.format(a, b)
assert a == b, f'{a} != {b}'
return a


Expand Down Expand Up @@ -100,7 +99,7 @@ def merge_annotations(A, *Bs):
# exc.args += ('key %s' % name,)
# raise
merged[name] = "MERGE CONFLICT" # temporary hack
logger.debug("Merging annotations: A=%s Bs=%s merged=%s", A, Bs, merged)
logger.debug(f"Merging annotations: A={A} Bs={Bs} merged={merged}")
return merged


Expand All @@ -122,8 +121,7 @@ def intersect_annotations(A, B):

for key in set(A.keys()) & set(B.keys()):
v1, v2 = A[key], B[key]
assert type(v1) == type(v2), 'type({}) {} != type({}) {}'.format(v1, type(v1),
v2, type(v2))
assert type(v1) == type(v2), f'type({v1}) {type(v1)} != type({v2}) {type(v2)}'
if isinstance(v1, dict) and v1 == v2:
result[key] = deepcopy(v1)
elif isinstance(v1, str) and v1 == v2:
Expand Down Expand Up @@ -299,7 +297,7 @@ def _repr_pretty_attrs_(self, pp, cycle):
else:
pp.breakable()
with pp.group(indent=1):
pp.text("{}: ".format(key))
pp.text(f"{key}: ")
pp.pretty(value)

def _repr_pretty_(self, pp, cycle):
Expand Down Expand Up @@ -366,8 +364,8 @@ def set_parent(self, obj):
according to the type of "obj"
"""
if obj.__class__.__name__ not in self._parent_objects:
raise TypeError("{} can only have parents of type {}, not {}".format(
self.__class__.__name__, self._parent_objects, obj.__class__.__name__))
raise TypeError((f"{self.__class__.__name__} can only have parents of "
f"type {self._parwents_objects}, not {obj.__class__.__name__}"))
loc = self._parent_objects.index(obj.__class__.__name__)
parent_attr = self._parent_attrs[loc]
setattr(self, parent_attr, obj)
2 changes: 1 addition & 1 deletion neo/core/basesignal.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ def merge(self, other):
if attr_self == attr_other:
kwargs[name] = attr_self
else:
kwargs[name] = "merge({}, {})".format(attr_self, attr_other)
kwargs[name] = f"merge({attr_self}, {attr_other})"
merged_annotations = merge_annotations(self.annotations, other.annotations)
kwargs.update(merged_annotations)

Expand Down
37 changes: 35 additions & 2 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 Expand Up @@ -530,7 +563,7 @@ def _repr_pretty_(self, pp, cycle):
for container in self._child_containers:
objs = getattr(self, container)
if objs:
vals.append('{} {}'.format(len(objs), container))
vals.append(f'{objs} {container}')
pp.text(', '.join(vals))

if self._has_repr_pretty_attrs_():
Expand All @@ -540,7 +573,7 @@ def _repr_pretty_(self, pp, cycle):
for container in self._repr_pretty_containers:
pp.breakable()
objs = getattr(self, container)
pp.text("# {} (N={})".format(container, len(objs)))
pp.text(f"# {container} (N={objs})")
for (i, obj) in enumerate(objs):
pp.breakable()
pp.text("%s: " % i)
Expand Down
10 changes: 5 additions & 5 deletions neo/core/dataobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def _normalize_array_annotations(value, length):

if not own_length == val_length:
raise ValueError(
"Incorrect length of array annotation: {} != {}".format(val_length, own_length))
f"Incorrect length of array annotation: {val_length} != {own_length}")

# Local function used to check single elements of a list or an array
# They must not be lists or arrays and fit the usual annotation data types
Expand Down Expand Up @@ -264,10 +264,10 @@ def _merge_array_annotations(self, other):

# Warn if keys were omitted
if omitted_keys_other or omitted_keys_self:
warnings.warn("The following array annotations were omitted, because they were only "
"present in one of the merged objects: {} from the one that was merged "
"into and {} from the one that was merged into the other"
"".format(omitted_keys_self, omitted_keys_other), UserWarning)
warnings.warn(f"The following array annotations were omitted, because they were only "
f"present in one of the merged objects: {omitted_keys_self} from the "
f"one that was merged into and {omitted_keys_other} from the one that "
f"was merged into the other", UserWarning)

# Return the merged array_annotations
return merged_array_annotations
Expand Down
10 changes: 5 additions & 5 deletions neo/core/epoch.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ def merge(self, other):
if attr_self == attr_other:
kwargs[name] = attr_self
else:
kwargs[name] = "merge({}, {})".format(attr_self, attr_other)
kwargs[name] = f"merge({attr_self}, {attr_other})"

merged_annotations = merge_annotations(self.annotations, other.annotations)
kwargs.update(merged_annotations)
Expand Down Expand Up @@ -354,8 +354,8 @@ def time_shift(self, t_shift):

def set_labels(self, labels):
if self.labels is not None and self.labels.size > 0 and len(labels) != self.size:
raise ValueError("Labels array has different length to times ({} != {})"
.format(len(labels), self.size))
raise ValueError(f"Labels array has different length to times "
f"({len(labels)} != {self.size})")
self._labels = np.array(labels)

def get_labels(self):
Expand All @@ -365,8 +365,8 @@ def get_labels(self):

def set_durations(self, durations):
if self.durations is not None and self.durations.size > 0 and len(durations) != self.size:
raise ValueError("Durations array has different length to times ({} != {})"
.format(len(durations), self.size))
raise ValueError("Durations array has different length to times "
f"({len(durations)} != {self.size})")
self._durations = durations

def get_durations(self):
Expand Down
12 changes: 6 additions & 6 deletions neo/core/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def __new__(cls, times=None, labels=None, units=None, name=None, description=Non
# reference dimensionality
if (len(dim) != 1 or list(dim.values())[0] != 1 or not isinstance(list(dim.keys())[0],
pq.UnitTime)):
ValueError("Unit {} has dimensions {}, not [time]".format(units, dim.simplified))
ValueError(f"Unit {units} has dimensions {dim.simplified}, not [time].")

obj = pq.Quantity(times, units=dim).view(cls)
obj._labels = labels
Expand Down Expand Up @@ -204,7 +204,7 @@ def merge(self, other):
if attr_self == attr_other:
kwargs[name] = attr_self
else:
kwargs[name] = "merge({}, {})".format(attr_self, attr_other)
kwargs[name] = f"merge({attr_self}, {attr_other})"

print('Event: merge annotations')
merged_annotations = merge_annotations(self.annotations, other.annotations)
Expand Down Expand Up @@ -244,8 +244,8 @@ def __getitem__(self, i):

def set_labels(self, labels):
if self.labels is not None and self.labels.size > 0 and len(labels) != self.size:
raise ValueError("Labels array has different length to times ({} != {})"
.format(len(labels), self.size))
raise ValueError(f"Labels array has different length to times "
f"({len(labels)} != {self.size})")
self._labels = np.array(labels)

def get_labels(self):
Expand Down Expand Up @@ -353,13 +353,13 @@ def to_epoch(self, pairwise=False, durations=None):
times = self.times[::2]
durations = self.times[1::2] - times
labels = np.array(
["{}-{}".format(a, b) for a, b in zip(self.labels[::2], self.labels[1::2])])
[f"{a}-{b}" for a, b in zip(self.labels[::2], self.labels[1::2])])
elif durations is None:
# Mode 1
times = self.times[:-1]
durations = np.diff(self.times)
labels = np.array(
["{}-{}".format(a, b) for a, b in zip(self.labels[:-1], self.labels[1:])])
[f"{a}-{b}" for a, b in zip(self.labels[:-1], self.labels[1:])])
else:
# Mode 3
times = self.times
Expand Down
Loading

0 comments on commit 8e3e421

Please sign in to comment.