[Python-checkins] python/dist/src/Modules cPickle.c,2.135,2.136

tim_one@users.sourceforge.net tim_one@users.sourceforge.net
Fri, 14 Feb 2003 15:05:30 -0800


Update of /cvsroot/python/python/dist/src/Modules
In directory sc8-pr-cvs1:/tmp/cvs-serv23870/Modules

Modified Files:
	cPickle.c 
Log Message:
cPickle produces NEWOBJ appropriately now.  It still doesn't know
how to unpickle the new slot-full state tuples.


Index: cPickle.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/cPickle.c,v
retrieving revision 2.135
retrieving revision 2.136
diff -C2 -d -r2.135 -r2.136
*** cPickle.c	13 Feb 2003 23:00:26 -0000	2.135
--- cPickle.c	14 Feb 2003 23:05:28 -0000	2.136
***************
*** 120,123 ****
--- 120,129 ----
  static PyObject *two_tuple;
  
+ /* object.__reduce__, the default reduce callable. */
+ PyObject *object_reduce;
+ 
+ /* copy_reg._better_reduce, the protocol 2 reduction function. */
+ PyObject *better_reduce;
+ 
  static PyObject *__class___str, *__getinitargs___str, *__dict___str,
    *__getstate___str, *__setstate___str, *__name___str, *__reduce___str,
***************
*** 2182,2201 ****
  }
  
! 
  static int
