Skip to content

Commit 74e9415

Browse files
committed
Merge branch 'master' into zm711-matlabio
2 parents 8094839 + 3af1010 commit 74e9415

37 files changed

+914
-278
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Black formatting
2+
3+
on:
4+
workflow_dispatch:
5+
schedule:
6+
- cron: "0 12 * * 0" # Weekly at noon UTC on Sundays
7+
8+
jobs:
9+
lint:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout repository
13+
uses: actions/checkout@v4
14+
15+
- name: Check and apply black formatting
16+
id: black-check
17+
uses: psf/black@stable
18+
with:
19+
options: "--check --verbose"
20+
continue-on-error: true
21+
22+
- name: Create PR
23+
uses: peter-evans/create-pull-request@v5
24+
if : ${{ steps.black-check.outcome == 'failure' }}
25+
with:
26+
commit-message: black formatting
27+
title: Black formatting
28+
body: Reformatting code with black style
29+
branch: black-formatting

.github/workflows/io-test.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ jobs:
4646
key: ${{ runner.os }}-datasets-${{ steps.ephy_testing_data.outputs.dataset_hash }}
4747
restore-keys: ${{ runner.os }}-datasets-
4848

49-
- uses: conda-incubator/setup-miniconda@v2.2.0
49+
- uses: conda-incubator/setup-miniconda@v3
5050
with:
5151
activate-environment: neo-test-env
52-
python-version: ${{ matrix.python-version }}
52+
python-version: "${{ matrix.python-version }}"
5353

