- Provisional support for pickling new-style objects. (*)

- Made cls.__module__ writable.

- Ensure that obj.__dict__ is returned as {}, not None, even upon first
  reference; it simply springs into life when you ask for it.

(*) The pickling support is provisional for the following reasons:

- It doesn't support classes with __slots__.

- It relies on additional support in copy_reg.py: the C method
  __reduce__, defined in the object class, really calls calling
  copy_reg._reduce(obj).  Eventually the Python code in copy_reg.py
  needs to be migrated to C, but I'd like to experiment with the
  Python implementation first.  The _reduce() code also relies on an
  additional helper function, _reconstructor(), defined in
  copy_reg.py; this should also be reimplemented in C.
diff --git a/Lib/copy_reg.py b/Lib/copy_reg.py
index e4f0b3c..e93c2a3 100644
--- a/Lib/copy_reg.py
+++ b/Lib/copy_reg.py
@@ -33,3 +33,32 @@
     return complex, (c.real, c.imag)
 
 pickle(type(1j), pickle_complex, complex)
+
+# Support for picking new-style objects
+
+_dummy_classes = {}
+
+def _reconstructor(cls, base, state):
+    dummy = _dummy_classes.get(base)
+    if dummy is None:
+        class dummy(base): pass
+        _dummy_classes[base] = dummy
+    obj = dummy(state)
+    obj._foo = 1; del obj._foo # hack to create __dict__
+    obj.__class__ = cls
+    return obj
+_reconstructor.__safe_for_unpickling__ = 1
+
+_HEAPTYPE = 1<<9
+
+def _reduce(self):
+    for base in self.__class__.__mro__:
+        if not base.__flags__ & _HEAPTYPE:
+            break
+    else:
+        base = object # not really reachable
+    if base is object:
+        state = None
+    else:
+        state = base(self)
+    return _reconstructor, (self.__class__, base, state), self.__dict__
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
index 766c399..0357a06 100644
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -785,7 +785,7 @@
     class Cdict(object):
         pass
     x = Cdict()
-    verify(x.__dict__ is None)
+    verify(x.__dict__ == {})
     x.foo = 1
     verify(x.foo == 1)
     verify(x.__dict__ == {'foo': 1})
@@ -2032,6 +2032,66 @@
     cant(object(), list)
     cant(list(), object)
 
+def pickles():
+    if verbose: print "Testing pickling new-style classes and objects..."
+    import pickle, cPickle
+
+    def sorteditems(d):
+        L = d.items()
+        L.sort()
+        return L
+
+    global C
+    class C(object):
+        def __init__(self, a, b):
+            super(C, self).__init__()
+            self.a = a
+            self.b = b
+        def __repr__(self):
+            return "C(%r, %r)" % (self.a, self.b)
+
+    global C1
+    class C1(list):
+        def __new__(cls, a, b):
+            return super(C1, cls).__new__(cls)
+        def __init__(self, a, b):
+            self.a = a
+            self.b = b
+        def __repr__(self):
+            return "C1(%r, %r)<%r>" % (self.a, self.b, list(self))
+
+    global C2
+    class C2(int):
+        def __new__(cls, a, b, val=0):
+            return super(C2, cls).__new__(cls, val)
+        def __init__(self, a, b, val=0):
+            self.a = a
+            self.b = b
+        def __repr__(self):
+            return "C2(%r, %r)<%r>" % (self.a, self.b, int(self))
+
+    for p in pickle, cPickle:
+        for bin in 0, 1:
+
+            for cls in C, C1, C2:
+                s = p.dumps(cls, bin)
+                cls2 = p.loads(s)
+                verify(cls2 is cls)
+
+            a = C1(1, 2); a.append(42); a.append(24)
+            b = C2("hello", "world", 42)
+            s = p.dumps((a, b), bin)
+            x, y = p.loads(s)
+            assert x.__class__ == a.__class__
+            assert sorteditems(x.__dict__) == sorteditems(a.__dict__)
+            assert y.__class__ == b.__class__
+            assert sorteditems(y.__dict__) == sorteditems(b.__dict__)
+            assert `x` == `a`
+            assert `y` == `b`
+            if verbose:
+                print "a = x =", a
+                print "b = y =", b
+
 
 def test_main():
     lists()
