@@ -436,10 +436,6 @@ def __setstate__(self, state):
436436 pass
437437
438438
439- _PM_WRAPPED_METHOD_TYPES = (staticmethod , classmethod )
440- _PM_FAST_METHOD_TYPES = _PM_WRAPPED_METHOD_TYPES + (FunctionType , partial )
441-
442-
443439# Descriptor version
444440class partialmethod :
445441 """Method descriptor with partial application of the given arguments
@@ -450,7 +446,7 @@ class partialmethod:
450446 """
451447 __slots__ = (
452448 "_func" , "_args" , "_keywords" , "__dict__" , "__weakref__" ,
453- "_cachedmethod" , "_iscacheable "
449+ "_cachedmethod" , "_cachetype "
454450 )
455451
456452 __repr__ = _partial_repr
@@ -477,15 +473,19 @@ def func(self):
477473
478474 @func .setter
479475 def func (self , func ):
480- if isinstance (func , _PM_FAST_METHOD_TYPES ):
481- self ._iscacheable = True
476+ if isinstance (func , staticmethod ):
477+ self ._cachetype = 1
478+ elif isinstance (func , classmethod ):
479+ self ._cachetype = 2
480+ elif isinstance (func , (FunctionType , partial )):
481+ self ._cachetype = 3
482482 elif getattr (func , '__get__' , None ) is None :
483483 if not callable (func ):
484484 raise TypeError (f'the first argument { func !r} must be a callable '
485485 'or a descriptor' )
486- self ._iscacheable = True
486+ self ._cachetype = 4
487487 else :
488- self ._iscacheable = False
488+ self ._cachetype = 0
489489 self ._func = func
490490 self ._cachedmethod = None
491491
@@ -503,7 +503,8 @@ def keywords(self):
503503 method = self ._cachedmethod
504504 if method is None :
505505 return self ._keywords
506- if isinstance (method , _PM_WRAPPED_METHOD_TYPES ):
506+ cachetype = self ._cachetype
507+ if cachetype in (1 , 2 ):
507508 method = method .__wrapped__
508509 return method .keywords
509510
@@ -516,47 +517,48 @@ def __make_method(self):
516517 func = self ._func
517518 args = self ._args
518519 keywords = self ._keywords
519- if isinstance ( func , staticmethod ):
520- deco = staticmethod
520+ cachetype = self . _cachetype
521+ if cachetype == 1 :
521522 method = partial (func .__wrapped__ , * args , ** keywords )
522- elif isinstance (func , classmethod ):
523- deco = classmethod
523+ method .__isabstractmethod__ = self .__isabstractmethod__
524+ return staticmethod (method )
525+ elif cachetype == 2 :
524526 ph_args = (Placeholder ,) if args else ()
525527 method = partial (func .__wrapped__ , * ph_args , * args , ** keywords )
528+ method .__isabstractmethod__ = self .__isabstractmethod__
529+ return classmethod (method )
526530 else :
527- # instance method. 2 cases :
528- # a) FunctionType | partial
531+ # Either :
532+ # a) FunctionType / partial
529533 # b) callable object without __get__
530- deco = None
534+ # c) non cacheable callable with __get__ that returned itself
531535 ph_args = (Placeholder ,) if args else ()
532536 method = partial (func , * ph_args , * args , ** keywords )
533-
534- method .__partialmethod__ = self
535- if self .__isabstractmethod__ :
536- method .__isabstractmethod__ = True
537- if deco is not None :
538- method = deco (method )
539- return method
537+ method .__isabstractmethod__ = self .__isabstractmethod__
538+ method .__partialmethod__ = self
539+ return method
540540
541541 def __get__ (self , obj , cls = None ):
542+ if not self ._cachetype :
543+ # Unknown descriptor == unknown binding
544+ # Get callable at runtime and apply partial on top
545+ func = self ._func
546+ new_func = func .__get__ (obj , cls )
547+ if new_func is not func :
548+ # Assume __get__ returning something new indicates the
549+ # creation of an appropriate callable
550+ result = partial (new_func , * self ._args , ** self ._keywords )
551+ try :
552+ result .__self__ = new_func .__self__
553+ except AttributeError :
554+ pass
555+ return result
556+ else :
557+ return self .__make_method ().__get__ (obj , cls )
558+
542559 method = self ._cachedmethod
543560 if method is None :
544- if not self ._iscacheable :
545- # Unknown descriptor == unknown binding
546- # Need to get callable at runtime and apply partial on top
547- func = self ._func
548- new_func = func .__get__ (obj , cls )
549- if new_func is not func :
550- result = partial (new_func , * self ._args , ** self ._keywords )
551- try :
552- result .__self__ = new_func .__self__
553- except AttributeError :
554- pass
555- return result
556-
557- # Cache method
558561 self ._cachedmethod = method = self .__make_method ()
559-
560562 return method .__get__ (obj , cls )
561563
562564 @property
0 commit comments