Skip to content

Commit cc5a65c

Browse files
musically-utvstinner
authored andcommitted
bpo-30302 Make timedelta.__repr__ more informative. (#1493)
1 parent 8300809 commit cc5a65c

File tree

6 files changed

+74
-33
lines changed

6 files changed

+74
-33
lines changed

Doc/library/datetime.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,11 +287,12 @@ Supported operations:
287287
| | ``[D day[s], ][H]H:MM:SS[.UUUUUU]``, where D |
288288
| | is negative for negative ``t``. (5) |
289289
+--------------------------------+-----------------------------------------------+
290-
| ``repr(t)`` | Returns a string in the form |
291-
| | ``datetime.timedelta(D[, S[, U]])``, where D |
292-
| | is negative for negative ``t``. (5) |
290+
| ``repr(t)`` | Returns a string representation of the |
291+
| | :class:`timedelta` object as a constructor |
292+
| | call with canonical attribute values. |
293293
+--------------------------------+-----------------------------------------------+
294294

295+
295296
Notes:
296297

297298
(1)

Lib/datetime.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -454,20 +454,18 @@ def __new__(cls, days=0, seconds=0, microseconds=0,
454454
return self
455455

456456
def __repr__(self):
457-
if self._microseconds:
458-
return "%s.%s(%d, %d, %d)" % (self.__class__.__module__,
459-
self.__class__.__qualname__,
460-
self._days,
461-
self._seconds,
462-
self._microseconds)
457+
args = []
458+
if self._days:
459+
args.append("days=%d" % self._days)
463460
if self._seconds:
464-
return "%s.%s(%d, %d)" % (self.__class__.__module__,
465-
self.__class__.__qualname__,
466-
self._days,
467-
self._seconds)
468-
return "%s.%s(%d)" % (self.__class__.__module__,
461+
args.append("seconds=%d" % self._seconds)
462+
if self._microseconds:
463+
args.append("microseconds=%d" % self._microseconds)
464+
if not args:
465+
args.append('0')
466+
return "%s.%s(%s)" % (self.__class__.__module__,
469467
self.__class__.__qualname__,
470-
self._days)
468+
', '.join(args))
471469

472470
def __str__(self):
473471
mm, ss = divmod(self._seconds, 60)

Lib/test/datetimetester.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -658,11 +658,21 @@ def test_str(self):
658658
def test_repr(self):
659659
name = 'datetime.' + self.theclass.__name__
660660
self.assertEqual(repr(self.theclass(1)),
661-
"%s(1)" % name)
661+
"%s(days=1)" % name)
662662
self.assertEqual(repr(self.theclass(10, 2)),
663-
"%s(10, 2)" % name)
663+
"%s(days=10, seconds=2)" % name)
664664
self.assertEqual(repr(self.theclass(-10, 2, 400000)),
665-
"%s(-10, 2, 400000)" % name)
665+
"%s(days=-10, seconds=2, microseconds=400000)" % name)
666+
self.assertEqual(repr(self.theclass(seconds=60)),
667+
"%s(seconds=60)" % name)
668+
self.assertEqual(repr(self.theclass()),
669+
"%s(0)" % name)
670+
self.assertEqual(repr(self.theclass(microseconds=100)),
671+
"%s(microseconds=100)" % name)
672+
self.assertEqual(repr(self.theclass(days=1, microseconds=100)),
673+
"%s(days=1, microseconds=100)" % name)
674+
self.assertEqual(repr(self.theclass(seconds=1, microseconds=100)),
675+
"%s(seconds=1, microseconds=100)" % name)
666676

667677
def test_roundtrip(self):
668678
for td in (timedelta(days=999999999, hours=23, minutes=59,

Lib/test/test_datetime.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ def tearDownClass(cls_):
5050
cls.tearDownClass = tearDownClass
5151
all_test_classes.extend(test_classes)
5252

53+
all_test_classes.extend(test_classes)
54+
5355
def test_main():
5456
run_unittest(*all_test_classes)
5557

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Use keywords in the ``repr`` of ``datetime.timedelta``.

Modules/_datetimemodule.c

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2284,21 +2284,50 @@ delta_bool(PyDateTime_Delta *self)
22842284
static PyObject *
22852285
delta_repr(PyDateTime_Delta *self)
22862286
{
2287-
if (GET_TD_MICROSECONDS(self) != 0)
2288-
return PyUnicode_FromFormat("%s(%d, %d, %d)",
2289-
Py_TYPE(self)->tp_name,
2290-
GET_TD_DAYS(self),
2291-
GET_TD_SECONDS(self),
2292-
GET_TD_MICROSECONDS(self));
2293-
if (GET_TD_SECONDS(self) != 0)
2294-
return PyUnicode_FromFormat("%s(%d, %d)",
2295-
Py_TYPE(self)->tp_name,
2296-
GET_TD_DAYS(self),
2297-
GET_TD_SECONDS(self));
2298-
2299-
return PyUnicode_FromFormat("%s(%d)",
2300-
Py_TYPE(self)->tp_name,
2301-
GET_TD_DAYS(self));
2287+
PyObject *args = PyUnicode_FromString("");
2288+
2289+
if (args == NULL) {
2290+
return NULL;
2291+
}
2292+
2293+
const char *sep = "";
2294+
2295+
if (GET_TD_DAYS(self) != 0) {
2296+
Py_SETREF(args, PyUnicode_FromFormat("days=%d", GET_TD_DAYS(self)));
2297+
if (args == NULL) {
2298+
return NULL;
2299+
}
2300+
sep = ", ";
2301+
}
2302+
2303+
if (GET_TD_SECONDS(self) != 0) {
2304+
Py_SETREF(args, PyUnicode_FromFormat("%U%sseconds=%d", args, sep,
2305+
GET_TD_SECONDS(self)));
2306+
if (args == NULL) {
2307+
return NULL;
2308+
}
2309+
sep = ", ";
2310+
}
2311+
2312+
if (GET_TD_MICROSECONDS(self) != 0) {
2313+
Py_SETREF(args, PyUnicode_FromFormat("%U%smicroseconds=%d", args, sep,
2314+
GET_TD_MICROSECONDS(self)));
2315+
if (args == NULL) {
2316+
return NULL;
2317+
}
2318+
}
2319+
2320+
if (PyUnicode_GET_LENGTH(args) == 0) {
2321+
Py_SETREF(args, PyUnicode_FromString("0"));
2322+
if (args == NULL) {
2323+
return NULL;
2324+
}
2325+
}
2326+
2327+
PyObject *repr = PyUnicode_FromFormat("%s(%S)", Py_TYPE(self)->tp_name,
2328+
args);
2329+
Py_DECREF(args);
2330+
return repr;
23022331
}
23032332

23042333
static PyObject *

0 commit comments

Comments
 (0)