diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index ed8a5cdfaf1cde..fcf4416f331092 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -3,6 +3,8 @@ __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR", "UTC") +__name__ = "datetime" + import time as _time import math as _math @@ -14,10 +16,10 @@ def _cmp(x, y): def _get_class_module(self): module_name = self.__class__.__module__ - if module_name == '_pydatetime': - return 'datetime' + if module_name == 'datetime': + return 'datetime.' else: - return module_name + return '' MINYEAR = 1 MAXYEAR = 9999 @@ -767,9 +769,9 @@ def __repr__(self): args.append("microseconds=%d" % self._microseconds) if not args: args.append('0') - return "%s.%s(%s)" % (_get_class_module(self), - self.__class__.__qualname__, - ', '.join(args)) + return "%s%s(%s)" % (_get_class_module(self), + self.__class__.__qualname__, + ', '.join(args)) def __str__(self): mm, ss = divmod(self._seconds, 60) @@ -1082,11 +1084,11 @@ def __repr__(self): >>> repr(d) 'datetime.date(2010, 1, 1)' """ - return "%s.%s(%d, %d, %d)" % (_get_class_module(self), - self.__class__.__qualname__, - self._year, - self._month, - self._day) + return "%s%s(%d, %d, %d)" % (_get_class_module(self), + self.__class__.__qualname__, + self._year, + self._month, + self._day) # XXX These shouldn't depend on time.localtime(), because that # clips the usable dates to [1970 .. 2038). At least ctime() is # easily done without using strftime() -- that's better too because @@ -1586,7 +1588,7 @@ def __repr__(self): s = ", %d" % self._second else: s = "" - s= "%s.%s(%d, %d%s)" % (_get_class_module(self), + s = "%s%s(%d, %d%s)" % (_get_class_module(self), self.__class__.__qualname__, self._hour, self._minute, s) if self._tzinfo is not None: @@ -2162,9 +2164,9 @@ def __repr__(self): del L[-1] if L[-1] == 0: del L[-1] - s = "%s.%s(%s)" % (_get_class_module(self), - self.__class__.__qualname__, - ", ".join(map(str, L))) + s = "%s%s(%s)" % (_get_class_module(self), + self.__class__.__qualname__, + ", ".join(map(str, L))) if self._tzinfo is not None: assert s[-1:] == ")" s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")" @@ -2461,12 +2463,12 @@ def __repr__(self): if self is self.utc: return 'datetime.timezone.utc' if self._name is None: - return "%s.%s(%r)" % (_get_class_module(self), - self.__class__.__qualname__, - self._offset) - return "%s.%s(%r, %r)" % (_get_class_module(self), - self.__class__.__qualname__, - self._offset, self._name) + return "%s%s(%r)" % (_get_class_module(self), + self.__class__.__qualname__, + self._offset) + return "%s%s(%r, %r)" % (_get_class_module(self), + self.__class__.__qualname__, + self._offset, self._name) def __str__(self): return self.tzname(None) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index b670973a71c748..ceeac9435dcb85 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -504,6 +504,9 @@ def test_harmful_mixed_comparison(self): ############################################################################# # timedelta tests +class SubclassTimeDelta(timedelta): + sub_var = 1 + class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase): theclass = timedelta @@ -788,6 +791,15 @@ def test_repr(self): self.assertEqual(repr(self.theclass(seconds=1, microseconds=100)), "%s(seconds=1, microseconds=100)" % name) + def test_repr_subclass(self): + """Subclasses should have bare names in the repr (gh-107773).""" + td = SubclassTimeDelta(days=1) + self.assertEqual(repr(td), "SubclassTimeDelta(days=1)") + td = SubclassTimeDelta(seconds=30) + self.assertEqual(repr(td), "SubclassTimeDelta(seconds=30)") + td = SubclassTimeDelta(weeks=2) + self.assertEqual(repr(td), "SubclassTimeDelta(days=14)") + def test_roundtrip(self): for td in (timedelta(days=999999999, hours=23, minutes=59, seconds=59, microseconds=999999), @@ -1224,6 +1236,15 @@ def test_roundtrip(self): dt2 = self.theclass(dt.year, dt.month, dt.day) self.assertEqual(dt, dt2) + def test_repr_subclass(self): + """Subclasses should have bare names in the repr (gh-107773).""" + td = SubclassDate(1, 2, 3) + self.assertEqual(repr(td), "SubclassDate(1, 2, 3)") + td = SubclassDate(2014, 1, 1) + self.assertEqual(repr(td), "SubclassDate(2014, 1, 1)") + td = SubclassDate(2010, 10, day=10) + self.assertEqual(repr(td), "SubclassDate(2010, 10, 10)") + def test_ordinal_conversions(self): # Check some fixed values. for y, m, d, n in [(1, 1, 1, 1), # calendar origin @@ -3587,6 +3608,15 @@ class DateTimeSubclass(self.theclass): self.assertEqual(dt, dt_rt) self.assertIsInstance(dt_rt, DateTimeSubclass) + def test_repr_subclass(self): + """Subclasses should have bare names in the repr (gh-107773).""" + td = SubclassDatetime(2014, 1, 1) + self.assertEqual(repr(td), "SubclassDatetime(2014, 1, 1, 0, 0)") + td = SubclassDatetime(2010, 10, day=10) + self.assertEqual(repr(td), "SubclassDatetime(2010, 10, 10, 0, 0)") + td = SubclassDatetime(2010, 10, 2, second=3) + self.assertEqual(repr(td), "SubclassDatetime(2010, 10, 2, 0, 0, 3)") + class TestSubclassDateTime(TestDateTime): theclass = SubclassDatetime @@ -3897,6 +3927,19 @@ def test_repr(self): self.assertEqual(repr(self.theclass(23, 15, 0, 0)), "%s(23, 15)" % name) + def test_repr_subclass(self): + """Subclasses should have bare names in the repr (gh-107773).""" + td = SubclassTime(hour=1) + self.assertEqual(repr(td), "SubclassTime(1, 0)") + td = SubclassTime(hour=2, minute=30) + self.assertEqual(repr(td), "SubclassTime(2, 30)") + td = SubclassTime(hour=2, minute=30, second=11) + self.assertEqual(repr(td), "SubclassTime(2, 30, 11)") + td = SubclassTime(minute=30, second=11, fold=0) + self.assertEqual(repr(td), "SubclassTime(0, 30, 11)") + td = SubclassTime(minute=30, second=11, fold=1) + self.assertEqual(repr(td), "SubclassTime(0, 30, 11, fold=1)") + def test_resolution_info(self): self.assertIsInstance(self.theclass.min, self.theclass) self.assertIsInstance(self.theclass.max, self.theclass) diff --git a/Misc/NEWS.d/next/Library/2025-02-19-20-29-33.gh-issue-107773.7y6Ug2.rst b/Misc/NEWS.d/next/Library/2025-02-19-20-29-33.gh-issue-107773.7y6Ug2.rst new file mode 100644 index 00000000000000..147010fe108c50 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-02-19-20-29-33.gh-issue-107773.7y6Ug2.rst @@ -0,0 +1,2 @@ +Make :mod:`datetime` subclass :meth:`~object.__repr__` consistent both +implementations. Patch by Semyon Moroz.