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 = [

`