diff options
author | Guido van Rossum <guido@python.org> | 2003-01-30 22:06:23 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 2003-01-30 22:06:23 (GMT) |
commit | 177e41a1178e501887d24610c0c3feba2cf7f70c (patch) | |
tree | 0be6007ce83caada8d793c2eefaeb53821541ae6 | |
parent | e14295cf5f5a21dd94a540341202deec9e58b4c9 (diff) | |
download | cpython-177e41a1178e501887d24610c0c3feba2cf7f70c.zip cpython-177e41a1178e501887d24610c0c3feba2cf7f70c.tar.gz cpython-177e41a1178e501887d24610c0c3feba2cf7f70c.tar.bz2 |
Change the approach to pickling to use __reduce__ everywhere. Most
classes have a __reduce__ that returns (self.__class__,
self.__getstate__()). tzinfo.__reduce__() is a bit smarter, calling
__getinitargs__ and __getstate__ if they exist, and falling back to
__dict__ if it exists and isn't empty.
-rw-r--r-- | Lib/test/test_datetime.py | 102 | ||||
-rw-r--r-- | Modules/datetimemodule.c | 463 |
2 files changed, 270 insertions, 295 deletions
diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py index 995b6a0..32a277e 100644 --- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -4,6 +4,8 @@ See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases """ import sys +import pickle +import cPickle import unittest from test import test_support @@ -15,6 +17,22 @@ from datetime import time from datetime import date, datetime +pickle_choices = [ + (pickle, pickle, 0), + (pickle, pickle, 1), + (pickle, pickle, 2), + (cPickle, cPickle, 0), + (cPickle, cPickle, 1), +## (cPickle, cPickle, 2), + (pickle, cPickle, 0), + (pickle, cPickle, 1), +## (pickle, cPickle, 2), + (cPickle, pickle, 0), + (cPickle, pickle, 1), +## (cPickle, pickle, 2), + ] + + # XXX The test suite uncovered a bug in Python 2.2.2: if x and y are # XXX instances of new-style classes (like date and time) that both # XXX define __cmp__, and x is compared to y, and one of the __cmp__ @@ -100,22 +118,17 @@ class TestTZInfo(unittest.TestCase): self.assertEqual(fo.dst(dt), timedelta(minutes=42)) def test_pickling_base(self): - import pickle, cPickle - # There's no point to pickling tzinfo objects on their own (they # carry no data), but they need to be picklable anyway else # concrete subclasses can't be pickled. orig = tzinfo.__new__(tzinfo) self.failUnless(type(orig) is tzinfo) - for pickler in pickle, cPickle: - for binary in 0, 1: - green = pickler.dumps(orig, binary) - derived = pickler.loads(green) + for pickler, unpickler, proto in pickle_choices: + green = pickler.dumps(orig, proto) + derived = unpickler.loads(green) self.failUnless(type(derived) is tzinfo) def test_pickling_subclass(self): - import pickle, cPickle - # Make sure we can pickle/unpickle an instance of a subclass. offset = timedelta(minutes=-300) orig = PicklableFixedOffset(offset, 'cookie') @@ -123,10 +136,9 @@ class TestTZInfo(unittest.TestCase): self.failUnless(type(orig) is PicklableFixedOffset) self.assertEqual(orig.utcoffset(None), offset) self.assertEqual(orig.tzname(None), 'cookie') - for pickler in pickle, cPickle: - for binary in 0, 1: - green = pickler.dumps(orig, binary) - derived = pickler.loads(green) + for pickler, unpickler, proto in pickle_choices: + green = pickler.dumps(orig, proto) + derived = unpickler.loads(green) self.failUnless(isinstance(derived, tzinfo)) self.failUnless(type(derived) is PicklableFixedOffset) self.assertEqual(derived.utcoffset(None), offset) @@ -264,7 +276,6 @@ class TestTimeDelta(unittest.TestCase): self.assertEqual(d[t1], 2) def test_pickling(self): - import pickle, cPickle args = 12, 34, 56 orig = timedelta(*args) state = orig.__getstate__() @@ -272,10 +283,9 @@ class TestTimeDelta(unittest.TestCase): derived = timedelta() derived.__setstate__(state) self.assertEqual(orig, derived) - for pickler in pickle, cPickle: - for binary in 0, 1: - green = pickler.dumps(orig, binary) - derived = pickler.loads(green) + for pickler, unpickler, proto in pickle_choices: + green = pickler.dumps(orig, proto) + derived = unpickler.loads(green) self.assertEqual(orig, derived) def test_compare(self): @@ -823,18 +833,16 @@ class TestDate(unittest.TestCase): self.assertEqual(t.tm_isdst, -1) def test_pickling(self): - import pickle, cPickle args = 6, 7, 23 orig = self.theclass(*args) state = orig.__getstate__() - self.assertEqual(state, '\x00\x06\x07\x17') + self.assertEqual(state, ('\x00\x06\x07\x17',), self.theclass) derived = self.theclass(1, 1, 1) derived.__setstate__(state) self.assertEqual(orig, derived) - for pickler in pickle, cPickle: - for binary in 0, 1: - green = pickler.dumps(orig, binary) - derived = pickler.loads(green) + for pickler, unpickler, proto in pickle_choices: + green = pickler.dumps(orig, proto) + derived = unpickler.loads(green) self.assertEqual(orig, derived) def test_compare(self): @@ -1182,7 +1190,6 @@ class TestDateTime(TestDate): self.assertRaises(TypeError, lambda: a + a) def test_pickling(self): - import pickle, cPickle args = 6, 7, 23, 20, 59, 1, 64**2 orig = self.theclass(*args) state = orig.__getstate__() @@ -1190,10 +1197,9 @@ class TestDateTime(TestDate): derived = self.theclass(1, 1, 1) derived.__setstate__(state) self.assertEqual(orig, derived) - for pickler in pickle, cPickle: - for binary in 0, 1: - green = pickler.dumps(orig, binary) - derived = pickler.loads(green) + for pickler, unpickler, proto in pickle_choices: + green = pickler.dumps(orig, proto) + derived = unpickler.loads(green) self.assertEqual(orig, derived) def test_more_compare(self): @@ -1568,7 +1574,6 @@ class TestTime(unittest.TestCase): self.assert_(self.theclass.max > self.theclass.min) def test_pickling(self): - import pickle, cPickle args = 20, 59, 16, 64**2 orig = self.theclass(*args) state = orig.__getstate__() @@ -1576,10 +1581,9 @@ class TestTime(unittest.TestCase): derived = self.theclass() derived.__setstate__(state) self.assertEqual(orig, derived) - for pickler in pickle, cPickle: - for binary in 0, 1: - green = pickler.dumps(orig, binary) - derived = pickler.loads(green) + for pickler, unpickler, proto in pickle_choices: + green = pickler.dumps(orig, proto) + derived = unpickler.loads(green) self.assertEqual(orig, derived) def test_bool(self): @@ -1882,8 +1886,6 @@ class TestTimeTZ(TestTime, TZInfoBase): self.assertEqual(hash(t1), hash(t2)) def test_pickling(self): - import pickle, cPickle - # Try one without a tzinfo. args = 20, 59, 16, 64**2 orig = self.theclass(*args) @@ -1892,10 +1894,9 @@ class TestTimeTZ(TestTime, TZInfoBase): derived = self.theclass() derived.__setstate__(state) self.assertEqual(orig, derived) - for pickler in pickle, cPickle: - for binary in 0, 1: - green = pickler.dumps(orig, binary) - derived = pickler.loads(green) + for pickler, unpickler, proto in pickle_choices: + green = pickler.dumps(orig, proto) + derived = unpickler.loads(green) self.assertEqual(orig, derived) # Try one with a tzinfo. @@ -1909,10 +1910,9 @@ class TestTimeTZ(TestTime, TZInfoBase): self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) self.assertEqual(derived.tzname(), 'cookie') - for pickler in pickle, cPickle: - for binary in 0, 1: - green = pickler.dumps(orig, binary) - derived = pickler.loads(green) + for pickler, unpickler, proto in pickle_choices: + green = pickler.dumps(orig, proto) + derived = unpickler.loads(green) self.assertEqual(orig, derived) self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset)) @@ -2101,8 +2101,6 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase): self.assertRaises(ValueError, lambda: t1 == t2) def test_pickling(self): - import pickle, cPickle - # Try one without a tzinfo. args = 6, 7, 23, 20, 59, 1, 64**2 orig = self.theclass(*args) @@ -2111,10 +2109,9 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase): derived = self.theclass(1, 1, 1) derived.__setstate__(state) self.assertEqual(orig, derived) - for pickler in pickle, cPickle: - for binary in 0, 1: - green = pickler.dumps(orig, binary) - derived = pickler.loads(green) + for pickler, unpickler, proto in pickle_choices: + green = pickler.dumps(orig, proto) + derived = unpickler.loads(green) self.assertEqual(orig, derived) # Try one with a tzinfo. @@ -2128,10 +2125,9 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase): self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) self.assertEqual(derived.tzname(), 'cookie') - for pickler in pickle, cPickle: - for binary in 0, 1: - green = pickler.dumps(orig, binary) - derived = pickler.loads(green) + for pickler, unpickler, proto in pickle_choices: + green = pickler.dumps(orig, proto) + derived = unpickler.loads(green) self.assertEqual(orig, derived) self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset)) diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c index 31e006d..3ba2d18 100644 --- a/Modules/datetimemodule.c +++ b/Modules/datetimemodule.c @@ -1350,12 +1350,6 @@ static PyObject *us_per_day = NULL; /* 1e6 * 3600 * 24 as Python long */ static PyObject *us_per_week = NULL; /* 1e6*3600*24*7 as Python long */ static PyObject *seconds_per_day = NULL; /* 3600*24 as Python int */ -/* Callables to support unpickling. */ -static PyObject *date_unpickler_object = NULL; -static PyObject *datetime_unpickler_object = NULL; -static PyObject *tzinfo_unpickler_object = NULL; -static PyObject *time_unpickler_object = NULL; - /* --------------------------------------------------------------------------- * Class implementations. */ @@ -2003,6 +1997,7 @@ delta_reduce(PyDateTime_Delta* self) #define OFFSET(field) offsetof(PyDateTime_Delta, field) static PyMemberDef delta_members[] = { + {"days", T_INT, OFFSET(days), READONLY, PyDoc_STR("Number of days.")}, @@ -2015,14 +2010,16 @@ static PyMemberDef delta_members[] = { }; static PyMethodDef delta_methods[] = { - {"__setstate__", (PyCFunction)delta_setstate, METH_O, - PyDoc_STR("__setstate__(state)")}, - {"__reduce__", (PyCFunction)delta_reduce, METH_NOARGS, + {"__setstate__", (PyCFunction)delta_setstate, METH_O, PyDoc_STR("__setstate__(state)")}, {"__getstate__", (PyCFunction)delta_getstate, METH_NOARGS, PyDoc_STR("__getstate__() -> state")}, + + {"__reduce__", (PyCFunction)delta_reduce, METH_NOARGS, + PyDoc_STR("__reduce__() -> (cls, state)")}, + {NULL, NULL}, }; @@ -2148,6 +2145,8 @@ static PyGetSetDef date_getset[] = { static char *date_kws[] = {"year", "month", "day", NULL}; +static PyObject *date_setstate(PyDateTime_Date *self, PyObject *arg); + static PyObject * date_new(PyTypeObject *type, PyObject *args, PyObject *kw) { @@ -2156,6 +2155,24 @@ date_new(PyTypeObject *type, PyObject *args, PyObject *kw) int month; int day; + /* Check for invocation from pickle with __getstate__ state */ + if (PyTuple_GET_SIZE(args) == 1 && + PyString_Check(PyTuple_GET_ITEM(args, 0))) + { + self = new_date(1, 1, 1); + if (self != NULL) { + PyObject *res = date_setstate( + (PyDateTime_Date *)self, args); + if (res == Py_None) + Py_DECREF(res); + else { + Py_DECREF(self); + self = NULL; + } + } + return self; + } + if (PyArg_ParseTupleAndKeywords(args, kw, "iii", date_kws, &year, &month, &day)) { if (check_date_args(year, month, day) < 0) @@ -2518,22 +2535,34 @@ date_weekday(PyDateTime_Date *self) static PyObject * date_getstate(PyDateTime_Date *self) { - return PyString_FromStringAndSize((char *)self->data, - _PyDateTime_DATE_DATASIZE); + return Py_BuildValue( + "(N)", + PyString_FromStringAndSize((char *)self->data, + _PyDateTime_DATE_DATASIZE)); } static PyObject * -date_setstate(PyDateTime_Date *self, PyObject *state) +date_setstate(PyDateTime_Date *self, PyObject *arg) { - const int len = PyString_Size(state); - unsigned char *pdata = (unsigned char*)PyString_AsString(state); + PyObject *state; + int len; + unsigned char *pdata; - if (! PyString_Check(state) || - len != _PyDateTime_DATE_DATASIZE) { + if (!PyTuple_Check(arg) || PyTuple_GET_SIZE(arg) != 1) { + error: PyErr_SetString(PyExc_TypeError, "bad argument to date.__setstate__"); return NULL; } + state = PyTuple_GET_ITEM(arg, 0); + if (!PyString_Check(state)) + goto error; + + len = PyString_Size(state); + if (len != _PyDateTime_DATE_DATASIZE) + goto error; + + pdata = (unsigned char*)PyString_AsString(state); memcpy(self->data, pdata, _PyDateTime_DATE_DATASIZE); self->hashcode = -1; @@ -2541,52 +2570,16 @@ date_setstate(PyDateTime_Date *self, PyObject *state) return Py_None; } -/* XXX This seems a ridiculously inefficient way to pickle a short string. */ -static PyObject * -date_pickler(PyObject *module, PyDateTime_Date *date) -{ - PyObject *state; - PyObject *result = NULL; - - if (! PyDate_CheckExact(date)) { - PyErr_Format(PyExc_TypeError, - "bad type passed to date pickler: %s", - date->ob_type->tp_name); - return NULL; - } - state = date_getstate(date); - if (state) { - result = Py_BuildValue("O(O)", date_unpickler_object, state); - Py_DECREF(state); - } - return result; -} - static PyObject * -date_unpickler(PyObject *module, PyObject *arg) +date_reduce(PyDateTime_Date *self, PyObject *arg) { - PyDateTime_Date *self; - - if (! PyString_CheckExact(arg)) { - PyErr_Format(PyExc_TypeError, - "bad type passed to date unpickler: %s", - arg->ob_type->tp_name); - return NULL; - } - self = PyObject_New(PyDateTime_Date, &PyDateTime_DateType); - if (self != NULL) { - PyObject *res = date_setstate(self, arg); - if (res == NULL) { - Py_DECREF(self); - return NULL; - } - Py_DECREF(res); - } - return (PyObject *)self; + return Py_BuildValue("(ON)", self->ob_type, date_getstate(self)); } static PyMethodDef date_methods[] = { + /* Class methods: */ + {"fromtimestamp", (PyCFunction)date_fromtimestamp, METH_VARARGS | METH_CLASS, PyDoc_STR("timestamp -> local date from a POSIX timestamp (like " @@ -2640,6 +2633,9 @@ static PyMethodDef date_methods[] = { {"__getstate__", (PyCFunction)date_getstate, METH_NOARGS, PyDoc_STR("__getstate__() -> state")}, + {"__reduce__", (PyCFunction)date_reduce, METH_NOARGS, + PyDoc_STR("__reduce__() -> (cls, state)")}, + {NULL, NULL} }; @@ -2834,23 +2830,66 @@ Fail: /* * Pickle support. This is solely so that tzinfo subclasses can use - * pickling -- tzinfo itself is supposed to be uninstantiable. The - * pickler and unpickler functions are given module-level private - * names, and registered with copy_reg, by the module init function. + * pickling -- tzinfo itself is supposed to be uninstantiable. */ -static PyObject* -tzinfo_pickler(PyDateTime_TZInfo *self) { - return Py_BuildValue("O()", tzinfo_unpickler_object); -} +static PyObject * +tzinfo_reduce(PyObject *self) +{ + PyObject *args, *state, *tmp; + PyObject *getinitargs, *getstate; -static PyObject* -tzinfo_unpickler(PyObject * unused) { - return PyType_GenericNew(&PyDateTime_TZInfoType, NULL, NULL); -} + tmp = PyTuple_New(0); + if (tmp == NULL) + return NULL; + + getinitargs = PyObject_GetAttrString(self, "__getinitargs__"); + if (getinitargs != NULL) { + args = PyObject_CallObject(getinitargs, tmp); + Py_DECREF(getinitargs); + if (args == NULL) { + Py_DECREF(tmp); + return NULL; + } + } + else { + PyErr_Clear(); + args = tmp; + Py_INCREF(args); + } + + getstate = PyObject_GetAttrString(self, "__getstate__"); + if (getstate != NULL) { + state = PyObject_CallObject(getstate, tmp); + Py_DECREF(getstate); + if (state == NULL) { + Py_DECREF(args); + Py_DECREF(tmp); + return NULL; + } + } + else { + PyObject **dictptr; + PyErr_Clear(); + state = Py_None; + dictptr = _PyObject_GetDictPtr(self); + if (dictptr && *dictptr && PyDict_Size(*dictptr)) + state = *dictptr; + Py_INCREF(state); + } + + Py_DECREF(tmp); + if (state == Py_None) { + Py_DECREF(state); + return Py_BuildValue("(ON)", self->ob_type, args); + } + else + return Py_BuildValue("(ONN)", self->ob_type, args, state); +} static PyMethodDef tzinfo_methods[] = { + {"tzname", (PyCFunction)tzinfo_tzname, METH_O, PyDoc_STR("datetime -> string name of time zone.")}, @@ -2864,6 +2903,9 @@ static PyMethodDef tzinfo_methods[] = { {"fromutc", (PyCFunction)tzinfo_fromutc, METH_O, PyDoc_STR("datetime in UTC -> datetime in local time.")}, + {"__reduce__", (PyCFunction)tzinfo_reduce, METH_NOARGS, + PyDoc_STR("-> (cls, state)")}, + {NULL, NULL} }; @@ -2970,6 +3012,8 @@ static PyGetSetDef time_getset[] = { static char *time_kws[] = {"hour", "minute", "second", "microsecond", "tzinfo", NULL}; +static PyObject *time_setstate(PyDateTime_Time *self, PyObject *state); + static PyObject * time_new(PyTypeObject *type, PyObject *args, PyObject *kw) { @@ -2980,6 +3024,27 @@ time_new(PyTypeObject *type, PyObject *args, PyObject *kw) int usecond = 0; PyObject *tzinfo = Py_None; + /* Check for invocation from pickle with __getstate__ state */ + if (PyTuple_GET_SIZE(args) >= 1 && + PyTuple_GET_SIZE(args) <= 2 && + PyString_Check(PyTuple_GET_ITEM(args, 0))) + { + if (PyTuple_GET_SIZE(args) == 2) + tzinfo = PyTuple_GET_ITEM(args, 1); + self = new_time(0, 0, 0, 0, tzinfo); + if (self != NULL) { + PyObject *res = time_setstate( + (PyDateTime_Time *)self, args); + if (res == Py_None) + Py_DECREF(res); + else { + Py_DECREF(self); + self = NULL; + } + } + return self; + } + if (PyArg_ParseTupleAndKeywords(args, kw, "|iiiiO", time_kws, &hour, &minute, &second, &usecond, &tzinfo)) { @@ -3337,62 +3402,13 @@ time_setstate(PyDateTime_Time *self, PyObject *state) } static PyObject * -time_pickler(PyObject *module, PyDateTime_Time *time) -{ - PyObject *state; - PyObject *result = NULL; - - if (! PyTime_CheckExact(time)) { - PyErr_Format(PyExc_TypeError, - "bad type passed to time pickler: %s", - time->ob_type->tp_name); - return NULL; - } - state = time_getstate(time); - if (state) { - result = Py_BuildValue("O(O)", - time_unpickler_object, - state); - Py_DECREF(state); - } - return result; -} - -static PyObject * -time_unpickler(PyObject *module, PyObject *arg) +time_reduce(PyDateTime_Time *self, PyObject *arg) { - PyDateTime_Time *self; - - /* We don't want to allocate space for tzinfo if it's not needed. - * Figuring that out in advance is irritating, so for now we - * realloc later. - */ - self = PyObject_New(PyDateTime_Time, &PyDateTime_TimeType); - if (self != NULL) { - PyObject *res; - - self->tzinfo = Py_None; - Py_INCREF(self->tzinfo); - self->hastzinfo = (char)1; /* true */ - res = time_setstate(self, arg); - if (res == NULL) { - Py_DECREF(self); - return NULL; - } - Py_DECREF(res); - if (self->tzinfo == Py_None) { - /* shrinking; can't fail */ - Py_DECREF(self->tzinfo); - self = (PyDateTime_Time *)PyObject_Realloc(self, - sizeof(_PyDateTime_BaseTime)); - assert(self != NULL); - self->hastzinfo = (char)0; - } - } - return (PyObject *)self; + return Py_BuildValue("(ON)", self->ob_type, time_getstate(self)); } static PyMethodDef time_methods[] = { + {"isoformat", (PyCFunction)time_isoformat, METH_KEYWORDS, PyDoc_STR("Return string in ISO 8601 format, HH:MM:SS[.mmmmmm]" "[+HH:MM].")}, @@ -3417,6 +3433,10 @@ static PyMethodDef time_methods[] = { {"__getstate__", (PyCFunction)time_getstate, METH_NOARGS, PyDoc_STR("__getstate__() -> state")}, + + {"__reduce__", (PyCFunction)time_reduce, METH_NOARGS, + PyDoc_STR("__reduce__() -> (cls, state)")}, + {NULL, NULL} }; @@ -3539,6 +3559,8 @@ static char *datetime_kws[] = { "microsecond", "tzinfo", NULL }; +static PyObject *datetime_setstate(PyDateTime_DateTime *self, PyObject *state); + static PyObject * datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw) { @@ -3552,6 +3574,27 @@ datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw) int usecond = 0; PyObject *tzinfo = Py_None; + /* Check for invocation from pickle with __getstate__ state */ + if (PyTuple_GET_SIZE(args) >= 1 && + PyTuple_GET_SIZE(args) <= 2 && + PyString_Check(PyTuple_GET_ITEM(args, 0))) + { + if (PyTuple_GET_SIZE(args) == 2) + tzinfo = PyTuple_GET_ITEM(args, 1); + self = new_datetime(1, 1, 1, 0, 0, 0, 0, tzinfo); + if (self != NULL) { + PyObject *res = datetime_setstate( + (PyDateTime_DateTime *)self, args); + if (res == Py_None) + Py_DECREF(res); + else { + Py_DECREF(self); + self = NULL; + } + } + return self; + } + if (PyArg_ParseTupleAndKeywords(args, kw, "iii|iiiiO", datetime_kws, &year, &month, &day, &hour, &minute, &second, &usecond, &tzinfo)) { @@ -4363,63 +4406,13 @@ datetime_setstate(PyDateTime_DateTime *self, PyObject *state) } static PyObject * -datetime_pickler(PyObject *module, PyDateTime_DateTime *datetime) -{ - PyObject *state; - PyObject *result = NULL; - - if (! PyDateTime_CheckExact(datetime)) { - PyErr_Format(PyExc_TypeError, - "bad type passed to datetime pickler: %s", - datetime->ob_type->tp_name); - return NULL; - } - state = datetime_getstate(datetime); - if (state) { - result = Py_BuildValue("O(O)", - datetime_unpickler_object, - state); - Py_DECREF(state); - } - return result; -} - -static PyObject * -datetime_unpickler(PyObject *module, PyObject *arg) +datetime_reduce(PyDateTime_DateTime *self, PyObject *arg) { - PyDateTime_DateTime *self; - - /* We don't want to allocate space for tzinfo if it's not needed. - * Figuring that out in advance is irritating, so for now we - * realloc later. - */ - self = PyObject_New(PyDateTime_DateTime, &PyDateTime_DateTimeType); - if (self != NULL) { - PyObject *res; - - self->tzinfo = Py_None; - Py_INCREF(self->tzinfo); - self->hastzinfo = (char)1; /* true */ - res = datetime_setstate(self, arg); - if (res == NULL) { - Py_DECREF(self); - return NULL; - } - Py_DECREF(res); - if (self->tzinfo == Py_None) { - /* shrinking; can't fail */ - Py_DECREF(self->tzinfo); - self = (PyDateTime_DateTime *)PyObject_Realloc(self, - sizeof(_PyDateTime_BaseDateTime)); - assert(self != NULL); - self->hastzinfo = (char)0; - } - } - return (PyObject *)self; + return Py_BuildValue("(ON)", self->ob_type, datetime_getstate(self)); } - static PyMethodDef datetime_methods[] = { + /* Class methods: */ {"now", (PyCFunction)datetime_now, @@ -4444,6 +4437,7 @@ static PyMethodDef datetime_methods[] = { PyDoc_STR("date, time -> datetime with same date and time fields")}, /* Instance methods: */ + {"date", (PyCFunction)datetime_getdate, METH_NOARGS, PyDoc_STR("Return date object with same year, month and day.")}, @@ -4488,6 +4482,10 @@ static PyMethodDef datetime_methods[] = { {"__getstate__", (PyCFunction)datetime_getstate, METH_NOARGS, PyDoc_STR("__getstate__() -> state")}, + + {"__reduce__", (PyCFunction)datetime_reduce, METH_NOARGS, + PyDoc_STR("__reduce__() -> (cls, state)")}, + {NULL, NULL} }; @@ -4557,18 +4555,6 @@ statichere PyTypeObject PyDateTime_DateTimeType = { */ static PyMethodDef module_methods[] = { - /* Private functions for pickling support, registered with the - * copy_reg module by the module init function. - */ - {"_date_pickler", (PyCFunction)date_pickler, METH_O, NULL}, - {"_date_unpickler", (PyCFunction)date_unpickler, METH_O, NULL}, - {"_datetime_pickler", (PyCFunction)datetime_pickler, METH_O, NULL}, - {"_datetime_unpickler", (PyCFunction)datetime_unpickler,METH_O, NULL}, - {"_time_pickler", (PyCFunction)time_pickler, METH_O, NULL}, - {"_time_unpickler", (PyCFunction)time_unpickler, METH_O, NULL}, - {"_tzinfo_pickler", (PyCFunction)tzinfo_pickler, METH_O, NULL}, - {"_tzinfo_unpickler", (PyCFunction)tzinfo_unpickler, METH_NOARGS, - NULL}, {NULL, NULL} }; @@ -4600,68 +4586,52 @@ initdatetime(void) if (PyType_Ready(&PyDateTime_TZInfoType) < 0) return; - /* Pickling support, via registering functions with copy_reg. */ + /* Make __getnewargs__ a true alias for __getstate__ */ { - PyObject *pickler; - PyObject *copyreg = PyImport_ImportModule("copy_reg"); - - if (copyreg == NULL) return; - - pickler = PyObject_GetAttrString(m, "_date_pickler"); - if (pickler == NULL) return; - date_unpickler_object = PyObject_GetAttrString(m, - "_date_unpickler"); - if (date_unpickler_object == NULL) return; - x = PyObject_CallMethod(copyreg, "pickle", "OOO", - &PyDateTime_DateType, - pickler, - date_unpickler_object); - if (x == NULL) return; - Py_DECREF(x); - Py_DECREF(pickler); - - pickler = PyObject_GetAttrString(m, "_time_pickler"); - if (pickler == NULL) return; - time_unpickler_object = PyObject_GetAttrString(m, - "_time_unpickler"); - if (time_unpickler_object == NULL) return; - x = PyObject_CallMethod(copyreg, "pickle", "OOO", - &PyDateTime_TimeType, - pickler, - time_unpickler_object); - if (x == NULL) return; - Py_DECREF(x); - Py_DECREF(pickler); - - pickler = PyObject_GetAttrString(m, "_tzinfo_pickler"); - if (pickler == NULL) return; - tzinfo_unpickler_object = PyObject_GetAttrString(m, - "_tzinfo_unpickler"); - if (tzinfo_unpickler_object == NULL) return; - x = PyObject_CallMethod(copyreg, "pickle", "OOO", - &PyDateTime_TZInfoType, - pickler, - tzinfo_unpickler_object); - if (x== NULL) return; - Py_DECREF(x); - Py_DECREF(pickler); - - pickler = PyObject_GetAttrString(m, "_datetime_pickler"); - if (pickler == NULL) return; - datetime_unpickler_object = PyObject_GetAttrString(m, - "_datetime_unpickler"); - if (datetime_unpickler_object == NULL) return; - x = PyObject_CallMethod(copyreg, "pickle", "OOO", - &PyDateTime_DateTimeType, - pickler, - datetime_unpickler_object); - if (x== NULL) return; - Py_DECREF(x); - Py_DECREF(pickler); + PyObject *d, *f; + + d = PyDateTime_DateType.tp_dict; + f = PyDict_GetItemString(d, "__getstate__"); + if (f != NULL) { + if (PyDict_SetItemString(d, "__getnewargs__", f) < 0) + return; + } - Py_DECREF(copyreg); + d = PyDateTime_DateTimeType.tp_dict; + f = PyDict_GetItemString(d, "__getstate__"); + if (f != NULL) { + if (PyDict_SetItemString(d, "__getnewargs__", f) < 0) + return; + } + + d = PyDateTime_DeltaType.tp_dict; + f = PyDict_GetItemString(d, "__getstate__"); + if (f != NULL) { + if (PyDict_SetItemString(d, "__getnewargs__", f) < 0) + return; + } + + d = PyDateTime_TimeType.tp_dict; + f = PyDict_GetItemString(d, "__getstate__"); + if (f != NULL) { + if (PyDict_SetItemString(d, "__getnewargs__", f) < 0) + return; + } + + d = PyDateTime_TZInfoType.tp_dict; + f = PyDict_GetItemString(d, "__getstate__"); + if (f != NULL) { + if (PyDict_SetItemString(d, "__getnewargs__", f) < 0) + return; + } } + /* tzinfo values */ + d = PyDateTime_TZInfoType.tp_dict; + + if (PyDict_SetItem(d, safepickle, Py_True) < 0) + return; + /* timedelta values */ d = PyDateTime_DeltaType.tp_dict; @@ -4686,6 +4656,9 @@ initdatetime(void) /* date values */ d = PyDateTime_DateType.tp_dict; + if (PyDict_SetItem(d, safepickle, Py_True) < 0) + return; + x = new_date(1, 1, 1); if (x == NULL || PyDict_SetItemString(d, "min", x) < 0) return; @@ -4704,6 +4677,9 @@ initdatetime(void) /* time values */ d = PyDateTime_TimeType.tp_dict; + if (PyDict_SetItem(d, safepickle, Py_True) < 0) + return; + x = new_time(0, 0, 0, 0, Py_None); if (x == NULL || PyDict_SetItemString(d, "min", x) < 0) return; @@ -4722,6 +4698,9 @@ initdatetime(void) /* datetime values */ d = PyDateTime_DateTimeType.tp_dict; + if (PyDict_SetItem(d, safepickle, Py_True) < 0) + return; + x = new_datetime(1, 1, 1, 0, 0, 0, 0, Py_None); if (x == NULL || PyDict_SetItemString(d, "min", x) < 0) return; |