diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 4ed6853e19f..cd65c2c963f 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -88,6 +88,38 @@ def testset3op(a, b, c, d, res, stmt="a[b:c]=d", meth="__setslice__"): bm(b, c, d) verify(dict['a'] == res) +def class_docstrings(): + class Classic: + "A classic docstring." + verify(Classic.__doc__ == "A classic docstring.") + verify(Classic.__dict__['__doc__'] == "A classic docstring.") + + class Classic2: + pass + verify(Classic2.__doc__ is None) + + class NewStatic: + "Another docstring." + __dynamic__ = 0 + verify(NewStatic.__doc__ == "Another docstring.") + verify(NewStatic.__dict__['__doc__'] == "Another docstring.") + + class NewStatic2: + __dynamic__ = 0 + pass + verify(NewStatic2.__doc__ is None) + + class NewDynamic: + "Another docstring." + __dynamic__ = 1 + verify(NewDynamic.__doc__ == "Another docstring.") + verify(NewDynamic.__dict__['__doc__'] == "Another docstring.") + + class NewDynamic2: + __dynamic__ = 1 + pass + verify(NewDynamic2.__doc__ is None) + def lists(): if verbose: print "Testing list operations..." testbinop([1], [2], [1,2], "a+b", "__add__") @@ -2168,7 +2200,7 @@ def __rpow__(self, other, mod=None): return I(pow(int(other), int(self), mod)) else: return I(pow(int(other), int(self), int(mod))) - + vereq(`I(1) + I(2)`, "I(3)") vereq(`I(1) + 2`, "I(3)") vereq(`1 + I(2)`, "I(3)") @@ -2182,6 +2214,7 @@ def __eq__(self, other): def test_main(): + class_docstrings() lists() dicts() dict_constructor() diff --git a/Lib/test/test_doctest2.py b/Lib/test/test_doctest2.py index 9a0e57c9c4b..00b6cc44828 100644 --- a/Lib/test/test_doctest2.py +++ b/Lib/test/test_doctest2.py @@ -7,7 +7,6 @@ import test_support -# XXX The class docstring is skipped. class C(object): """Class C. @@ -29,7 +28,6 @@ def __str__(self): """ return "42" - # XXX The class docstring is skipped. class D(object): """A nested D class. @@ -96,9 +94,7 @@ def clsm(cls, val): def test_main(): import test_doctest2 - # XXX 2 class docstrings are skipped. - # EXPECTED = 19 - EXPECTED = 17 + EXPECTED = 19 f, t = test_support.run_doctest(test_doctest2) if t != EXPECTED: raise test_support.TestFailed("expected %d tests to run, not %d" % diff --git a/Objects/typeobject.c b/Objects/typeobject.c index c50b4465faa..e8f436c770f 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -900,6 +900,24 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) } } + /* Set tp_doc to a copy of dict['__doc__'], if the latter is there + and is a string (tp_doc is a char* -- can't copy a general object + into it). + XXX What if it's a Unicode string? Don't know -- this ignores it. + */ + { + PyObject *doc = PyDict_GetItemString(dict, "__doc__"); + if (doc != NULL && PyString_Check(doc)) { + const size_t n = (size_t)PyString_GET_SIZE(doc); + type->tp_doc = PyObject_MALLOC(n+1); + if (type->tp_doc == NULL) { + Py_DECREF(type); + return NULL; + } + memcpy(type->tp_doc, PyString_AS_STRING(doc), n+1); + } + } + /* Special-case __new__: if it's a plain function, make it a static function */ tmp = PyDict_GetItemString(dict, "__new__"); @@ -1162,6 +1180,11 @@ type_clear(PyTypeObject *type) CLEAR(type->tp_base); CLEAR(et->slots); + if (type->tp_doc != NULL) { + PyObject_FREE(type->tp_doc); + type->tp_doc = NULL; + } + #undef CLEAR return 0; @@ -1350,7 +1373,7 @@ object_set_class(PyObject *self, PyObject *value, void *closure) PyErr_Format(PyExc_TypeError, "__class__ assignment: " "'%s' object layout differs from '%s'", - new->tp_name, + new->tp_name, old->tp_name); return -1; }