bpo-32417: Make timedelta arithmetic respect subclasses (#10902) · python/cpython@89427cd (original) (raw)
`@@ -820,6 +820,44 @@ def as_hours(self):
`
820
820
`self.assertEqual(str(t3), str(t4))
`
821
821
`self.assertEqual(t4.as_hours(), -1)
`
822
822
``
``
823
`+
def test_subclass_date(self):
`
``
824
`+
class DateSubclass(date):
`
``
825
`+
pass
`
``
826
+
``
827
`+
d1 = DateSubclass(2018, 1, 5)
`
``
828
`+
td = timedelta(days=1)
`
``
829
+
``
830
`+
tests = [
`
``
831
`+
('add', lambda d, t: d + t, DateSubclass(2018, 1, 6)),
`
``
832
`+
('radd', lambda d, t: t + d, DateSubclass(2018, 1, 6)),
`
``
833
`+
('sub', lambda d, t: d - t, DateSubclass(2018, 1, 4)),
`
``
834
`+
]
`
``
835
+
``
836
`+
for name, func, expected in tests:
`
``
837
`+
with self.subTest(name):
`
``
838
`+
act = func(d1, td)
`
``
839
`+
self.assertEqual(act, expected)
`
``
840
`+
self.assertIsInstance(act, DateSubclass)
`
``
841
+
``
842
`+
def test_subclass_datetime(self):
`
``
843
`+
class DateTimeSubclass(datetime):
`
``
844
`+
pass
`
``
845
+
``
846
`+
d1 = DateTimeSubclass(2018, 1, 5, 12, 30)
`
``
847
`+
td = timedelta(days=1, minutes=30)
`
``
848
+
``
849
`+
tests = [
`
``
850
`+
('add', lambda d, t: d + t, DateTimeSubclass(2018, 1, 6, 13)),
`
``
851
`+
('radd', lambda d, t: t + d, DateTimeSubclass(2018, 1, 6, 13)),
`
``
852
`+
('sub', lambda d, t: d - t, DateTimeSubclass(2018, 1, 4, 12)),
`
``
853
`+
]
`
``
854
+
``
855
`+
for name, func, expected in tests:
`
``
856
`+
with self.subTest(name):
`
``
857
`+
act = func(d1, td)
`
``
858
`+
self.assertEqual(act, expected)
`
``
859
`+
self.assertIsInstance(act, DateTimeSubclass)
`
``
860
+
823
861
`def test_division(self):
`
824
862
`t = timedelta(hours=1, minutes=24, seconds=19)
`
825
863
`second = timedelta(seconds=1)
`
`@@ -2604,33 +2642,58 @@ def new(cls, *args, **kwargs):
`
2604
2642
`ts = base_d.timestamp()
`
2605
2643
``
2606
2644
`test_cases = [
`
2607
``
`-
('fromtimestamp', (ts,)),
`
``
2645
`+
('fromtimestamp', (ts,), base_d),
`
2608
2646
`# See https://bugs.python.org/issue32417
`
2609
``
`-
('fromtimestamp', (ts, timezone.utc)),
`
2610
``
`-
('utcfromtimestamp', (utc_ts,)),
`
2611
``
`-
('fromisoformat', (d_isoformat,)),
`
2612
``
`-
('strptime', (d_isoformat, '%Y-%m-%dT%H:%M:%S.%f')),
`
2613
``
`-
('combine', (date(*args[0:3]), time(*args[3:]))),
`
``
2647
`+
('fromtimestamp', (ts, timezone.utc),
`
``
2648
`+
base_d.astimezone(timezone.utc)),
`
``
2649
`+
('utcfromtimestamp', (utc_ts,), base_d),
`
``
2650
`+
('fromisoformat', (d_isoformat,), base_d),
`
``
2651
`+
('strptime', (d_isoformat, '%Y-%m-%dT%H:%M:%S.%f'), base_d),
`
``
2652
`+
('combine', (date(*args[0:3]), time(*args[3:])), base_d),
`
2614
2653
` ]
`
2615
2654
``
2616
``
`-
for constr_name, constr_args in test_cases:
`
``
2655
`+
for constr_name, constr_args, expected in test_cases:
`
2617
2656
`for base_obj in (DateTimeSubclass, base_d):
`
2618
2657
`# Test both the classmethod and method
`
2619
2658
`with self.subTest(base_obj_type=type(base_obj),
`
2620
2659
`constr_name=constr_name):
`
2621
``
`-
constr = getattr(base_obj, constr_name)
`
``
2660
`+
constructor = getattr(base_obj, constr_name)
`
2622
2661
``
2623
``
`-
dt = constr(*constr_args)
`
``
2662
`+
dt = constructor(*constr_args)
`
2624
2663
``
2625
2664
`# Test that it creates the right subclass
`
2626
2665
`self.assertIsInstance(dt, DateTimeSubclass)
`
2627
2666
``
2628
2667
`# Test that it's equal to the base object
`
2629
``
`-
self.assertEqual(dt, base_d.replace(tzinfo=None))
`
``
2668
`+
self.assertEqual(dt, expected)
`
2630
2669
``
2631
2670
`# Test that it called the constructor
`
2632
2671
`self.assertEqual(dt.extra, 7)
`
2633
2672
``
``
2673
`+
def test_subclass_now(self):
`
``
2674
`+
Test that alternate constructors call the constructor
`
``
2675
`+
class DateTimeSubclass(self.theclass):
`
``
2676
`+
def new(cls, *args, **kwargs):
`
``
2677
`+
result = self.theclass.new(cls, *args, **kwargs)
`
``
2678
`+
result.extra = 7
`
``
2679
+
``
2680
`+
return result
`
``
2681
+
``
2682
`+
test_cases = [
`
``
2683
`+
('now', 'now', {}),
`
``
2684
`+
('utcnow', 'utcnow', {}),
`
``
2685
`+
('now_utc', 'now', {'tz': timezone.utc}),
`
``
2686
`+
('now_fixed', 'now', {'tz': timezone(timedelta(hours=-5), "EST")}),
`
``
2687
`+
]
`
``
2688
+
``
2689
`+
for name, meth_name, kwargs in test_cases:
`
``
2690
`+
with self.subTest(name):
`
``
2691
`+
constr = getattr(DateTimeSubclass, meth_name)
`
``
2692
`+
dt = constr(**kwargs)
`
``
2693
+
``
2694
`+
self.assertIsInstance(dt, DateTimeSubclass)
`
``
2695
`+
self.assertEqual(dt.extra, 7)
`
``
2696
+
2634
2697
`def test_fromisoformat_datetime(self):
`
2635
2698
`# Test that isoformat() is reversible
`
2636
2699
`base_dates = [
`