Repository: obspy/obspy

Issue:
Prevent Trace initialization from numpy arrays with ndim>1
Currently we do not check the number of dimensions of numpy arrays provided in Trace initialization. This can lead to confusing errors later on, e.g. with numpy/scipy operations that can operate on multidimensional data (with optional axis argument).



Discussion:

Resolving patch:
diff --git a/obspy/core/preview.py b/obspy/core/preview.py
--- a/obspy/core/preview.py
+++ b/obspy/core/preview.py
@@ -198,9 +198,9 @@ def resamplePreview(trace, samples, method='accurate'):
         return 0
     # Fast method.
     if method == 'fast':
-        trace.data = trace.data[:int(npts / samples) * samples]
-        trace.data = trace.data.reshape(samples, len(trace.data) // samples)
-        trace.data = trace.data.max(axis=1)
+        data = trace.data[:int(npts / samples) * samples]
+        data = data.reshape(samples, len(data) // samples)
+        trace.data = data.max(axis=1)
         # Set new sampling rate.
         trace.stats.delta = (endtime - trace.stats.starttime) / \
             float(samples - 1)
diff --git a/obspy/core/trace.py b/obspy/core/trace.py
--- a/obspy/core/trace.py
+++ b/obspy/core/trace.py
@@ -227,11 +227,9 @@ class Trace(object):
     """
 
     def __init__(self, data=np.array([]), header=None):
-        # make sure Trace gets initialized with ndarray as self.data
+        # make sure Trace gets initialized with suitable ndarray as self.data
         # otherwise we could end up with e.g. a list object in self.data
-        if not isinstance(data, np.ndarray):
-            msg = "Trace.data must be a NumPy array."
-            raise ValueError(msg)
+        _data_sanity_checks(data)
         # set some defaults if not set yet
         if header is None:
             # Default values: For detail see
@@ -364,9 +362,7 @@ def __setattr__(self, key, value):
         """
         # any change in Trace.data will dynamically set Trace.stats.npts
         if key == 'data':
-            if not isinstance(value, np.ndarray):
-                msg = "Trace.data must be a NumPy array."
-                raise ValueError(msg)
+            _data_sanity_checks(value)
             self.stats.npts = len(value)
         return super(Trace, self).__setattr__(key, value)
 
@@ -2052,6 +2048,20 @@ def attach_response(self, inventories):
         self.stats.response = responses[0]
 
 
+def _data_sanity_checks(value):
+    """
+    Checks if a given input is suitable to be used for Trace.data. Raises the
+    corresponding exception if it is not, otherwise silently passes.
+    """
+    if not isinstance(value, np.ndarray):
+        msg = "Trace.data must be a NumPy array."
+        raise ValueError(msg)
+    if value.ndim != 1:
+        msg = ("Numpy array for Trace.data has bad shape ('%s'). Only 1-d "
+               "arrays are allowed for initialization.") % str(value.shape)
+        raise ValueError(msg)
+
+
 if __name__ == '__main__':
     import doctest
     doctest.testmod(exclude_empty=True)


Test patch:
diff --git a/obspy/core/tests/test_trace.py b/obspy/core/tests/test_trace.py
--- a/obspy/core/tests/test_trace.py
+++ b/obspy/core/tests/test_trace.py
@@ -1507,6 +1507,19 @@ def test_taper_backwards_compatibility(self):
             tr1 = tr.copy().taper(max_percentage=0.5, type='cosine')
             self.assertTrue(np.all(tr1.data[6:] < 1))
 
+    def test_issue_695(self):
+        x = np.zeros(12)
+        data = [x.reshape((12, 1)),
+                x.reshape((1, 12)),
+                x.reshape((2, 6)),
+                x.reshape((6, 2)),
+                x.reshape((2, 2, 3)),
+                x.reshape((1, 2, 2, 3)),
+                x[0][()],  # 0-dim array
+                ]
+        for d in data:
+            self.assertRaises(ValueError, Trace, data=d)
+
 
 def suite():
     return unittest.makeSuite(TraceTestCase, 'test')