@@ -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,137 @@ 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
+ try :
2536
+ with WithoutSingleDispatch .cls_context_manager (3 ) as foo :
2537
+ assert foo == '3' , (
2538
+ "Classmethod contextmanager called from class not working"
2539
+ )
2540
+ with WithoutSingleDispatch ().cls_context_manager (3 ) as bar :
2541
+ assert bar == '3' , (
2542
+ "Classmethod contextmanager called from instance not working"
2543
+ )
2544
+ assert WithoutSingleDispatch .decorated_classmethod (666 ) == '666' , (
2545
+ "Wrapped classmethod called from class not working"
2546
+ )
2547
+ assert WithoutSingleDispatch ().decorated_classmethod (666 ) == '666' , (
2548
+ "Wrapped classmethod called from instance not working"
2549
+ )
2550
+ except AssertionError as e :
2551
+ self .fail (f"There's a bug in this test: '{ e } '" )
2552
+
2553
+ class A :
2554
+ @functools .singledispatchmethod
2555
+ @classmethod
2556
+ @contextlib .contextmanager
2557
+ def cls_context_manager (cls , arg : int ) -> str :
2558
+ """My function docstring"""
2559
+ try :
2560
+ yield str (arg )
2561
+ finally :
2562
+ return 'Done'
2563
+
2564
+ @functools .singledispatchmethod
2565
+ @classmethod_friendly_decorator
2566
+ @classmethod
2567
+ def decorated_classmethod (cls , arg : int ) -> str :
2568
+ """My function docstring"""
2569
+ return str (arg )
2570
+
2571
+ with WithoutSingleDispatch .cls_context_manager (5 ) as foo :
2572
+ without_single_dispatch_foo = foo
2573
+
2574
+ with A .cls_context_manager (5 ) as foo :
2575
+ single_dispatch_foo = foo
2576
+
2577
+ self .assertEqual (without_single_dispatch_foo , single_dispatch_foo )
2578
+ self .assertEqual (single_dispatch_foo , '5' )
2579
+
2580
+ for method_name in ('cls_context_manager' , 'decorated_classmethod' ):
2581
+ with self .subTest (method = method_name ):
2582
+ self .assertEqual (
2583
+ getattr (A , method_name ).__name__ ,
2584
+ getattr (WithoutSingleDispatch , method_name ).__name__
2585
+ )
2586
+
2587
+ self .assertEqual (
2588
+ getattr (A (), method_name ).__name__ ,
2589
+ getattr (WithoutSingleDispatch (), method_name ).__name__
2590
+ )
2591
+
2592
+ for meth in (
2593
+ A .cls_context_manager ,
2594
+ A ().cls_context_manager ,
2595
+ A .decorated_classmethod ,
2596
+ A ().decorated_classmethod
2597
+ ):
2598
+ with self .subTest (meth = meth ):
2599
+ self .assertEqual (meth .__doc__ , 'My function docstring' )
2600
+ self .assertEqual (meth .__annotations__ ['arg' ], int )
2601
+
2602
+ self .assertEqual (A .cls_context_manager .__name__ , 'cls_context_manager' )
2603
+ self .assertEqual (A ().cls_context_manager .__name__ , 'cls_context_manager' )
2604
+ self .assertEqual (A .decorated_classmethod .__name__ , 'decorated_classmethod' )
2605
+ self .assertEqual (A ().decorated_classmethod .__name__ , 'decorated_classmethod' )
2606
+
2472
2607
def test_invalid_registrations (self ):
2473
2608
msg_prefix = "Invalid first argument to `register()`: "
2474
2609
msg_suffix = (
0 commit comments