Skip to content

Commit bfb502d

Browse files
committed
deprecate copy behavior
1 parent 3e1716f commit bfb502d

File tree

7 files changed

+149
-121
lines changed

7 files changed

+149
-121
lines changed

neo/core/analogsignal.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def _new_AnalogSignalArray(
5252
signal,
5353
units=None,
5454
dtype=None,
55-
copy=True,
55+
copy=None,
5656
t_start=0 * pq.s,
5757
sampling_rate=None,
5858
sampling_period=None,
@@ -180,7 +180,7 @@ def __new__(
180180
signal,
181181
units=None,
182182
dtype=None,
183-
copy=True,
183+
copy=None,
184184
t_start=0 * pq.s,
185185
sampling_rate=None,
186186
sampling_period=None,
@@ -198,8 +198,13 @@ def __new__(
198198
199199
__array_finalize__ is called on the new object.
200200
"""
201+
if copy is not None:
202+
raise ValueError(
203+
"`copy` is now deprecated in Neo due to removal in NumPy 2.0 and will be removed in 0.15.0."
204+
)
205+
201206
signal = cls._rescale(signal, units=units)
202-
obj = pq.Quantity(signal, units=units, dtype=dtype, copy=copy).view(cls)
207+
obj = pq.Quantity(signal, units=units, dtype=dtype).view(cls)
203208

204209
if obj.ndim == 1:
205210
obj.shape = (-1, 1)
@@ -547,6 +552,7 @@ def time_shift(self, t_shift):
547552

548553
return new_sig
549554

555+
# copy in splice is a deepcopy not a numpy copy so we can keep
550556
def splice(self, signal, copy=False):
551557
"""
552558
Replace part of the current signal by a new piece of signal.

neo/core/imagesequence.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class ImageSequence(BaseSignal):
7070
7171
*Optional attributes/properties*:
7272
:dtype: (numpy dtype or str) Override the dtype of the signal array.
73-
:copy: (bool) True by default.
73+
:copy: deprecated
7474
7575
Note: Any other additional arguments are assumed to be user-specific
7676
metadata and stored in :attr:`annotations`.
@@ -103,7 +103,7 @@ def __new__(
103103
image_data,
104104
units=pq.dimensionless,
105105
dtype=None,
106-
copy=True,
106+
copy=None,
107107
t_start=0 * pq.s,
108108
spatial_scale=None,
109109
frame_duration=None,
@@ -121,14 +121,20 @@ def __new__(
121121
122122
__array_finalize__ is called on the new object.
123123
"""
124+
125+
if copy is not None:
126+
raise ValueError(
127+
"`copy` is now deprecated in Neo due to removal in NumPy 2.0 and will be removed in 0.15.0."
128+
)
129+
124130
if spatial_scale is None:
125131
raise ValueError("spatial_scale is required")
126132

127133
image_data = np.stack(image_data)
128134
if len(image_data.shape) != 3:
129135
raise ValueError("list doesn't have the correct number of dimensions")
130136

131-
obj = pq.Quantity(image_data, units=units, dtype=dtype, copy=copy).view(cls)
137+
obj = pq.Quantity(image_data, units=units, dtype=dtype).view(cls)
132138
obj.segment = None
133139
# function from analogsignal.py in neo/core directory
134140
obj.sampling_rate = _get_sampling_rate(sampling_rate, frame_duration)
@@ -144,7 +150,7 @@ def __init__(
144150
image_data,
145151
units=pq.dimensionless,
146152
dtype=None,
147-
copy=True,
153+
copy=None,
148154
t_start=0 * pq.s,
149155
spatial_scale=None,
150156
frame_duration=None,

neo/core/irregularlysampledsignal.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def _new_IrregularlySampledSignal(
3838
units=None,
3939
time_units=None,
4040
dtype=None,
41-
copy=True,
41+
copy=None,
4242
name=None,
4343
file_origin=None,
4444
description=None,
@@ -93,8 +93,8 @@ class IrregularlySampledSignal(BaseSignal):
9393
dtype: numpy dtype | string | None, default: None
9494
Overrides the signal array dtype
9595
Does not affect the dtype of the times which must be floats
96-
copy: bool, default: True
97-
Whether copy should be set to True when making the quantity array
96+
copy: bool | None, default: None
97+
deprecated and no longer used (for NumPy 2.0 support). Will be removed.
9898
name: str | None, default: None
9999
An optional label for the dataset
100100
description: str | None, default: None
@@ -158,7 +158,7 @@ def __new__(
158158
units=None,
159159
time_units=None,
160160
dtype=None,
161-
copy=True,
161+
copy=None,
162162
name=None,
163163
file_origin=None,
164164
description=None,
@@ -171,6 +171,12 @@ def __new__(
171171
This is called whenever a new :class:`IrregularlySampledSignal` is
172172
created from the constructor, but not when slicing.
173173
"""
174+
175+
if copy is not None:
176+
raise ValueError(
177+
"`copy` is now deprecated in Neo due to removal in NumPy 2.0 and will be removed in 0.15.0."
178+
)
179+
174180
signal = cls._rescale(signal, units=units)
175181
if time_units is None:
176182
if hasattr(times, "units"):

neo/core/spiketrain.py

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def _new_spiketrain(
138138
t_stop,
139139
units=None,
140140
dtype=None,
141-
copy=True,
141+
copy=None,
142142
sampling_rate=1.0 * pq.Hz,
143143
t_start=0.0 * pq.s,
144144
waveforms=None,
@@ -158,19 +158,19 @@ def _new_spiketrain(
158158
if annotations is None:
159159
annotations = {}
160160
obj = SpikeTrain(
161-
signal,
162-
t_stop,
163-
units,
164-
dtype,
165-
copy,
166-
sampling_rate,
167-
t_start,
168-
waveforms,
169-
left_sweep,
170-
name,
171-
file_origin,
172-
description,
173-
array_annotations,
161+
signal=signal,
162+
t_stop=t_stop,
163+
units=units,
164+
dtype=dtype,
165+
copy=copy,
166+
sampling_rate=sampling_rate,
167+
t_start=t_start,
168+
waveforms=waveforms,
169+
left_sweep=left_sweep,
170+
name=name,
171+
file_origin=file_origin,
172+
description=description,
173+
array_annotations=array_annotations,
174174
**annotations,
175175
)
176176
obj.segment = segment
@@ -246,11 +246,9 @@ class SpikeTrain(DataObject):
246246
Required if `times` is a list or numpy.ndarray`
247247
Not required if times is a quantities.Quantity
248248
dtype: numpy dtype | str | None, default: None
249-
Overrides the dtype of the times array if given.
250-
If None, the dtype of the times is used
251-
copy: bool, default: True
252-
Whether to copy the times array.
253-
Must be True when you request a change of units or dtype.
249+
Due to change in `copy` behavior this argument is also deprecated during construction
250+
copy: bool, default: None
251+
Deprecated in order to support NumPy 2.0 and will be removed.
254252
sampling_rate: quantity scalar, default: 1.0 pq.Hz
255253
Number of samples per unit time for the waveforms.
256254
t_start: quantity scalar | numpy scalar | float
@@ -330,7 +328,7 @@ def __new__(
330328
t_stop,
331329
units=None,
332330
dtype=None,
333-
copy=True,
331+
copy=None,
334332
sampling_rate=1.0 * pq.Hz,
335333
t_start=0.0 * pq.s,
336334
waveforms=None,
@@ -347,13 +345,17 @@ def __new__(
347345
This is called whenever a new :class:`SpikeTrain` is created from the
348346
constructor, but not when slicing.
349347
"""
348+
if copy is not None:
349+
raise ValueError(
350+
"`copy` is now deprecated in Neo due to removal in NumPy 2.0 and will be removed in 0.15.0."
351+
)
352+
350353
if len(times) != 0 and waveforms is not None and len(times) != waveforms.shape[0]:
351354
# len(times)!=0 has been used to workaround a bug occurring during neo import
352355
raise ValueError("the number of waveforms should be equal to the number of spikes")
353356

354357
if dtype is not None and hasattr(times, "dtype") and times.dtype != dtype:
355-
if not copy:
356-
raise ValueError("cannot change dtype and return view")
358+
raise ValueError("cannot change dtype during construction due to change in copy behavior")
357359

358360
# if t_start.dtype or t_stop.dtype != times.dtype != dtype,
359361
# _check_time_in_range can have problems, so we set the t_start
@@ -420,7 +422,7 @@ def __init__(
420422
t_stop,
421423
units=None,
422424
dtype=None,
423-
copy=True,
425+
copy=None,
424426
sampling_rate=1.0 * pq.Hz,
425427
t_start=0.0 * pq.s,
426428
waveforms=None,

neo/test/coretest/test_analogsignal.py

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -131,30 +131,31 @@ def test__create_inconsistent_sampling_rate_and_period_ValueError(self):
131131
data = np.arange(10.0) * pq.mV
132132
self.assertRaises(ValueError, AnalogSignal, data, sampling_rate=1 * pq.kHz, sampling_period=5 * pq.s)
133133

134-
def test__create_with_copy_true_should_return_copy(self):
135-
data = np.arange(10.0) * pq.mV
136-
rate = 5000 * pq.Hz
137-
signal = AnalogSignal(data, copy=True, sampling_rate=rate)
138-
data[3] = 99 * pq.mV
139-
assert_neo_object_is_compliant(signal)
140-
self.assertNotEqual(signal[3, 0], 99 * pq.mV)
141-
142-
def test__create_with_copy_false_should_return_view(self):
143-
data = np.arange(10.0) * pq.mV
144-
rate = 5000 * pq.Hz
145-
signal = AnalogSignal(data, copy=False, sampling_rate=rate)
146-
data[3] = 99 * pq.mV
147-
assert_neo_object_is_compliant(signal)
148-
self.assertEqual(signal[3, 0], 99 * pq.mV)
149-
150-
def test__create2D_with_copy_false_should_return_view(self):
151-
data = np.arange(10.0) * pq.mV
152-
data = data.reshape((5, 2))
153-
rate = 5000 * pq.Hz
154-
signal = AnalogSignal(data, copy=False, sampling_rate=rate)
155-
data[3, 0] = 99 * pq.mV
156-
assert_neo_object_is_compliant(signal)
157-
self.assertEqual(signal[3, 0], 99 * pq.mV)
134+
# to be removed after testing in CI
135+
#def test__create_with_copy_true_should_return_copy(self):
136+
# data = np.arange(10.0) * pq.mV
137+
# rate = 5000 * pq.Hz
138+
# signal = AnalogSignal(data, copy=True, sampling_rate=rate)
139+
# data[3] = 99 * pq.mV
140+
# assert_neo_object_is_compliant(signal)
141+
# self.assertNotEqual(signal[3, 0], 99 * pq.mV)
142+
143+
#def test__create_with_copy_false_should_return_view(self):
144+
# data = np.arange(10.0) * pq.mV
145+
# rate = 5000 * pq.Hz
146+
# signal = AnalogSignal(data, copy=False, sampling_rate=rate)
147+
# data[3] = 99 * pq.mV
148+
# assert_neo_object_is_compliant(signal)
149+
# self.assertEqual(signal[3, 0], 99 * pq.mV)
150+
151+
#def test__create2D_with_copy_false_should_return_view(self):
152+
# data = np.arange(10.0) * pq.mV
153+
# data = data.reshape((5, 2))
154+
# rate = 5000 * pq.Hz
155+
# signal = AnalogSignal(data, copy=False, sampling_rate=rate)
156+
# data[3, 0] = 99 * pq.mV
157+
# assert_neo_object_is_compliant(signal)
158+
# self.assertEqual(signal[3, 0], 99 * pq.mV)
158159

159160
def test__create_with_additional_argument(self):
160161
signal = AnalogSignal(
@@ -1048,6 +1049,7 @@ def test_splice_1channel_inplace(self):
10481049
assert_array_equal(result.array_annotations["anno2"], np.array(["a"]))
10491050
self.assertIsInstance(result.array_annotations, ArrayDict)
10501051

1052+
# splice copy is a deepcopy not a numpy copy still need to test for numpy 2.0
10511053
def test_splice_1channel_with_copy(self):
10521054
signal_for_splicing = AnalogSignal(
10531055
[0.1, 0.1, 0.1],
@@ -1066,6 +1068,7 @@ def test_splice_1channel_with_copy(self):
10661068
assert_array_equal(result.array_annotations["anno2"], np.array(["a"]))
10671069
self.assertIsInstance(result.array_annotations, ArrayDict)
10681070

1071+
# splice is a deepcopy still need to test for numpy 2.0
10691072
def test_splice_2channels_inplace(self):
10701073
arr_ann1 = {"index": np.arange(10, 12)}
10711074
arr_ann2 = {"index": np.arange(2), "test": ["a", "b"]}

0 commit comments

Comments
 (0)