@@ -2401,14 +2401,18 @@ def _(cls, arg):
2401
2401
self .assertEqual (A .t (0.0 ).arg , "base" )
2402
2402
2403
2403
def test_abstractmethod_register (self ):
2404
- class Abstract (abc .ABCMeta ):
2404
+ class Abstract (metaclass = abc .ABCMeta ):
2405
2405
2406
2406
@functools .singledispatchmethod
2407
2407
@abc .abstractmethod
2408
2408
def add (self , x , y ):
2409
2409
pass
2410
2410
2411
2411
self .assertTrue (Abstract .add .__isabstractmethod__ )
2412
+ self .assertTrue (Abstract .__dict__ ['add' ].__isabstractmethod__ )
2413
+
2414
+ with self .assertRaises (TypeError ):
2415
+ Abstract ()
2412
2416
2413
2417
def test_type_ann_register (self ):
2414
2418
class A :
@@ -2469,6 +2473,141 @@ def _(cls, arg: str):
2469
2473
self .assertEqual (A .t ('' ).arg , "str" )
2470
2474
self .assertEqual (A .t (0.0 ).arg , "base" )
2471
2475
2476
+ def test_method_wrapping_attributes (self ):
2477
+ class A :
2478
+ @functools .singledispatchmethod
2479
+ def func (self , arg : int ) -> str :
2480
+ """My function docstring"""
2481
+ return str (arg )
2482
+ @functools .singledispatchmethod
2483
+ @classmethod
2484
+ def cls_func (cls , arg : int ) -> str :
2485
+ """My function docstring"""
2486
+ return str (arg )
2487
+ @functools .singledispatchmethod
2488
+ @staticmethod
2489
+ def static_func (arg : int ) -> str :
2490
+ """My function docstring"""
2491
+ return str (arg )
2492
+
2493
+ for meth in (
2494
+ A .func ,
2495
+ A ().func ,
2496
+ A .cls_func ,
2497
+ A ().cls_func ,
2498
+ A .static_func ,
2499
+ A ().static_func
2500
+ ):
2501
+ with self .subTest (meth = meth ):
2502
+ self .assertEqual (meth .__doc__ , 'My function docstring' )
2503
+ self .assertEqual (meth .__annotations__ ['arg' ], int )
2504
+
2505
+ self .assertEqual (A .func .__name__ , 'func' )
2506
+ self .assertEqual (A ().func .__name__ , 'func' )
2507
+ self .assertEqual (A .cls_func .__name__ , 'cls_func' )
2508
+ self .assertEqual (A ().cls_func .__name__ , 'cls_func' )
2509
+ self .assertEqual (A .static_func .__name__ , 'static_func' )
2510
+ self .assertEqual (A ().static_func .__name__ , 'static_func' )
2511
+
2512
+ def test_double_wrapped_methods (self ):
2513
+ def classmethod_friendly_decorator (func ):
2514
+ wrapped = func .__func__
2515
+ @classmethod
2516
+ @functools .wraps (wrapped )
2517
+ def wrapper (* args , ** kwargs ):
2518
+ return wrapped (* args , ** kwargs )
2519
+ return wrapper
2520
+
2521
+ class WithoutSingleDispatch :
2522
+ @classmethod
2523
+ @contextlib .contextmanager
2524
+ def cls_context_manager (cls , arg : int ) -> str :
2525
+ try :
2526
+ yield str (arg )
2527
+ finally :
2528
+ return 'Done'
2529
+
2530
+ @classmethod_friendly_decorator
2531
+ @classmethod
2532
+ def decorated_classmethod (cls , arg : int ) -> str :
2533
+ return str (arg )
2534
+
2535
+ class WithSingleDispatch :
2536
+ @functools .singledispatchmethod
2537
+ @classmethod
2538
+ @contextlib .contextmanager
2539
+ def cls_context_manager (cls , arg : int ) -> str :
2540
+ """My function docstring"""
2541
+ try :
2542
+ yield str (arg )
2543
+ finally :
2544
+ return 'Done'
2545
+
2546
+ @functools .singledispatchmethod
2547
+ @classmethod_friendly_decorator
2548
+ @classmethod
2549
+ def decorated_classmethod (cls , arg : int ) -> str :
2550
+ """My function docstring"""
2551
+ return str (arg )
2552
+
2553
+ # These are sanity checks
2554
+ # to test the test itself is working as expected
2555
+ with WithoutSingleDispatch .cls_context_manager (5 ) as foo :
2556
+ without_single_dispatch_foo = foo
2557
+
2558
+ with WithSingleDispatch .cls_context_manager (5 ) as foo :
2559
+ single_dispatch_foo = foo
2560
+
2561
+ self .assertEqual (without_single_dispatch_foo , single_dispatch_foo )
2562
+ self .assertEqual (single_dispatch_foo , '5' )
2563
+
2564
+ self .assertEqual (
2565
+ WithoutSingleDispatch .decorated_classmethod (5 ),
2566
+ WithSingleDispatch .decorated_classmethod (5 )
2567
+ )
2568
+
2569
+ self .assertEqual (WithSingleDispatch .decorated_classmethod (5 ), '5' )
2570
+
2571
+ # Behavioural checks now follow
2572
+ for method_name in ('cls_context_manager' , 'decorated_classmethod' ):
2573
+ with self .subTest (method = method_name ):
2574
+ self .assertEqual (
2575
+ getattr (WithSingleDispatch , method_name ).__name__ ,
2576
+ getattr (WithoutSingleDispatch , method_name ).__name__
2577
+ )
2578
+
2579
+ self .assertEqual (
2580
+ getattr (WithSingleDispatch (), method_name ).__name__ ,
2581
+ getattr (WithoutSingleDispatch (), method_name ).__name__
2582
+ )
2583
+
2584
+ for meth in (
2585
+ WithSingleDispatch .cls_context_manager ,
2586
+ WithSingleDispatch ().cls_context_manager ,
2587
+ WithSingleDispatch .decorated_classmethod ,
2588
+ WithSingleDispatch ().decorated_classmethod
2589
+ ):
2590
+ with self .subTest (meth = meth ):
2591
+ self .assertEqual (meth .__doc__ , 'My function docstring' )
2592
+ self .assertEqual (meth .__annotations__ ['arg' ], int )
2593
+
2594
+ self .assertEqual (
2595
+ WithSingleDispatch .cls_context_manager .__name__ ,
2596
+ 'cls_context_manager'
2597
+ )
2598
+ self .assertEqual (
2599
+ WithSingleDispatch ().cls_context_manager .__name__ ,
2600
+ 'cls_context_manager'
2601
+ )
2602
+ self .assertEqual (
2603
+ WithSingleDispatch .decorated_classmethod .__name__ ,
2604
+ 'decorated_classmethod'
2605
+ )
2606
+ self .assertEqual (
2607
+ WithSingleDispatch ().decorated_classmethod .__name__ ,
2608
+ 'decorated_classmethod'
2609
+ )
2610
+
2472
2611
def test_invalid_registrations (self ):
2473
2612
msg_prefix = "Invalid first argument to `register()`: "
2474
2613
msg_suffix = (
0 commit comments