[Python-checkins] CVS: python/dist/src/Lib/test test_descr.py,1.113.4.6,1.113.4.7

Michael Hudson mwh@users.sourceforge.net
Sat, 16 Mar 2002 09:57:28 -0800


Update of /cvsroot/python/python/dist/src/Lib/test
In directory usw-pr-cvs1:/tmp/cvs-serv22199

Modified Files:
      Tag: release22-maint
	test_descr.py 
Log Message:
backport gvanrossum's checkin of
    revision 1.121 of test_descr.py

"Fix" for SF bug #520644: __slots__ are not pickled.

As promised in my response to the bug report, I'm not really fixing
it; in fact, one could argule over what the proper fix should do.
Instead, I'm adding a little magic that raises TypeError if you try to
pickle an instance of a class that has __slots__ but doesn't define or
override __getstate__.  This is done by adding a bozo __getstate__
that always raises TypeError.

Bugfix candidate (also the checkin to typeobject.c, of course).


Index: test_descr.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/test/test_descr.py,v
retrieving revision 1.113.4.6
retrieving revision 1.113.4.7
diff -C2 -d -r1.113.4.6 -r1.113.4.7
*** test_descr.py	15 Mar 2002 10:35:55 -0000	1.113.4.6
--- test_descr.py	16 Mar 2002 17:57:26 -0000	1.113.4.7
***************
*** 2451,2454 ****
--- 2451,2542 ----
          print "b = y =", b
  
+ def pickleslots():
+     if verbose: print "Testing pickling of classes with __slots__ ..."
+     import pickle, cPickle
+     # Pickling of classes with __slots__ but without __getstate__ should fail
+     global B, C, D, E
+     class B(object):
+         pass
+     for base in [object, B]:
+         class C(base):
+             __slots__ = ['a']
+         class D(C):
+             pass
+         try:
+             pickle.dumps(C())
+         except TypeError:
+             pass
+         else:
+             raise TestFailed, "should fail: pickle C instance - %s" % base
+         try:
+             cPickle.dumps(C())
+         except TypeError:
+             pass
+         else:
+             raise TestFailed, "should fail: cPickle C instance - %s" % base
+         try:
+             pickle.dumps(C())
+         except TypeError:
+             pass
+         else:
+             raise TestFailed, "should fail: pickle D instance - %s" % base
+         try:
+             cPickle.dumps(D())
+         except TypeError:
+             pass
+         else:
+             raise TestFailed, "should fail: cPickle D instance - %s" % base
+         # Give C a __getstate__ and __setstate__
+         class C(base):
+             __slots__ = ['a']
+             def __getstate__(self):
+                 try:
+                     d = self.__dict__.copy()
+                 except AttributeError:
+                     d = {}
+                 try:
+                     d['a'] = self.a
+                 except AttributeError:
+                     pass
+                 return d
+             def __setstate__(self, d):
+                 for k, v in d.items():
+                     setattr(self, k, v)
+         class D(C):
+             pass
+         # Now it should work
+         x = C()
+         y = pickle.loads(pickle.dumps(x))
+         vereq(hasattr(y, 'a'), 0)
+         y = cPickle.loads(cPickle.dumps(x))
+         vereq(hasattr(y, 'a'), 0)
+         x.a = 42
+         y = pickle.loads(pickle.dumps(x))
+         vereq(y.a, 42)
+         y = cPickle.loads(cPickle.dumps(x))
+         vereq(y.a, 42)
+         x = D()
+         x.a = 42
+         x.b = 100
+         y = pickle.loads(pickle.dumps(x))
+         vereq(y.a + y.b, 142)
+         y = cPickle.loads(cPickle.dumps(x))
+         vereq(y.a + y.b, 142)
+         # But a subclass that adds a slot should not work
+         class E(C):
+             __slots__ = ['b']
+         try:
+             pickle.dumps(E())
+         except TypeError:
+             pass
+         else:
+             raise TestFailed, "should fail: pickle E instance - %s" % base
+         try:
+             cPickle.dumps(E())
+         except TypeError:
+             pass
+         else:
+             raise TestFailed, "should fail: cPickle E instance - %s" % base
+ 
  def copies():
      if verbose: print "Testing copy.copy() and copy.deepcopy()..."
***************
*** 2799,2802 ****
--- 2887,2891 ----
      deepcopyrecursive()
      modules()
+     pickleslots()
      if verbose: print "All OK"