5454
- name: Get current dependencies hash
5555
id: dependencies
@@ -81,6 +81,11 @@ jobs:
8181
git config --global user.email "neo_ci@fake_mail.com"
8282
git config --global user.name "neo CI"
8383
84+
- name: Python version
85+
run: |
86+
which python
87+
python --version
88+
8489
- name: Install neo including dependencies
8590
# installation with dependencies is only required if no cache was found
8691
# restore-key hits should result in `cache-hit` == 'false'
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Release to Test PyPI
2+
3+
on:
4+
push:
5+
tags:
6+
- '*'
7+
jobs:
8+
release:
9+
environment: TEST_PYPI_API_TOKEN
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v4
14+
- name: Set up Python 3.10
15+
uses: actions/setup-python@v4
16+
with:
17+
python-version: "3.10"
18+
- name: Install Tools
19+
run: |
20+
python -m pip install --upgrade pip
21+
pip install setuptools wheel twine build
22+
pip install .
23+
- name: Get the tag version
24+
id: get-version
25+
run: |
26+
echo ${GITHUB_REF#refs/tags/}
27+
echo ::set-output name=TAG::${GITHUB_REF#refs/tags/}
28+
- name: Test version/tag correspondence
29+
id: version-check
30+
run: |
31+
neo_version=$(python -c "import neo; print(neo.__version__)")
32+
tag_version=${{ steps.get-version.outputs.TAG }}
33+
echo $neo_version
34+
echo $tag_version
35+
if [[ $tag_version == $neo_version ]]; then
36+
echo "VERSION_TAG_MATCH=true" >> $GITHUB_OUTPUT
37+
echo "Version matches tag, proceeding with release to Test PyPI"
38+
else
39+
echo "VERSION_TAG_MATCH=false" >> $GITHUB_OUTPUT
40+
echo "Version does not match tag! Fix this before proceeding."
41+
exit 1
42+
fi
43+
- name: Package and Upload
44+
env:
45+
STACKMANAGER_VERSION: ${{ github.event.release.tag_name }}
46+
TWINE_USERNAME: __token__
47+
TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
48+
if: ${{ steps.version-check.outputs.VERSION_TAG_MATCH == 'true' }}
49+
run: |
50+
python -m build --sdist --wheel
51+
twine upload --repository testpypi dist/*
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Release to PyPI
2+
3+
on:
4+
workflow_dispatch:
5+
6+
jobs:
7+
release:
8+
environment: PYPI_API_TOKEN
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- uses: actions/checkout@v4
13+
- name: Set up Python 3.10
14+
uses: actions/setup-python@v4
15+
with:
16+
python-version: "3.10"
17+
- name: Install Tools
18+
run: |
19+
python -m pip install --upgrade pip
20+
pip install setuptools wheel twine build
21+
- name: Package and Upload
22+
env:
23+
STACKMANAGER_VERSION: ${{ github.event.release.tag_name }}
24+
TWINE_USERNAME: __token__
25+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
26+
run: |
27+
python -m build --sdist --wheel
28+
twine upload dist/*

doc/source/authors.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ and may not be the current affiliation of a contributor.
6060
* Etienne Combrisson [6]
6161
* Ben Dichter [24]
6262
* Elodie Legouée [21]
63+
* Oliver Kloss [13]
6364
* Heberto Mayorquin [24]
6465
* Thomas Perret [25]
6566
* Kyle Johnsen [26, 27]

neo/core/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
Classes:
1111
1212
.. autoclass:: Block
13+
.. automethod:: Block.filter
1314
.. autoclass:: Segment
15+
.. automethod:: Segment.filter
1416
.. autoclass:: Group
1517
1618
.. autoclass:: AnalogSignal
@@ -35,6 +37,9 @@
3537
from neo.core.analogsignal import AnalogSignal
3638
from neo.core.irregularlysampledsignal import IrregularlySampledSignal
3739

40+
# Import FilterClasses
41+
from neo.core import filters
42+
3843
from neo.core.event import Event
3944
from neo.core.epoch import Epoch
4045

neo/core/baseneo.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,11 @@ def _container_name(class_name):
154154
referenced by `block.segments`. The attribute name `segments` is
155155
obtained by calling `_container_name_plural("Segment")`.
156156
"""
157-
return _reference_name(class_name) + 's'
157+
if "RegionOfInterest" in class_name:
158+
# this is a hack, pending a more principled way to handle this
159+
return "regionsofinterest"
160+
else:
161+
return _reference_name(class_name) + 's'
158162

159163

160164
class BaseNeo:

neo/core/block.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from neo.core.container import Container, unique_objs
1313
from neo.core.group import Group
1414
from neo.core.objectlist import ObjectList
15-
from neo.core.regionofinterest import RegionOfInterest
1615
from neo.core.segment import Segment
1716

1817

@@ -91,7 +90,6 @@ def __init__(self, name=None, description=None, file_origin=None,
9190
self.index = index
9291
self._segments = ObjectList(Segment, parent=self)
9392
self._groups = ObjectList(Group, parent=self)
94-
self._regionsofinterest = ObjectList(RegionOfInterest, parent=self)
9593

9694
segments = property(
9795
fget=lambda self: self._get_object_list("_segments"),
@@ -105,12 +103,6 @@ def __init__(self, name=None, description=None, file_origin=None,
105103
doc="list of Groups contained in this block"
106104
)
107105

108-
regionsofinterest = property(
109-
fget=lambda self: self._get_object_list("_regionsofinterest"),
110-
fset=lambda self, value: self._set_object_list("_regionsofinterest", value),
111-
doc="list of RegionOfInterest objects contained in this block"
112-
)
113-
114106
@property
115107
def data_children_recur(self):
116108
'''

neo/core/container.py

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
"""
77

88
from copy import deepcopy
9+
10+
from neo.core import filters
911
from neo.core.baseneo import BaseNeo, _reference_name, _container_name
1012
from neo.core.objectlist import ObjectList
1113
from neo.core.spiketrain import SpikeTrain
@@ -21,24 +23,25 @@ def unique_objs(objs):
2123
return [obj for obj in objs
2224
if id(obj) not in seen and not seen.add(id(obj))]
2325

24-
2526
def filterdata(data, targdict=None, objects=None, **kwargs):
2627
"""
2728
Return a list of the objects in data matching *any* of the search terms
2829
in either their attributes or annotations. Search terms can be
2930
provided as keyword arguments or a dictionary, either as a positional
30-
argument after data or to the argument targdict. targdict can also
31-
be a list of dictionaries, in which case the filters are applied
32-
sequentially. If targdict and kwargs are both supplied, the
33-
targdict filters are applied first, followed by the kwarg filters.
34-
A targdict of None or {} and objects = None corresponds to no filters
35-
applied, therefore returning all child objects.
36-
Default targdict and objects is None.
31+
argument after data or to the argument targdict.
32+
A key of a provided dictionary is the name of the requested annotation
33+
and the value is a FilterCondition object.
34+
E.g.: Equal(x), LessThan(x), InRange(x, y).
3735
36+
targdict can also
37+
be a list of dictionaries, in which case the filters are applied
38+
sequentially.
3839
39-
objects (optional) should be the name of a Neo object type,
40-
a neo object class, or a list of one or both of these. If specified,
41-
only these objects will be returned.
40+
A list of dictionaries is handled as follows: [ { or } and { or } ]
41+
If targdict and kwargs are both supplied, the
42+
targdict filters are applied first, followed by the kwarg filters.
43+
A targdict of None or {} corresponds to no filters applied, therefore
44+
returning all child objects. Default targdict is None.
4245
"""
4346

4447
# if objects are specified, get the classes
@@ -72,20 +75,26 @@ def filterdata(data, targdict=None, objects=None, **kwargs):
7275
else:
7376
# do the actual filtering
7477
results = []
75-
for key, value in sorted(targdict.items()):
76-
for obj in data:
77-
if (hasattr(obj, key) and getattr(obj, key) == value and
78-
all([obj is not res for res in results])):
78+
for obj in data:
79+
for key, value in sorted(targdict.items()):
80+
if hasattr(obj, key) and getattr(obj, key) == value:
7981
results.append(obj)
80-
elif (key in obj.annotations and obj.annotations[key] == value and
81-
all([obj is not res for res in results])):
82+
break
83+
if isinstance(value, filters.FilterCondition) and key in obj.annotations:
84+
if value.evaluate(obj.annotations[key]):
85+
results.append(obj)
86+
break
87+
if key in obj.annotations and obj.annotations[key] == value:
8288
results.append(obj)
89+
break
90+
91+
# remove duplicates from results
92+
results = list({ id(res): res for res in results }.values())
8393

8494
# keep only objects of the correct classes
8595
if objects:
8696
results = [result for result in results if
87-
result.__class__ in objects or
88-
result.__class__.__name__ in objects]
97+
result.__class__ in objects or result.__class__.__name__ in objects]
8998

9099
if results and all(isinstance(obj, SpikeTrain) for obj in results):
91100
return SpikeTrainList(results)
@@ -366,9 +375,17 @@ def filter(self, targdict=None, data=True, container=False, recursive=True,
366375
Return a list of child objects matching *any* of the search terms
367376
in either their attributes or annotations. Search terms can be
368377
provided as keyword arguments or a dictionary, either as a positional
369-
argument after data or to the argument targdict. targdict can also
378+
argument after data or to the argument targdict.
379+
A key of a provided dictionary is the name of the requested annotation
380+
and the value is a FilterCondition object.
381+
E.g.: equal(x), less_than(x), InRange(x, y).
382+
383+
targdict can also
370384
be a list of dictionaries, in which case the filters are applied
371-
sequentially. If targdict and kwargs are both supplied, the
385+
sequentially.
386+
387+
A list of dictionaries is handled as follows: [ { or } and { or } ]
388+
If targdict and kwargs are both supplied, the
372389
targdict filters are applied first, followed by the kwarg filters.
373390
A targdict of None or {} corresponds to no filters applied, therefore
374391
returning all child objects. Default targdict is None.
@@ -391,6 +408,8 @@ def filter(self, targdict=None, data=True, container=False, recursive=True,
391408
>>> obj.filter(name="Vm")
392409
>>> obj.filter(objects=neo.SpikeTrain)
393410
>>> obj.filter(targdict={'myannotation':3})
411+
>>> obj.filter(name=neo.core.filters.Equal(5))
412+
>>> obj.filter({'name': neo.core.filters.LessThan(5)})
394413
"""
395414

396415
if isinstance(targdict, str):

0 commit comments

Comments
 (0)