Serialize hooke.experiment.Experiment instances in Curves.
authorW. Trevor King <wking@drexel.edu>
Sat, 21 Aug 2010 15:33:46 +0000 (11:33 -0400)
committerW. Trevor King <wking@drexel.edu>
Sat, 21 Aug 2010 15:33:46 +0000 (11:33 -0400)
Also use singleton instances of the classes rather than the classes
themselves.  This will allow usage of isinstance() rather than
.__mro__ to determine experiment type.

hooke/curve.py
hooke/driver/hemingway.py
hooke/driver/jpk.py
hooke/driver/mfp3d.py
hooke/driver/picoforce.py
hooke/driver/tutorial.py
hooke/driver/wtk.py
hooke/experiment.py
hooke/util/singleton.py [new file with mode: 0644]

index 629e0a8..af572f3 100644 (file)
@@ -26,6 +26,7 @@ import os.path
 import numpy
 
 from .command_stack import CommandStack
+from . import experiment
 
 
 class NotRecognized (ValueError):
@@ -195,6 +196,12 @@ class Curve (object):
         dc = state['command_stack']
         if hasattr(dc, '__getstate__'):
             state['command_stack'] = dc.__getstate__()
+        if self.info.get('experiment', None) != None:
+            e = self.info['experiment']
+            assert isinstance(e, experiment.Experiment)
+            # HACK? require Experiment classes to be defined in the
+            # experiment module.
+            self.info['experiment'] = e.__class__.__name__
         return state
 
     def __setstate__(self, state):
@@ -203,6 +210,14 @@ class Curve (object):
             if key == 'path':
                 self.set_path(value)
                 continue
+            elif key == 'info':
+                if 'experiment' not in value:
+                    value['experiment'] = None
+                else:
+                    # HACK? require Experiment classes to be defined in the
+                    # experiment module.
+                    cls = getattr(experiment, value['experiment'])
+                    value['experiment'] = cls()
             elif key == 'command_stack':
                 v = CommandStack()
                 v.__setstate__(value)
index e65b613..af351f7 100644 (file)
@@ -74,5 +74,5 @@ class HemingwayDriver (Driver):
         ret[:,0] = numpy.arange(0, 1e-3*data.shape[0], 1e-3, dtype=ret.dtype)
         
         file_info['filetype'] = self.name
-        file_info['experiment'] = experiment.ForceClamp
+        file_info['experiment'] = experiment.ForceClamp()
         return ([ret,], file_info)
index 39717ef..6c95fe9 100644 (file)
@@ -154,7 +154,7 @@ class JPKDriver (Driver):
         # and velocity clamp segments in a single experiment), but I
         # have no idea what sort of analysis such experiments would
         # require ;).
-        info['experiment'] = experiment.VelocityClamp
+        info['experiment'] = experiment.VelocityClamp()
         force_unit = chan_info['channel']['vDeflection']['conversion-set']['conversion']['force']['scaling']['unit']['unit']
         assert force_unit == 'N', force_unit
         force_base = chan_info['channel']['vDeflection']['conversion-set']['conversion']['force']['base-calibration-slot']
index 6d2cdc5..3347e3d 100644 (file)
@@ -73,7 +73,7 @@ class MFP3DDriver (Driver):
         data,bin_info,wave_info = loadibw(path)
         blocks,info = self._translate_ibw(data, bin_info, wave_info)
         info['filetype'] = self.name
-        info['experiment'] = experiment.VelocityClamp
+        info['experiment'] = experiment.VelocityClamp()
         return (blocks, info)
      
     def _translate_ibw(self, data, bin_info, wave_info):
index 1f1fefb..ca84792 100644 (file)
@@ -55,7 +55,7 @@ class PicoForceDriver (Driver):
         self._check_version(info)
         data = self._read_data_path(path, info)
         info['filetype'] = self.name
-        info['experiment'] = experiment.VelocityClamp
+        info['experiment'] = experiment.VelocityClamp()
         return (data, info)
 
     def _read_header_path(self, path):
index 7fe5298..6856f8d 100644 (file)
@@ -136,5 +136,5 @@ class TutorialDriver (Driver):
         f.close() # remember to close the file
 
         data = curve.Data()
-        info = {'filetype':'tutorial', 'experiment':experiment.Experiment}
+        info = {'filetype':'tutorial', 'experiment':experiment.Experiment()}
         return (data, info)
index b39bdd9..4124701 100644 (file)
@@ -98,7 +98,7 @@ class WTKDriver (Driver):
             unlabeled_approach_data, params, 'approach')
         retract = self._scale_block(
             unlabeled_retract_data, params, 'retract')
-        info = {'filetype':self.name, 'experiment':experiment.VelocityClamp}
+        info = {'filetype':self.name, 'experiment':experiment.VelocityClamp()}
         return ([approach, retract], info)
 
     def _paths(self, path):
index 81decf9..646b415 100644 (file)
@@ -22,7 +22,10 @@ This allows :class:`hooke.plugin.Plugin`\s to specify the types of
 experiments they can handle.
 """
 
-class Experiment (object):
+from .util.singleton import Singleton
+
+
+class Experiment (Singleton):
     """Base class for experiment classification.
     """
     pass
diff --git a/hooke/util/singleton.py b/hooke/util/singleton.py
new file mode 100644 (file)
index 0000000..26a657e
--- /dev/null
@@ -0,0 +1,57 @@
+# Copyright
+
+"""Define the :class:`Singleton` class.
+
+>>> class A (Singleton):
+...     def init(self):
+...         print 'initializing instance of %s at (%s)' % (
+...             self.__class__.__name__, id(self))
+
+>>> A_instances = [A() for i in range(3)]  # doctest: +ELLIPSIS
+initializing instance of A at (...)
+>>> for i in A_instances[1:]:
+...     print id(i) == id(A_instances[0])
+True
+True
+
+Singletons can also be subclassed.
+
+>>> class B (A):
+...     pass
+>>> B_instances = [B() for i in range(3)]  # doctest: +ELLIPSIS
+initializing instance of B at (...)
+>>> for i in B_instances[1:]:
+...     print id(i) == id(B_instances[0])
+True
+True
+>>> id(A_instances[0]) == id(B_instances[0])
+False
+"""
+
+class Singleton (object):
+    """A singleton class.
+
+    To create a singleton class, you subclass from Singleton; each
+    subclass will have a single instance, no matter how many times its
+    constructor is called. To further initialize the subclass
+    instance, subclasses should override 'init' instead of __init__ -
+    the __init__ method is called each time the constructor is called.
+
+    Notes
+    -----
+    Original implementation from Guido van Rossum's
+    `Unifying types and classes in Python 2.2`_.
+
+    .. Unifying types and classes in Python 2.2:
+      http://www.python.org/download/releases/2.2.3/descrintro/#__new__
+    """
+    def __new__(cls, *args, **kwds):
+        it = cls.__dict__.get('__it__')
+        if it is not None:
+            return it
+        cls.__it__ = it = object.__new__(cls)
+        it.init(*args, **kwds)
+        return it
+
+    def init(self, *args, **kwds):
+        pass