Hello,

This to thread has gotten no responses at all, so: 
Ping!

On Sun, Aug 22, 2021, 3:16 PM Finn Mason <finnjavier08@gmail.com> wrote:
Sorry, the formatting was terrible. I copy and pasted it from the original issue without really thinking about it.
Here's the same thing, but actually readable:

In _collections_abc.py is a private function titled _check_methods(). It takes a class and a number of method names (as strings), checks if the class has all of the methods, and returns NotImplemented if any are missing. The code is below:

 def _check_methods(C, *methods):
     mro = C.__mro__ 
     for method in methods:
        for B in mro:
             if method in B.__dict__: 
                 if B.__dict__[method] is None: 
                     return NotImplemented 
                     break
             else: 
                     return NotImplemented
     return True 

This is an incredibly convenient function (referred to as check_methods here on out) for creating abstract base classes, and is much simpler than using hasattr for each method you want to check. For example: 

 from abc import ABCMeta
 # Without check_methods
 class A(metaclass=ABCMeta):
     @classmethod
     def __subclasshook__(cls, subclass):
         return (hasattr(subclass, 'foo') and 
         callable(subclass.foo) and
         hasattr(subclass, 'bar') and
         callable(subclass.bar) or
         NotImplemented)

 # With check_methods
 class B(metaclass=ABCMeta): 
     @classmethod
     def __subclasshook(cls, subclass):
         return check_methods(subclass, 'foo', 'bar')

This would be a great function to add to the standard lib, perhaps in the abc module. One problem with check_methods() as defined in _collections_abc.py is that it doesn't check if the name is callable. Also, type hints and more readable variables may be desirable. The final code, if implemented, may look something like this:

 # In imports section: from typing import Literal
 def check_methods(Class: type, *methods: str) -> Literal[True, NotImplemented]:
     """Check if class `Class` has methods `methods`."""
     mro = Class.__mro__
     for method in methods:
         for Base in mro:
             if (attr := getattr(Base, method, None)) is not None:
                 if not callable(attr):
                     return NotImplemented
                     break 
                 else:
                     return NotImplemented
     return True

Again, this would be a great function to add to the `abc` module or a similar one. I've proposed this in issue 44941. 
Another possible implementation may be similar to a dataclass that implements __subclasshook__ for you with the desired method checks.

--
Finn