@@ -2519,6 +2519,105 @@ def static_func(arg: int) -> str:
25192519 self .assertEqual (A .static_func .__name__ , 'static_func' )
25202520 self .assertEqual (A ().static_func .__name__ , 'static_func' )
25212521
2522+ def test_double_wrapped_methods (self ):
2523+ def classmethod_friendly_decorator (func ):
2524+ wrapped = func .__func__
2525+ @classmethod
2526+ @functools .wraps (wrapped )
2527+ def wrapper (* args , ** kwargs ):
2528+ return wrapped (* args , ** kwargs )
2529+ return wrapper
2530+
2531+ class WithoutSingleDispatch :
2532+ @classmethod
2533+ @contextlib .contextmanager
2534+ def cls_context_manager (cls , arg : int ) -> str :
2535+ try :
2536+ yield str (arg )
2537+ finally :
2538+ return 'Done'
2539+
2540+ @classmethod_friendly_decorator
2541+ @classmethod
2542+ def decorated_classmethod (cls , arg : int ) -> str :
2543+ return str (arg )
2544+
2545+ class WithSingleDispatch :
2546+ @functools .singledispatchmethod
2547+ @classmethod
2548+ @contextlib .contextmanager
2549+ def cls_context_manager (cls , arg : int ) -> str :
2550+ """My function docstring"""
2551+ try :
2552+ yield str (arg )
2553+ finally :
2554+ return 'Done'
2555+
2556+ @functools .singledispatchmethod
2557+ @classmethod_friendly_decorator
2558+ @classmethod
2559+ def decorated_classmethod (cls , arg : int ) -> str :
2560+ """My function docstring"""
2561+ return str (arg )
2562+
2563+ # These are sanity checks
2564+ # to test the test itself is working as expected
2565+ with WithoutSingleDispatch .cls_context_manager (5 ) as foo :
2566+ without_single_dispatch_foo = foo
2567+
2568+ with WithSingleDispatch .cls_context_manager (5 ) as foo :
2569+ single_dispatch_foo = foo
2570+
2571+ self .assertEqual (without_single_dispatch_foo , single_dispatch_foo )
2572+ self .assertEqual (single_dispatch_foo , '5' )
2573+
2574+ self .assertEqual (
2575+ WithoutSingleDispatch .decorated_classmethod (5 ),
2576+ WithSingleDispatch .decorated_classmethod (5 )
2577+ )
2578+
2579+ self .assertEqual (WithSingleDispatch .decorated_classmethod (5 ), '5' )
2580+
2581+ # Behavioural checks now follow
2582+ for method_name in ('cls_context_manager' , 'decorated_classmethod' ):
2583+ with self .subTest (method = method_name ):
2584+ self .assertEqual (
2585+ getattr (WithSingleDispatch , method_name ).__name__ ,
2586+ getattr (WithoutSingleDispatch , method_name ).__name__
2587+ )
2588+
2589+ self .assertEqual (
2590+ getattr (WithSingleDispatch (), method_name ).__name__ ,
2591+ getattr (WithoutSingleDispatch (), method_name ).__name__
2592+ )
2593+
2594+ for meth in (
2595+ WithSingleDispatch .cls_context_manager ,
2596+ WithSingleDispatch ().cls_context_manager ,
2597+ WithSingleDispatch .decorated_classmethod ,
2598+ WithSingleDispatch ().decorated_classmethod
2599+ ):
2600+ with self .subTest (meth = meth ):
2601+ self .assertEqual (meth .__doc__ , 'My function docstring' )
2602+ self .assertEqual (meth .__annotations__ ['arg' ], int )
2603+
2604+ self .assertEqual (
2605+ WithSingleDispatch .cls_context_manager .__name__ ,
2606+ 'cls_context_manager'
2607+ )
2608+ self .assertEqual (
2609+ WithSingleDispatch ().cls_context_manager .__name__ ,
2610+ 'cls_context_manager'
2611+ )
2612+ self .assertEqual (
2613+ WithSingleDispatch .decorated_classmethod .__name__ ,
2614+ 'decorated_classmethod'
2615+ )
2616+ self .assertEqual (
2617+ WithSingleDispatch ().decorated_classmethod .__name__ ,
2618+ 'decorated_classmethod'
2619+ )
2620+
25222621 def test_invalid_registrations (self ):
25232622 msg_prefix = "Invalid first argument to `register()`: "
25242623 msg_suffix = (
0 commit comments