]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Change the approach to pickling to use __reduce__ everywhere. Most
authorGuido van Rossum <guido@python.org>
Thu, 30 Jan 2003 22:06:23 +0000 (22:06 +0000)
committerGuido van Rossum <guido@python.org>
Thu, 30 Jan 2003 22:06:23 +0000 (22:06 +0000)
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.

Lib/test/test_datetime.py
Modules/datetimemodule.c

index 995b6a03f16f34d5c4f0a6ab0c8423f4b63cd3e1..32a277ed010b8a21c084695a7f3b4b2ee2491b70 100644 (file)
@@ -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))
index 31e006d6fa05ae2ca5345a5cdae547410cd0e290..3ba2d18b967c8f42ba4d1cf0e6e4aeea65df6263 100644 (file)
@@ -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;