@@ -2075,6 +2135,7 @@
     coercions()
     descrdoc()
     setclass()
+    pickles()
     if verbose: print "All OK"
 
 if __name__ == "__main__":
diff --git a/Lib/test/test_descrtut.py b/Lib/test/test_descrtut.py
index a0de4cc..80e7f05 100644
--- a/Lib/test/test_descrtut.py
+++ b/Lib/test/test_descrtut.py
@@ -206,6 +206,7 @@
      '__mul__',
      '__ne__',
      '__new__',
+     '__reduce__',
      '__repr__',
      '__rmul__',
      '__setattr__',
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 964164f..1354b81 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -53,6 +53,23 @@
 	return NULL;
 }
 
+static int
+type_set_module(PyTypeObject *type, PyObject *value, void *context)
+{
+	if (!(type->tp_flags & Py_TPFLAGS_DYNAMICTYPE) ||
+	    strrchr(type->tp_name, '.')) {
+		PyErr_Format(PyExc_TypeError,
+			     "can't set %s.__module__", type->tp_name);
+		return -1;
+	}
+	if (!value) {
+		PyErr_Format(PyExc_TypeError,
+			     "can't delete %s.__module__", type->tp_name);
+		return -1;
+	}
+	return PyDict_SetItemString(type->tp_dict, "__module__", value);
+}
+
 static PyObject *
 type_dict(PyTypeObject *type, void *context)
 {
@@ -93,7 +110,7 @@
 
 PyGetSetDef type_getsets[] = {
 	{"__name__", (getter)type_name, NULL, NULL},
-	{"__module__", (getter)type_module, NULL, NULL},
+	{"__module__", (getter)type_module, (setter)type_set_module, NULL},
 	{"__dict__",  (getter)type_dict,  NULL, NULL},
 	{"__defined__",  (getter)type_defined,  NULL, NULL},
 	{"__dynamic__", (getter)type_dynamic, NULL, NULL},
@@ -656,14 +673,10 @@
 		return NULL;
 	}
 	dict = *dictptr;
-	if (dict == NULL) {
-		Py_INCREF(Py_None);
-		return Py_None;
-	}
-	else {
-		Py_INCREF(dict);
-		return dict;
-	}
+	if (dict == NULL)
+		*dictptr = dict = PyDict_New();
+	Py_XINCREF(dict);
+	return dict;
 }
 
 PyGetSetDef subtype_getsets[] = {
@@ -1283,6 +1296,31 @@
 	{0}
 };
 
+static PyObject *
+object_reduce(PyObject *self, PyObject *args)
+{
+	/* Call copy_reg._reduce(self) */
+	static PyObject *copy_reg_str;
+	PyObject *copy_reg, *res;
+
+	if (!copy_reg_str) {
+		copy_reg_str = PyString_InternFromString("copy_reg");
+		if (copy_reg_str == NULL)
+			return NULL;
+	}
+	copy_reg = PyImport_Import(copy_reg_str);
+	if (!copy_reg)
+		return NULL;
+	res = PyEval_CallMethod(copy_reg, "_reduce", "(O)", self);
+	Py_DECREF(copy_reg);
+	return res;
+}
+
+static PyMethodDef object_methods[] = {
+	{"__reduce__", object_reduce, METH_NOARGS, "helper for pickle"},
+	{0}
+};
+
 PyTypeObject PyBaseObject_Type = {
 	PyObject_HEAD_INIT(&PyType_Type)
  	0,					/* ob_size */
@@ -1312,7 +1350,7 @@
 	0,					/* tp_weaklistoffset */
 	0,					/* tp_iter */
 	0,					/* tp_iternext */
-	0,					/* tp_methods */
+	object_methods,				/* tp_methods */
 	0,					/* tp_members */
 	object_getsets,				/* tp_getset */
 	0,					/* tp_base */
@@ -3009,7 +3047,8 @@
 		res = PyObject_GenericGetAttr(self, name);
 	else
 		res = PyObject_CallFunction(getattribute, "OO", self, name);
-	if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
+	if (getattr != NULL &&
+	    res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
 		PyErr_Clear();
 		res = PyObject_CallFunction(getattr, "OO", self, name);
 	}