gwpy 3.0.8__py3-none-any.whl → 3.0.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of gwpy might be problematic. Click here for more details.

Files changed (47) hide show
  1. gwpy/_version.py +2 -2
  2. gwpy/astro/range.py +3 -3
  3. gwpy/astro/tests/test_range.py +4 -5
  4. gwpy/cli/gwpy_plot.py +1 -1
  5. gwpy/cli/qtransform.py +1 -1
  6. gwpy/cli/tests/base.py +11 -1
  7. gwpy/detector/units.py +2 -1
  8. gwpy/frequencyseries/tests/test_hist.py +4 -3
  9. gwpy/io/datafind.py +1 -0
  10. gwpy/io/ffldatafind.py +27 -16
  11. gwpy/io/tests/test_ffldatafind.py +10 -3
  12. gwpy/plot/plot.py +12 -2
  13. gwpy/plot/tests/test_segments.py +1 -1
  14. gwpy/plot/tex.py +6 -4
  15. gwpy/segments/flag.py +13 -11
  16. gwpy/signal/filter_design.py +5 -4
  17. gwpy/signal/tests/test_coherence.py +31 -10
  18. gwpy/signal/tests/test_filter_design.py +3 -3
  19. gwpy/spectrogram/tests/test_spectrogram.py +2 -2
  20. gwpy/table/io/ligolw.py +1 -1
  21. gwpy/table/tests/test_io_ligolw.py +1 -1
  22. gwpy/testing/errors.py +1 -0
  23. gwpy/testing/fixtures.py +1 -18
  24. gwpy/testing/utils.py +15 -6
  25. gwpy/timeseries/core.py +0 -1
  26. gwpy/timeseries/io/cache.py +47 -29
  27. gwpy/timeseries/io/gwf/lalframe.py +8 -0
  28. gwpy/timeseries/statevector.py +4 -2
  29. gwpy/timeseries/tests/test_core.py +7 -13
  30. gwpy/timeseries/tests/test_io_cache.py +74 -0
  31. gwpy/timeseries/tests/test_io_gwf_framecpp.py +1 -1
  32. gwpy/timeseries/tests/test_statevector.py +29 -17
  33. gwpy/timeseries/tests/test_timeseries.py +37 -34
  34. gwpy/types/array.py +16 -3
  35. gwpy/types/index.py +7 -5
  36. gwpy/types/tests/test_array.py +12 -10
  37. gwpy/types/tests/test_array2d.py +5 -9
  38. gwpy/types/tests/test_series.py +5 -5
  39. gwpy/utils/sphinx/zenodo.py +5 -1
  40. gwpy/utils/tests/test_sphinx_zenodo.py +23 -0
  41. gwpy-3.0.10.dist-info/METADATA +125 -0
  42. {gwpy-3.0.8.dist-info → gwpy-3.0.10.dist-info}/RECORD +46 -45
  43. {gwpy-3.0.8.dist-info → gwpy-3.0.10.dist-info}/WHEEL +1 -1
  44. gwpy-3.0.8.dist-info/METADATA +0 -124
  45. {gwpy-3.0.8.dist-info → gwpy-3.0.10.dist-info}/LICENSE +0 -0
  46. {gwpy-3.0.8.dist-info → gwpy-3.0.10.dist-info}/entry_points.txt +0 -0
  47. {gwpy-3.0.8.dist-info → gwpy-3.0.10.dist-info}/top_level.txt +0 -0
@@ -19,22 +19,34 @@
19
19
  """I/O utilities for reading `TimeSeries` from a `list` of file paths.
20
20
  """
21
21
 
22
- from ...io.cache import (FILE_LIKE, read_cache, file_segment, sieve)
22
+ from io import BytesIO
23
+ from math import inf
24
+ from os import PathLike
25
+
26
+ from ...io.cache import (
27
+ FILE_LIKE,
28
+ file_segment,
29
+ read_cache,
30
+ write_cache,
31
+ )
23
32
  from ...segments import Segment