! save_reduce(Picklerobject *self, PyObject *callable,
!             PyObject *tup, PyObject *state, PyObject *ob)
  {
! 	static char reduce = REDUCE, build = BUILD;
  
! 	if (save(self, callable, 0) < 0)
! 		return -1;
  
! 	if (save(self, tup, 0) < 0)
! 		return -1;
  
! 	if (self->write_func(self, &reduce, 1) < 0)
  		return -1;
  
  	if (ob != NULL) {
  		if (state && !PyDict_Check(state)) {
--- 2188,2308 ----
  }
  
! /* We're saving ob, and args is the 2-thru-5 tuple returned by the
!  * appropriate __reduce__ method for ob.
!  */
  static int
! save_reduce(Picklerobject *self, PyObject *args, PyObject *ob)
  {
! 	PyObject *callable;
! 	PyObject *argtup;
!         PyObject *state = NULL;
!         PyObject *listitems = NULL;
!         PyObject *dictitems = NULL;
  
! 	int use_newobj = self->proto >= 2;
  
! 	static char reduce = REDUCE;
! 	static char build = BUILD;
! 	static char newobj = NEWOBJ;
  
! 	if (! PyArg_UnpackTuple(args, "save_reduce", 2, 5,
! 				&callable,
! 				&argtup,
! 				&state,
! 				&listitems,
! 				&dictitems))
  		return -1;
  
+ 	if (state == Py_None)
+ 		state = NULL;
+ 	if (listitems == Py_None)
+ 		listitems = NULL;
+ 	if (dictitems == Py_None)
+ 		dictitems = NULL;
+ 
+         /* Protocol 2 special case: if callable's name is __newobj__, use
+          * NEWOBJ.  This consumes a lot of code.
+          */
+         if (use_newobj) {
+         	PyObject *temp = PyObject_GetAttr(callable, __name___str);
+ 
+ 		if (temp == NULL) {
+ 			PyErr_Clear();
+ 			use_newobj = 0;
+ 		}
+ 		else {
+ 			use_newobj = PyString_Check(temp) &&
+ 				     strcmp(PyString_AS_STRING(temp),
+ 				     	    "__newobj__") == 0;
+ 			Py_DECREF(temp);
+ 		}
+ 	}
+ 	if (use_newobj) {
+ 		PyObject *cls;
+ 		PyObject *newargtup;
+ 		int n, i;
+ 
+ 		/* Sanity checks. */
+ 		n = PyTuple_Size(argtup);
+ 		if (n < 1) {
+ 			PyErr_SetString(PicklingError, "__newobj__ arglist "
+ 				"is empty");
+ 			return -1;
+ 		}
+ 
+ 		cls = PyTuple_GET_ITEM(argtup, 0);
+ 		if (! PyObject_HasAttrString(cls, "__new__")) {
+ 			PyErr_SetString(PicklingError, "args[0] from "
+ 				"__newobj__ args has no __new__");
+ 			return -1;
+ 		}
+ 
+ 		/* XXX How could ob be NULL? */
+ 		if (ob != NULL) {
+ 			PyObject *ob_dot_class;
+ 
+ 			ob_dot_class = PyObject_GetAttr(ob, __class___str);
+ 			if (ob_dot_class == NULL)
+ 				PyErr_Clear();
+ 			i = ob_dot_class != cls; /* true iff a problem */
+ 			Py_XDECREF(ob_dot_class);
+ 			if (i) {
+ 				PyErr_SetString(PicklingError, "args[0] from "
+ 					"__newobj__ args has the wrong class");
+ 				return -1;
+ 			}
+ 		}
+ 
+ 		/* Save the class and its __new__ arguments. */
+ 		if (save(self, cls, 0) < 0)
+ 			return -1;
+ 
+ 		newargtup = PyTuple_New(n-1);  /* argtup[1:] */
+ 		if (newargtup == NULL)
+ 			return -1;
+ 		for (i = 1; i < n; ++i) {
+ 			PyObject *temp = PyTuple_GET_ITEM(argtup, i);
+ 			Py_INCREF(temp);
+ 			PyTuple_SET_ITEM(newargtup, i-1, temp);
+ 		}
+ 		i = save(self, newargtup, 0) < 0;
+ 		Py_DECREF(newargtup);
+ 		if (i < 0)
+ 			return -1;
+ 
+ 		/* Add NEWOBJ opcode. */
+ 		if (self->write_func(self, &newobj, 1) < 0)
+ 			return -1;
+ 	}
+ 	else {
+ 		/* Not using NEWOBJ. */
+ 		if (save(self, callable, 0) < 0 ||
+ 		    save(self, argtup, 0) < 0 ||
+ 		    self->write_func(self, &reduce, 1) < 0)
+ 			return -1;
+ 	}
+ 
+ 	/* Memoize. */
+ 	/* XXX How can ob be NULL? */
  	if (ob != NULL) {
  		if (state && !PyDict_Check(state)) {
***************
*** 2203,2217 ****
  				return -1;
  		}
! 		else {
! 			if (put(self, ob) < 0)
  				return -1;
- 		}
  	}
  
- 	if (state) {
- 		if (save(self, state, 0) < 0)
- 			return -1;
  
! 		if (self->write_func(self, &build, 1) < 0)
  			return -1;
  	}
--- 2310,2327 ----
  				return -1;
  		}
! 		else if (put(self, ob) < 0)
  				return -1;
  	}
  
  
!         if (listitems && batch_list(self, listitems) < 0)
!         	return -1;
! 
!         if (dictitems && batch_dict(self, dictitems) < 0)
!         	return -1;
! 
! 	if (state) {
! 		if (save(self, state, 0) < 0 ||
! 		    self->write_func(self, &build, 1) < 0)
  			return -1;
  	}
***************
*** 2224,2230 ****
  {
  	PyTypeObject *type;
! 	PyObject *py_ob_id = 0, *__reduce__ = 0, *t = 0, *arg_tup = 0,
! 		*callable = 0, *state = 0;
! 	int res = -1, tmp, size;
  
          if (self->nesting++ > Py_GetRecursionLimit()){
--- 2334,2341 ----
  {
  	PyTypeObject *type;
! 	PyObject *py_ob_id = 0, *__reduce__ = 0, *t = 0;
! 	PyObject *arg_tup;
! 	int res = -1;
! 	int tmp, size;
  
          if (self->nesting++ > Py_GetRecursionLimit()){
***************
*** 2393,2462 ****
  	}
  
! 	assert(t == NULL);	/* just a reminder */
  	__reduce__ = PyDict_GetItem(dispatch_table, (PyObject *)type);
  	if (__reduce__ != NULL) {
  		Py_INCREF(__reduce__);
- 		Py_INCREF(args);
- 		ARG_TUP(self, args);
- 		if (self->arg) {
- 			t = PyObject_Call(__reduce__, self->arg, NULL);
- 			FREE_ARG_TUP(self);
- 		}
- 		if (! t) goto finally;
  	}
  	else {
! 		__reduce__ = PyObject_GetAttr(args, __reduce___str);
! 		if (__reduce__ == NULL)
  			PyErr_Clear();
! 		else {
! 			t = PyObject_Call(__reduce__, empty_tuple, NULL);
! 			if (!t)
! 				goto finally;
! 		}
! 	}
! 
! 	if (t) {
! 		if (PyString_Check(t)) {
! 			res = save_global(self, args, t);
! 			goto finally;
! 		}
! 
! 		if (!PyTuple_Check(t)) {
! 			cPickle_ErrFormat(PicklingError, "Value returned by "
! 					"%s must be a tuple",
! 					"O", __reduce__);
  			goto finally;
  		}
  
! 		size = PyTuple_Size(t);
! 
! 		if (size != 3 && size != 2) {
! 			cPickle_ErrFormat(PicklingError, "tuple returned by "
! 				"%s must contain only two or three elements",
! 				"O", __reduce__);
! 			goto finally;
  		}
  
! 		callable = PyTuple_GET_ITEM(t, 0);
! 		arg_tup = PyTuple_GET_ITEM(t, 1);
  
! 		if (size > 2) {
! 			state = PyTuple_GET_ITEM(t, 2);
! 			if (state == Py_None)
! 				state = NULL;
! 		}
  
! 		if (!( PyTuple_Check(arg_tup) || arg_tup==Py_None ))  {
! 			cPickle_ErrFormat(PicklingError, "Second element of "
! 				"tuple returned by %s must be a tuple",
  				"O", __reduce__);
! 			goto finally;
! 		}
  
! 		res = save_reduce(self, callable, arg_tup, state, args);
  		goto finally;
  	}
  
! 	PyErr_SetObject(UnpickleableError, args);
  
    finally:
--- 2504,2581 ----
  	}
  
! 	/* Get a reduction callable.  This may come from
! 	 * copy_reg.dispatch_table, the object's __reduce__ method,
! 	 * the default object.__reduce__, or copy_reg._better_reduce.
! 	 */
  	__reduce__ = PyDict_GetItem(dispatch_table, (PyObject *)type);
  	if (__reduce__ != NULL) {
  		Py_INCREF(__reduce__);
  	}
  	else {
! 		/* Check for a __reduce__ method.
! 		 * Subtle: get the unbound method from the class, so that
! 		 * protocol 2 can override the default __reduce__ that all
! 		 * classes inherit from object.
! 		 * XXX object.__reduce__ should really be rewritten so that
! 		 * XXX we don't need to call back into Python code here
! 		 * XXX (better_reduce), but no time to do that.
! 		 */
! 		__reduce__ = PyObject_GetAttr((PyObject *)type,
! 					      __reduce___str);
! 		if (__reduce__ == NULL) {
  			PyErr_Clear();
! 			PyErr_SetObject(UnpickleableError, args);
  			goto finally;
  		}
  
! 		if (self->proto >= 2 && __reduce__ == object_reduce) {
! 			/* Proto 2 can do better than the default. */
! 			Py_DECREF(__reduce__);
! 			Py_INCREF(better_reduce);
! 			__reduce__ = better_reduce;
  		}
+ 	}
  
! 	/* Call the reduction callable, setting t to the result. */
! 	assert(__reduce__ != NULL);
! 	assert(t == NULL);
! 	Py_INCREF(args);
! 	ARG_TUP(self, args);
! 	if (self->arg) {
! 		t = PyObject_Call(__reduce__, self->arg, NULL);
! 		FREE_ARG_TUP(self);
! 	}
! 	if (t == NULL)
! 		goto finally;
  
! 	if (PyString_Check(t)) {
! 		res = save_global(self, args, t);
! 		goto finally;
! 	}
  
! 	if (! PyTuple_Check(t)) {
! 		cPickle_ErrFormat(PicklingError, "Value returned by "
! 				"%s must be string or tuple",
  				"O", __reduce__);
! 		goto finally;
! 	}
  
! 	size = PyTuple_Size(t);
! 	if (size < 2 || size > 5) {
! 		cPickle_ErrFormat(PicklingError, "tuple returned by "
! 			"%s must contain 2 through 5 elements",
! 			"O", __reduce__);
  		goto finally;
  	}
  
! 	arg_tup = PyTuple_GET_ITEM(t, 1);
! 	if (!(PyTuple_Check(arg_tup) || arg_tup == Py_None))  {
! 		cPickle_ErrFormat(PicklingError, "Second element of "
! 			"tuple returned by %s must be a tuple",
! 			"O", __reduce__);
! 		goto finally;
! 	}
! 
! 	res = save_reduce(self, t, args);
  
    finally:
***************
*** 5448,5452 ****
--- 5567,5578 ----
  	if (!extension_cache) return -1;
  
+ 	better_reduce = PyObject_GetAttrString(copy_reg, "_better_reduce");
+ 	if (!better_reduce) return -1;
+ 
  	Py_DECREF(copy_reg);
+ 
+ 	object_reduce = PyObject_GetAttrString((PyObject *)&PyBaseObject_Type,
+ 					       "__reduce__");
+ 	if (object_reduce == NULL) return -1;
  
  	if (!(empty_tuple = PyTuple_New(0)))