Repository: enthought/chaco

Issue:
Serializable mixin should be removed
The class is not used by any current code, appears to be broken, and the problem it was meant to solve (selection of which traits to pickle) is better solved via the use of `transient` traits metadata.

Technically this is a backwards-incompatible change, however.



Discussion:
@corranwebster it's not clear to me what all code can be thrown out but can we throw out the persistence methods e.g. `__getstate__` along with `_post_init`.
I was referring to this class: https://github.com/enthought/chaco/blob/e3423614d654a203bd82ee087c085abe13b465b1/chaco/serializable.py and its tests.

Also `__getstate__` methods should not be thrown out - it is standard Python stuff: https://docs.python.org/3/library/pickle.html#object.__getstate__

We do need to make sure it behaves nicely with Traits `__getstate__`: https://github.com/enthought/traits/blob/5f2effc0254570dfa7736d8bf53eb224c9877c57/traits/has_traits.py#L1267

Resolving patch:
diff --git a/chaco/serializable.py b/chaco/serializable.py
deleted file mode 100644
--- a/chaco/serializable.py
+++ /dev/null
@@ -1,110 +0,0 @@
-""" Defines the Serializable mix-in class.
-"""
-
-
-class Serializable(object):
-    """
-    Mix-in class to help serialization.  Serializes just the attributes in
-    **_pickles**.
-
-    This mix-in works best when all the classes in a hierarchy subclass
-    from it.  It solves the problem of allowing each class to specify
-    its own set of attributes to pickle and attributes to ignore, without
-    having to also implement __getstate__ and __setstate__.
-    """
-
-    # The basic list of attributes to save.  These get set without firing
-    # any trait events.
-    _pickles = None
-
-    # A list of the parents of this class that will be searched for their
-    # list of _pickles.  Only the parents in this list that inherit from
-    # Serialized will be pickled.  The process stops at the first item in
-    # __pickle_parents that is not a subclass of Serialized.
-    #
-    # This is a double-underscore variable so that Python's attribute name
-    # will shield base class
-    #    __pickle_parents = None
-
-    def _get_pickle_parents(self):
-        """
-        Subclasses can override this method to return the list of base
-        classes they want to have the serializer look at.
-        """
-        bases = []
-        for cls in self.__class__.__mro__:
-            if cls is Serializable:
-                # don't add Serializable to the list of parents
-                continue
-            elif issubclass(cls, Serializable):
-                bases.append(cls)
-            else:
-                break
-        return bases
-
-    def _pre_save(self):
-        """
-        Called before __getstate__ to give the object a chance to tidy up
-        and get ready to be saved.  This usually also calls the superclass.
-        """
-
-    def _post_load(self):
-        """
-        Called after __setstate__ finishes restoring the state on the object.
-        This method usually needs to include a call to super(cls, self)._post_load().
-        Avoid explicitly calling a parent class by name, because in general
-        you want post_load() to happen in the same order as MRO, which super()
-        does automatically.
-        """
-        print("Serializable._post_load")
-        pass
-
-    def _do_setstate(self, state):
-        """
-        Called by __setstate__ to allow the subclass to set its state in a
-        special way.
-
-        Subclasses should override this instead of Serializable.__setstate__
-        because we need Serializable's implementation to call _post_load() after
-        all the _do_setstate() have returned.)
-        """
-        # Quietly set all the attributes
-        self.trait_setq(**state)
-
-    # ------------------------------------------------------------------------
-    # Private methods
-    # ------------------------------------------------------------------------
-
-
-#    def __getstate__(self):
-#        #idstring = self.__class__.__name__ + " id=" + str(id(self))
-#        # Give the object a chance to tidy up before saving
-#        self._pre_save()
-#
-#        # Get the attributes that this class needs to serialize.  We do this by
-#        # marching up the list of parent classes in _pickle_parents and getting
-#        # their lists of _pickles.
-#        all_pickles = Set()
-#        pickle_parents = self._get_pickle_parents()
-#        for parent_class in pickle_parents:
-#            all_pickles.update(parent_class._pickles)
-#
-#        if self._pickles is not None:
-#            all_pickles.update(self._pickles)
-#
-#        state = {}
-#        for attrib in all_pickles:
-#            state[attrib] = getattr(self, attrib)
-#
-#        print('<<<<<<<<<<<<<', self)
-#        for key,value in state.items():
-#            print(key, type(value))
-#        print '>>>>>>>>>>>>>'
-#
-#        return state
-
-# ~ def __setstate__(self, state):
-# ~ idstring = self.__class__.__name__ + " id=" + str(id(self))
-# ~ self._do_setstate(state)
-# ~ self._post_load()
-# ~ return


Test patch:
diff --git a/chaco/tests/serializable_base.py b/chaco/tests/serializable_base.py
deleted file mode 100644
--- a/chaco/tests/serializable_base.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from traits.api import Bool, HasTraits, Str, Float, Enum, List, Int
-from chaco.serializable import Serializable
-
-
-class Root(HasTraits):
-    name = Str
-    x = Float(0.0)
-    y = Float(0.0)
-
-
-class Shape(Serializable, Root):
-    color = Enum("red", "green", "blue")
-    filled = Bool(True)
-    tools = List
-    _pickles = ("tools", "filled", "color", "x")
-
-
-class Circle(Shape):
-    radius = Float(10.0)
-    _pickles = ("radius",)
-
-
-class Poly(Shape):
-    numsides = Int(5)
-    length = Float(5.0)
-    _pickles = ("numsides", "length")
diff --git a/chaco/tests/test_serializable.py b/chaco/tests/test_serializable.py
deleted file mode 100644
--- a/chaco/tests/test_serializable.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import pickle
-import unittest
-import warnings
-
-
-# pickling child classes doesn't work well in the unittest framework unless
-# the classes to be pickled are in a different file
-from .serializable_base import Circle, Poly
-
-
-class SimpleSerializationTestCase(unittest.TestCase):
-    def compare_traits(self, a, b, trait_names=None):
-        "Checks the traits of objects 'a' and 'b' and makes sure they all match."
-        if trait_names is None:
-            trait_names = a.trait_names()
-        for name in trait_names:
-            if name in ("trait_added", "trait_modified"):
-                continue
-            o1 = getattr(a, name)
-            o2 = getattr(b, name)
-            if isinstance(o1, list) or isinstance(o1, tuple):
-                raise RuntimeError(
-                    "Warning: Cowardly refusing to do deep compares"
-                )
-            else:
-                self.assertTrue(o1 == o2)
-
-    def test_basic_save(self):
-        c = Circle(radius=5.0, name="c1", x=1.0, y=2.0)
-        c2 = pickle.loads(pickle.dumps(c))
-        for attrib in ("tools", "filled", "color", "x", "radius"):
-            self.assertTrue(getattr(c, attrib) == getattr(c2, attrib))
-        self.assertEqual(c2.y, 2.0)
-
-    def test_basic_save2(self):
-        p = Poly(numside=3, name="poly", x=3.0, y=4.0)
-        p2 = pickle.loads(pickle.dumps(p))
-        for attrib in ("tools", "filled", "color", "x", "numsides", "length"):
-            self.assertTrue(getattr(p, attrib) == getattr(p2, attrib))
-        self.assertEqual(p2.y, 4.0)
-
-
-class PlotSerializationTestCase(unittest.TestCase):
-    pass