24
33
 
25
34
  __author__ = "Duncan Macleod <duncan.macleod@ligo.org>"
26
35
 
27
36
 
28
- def preformat_cache(cache, start=None, end=None):
37
+ def preformat_cache(cache, start=None, end=None, sort=file_segment):
29
38
  """Preprocess a `list` of file paths for reading.
30
39
 
31
- - read the cache from the file (if necessary)
32
- - sieve the cache to only include data we need
40
+ This function does the following:
41
+
42
+ - read the list of paths cache file (if necessary),
43
+ - sort the cache in time order (if possible),
44
+ - sieve the cache to only include data we need.
33
45
 
34
46
  Parameters
35
47
  ----------
36
- cache : `list`, `str`
37
- List of file paths, or path to a LAL-format cache file on disk.
48
+ cache : `list`, `str`, `pathlib.Path`
49
+ List of file paths, or path to a cache file.
38
50
 
39
51
  start : `~gwpy.time.LIGOTimeGPS`, `float`, `str`, optional
40
52
  GPS start time of required data, defaults to start of data found;
@@ -44,31 +56,37 @@ def preformat_cache(cache, start=None, end=None):
44
56
  GPS end time of required data, defaults to end of data found;
45
57
  any input parseable by `~gwpy.time.to_gps` is fine.
46
58
 
59
+ sort : `callable`, optional
60
+ A callable key function by which to sort the file paths.
61
+
47
62
  Returns
48
63
  -------
49
64
  modcache : `list`
50
65
  A parsed, sieved list of paths based on the input arguments.
66
+
67
+ See also
68
+ --------
69
+ gwpy.io.cache.read_cache
70
+ For details of how the sorting and sieving is implemented
51
71
  """
