diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst index dd6395d7b9a..892f05ddf36 100644 --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -924,6 +924,15 @@ create a new named tuple type from the :attr:`_fields` attribute: >>> Point3D = namedtuple('Point3D', Point._fields + ('z',)) +Docstrings can be customized by making direct assignments to the ``__doc__`` +fields: + + >>> Book = namedtuple('Book', ['id', 'title', 'authors']) + >>> Book.__doc__ = 'Hardcover book in active collection' + >>> Book.id = '13-digit ISBN' + >>> Book.title = 'Title of first printing' + >>> Book.author = 'List of authors sorted by last name' + Default values can be implemented by using :meth:`_replace` to customize a prototype instance: diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst index 48d871a3f14..a29ff106360 100644 --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -234,6 +234,10 @@ Some smaller changes made to the core Python language are: * New Kazakh :ref:`codec ` ``kz1048``. (Contributed by Serhiy Storchaka in :issue:`22682`.) +* Property docstrings are now writable. This is especially useful for + :func:`collections.namedtuple` docstrings. + (Contributed by Berker Peksag in :issue:`24064`.) + * New Tajik :ref:`codec ` ``koi8_t``. (Contributed by Serhiy Storchaka in :issue:`22681`.) @@ -283,6 +287,18 @@ code the full chained traceback, just like the interactive interpreter. (Contributed by Claudiu Popa in :issue:`17442`.) +collections +----------- + +* You can now update docstrings produced by :func:`collections.namedtuple`:: + + Point = namedtuple('Point', ['x', 'y']) + Point.__doc__ = 'ordered pair' + Point.x.__doc__ = 'abscissa' + Point.y.__doc__ = 'ordinate' + + (Contributed by Berker Peksag in :issue:`24064`.) + compileall ---------- diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index ad94fddfbeb..2bb55385e31 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -199,6 +199,14 @@ class TestNamedTuple(unittest.TestCase): Point = namedtuple('Point', 'x y') self.assertEqual(Point.__doc__, 'Point(x, y)') + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + def test_doc_writable(self): + Point = namedtuple('Point', 'x y') + self.assertEqual(Point.x.__doc__, 'Alias for field number 0') + Point.x.__doc__ = 'docstring for Point.x' + self.assertEqual(Point.x.__doc__, 'docstring for Point.x') + def test_name_fixer(self): for spec, renamed in [ [('efg', 'g%hi'), ('efg', '_1')], # field with non-alpha char diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 9f3d34d1e49..80a526d78cc 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -2022,7 +2022,7 @@ order (MRO) for bases """ self.assertIs(raw.fset, C.__dict__['setx']) self.assertIs(raw.fdel, C.__dict__['delx']) - for attr in "__doc__", "fget", "fset", "fdel": + for attr in "fget", "fset", "fdel": try: setattr(raw, attr, 42) except AttributeError as msg: @@ -2033,6 +2033,9 @@ order (MRO) for bases """ self.fail("expected AttributeError from trying to set readonly %r " "attr on a property" % attr) + raw.__doc__ = 42 + self.assertEqual(raw.__doc__, 42) + class D(object): __getitem__ = property(lambda s: 1/0) diff --git a/Lib/test/test_property.py b/Lib/test/test_property.py index e72eb55647b..5addd363ed4 100644 --- a/Lib/test/test_property.py +++ b/Lib/test/test_property.py @@ -76,6 +76,13 @@ class PropertyNewGetter(object): """new docstring""" return 8 +class PropertyWritableDoc(object): + + @property + def spam(self): + """Eggs""" + return "eggs" + class PropertyTests(unittest.TestCase): def test_property_decorator_baseclass(self): # see #1620 @@ -150,6 +157,21 @@ class PropertyTests(unittest.TestCase): foo = property(foo) C.foo.__isabstractmethod__ + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + def test_property_builtin_doc_writable(self): + p = property(doc='basic') + self.assertEqual(p.__doc__, 'basic') + p.__doc__ = 'extended' + self.assertEqual(p.__doc__, 'extended') + + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + def test_property_decorator_doc_writable(self): + sub = PropertyWritableDoc() + self.assertEqual(sub.__class__.spam.__doc__, 'Eggs') + sub.__class__.spam.__doc__ = 'Spam' + self.assertEqual(sub.__class__.spam.__doc__, 'Spam') # Issue 5890: subclasses of property do not preserve method __doc__ strings class PropertySub(property): diff --git a/Misc/NEWS b/Misc/NEWS index 58a1daa8e34..20cc9d72b1a 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -45,6 +45,9 @@ Library - Issue #22486: Added the math.gcd() function. The fractions.gcd() function now is deprecated. Based on patch by Mark Dickinson. +- Issue #24064: Property() docstrings are now writeable. + (Patch by Berker Peksag.) + - Issue #22681: Added support for the koi8_t encoding. - Issue #22682: Added support for the kz1048 encoding. diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 822fb4182b2..906f76d505b 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -1313,7 +1313,7 @@ static PyMemberDef property_members[] = { {"fget", T_OBJECT, offsetof(propertyobject, prop_get), READONLY}, {"fset", T_OBJECT, offsetof(propertyobject, prop_set), READONLY}, {"fdel", T_OBJECT, offsetof(propertyobject, prop_del), READONLY}, - {"__doc__", T_OBJECT, offsetof(propertyobject, prop_doc), READONLY}, + {"__doc__", T_OBJECT, offsetof(propertyobject, prop_doc), 0}, {0} };