From cc5a65cd9025280ea67ef4bbc2a8bfe31ced6c30 Mon Sep 17 00:00:00 2001 From: Utkarsh Upadhyay Date: Tue, 25 Jul 2017 23:51:33 +0200 Subject: [PATCH] bpo-30302 Make timedelta.__repr__ more informative. (#1493) --- Doc/library/datetime.rst | 7 ++- Lib/datetime.py | 22 ++++--- Lib/test/datetimetester.py | 16 +++++- Lib/test/test_datetime.py | 2 + .../2017-06-30-23-05-47.bpo-30302.itwK_k.rst | 1 + Modules/_datetimemodule.c | 57 ++++++++++++++----- 6 files changed, 73 insertions(+), 32 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2017-06-30-23-05-47.bpo-30302.itwK_k.rst diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index e757e5d685b..625e1590a87 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -287,11 +287,12 @@ Supported operations: | | ``[D day[s], ][H]H:MM:SS[.UUUUUU]``, where D | | | is negative for negative ``t``. (5) | +--------------------------------+-----------------------------------------------+ -| ``repr(t)`` | Returns a string in the form | -| | ``datetime.timedelta(D[, S[, U]])``, where D | -| | is negative for negative ``t``. (5) | +| ``repr(t)`` | Returns a string representation of the | +| | :class:`timedelta` object as a constructor | +| | call with canonical attribute values. | +--------------------------------+-----------------------------------------------+ + Notes: (1) diff --git a/Lib/datetime.py b/Lib/datetime.py index b95536fb7af..76a6f957e08 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -454,20 +454,18 @@ class timedelta: return self def __repr__(self): - if self._microseconds: - return "%s.%s(%d, %d, %d)" % (self.__class__.__module__, - self.__class__.__qualname__, - self._days, - self._seconds, - self._microseconds) + args = [] + if self._days: + args.append("days=%d" % self._days) if self._seconds: - return "%s.%s(%d, %d)" % (self.__class__.__module__, - self.__class__.__qualname__, - self._days, - self._seconds) - return "%s.%s(%d)" % (self.__class__.__module__, + args.append("seconds=%d" % self._seconds) + if self._microseconds: + args.append("microseconds=%d" % self._microseconds) + if not args: + args.append('0') + return "%s.%s(%s)" % (self.__class__.__module__, self.__class__.__qualname__, - self._days) + ', '.join(args)) def __str__(self): mm, ss = divmod(self._seconds, 60) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index b25e6c17136..22008884b54 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -658,11 +658,21 @@ class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase): def test_repr(self): name = 'datetime.' + self.theclass.__name__ self.assertEqual(repr(self.theclass(1)), - "%s(1)" % name) + "%s(days=1)" % name) self.assertEqual(repr(self.theclass(10, 2)), - "%s(10, 2)" % name) + "%s(days=10, seconds=2)" % name) self.assertEqual(repr(self.theclass(-10, 2, 400000)), - "%s(-10, 2, 400000)" % name) + "%s(days=-10, seconds=2, microseconds=400000)" % name) + self.assertEqual(repr(self.theclass(seconds=60)), + "%s(seconds=60)" % name) + self.assertEqual(repr(self.theclass()), + "%s(0)" % name) + self.assertEqual(repr(self.theclass(microseconds=100)), + "%s(microseconds=100)" % name) + self.assertEqual(repr(self.theclass(days=1, microseconds=100)), + "%s(days=1, microseconds=100)" % name) + self.assertEqual(repr(self.theclass(seconds=1, microseconds=100)), + "%s(seconds=1, microseconds=100)" % name) def test_roundtrip(self): for td in (timedelta(days=999999999, hours=23, minutes=59, diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py index d659f369d54..bb22871f2b1 100644 --- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -50,6 +50,8 @@ for module, suffix in zip(test_modules, test_suffixes): cls.tearDownClass = tearDownClass all_test_classes.extend(test_classes) + all_test_classes.extend(test_classes) + def test_main(): run_unittest(*all_test_classes) diff --git a/Misc/NEWS.d/next/Library/2017-06-30-23-05-47.bpo-30302.itwK_k.rst b/Misc/NEWS.d/next/Library/2017-06-30-23-05-47.bpo-30302.itwK_k.rst new file mode 100644 index 00000000000..927250d04c4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-06-30-23-05-47.bpo-30302.itwK_k.rst @@ -0,0 +1 @@ +Use keywords in the ``repr`` of ``datetime.timedelta``. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 3439040d2d9..28805d18da8 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -2284,21 +2284,50 @@ delta_bool(PyDateTime_Delta *self) static PyObject * delta_repr(PyDateTime_Delta *self) { - if (GET_TD_MICROSECONDS(self) != 0) - return PyUnicode_FromFormat("%s(%d, %d, %d)", - Py_TYPE(self)->tp_name, - GET_TD_DAYS(self), - GET_TD_SECONDS(self), - GET_TD_MICROSECONDS(self)); - if (GET_TD_SECONDS(self) != 0) - return PyUnicode_FromFormat("%s(%d, %d)", - Py_TYPE(self)->tp_name, - GET_TD_DAYS(self), - GET_TD_SECONDS(self)); + PyObject *args = PyUnicode_FromString(""); - return PyUnicode_FromFormat("%s(%d)", - Py_TYPE(self)->tp_name, - GET_TD_DAYS(self)); + if (args == NULL) { + return NULL; + } + + const char *sep = ""; + + if (GET_TD_DAYS(self) != 0) { + Py_SETREF(args, PyUnicode_FromFormat("days=%d", GET_TD_DAYS(self))); + if (args == NULL) { + return NULL; + } + sep = ", "; + } + + if (GET_TD_SECONDS(self) != 0) { + Py_SETREF(args, PyUnicode_FromFormat("%U%sseconds=%d", args, sep, + GET_TD_SECONDS(self))); + if (args == NULL) { + return NULL; + } + sep = ", "; + } + + if (GET_TD_MICROSECONDS(self) != 0) { + Py_SETREF(args, PyUnicode_FromFormat("%U%smicroseconds=%d", args, sep, + GET_TD_MICROSECONDS(self))); + if (args == NULL) { + return NULL; + } + } + + if (PyUnicode_GET_LENGTH(args) == 0) { + Py_SETREF(args, PyUnicode_FromString("0")); + if (args == NULL) { + return NULL; + } + } + + PyObject *repr = PyUnicode_FromFormat("%s(%S)", Py_TYPE(self)->tp_name, + args); + Py_DECREF(args); + return repr; } static PyObject *