52
- # open cache file
53
- if isinstance(cache, (str,) + FILE_LIKE):
54
- return read_cache(cache, sort=file_segment,
55
- segment=Segment(start, end))
56
-
57
- # format existing cache file
58
- cache = type(cache)(cache) # copy cache
59
-
60
- # sort cache
61
- try:
62
- cache.sort(key=file_segment) # sort
63
- except ValueError:
64
- # if this failed, then the sieving will also fail, but lets proceed
65
- # anyway, since the user didn't actually ask us to do this (but
66
- # its a very good idea)
67
- return cache
68
-
69
- # sieve cache
70
- if start is None: # start time of earliest file
71
- start = file_segment(cache[0])[0]
72
- if end is None: # end time of latest file
73
- end = file_segment(cache[-1])[-1]
74
- return sieve(cache, segment=Segment(start, end))
72
+ # if given a list of paths, write it to a file-like structure
73
+ # so that we can use read_cache to do all the work
74
+ if not isinstance(cache, (str, PathLike) + FILE_LIKE):
75
+ cachef = BytesIO()
76
+ write_cache(cache, cachef)
77
+ cachef.seek(0)
78
+ cache = cachef
79
+
80
+ # need start and end times to sieve the cache
81
+ if start is None:
82
+ start = -inf
83
+ if end is None:
84
+ end = +inf
85
+
86
+ # read the cache
87
+ return read_cache(
88
+ cache,
89
+ coltype=type(start),
90
+ sort=sort,
91
+ segment=Segment(start, end),
92
+ )
@@ -149,6 +149,14 @@ def read(source, channels, start=None, end=None, series_class=TimeSeries,
149
149
  end = epoch + streamdur
150
150
  end = min(epoch + streamdur, lalutils.to_lal_ligotimegps(end))
151
151
  duration = float(end - start)
152
+ if start >= (epoch + streamdur):
153
+ raise ValueError(
154
+ "cannot read data starting after stream ends",
155
+ )
156
+ if duration < 0:
157
+ raise ValueError(
158
+ "cannot read data with negative duration",
159
+ )
152
160
 
153
161
  # read data
154
162
  out = series_class.DictClass()
@@ -184,8 +184,10 @@ class StateTimeSeries(TimeSeriesBase):
184
184
  def unit(self):
185
185
  return units.dimensionless_unscaled
186
186
 
187
- def override_unit(self, unit, parse_strict='raise'):
188
- return NotImplemented
187
+ def override_unit(self, *args, **kwargs):
188
+ raise NotImplementedError(
189
+ f"overriding units is not supported for {type(self).__name__}",
190
+ )
189
191
 
190
192
  def _to_own_unit(self, value, check_precision=True):
191
193
  if isinstance(value, units.Quantity) and value.unit != self.unit:
@@ -53,7 +53,8 @@ class TestTimeSeriesBase(_TestSeries):
53
53
  def test_new(self):
54
54
  """Test `gwpy.timeseries.TimeSeriesBase` constructor
55
55
  """
56
- array = super().test_new()
56
+ array = self.create()
57
+ super().test_new()
57
58
 
58
59
  # check time-domain metadata
59
60
  assert array.epoch == GPS_EPOCH
@@ -145,7 +146,7 @@ class TestTimeSeriesBase(_TestSeries):
145
146
  def test_sample_rate_ghz(self, array):
146
147
  """Test that very large sample rates don't get rounded to dt=0.
147
148
 
148
- Regression: https://github.com/gwpy/gwpy/issues/1646
149
+ Regression: https://gitlab.com/gwpy/gwpy/-/issues/1646
149
150
  """
150
151
  array.sample_rate = 1e9
151
152
  assert array.dt.value > 0.
@@ -400,20 +401,13 @@ class TestTimeSeriesBaseDict(object):
400
401
 
401
402
  def test_resample(self, instance):
402
403
  if self.ENTRY_CLASS is TimeSeriesBase: # currently only for subclasses
403
- return NotImplemented
404
+ pytest.skip(f"not implemented for {type(instance).__name__}")
405
+
406
+ # for all subclasses
404
407
  a = instance.resample(.5)
405
408
  for key in a:
406
409
  assert a[key].dx == 1/.5 * a[key].xunit
407
410
 
408
- def test_fetch(self):
409
- return NotImplemented
410
-
411
- def test_find(self):
412
- return NotImplemented
413
-
414
- def test_get(self):
415
- return NotImplemented
416
-
417
411
  @pytest.mark.requires("nds2")
418
412
  def test_from_nds2_buffers(self):
419
413
  buffers = [
@@ -445,7 +439,7 @@ class TestTimeSeriesBaseDict(object):
445
439
  def test_plot_separate(self, instance):
446
440
  """Test plotting `TimeSeriesDict` on separate axes.
447
441
 
448
- See https://github.com/gwpy/gwpy/issues/1609
442
+ See https://gitlab.com/gwpy/gwpy/-/issues/1609
449
443
  """
450
444
  with rc_context(rc={'text.usetex': False}):
451
445
  plot = instance.plot(separate=True)
@@ -0,0 +1,74 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (C) Cardiff University 2023
3
+ #
4
+ # This file is part of GWpy.
5
+ #
6
+ # GWpy is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # GWpy is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with GWpy. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ """Tests for :mod:`gwpy.timeseries.io.cache`
20
+ """
21
+
22
+ import pytest
23
+
24
+ from ...io.cache import write_cache
25
+ from ..io import cache as ts_io_cache
26
+
27
+
28
+ @pytest.fixture
29
+ def cache():
30
+ """List of files over which to test sorting/sieving.
31
+ """
32
+ return [
33
+ "/tmp/A-TEST-0-10.tmp",
34
+ "/tmp/A-TEST-10-10.tmp",
35
+ "/tmp/A-TEST-20-10.tmp",
36
+ "/tmp/A-TEST-30-5.tmp",
37
+ "/tmp/A-TEST-35-15.tmp",
38
+ ]
39
+
40
+
41
+ @pytest.fixture
42
+ def cache_file(tmp_path, cache):
43
+ """File version of `cache()`.
44
+ """
45
+ path = tmp_path / "cache.txt"
46
+ write_cache(cache, path)
47
+ return path
48
+
49
+
50
+ @pytest.mark.parametrize("source", ("cache", "cache_file"))
51
+ @pytest.mark.parametrize(("start", "end", "idx"), [
52
+ # use everything in the cache
53
+ (None, None, slice(None)),
54
+ # use only GPS time '25' onwards, which is cache[2:]
55
+ (25, None, slice(2, None)),
56
+ # use only up to GPS time '25', which is cache[:3]
57
+ (None, 25, slice(None, 3)),
58
+ # use interval [10, 35), which needs cache[1:4]
59
+ (10, 35, slice(1, 4)),
60
+ ])
61
+ def test_preformat_cache(request, cache, source, start, end, idx):
62
+ """Test that `gwpy.timeseries.io.cache.preformat_cache` works properly.
63
+
64
+ Here `[start, end)` is a GPS segment, and `idx` the corresponding slice
65
+ needed to restrict the cache object.
66
+
67
+ Loops over a variety of input arguments, using `request` to dynamically
68
+ loop over `cache` or `cache_file` as the input.
69
+ """
70
+ assert ts_io_cache.preformat_cache(
71
+ request.getfixturevalue(source), # cache or cache_file
72
+ start=start,
73
+ end=end,
74
+ ) == cache[idx]
@@ -63,7 +63,7 @@ def test_read_scaled_type_change(int32ts, tmp_path):
63
63
 
64
64
 
65
65
  def test_read_write_frvect_name(tmp_path):
66
- """Test against regression of https://github.com/gwpy/gwpy/issues/1206
66
+ """Test against regression of https://gitlab.com/gwpy/gwpy/-/issues/1206
67
67
  """
68
68
  data = TimeSeries(
69
69
  numpy.random.random(10),
@@ -144,29 +144,39 @@ class TestStateTimeSeries(_TestTimeSeriesBase):
144
144
  assert flag.label == 'Label'
145
145
  assert flag.description == 'Description'
146
146
 
147
- def test_override_unit(self):
148
- return NotImplemented
147
+ def test_override_unit(self, array):
148
+ with pytest.raises(NotImplementedError):
149
+ super().test_override_unit(array)
149
150
 
150
151
  def test_is_compatible_error_unit(self):
151
- return NotImplemented
152
+ pytest.skip(f"not implemented for {self.TEST_CLASS.__name__}")
152
153
 
153
154
  def test_to_from_pycbc(self):
154
- return NotImplemented
155
+ pytest.skip(f"not implemented for {self.TEST_CLASS.__name__}")
155
156
 
156
- def test_to_from_lal(self):
157
- return NotImplemented
157
+ def test_to_from_lal(self, array):
158
+ with pytest.raises(NotImplementedError):
159
+ super().test_to_from_lal(array)
158
160
 
159
- def test_to_from_lal_no_copy(self):
160
- return NotImplemented
161
+ @pytest.mark.parametrize("copy", (False, True))
162
+ def test_to_from_lal_no_copy(self, array, copy):
163
+ with pytest.raises(NotImplementedError):
164
+ super().test_to_from_lal_no_copy(array, copy)
161
165
 
162
- def test_to_from_lal_pow10_units(self):
163
- return NotImplemented
166
+ @pytest.mark.requires("lal")
167
+ def test_to_from_lal_pow10_units(self, array):
168
+ with pytest.raises(NotImplementedError):
169
+ super().test_to_from_lal_pow10_units(array)
164
170
 
165
- def test_to_from_lal_scaled_units(self):
166
- return NotImplemented
171
+ @pytest.mark.requires("lal")
172
+ def test_to_from_lal_scaled_units(self, array):
173
+ with pytest.raises(NotImplementedError):
174
+ super().test_to_from_lal_scaled_units(array)
167
175
 
168
- def test_to_from_lal_unrecognised_units(self):
169
- return NotImplemented
176
+ @pytest.mark.requires("lal")
177
+ def test_to_from_lal_unrecognised_units(self, array):
178
+ with pytest.raises(NotImplementedError):
179
+ super().test_to_from_lal_unrecognised_units(array)
170
180
 
171
181
 
172
182
  # -- StateTimeSeriesDict ------------------------------------------------------
@@ -176,8 +186,8 @@ class TestStateTimeSeriesDict(_TestTimeSeriesBaseDict):
176
186
  ENTRY_CLASS = StateTimeSeries
177
187
  DTYPE = 'bool'
178
188
 
179
- def test_resample(self):
180
- return NotImplemented
189
+ def test_resample(self, instance):
190
+ pytest.skip(f"not implemented for {type(instance).__name__}")
181
191
 
182
192
 
183
193
  # -- Bits ---------------------------------------------------------------------
@@ -326,7 +336,9 @@ class TestStateVector(_TestTimeSeriesBase):
326
336
  array.resample(array.sample_rate * 1.5)
327
337
 
328
338
  def test_to_from_lal_scaled_units(self):
329
- return NotImplemented
339
+ pytest.skip(
340
+ f"not implemented for {self.TEST_CLASS.__name__}",
341
+ )
330
342
 
331
343
  # -- data access ----------------------------
332
344
 
@@ -201,7 +201,7 @@ class TestTimeSeries(_TestTimeSeriesBase):
201
201
  def test_read_ascii_header(self, tmpdir):
202
202
  """Check that ASCII files with headers are read without extra options
203
203
 
204
- [regression: https://github.com/gwpy/gwpy/issues/1473]
204
+ [regression: https://gitlab.com/gwpy/gwpy/-/issues/1473]
205
205
  """
206
206
  txt = tmpdir / "text.txt"
207
207
  txt.write_text(
@@ -253,28 +253,29 @@ class TestTimeSeries(_TestTimeSeriesBase):
253
253
  exclude=['channel'])
254
254
 
255
255
  @pytest.mark.parametrize('api', GWF_APIS)
256
- def test_read_write_gwf_gps_errors(self, tmp_path, api):
256
+ def test_read_gwf_end_error(self, api):
257
+ """Test that reading past the end of available data fails.
258
+ """
257
259
  fmt = "gwf" if api is None else "gwf." + api
258
- array = self.create(name='TEST')
259
- tmp = tmp_path / "test.gwf"
260
- array.write(tmp, format=fmt)
261
-
262
- # check that reading past the end of the array fails
263
- with pytest.raises((ValueError, RuntimeError)):
260
+ with pytest.raises(ValueError):
264
261
  self.TEST_CLASS.read(
265
- tmp,
266
- array.name,
262
+ utils.TEST_GWF_FILE,
263
+ "L1:LDAS-STRAIN",
267
264
  format=fmt,
268
- start=array.span[1],
265
+ start=utils.TEST_GWF_SPAN[1],
269
266
  )
270
267
 
271
- # check that reading before the start of the array also fails
272
- with pytest.raises((ValueError, RuntimeError)):
268
+ @pytest.mark.parametrize('api', GWF_APIS)
269
+ def test_read_gwf_negative_duration_error(self, api):
270
+ """Test that reading a negative duration fails.
271
+ """
272
+ fmt = "gwf" if api is None else "gwf." + api
273
+ with pytest.raises(ValueError):
273
274
  self.TEST_CLASS.read(
274
- tmp,
275
- array.name,
275
+ utils.TEST_GWF_FILE,
276
+ "L1:LDAS-STRAIN",
276
277
  format=fmt,
277
- end=array.span[0]-1,
278
+ end=utils.TEST_GWF_SPAN[0]-1,
278
279
  )
279
280
 
280
281
  @pytest.mark.parametrize('api', GWF_APIS)
@@ -283,7 +284,7 @@ class TestTimeSeries(_TestTimeSeriesBase):
283
284
  """Check that each GWF API can read a series of files, either in
284
285
  a single process, or in multiple processes
285
286
 
286
- Regression: https://github.com/gwpy/gwpy/issues/1486
287
+ Regression: https://gitlab.com/gwpy/gwpy/-/issues/1486
287
288
  """
288
289
  fmt = "gwf" if api is None else "gwf." + api
289
290
  a1 = self.create(name='TEST')
@@ -511,7 +512,7 @@ class TestTimeSeries(_TestTimeSeriesBase):
511
512
  """Check that `TimeSeries.read` with `gap='raise'` actually
512
513
  raises appropriately.
513
514
 
514
- [regression: https://github.com/gwpy/gwpy/issues/1211]
515
+ [regression: https://gitlab.com/gwpy/gwpy/-/issues/1211]
515
516
  """
516
517
  from gwpy.io.cache import file_segment
517
518
  span = file_segment(utils.TEST_HDF5_FILE)
@@ -845,9 +846,7 @@ class TestTimeSeries(_TestTimeSeriesBase):
845
846
  gw150914.psd(abs(gw150914.span), method='lal_median_mean')
846
847
 
847
848
  # odd number of segments should warn
848
- # pytest hides the second DeprecationWarning that should have been
849
- # triggered here, for some reason
850
- with pytest.warns(UserWarning):
849
+ with pytest.warns(UserWarning), pytest.deprecated_call():
851
850
  gw150914.psd(1, .5, method='lal_median_mean')
852
851
 
853
852
  @pytest.mark.parametrize('method', ('welch', 'bartlett', 'median'))
@@ -1021,18 +1020,16 @@ class TestTimeSeries(_TestTimeSeriesBase):
1021
1020
  pytest.param('pycbc', marks=pytest.mark.requires("pycbc.psd")),
1022
1021
  ])
1023
1022
  def test_spectrogram_median_mean(self, gw150914, library):
1024
- method = '{0}-median-mean'.format(library)
1023
+ method = f"{library}-median-mean"
1025
1024
 
1026
- # median-mean warn on LAL if not given the correct data for an
1027
- # even number of FFTs.
1028
- # pytest only asserts a single warning, and UserWarning will take
1029
- # precedence apparently, so check that for lal
1025
+ # the LAL implementation of median-mean warns if not given the
1026
+ # correct amount of data for an even number of FFTs.
1030
1027
  if library == 'lal':
1031
- warn_ctx = pytest.warns(UserWarning)
1028
+ lal_warn_ctx = pytest.warns(UserWarning)
1032
1029
  else:
1033
- warn_ctx = pytest.deprecated_call()
1030
+ lal_warn_ctx = nullcontext()
1034
1031
 
1035
- with warn_ctx:
1032
+ with pytest.deprecated_call(), lal_warn_ctx:
1036
1033
  sg = gw150914.spectrogram(
1037
1034
  1.5,
1038
1035
  fftlength=.5,
@@ -1073,18 +1070,22 @@ class TestTimeSeries(_TestTimeSeriesBase):
1073
1070
  fgram = gw150914.fftgram(1)
1074
1071
  fs = int(gw150914.sample_rate.value)
1075
1072
  f, t, sxx = signal.spectrogram(
1076
- gw150914, fs,
1073
+ gw150914,
1074
+ fs,
1077
1075
  window='hann',
1078
1076
  nperseg=fs,
1079
1077
  mode='complex',
1080
1078
  )
1081
1079
  utils.assert_array_equal(gw150914.t0.value + t, fgram.xindex.value)
1082
1080
  utils.assert_array_equal(f, fgram.yindex.value)
1083
- utils.assert_array_equal(sxx.T, fgram)
1081
+ utils.assert_array_equal(sxx.T, fgram.value)
1084
1082
 
1083
+ def test_fftgram_overlap(self, gw150914):
1085
1084
  fgram = gw150914.fftgram(1, overlap=0.5)
1085
+ fs = int(gw150914.sample_rate.value)
1086
1086
  f, t, sxx = signal.spectrogram(
1087
- gw150914, fs,
1087
+ gw150914,
1088
+ fs,
1088
1089
  window='hann',
1089
1090
  nperseg=fs,
1090
1091
  noverlap=fs//2,
@@ -1092,7 +1093,7 @@ class TestTimeSeries(_TestTimeSeriesBase):
1092
1093
  )
1093
1094
  utils.assert_array_equal(gw150914.t0.value + t, fgram.xindex.value)
1094
1095
  utils.assert_array_equal(f, fgram.yindex.value)
1095
- utils.assert_array_equal(sxx.T, fgram)
1096
+ utils.assert_array_equal(sxx.T, fgram.value)
1096
1097
 
1097
1098
  def test_spectral_variance(self, gw150914):
1098
1099
  variance = gw150914.spectral_variance(.5, method="median")
@@ -1381,7 +1382,9 @@ class TestTimeSeries(_TestTimeSeriesBase):
1381
1382
 
1382
1383
  def test_convolve(self):
1383
1384
  data = self.TEST_CLASS(
1384
- signal.hann(1024), sample_rate=512, epoch=-1
1385
+ signal.get_window("hann", 1024),
1386
+ sample_rate=512,
1387
+ epoch=-1,
1385
1388
  )
1386
1389
  filt = numpy.array([1, 0])
1387
1390
 
gwpy/types/array.py CHANGED
@@ -34,6 +34,11 @@ from math import modf
34
34
  import numpy
35
35
 
36
36
  from astropy.units import Quantity
37
+ try:
38
+ from astropy.utils.compat.numpycompat import COPY_IF_NEEDED
39
+ except ImportError: # astropy < 6.1
40
+ from astropy.utils import minversion
41
+ COPY_IF_NEEDED = None if minversion(numpy, "2.0.0.dev") else False
37
42
 
38
43
  from ..detector import Channel
39
44
  from ..detector.units import parse_unit
@@ -119,8 +124,16 @@ class Array(Quantity):
119
124
  unit = parse_unit(unit, parse_strict='warn')
120
125
 
121
126
  # create new array
122
- new = super().__new__(cls, value, unit=unit, dtype=dtype, copy=False,
123
- order=order, subok=subok, ndmin=ndmin)
127
+ new = super().__new__(
128
+ cls,
129
+ value,
130
+ unit=unit,
131
+ dtype=dtype,
132
+ copy=COPY_IF_NEEDED,
133
+ order=order,
134
+ subok=subok,
135
+ ndmin=ndmin,
136
+ )
124
137
 
125
138
  # explicitly copy here to get ownership of the data,
126
139
  # see (astropy/astropy#7244)
@@ -397,7 +410,7 @@ class Array(Quantity):
397
410
  out = super().__array_ufunc__(function, method, *inputs, **kwargs)
398
411
  # if a ufunc returns a scalar, return a Quantity
399
412
  if not out.ndim:
400
- return Quantity(out, copy=False)
413
+ return Quantity(out, copy=COPY_IF_NEEDED)
401
414
  # otherwise return an array
402
415
  return out
403
416
 
gwpy/types/index.py CHANGED
@@ -23,6 +23,8 @@ import numpy
23
23
 
24
24
  from astropy.units import Quantity
25
25
 
26
+ from .array import COPY_IF_NEEDED
27
+
26
28
 
27
29
  class Index(Quantity):
28
30
  """1-D `~astropy.units.Quantity` array for indexing a `Series`
@@ -57,11 +59,11 @@ class Index(Quantity):
57
59
  """
58
60
  if dtype is None:
59
61
  dtype = max(
60
- numpy.array(start, subok=True, copy=False).dtype,
61
- numpy.array(step, subok=True, copy=False).dtype,
62
+ numpy.array(start, subok=True, copy=COPY_IF_NEEDED).dtype,
63
+ numpy.array(step, subok=True, copy=COPY_IF_NEEDED).dtype,
62
64
  )
63
- start = Quantity(start, dtype=dtype, copy=False)
64
- step = Quantity(step, dtype=dtype, copy=False).to(start.unit)
65
+ start = Quantity(start, dtype=dtype, copy=COPY_IF_NEEDED)
66
+ step = Quantity(step, dtype=dtype, copy=COPY_IF_NEEDED).to(start.unit)
65
67
  stop = start + step * num
66
68
  return cls(
67
69
  numpy.arange(
@@ -71,7 +73,7 @@ class Index(Quantity):
71
73
  dtype=dtype,
72
74
  )[:num],
73
75
  unit=start.unit,
74
- copy=False,
76
+ copy=COPY_IF_NEEDED,
75
77
  )
76
78
 
77
79
  @property
@@ -88,23 +88,25 @@ class TestArray(object):
88
88
 
89
89
  # -- test basic construction ----------------
90
90
 
91
- def test_new(self):
92
- """Test Array creation
91
+ def assert_new(self, array):
92
+ """Run basic assertions for a new instance of the type under test.
93
93
  """
94
- # test basic empty contructor
95
- with pytest.raises(TypeError):
96
- self.TEST_CLASS()
97
-
98
- # test with some data
99
- array = self.create()
100
94
  utils.assert_array_equal(array.value, self.data)
101
95
 
102
96
  # test that copy=True ensures owndata
103
97
  assert self.create(copy=False).flags.owndata is False
104
98
  assert self.create(copy=True).flags.owndata is True
105
99
 
106
- # return array for subclasses to use
107
- return array
100
+ def test_new_empty(self):
101
+ """Test that `Array()` raises an exception.
102
+ """
103
+ with pytest.raises(TypeError):
104
+ self.TEST_CLASS()
105
+
106
+ def test_new(self):
107
+ """Test Array creation.
108
+ """
109
+ self.assert_new(self.create())
108
110
 
109
111
  def test_unit(self, array):
110
112
  # test default unit is dimensionless
@@ -198,7 +198,7 @@ class TestArray2D(_TestSeries):
198
198
 
199
199
  But still represent the output as an `Array2D` with `Index` arrays.
200
200
 
201
- This tests regression of https://github.com/gwpy/gwpy/issues/1504.
201
+ This tests regression of https://gitlab.com/gwpy/gwpy/-/issues/1504.
202
202
  """
203
203
  # create an array with indices
204
204
  a = self.create()
@@ -239,18 +239,14 @@ class TestArray2D(_TestSeries):
239
239
  with pytest.raises(IndexError):
240
240
  array.value_at(1.6, 4.8)
241
241
 
242
- @pytest.mark.skip("not implemented for >1D arrays")
243
242
  def test_pad(self):
244
- return NotImplemented
243
+ pytest.skip("not implemented for >1D arrays")
245
244
 
246
- @pytest.mark.skip("not implemented for >1D arrays")
247
245
  def test_pad_index(self):
248
- return NotImplemented
246
+ pytest.skip("not implemented for >1D arrays")
249
247
 
250
- @pytest.mark.skip("not implemented for >1D arrays")
251
248
  def test_pad_asymmetric(self):
252
- return NotImplemented
249
+ pytest.skip("not implemented for >1D arrays")
253
250
 
254
- @pytest.mark.skip("not applicable for >1D arrays")
255
251
  def test_single_getitem_not_created(self):
256
- return NotImplemented
252
+ pytest.skip("not implemented for >1D arrays")