From a2a5f958eb4240c60a155d84363db5acac8f9d8a Mon Sep 17 00:00:00 2001 From: Duncan Macleod Date: Thu, 27 Feb 2020 12:41:56 +0000 Subject: [PATCH] gwpy.timeseries: raise ValueError in TimeSeries.read if gap='raise' is given and the file(s) return a proper subset of the request fixes #1211 --- gwpy/timeseries/io/core.py | 57 +++++++++++++++++++++--- gwpy/timeseries/tests/test_timeseries.py | 18 ++++++++ 2 files changed, 68 insertions(+), 7 deletions(-) diff --git a/gwpy/timeseries/io/core.py b/gwpy/timeseries/io/core.py index 5c7440ed5..75eb3641c 100644 --- a/gwpy/timeseries/io/core.py +++ b/gwpy/timeseries/io/core.py @@ -61,13 +61,14 @@ def _join(data): tsd = data.pop(0) out.append(tsd, gap=gap, pad=pad) del tsd - if gap == "pad": + if gap in ("pad", "raise"): for key in out: out[key] = _pad_series( out[key], pad, start, end, + error=(gap == "raise"), ) return out else: @@ -76,21 +77,54 @@ def _join(data): def _join(arrays): list_ = TimeSeriesBaseList(*arrays) joined = list_.join(pad=pad, gap=gap) - if gap == "pad": + if gap in ("pad", "raise"): return _pad_series( joined, pad, start, end, + error=(gap == "raise"), ) return joined return _join -def _pad_series(ts, pad, start=None, end=None): +def _pad_series(ts, pad, start=None, end=None, error=False): """Pad a timeseries to match the specified [start, end) limits - To cover a gap in data returned from a data source + To cover a gap in data returned from a data source. + + Parameters + ---------- + ts : `gwpy.types.Series` + the input series + + pad : `float`, `astropy.units.Quantity` + the value with which to pad + + start : `float`, `astropy.units.Quantity`, optional + the desired start point of the X-axis, defaults to + the start point of the incoming series + + end : `float`, `astropy.units.Quantity`, optional + the desired end point of the X-axis, defaults to + the end point of the incoming series + + error : `bool`, optional + raise `ValueError` when gaps are present, rather than padding + anything + + Returns + ------- + series : instance of incoming series type + a padded version of the series. This may be the same + object if not padding is needed. + + Raises + ------ + ValueError + if `error=True` is given and padding would have been required + to match the request. """ span = ts.span if start is None: @@ -99,6 +133,15 @@ def _pad_series(ts, pad, start=None, end=None): end = span[1] pada = max(int((span[0] - start) * ts.sample_rate.value), 0) padb = max(int((end - span[1]) * ts.sample_rate.value), 0) - if pada or padb: - return ts.pad((pada, padb), mode='constant', constant_values=(pad,)) - return ts + if not (pada or padb): # if noop, just return the input + return ts + if error: # if error, bail out now + raise ValueError( + "{} with span {} does not cover requested interval {}".format( + type(ts).__name__, + span, + type(span)(start, end), + ) + ) + # otherwise applying the padding + return ts.pad((pada, padb), mode='constant', constant_values=(pad,)) diff --git a/gwpy/timeseries/tests/test_timeseries.py b/gwpy/timeseries/tests/test_timeseries.py index 5a9a491cd..f15bbde94 100644 --- a/gwpy/timeseries/tests/test_timeseries.py +++ b/gwpy/timeseries/tests/test_timeseries.py @@ -442,6 +442,24 @@ def test_read_pad(self, pre, post): b, ) + def test_read_pad_raise(self): + """Check that `TimeSeries.read` with `gap='raise'` actually + raises appropriately. + + [regression: https://github.com/gwpy/gwpy/issues/1211] + """ + from gwpy.io.cache import file_segment + span = file_segment(utils.TEST_HDF5_FILE) + with pytest.raises(ValueError): + self.TEST_CLASS.read( + utils.TEST_HDF5_FILE, + "H1:LDAS-STRAIN", + pad=0., + start=span[0], + end=span[1]+1., + gap="raise", + ) + @utils.skip_missing_dependency('nds2') def test_from_nds2_buffer_dynamic_scaled(self): # build fake buffer for LIGO channel