bpo-32403: Faster date and datetime constructors (#4993)

* Add tests for date subclass alternate constructors

* Switch over alternate date constructors to fast path

* Switch datetime constructors to fastpath, fix bpo-32404

* Add fast path for datetime in date subclass constructor

* Set fold in constructor in datetime.combine

* Add news entries.
This commit is contained in:
Paul Ganssle 2018-01-16 13:06:31 -05:00 committed by Alexander Belopolsky
parent 6b5a27975a
commit 9f1b7b93f5
4 changed files with 171 additions and 45 deletions

View file

@ -1552,6 +1552,50 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase):
self.assertEqual(dt1.toordinal(), dt2.toordinal())
self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
def test_subclass_alternate_constructors(self):
# Test that alternate constructors call the constructor
class DateSubclass(self.theclass):
def __new__(cls, *args, **kwargs):
result = self.theclass.__new__(cls, *args, **kwargs)
result.extra = 7
return result
args = (2003, 4, 14)
d_ord = 731319 # Equivalent ordinal date
d_isoformat = '2003-04-14' # Equivalent isoformat()
base_d = DateSubclass(*args)
self.assertIsInstance(base_d, DateSubclass)
self.assertEqual(base_d.extra, 7)
# Timestamp depends on time zone, so we'll calculate the equivalent here
ts = datetime.combine(base_d, time(0)).timestamp()
test_cases = [
('fromordinal', (d_ord,)),
('fromtimestamp', (ts,)),
('fromisoformat', (d_isoformat,)),
]
for constr_name, constr_args in test_cases:
for base_obj in (DateSubclass, base_d):
# Test both the classmethod and method
with self.subTest(base_obj_type=type(base_obj),
constr_name=constr_name):
constr = getattr(base_obj, constr_name)
dt = constr(*constr_args)
# Test that it creates the right subclass
self.assertIsInstance(dt, DateSubclass)
# Test that it's equal to the base object
self.assertEqual(dt, base_d)
# Test that it called the constructor
self.assertEqual(dt.extra, 7)
def test_pickling_subclass_date(self):
args = 6, 7, 23
@ -2420,6 +2464,54 @@ class TestDateTime(TestDate):
self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
dt1.second - 7)
def test_subclass_alternate_constructors_datetime(self):
# Test that alternate constructors call the constructor
class DateTimeSubclass(self.theclass):
def __new__(cls, *args, **kwargs):
result = self.theclass.__new__(cls, *args, **kwargs)
result.extra = 7
return result
args = (2003, 4, 14, 12, 30, 15, 123456)
d_isoformat = '2003-04-14T12:30:15.123456' # Equivalent isoformat()
utc_ts = 1050323415.123456 # UTC timestamp
base_d = DateTimeSubclass(*args)
self.assertIsInstance(base_d, DateTimeSubclass)
self.assertEqual(base_d.extra, 7)
# Timestamp depends on time zone, so we'll calculate the equivalent here
ts = base_d.timestamp()
test_cases = [
('fromtimestamp', (ts,)),
# See https://bugs.python.org/issue32417
# ('fromtimestamp', (ts, timezone.utc)),
('utcfromtimestamp', (utc_ts,)),
('fromisoformat', (d_isoformat,)),
('strptime', (d_isoformat, '%Y-%m-%dT%H:%M:%S.%f')),
('combine', (date(*args[0:3]), time(*args[3:]))),
]
for constr_name, constr_args in test_cases:
for base_obj in (DateTimeSubclass, base_d):
# Test both the classmethod and method
with self.subTest(base_obj_type=type(base_obj),
constr_name=constr_name):
constr = getattr(base_obj, constr_name)
dt = constr(*constr_args)
# Test that it creates the right subclass
self.assertIsInstance(dt, DateTimeSubclass)
# Test that it's equal to the base object
self.assertEqual(dt, base_d.replace(tzinfo=None))
# Test that it called the constructor
self.assertEqual(dt.extra, 7)
def test_fromisoformat_datetime(self):
# Test that isoformat() is reversible
base_dates = [