Add test/delta.py
[hooke.git] / hooke / curve.py
index 216722dc8d849cac3ccfdf8b2de29963c08b32af..7e3d084c213e909ae2d36ba74a35b608232ab514 100644 (file)
@@ -2,15 +2,15 @@
 #
 # This file is part of Hooke.
 #
-# Hooke is free software: you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation, either
-# version 3 of the License, or (at your option) any later version.
+# Hooke is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
 #
-# Hooke is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU Lesser General Public License for more details.
+# Hooke is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General
+# Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public
 # License along with Hooke.  If not, see
@@ -44,16 +44,52 @@ class Data (numpy.ndarray):
 
     See :mod:`numpy.doc.subclassing` for the peculiarities of
     subclassing :class:`numpy.ndarray`.
+
+    Examples
+    --------
+
+    >>> d = Data(shape=(3,2), info={'columns':['distance (m)', 'force (N)']})
+    >>> type(d)
+    <class 'hooke.curve.Data'>
+    >>> for i in range(3): # initialize d
+    ...    for j in range(2):
+    ...        d[i,j] = i*10 + j
+    >>> d
+    Data([[  0.,   1.],
+           [ 10.,  11.],
+           [ 20.,  21.]])
+    >>> d.info
+    {'columns': ['distance (m)', 'force (N)']}
+
+    The information gets passed on to slices.
+
+    >>> row_a = d[:,0]
+    >>> row_a
+    Data([  0.,  10.,  20.])
+    >>> row_a.info
+    {'columns': ['distance (m)', 'force (N)']}
+
+    The data-type is also pickleable, to ensure we can move it between
+    processes with :class:`multiprocessing.Queue`\s.
+
+    >>> import pickle
+    >>> s = pickle.dumps(d)
+    >>> z = pickle.loads(s)
+    >>> z
+    Data([[  0.,   1.],
+           [ 10.,  11.],
+           [ 20.,  21.]])
+    >>> z.info
+    {'columns': ['distance (m)', 'force (N)']}
     """
-    def __new__(self, subtype, shape, dtype=numpy.float, buffer=None, offset=0,
+    def __new__(subtype, shape, dtype=numpy.float, buffer=None, offset=0,
                 strides=None, order=None, info=None):
         """Create the ndarray instance of our type, given the usual
         input arguments.  This will call the standard ndarray
         constructor, but return an object of our type.
         """
-        obj = np.ndarray.__new__(subtype=subtype, shape=shape, dtype=dtype,
-                                 buffer=buffer, offset=offset, strides=strides,
-                                 order=order)
+        obj = numpy.ndarray.__new__(
+            subtype, shape, dtype, buffer, offset, strides, order)
         # add the new attribute to the created instance
         if info == None:
             info = {}
@@ -68,6 +104,34 @@ class Data (numpy.ndarray):
         self.info = getattr(obj, 'info', {})
         # We do not need to return anything
 
+    def __reduce__(self):
+        """Collapse an instance for pickling.
+
+        Returns
+        -------
+        reconstruct : callable
+            Called to create the initial version of the object.
+        args : tuple
+            A tuple of arguments for `reconstruct`
+        state : (optional)
+            The state to be passed to __setstate__, if present.
+        iter : iterator (optional)
+            Yielded items will be appended to the reconstructed
+            object.
+        dict : iterator (optional)
+            Yielded (key,value) tuples pushed back onto the
+            reconstructed object.
+        """
+        base_reduce = list(numpy.ndarray.__reduce__(self))
+        # tack our stuff onto ndarray's setstate portion.
+        base_reduce[2] = (base_reduce[2], (self.info,))
+        return tuple(base_reduce)
+
+    def __setstate__(self, state):
+        base_class_state,own_state = state
+        numpy.ndarray.__setstate__(self, base_class_state)
+        self.info, = own_state
+
 
 class Curve (object):
     """A grouped set of :class:`Data` runs from the same file with metadata.
@@ -104,6 +168,13 @@ class Curve (object):
         """Identify the appropriate :class:`hooke.driver.Driver` for
         the curve file (`.path`).
         """
+        if 'filetype' in self.info:
+            driver = [d for d in drivers if d.name == self.info['filetype']]
+            if len(driver) == 1:
+                driver = driver[0]
+                if driver.is_me(self.path):
+                    self.driver = driver
+                    return
         for driver in drivers:
             if driver.is_me(self.path):
                 self.driver = driver # remember the working driver
@@ -113,7 +184,7 @@ class Curve (object):
     def load(self):
         """Use the driver to read the curve into memory.
         """
-        data,info = self.driver.read(self.path)
+        data,info = self.driver.read(self.path, self.info)
         self.data = data
         for key,value in info.items():
             self.info[key] = value