Differences Between protocols.adapt() and PEP 246

If you have read PEP 246 or are looking for an exact implementation of it, you should know that there are a few differences between the protocols implementation of adapt() and the PEP 246 specification. If you don't care about these differences, you can skip this mini-appendix and proceed directly to section 1.1.4, ``Defining and Subclassing Interfaces''.

The first difference is that TypeError is treated differently in each implementation. PEP 246 says that if a __conform__ or __adapt__ method raises a TypeError, it should be treated in the same way as if the method returned None. This was a workaround for the issue of accidentally calling an unbound class method, in the case where a component or protocol supplied to adapt() was a class.

The protocols implementation of adapt() attempts to catch such errors also, but will reraise any exception that appears to come from within the execution of the __conform__ or __adapt__ method. So if these methods raise a TypeError, it will be passed through to the caller of adapt. Thus, if you are writing one of these methods, you should not raise a TypeError to signal the lack of an adaptation. Rather, you should return None.

Second, protocols.AdaptationFailure is raised when no adaptation is found, and no default is supplied, rather than the TypeError specified by PEP 246. (Note: protocols.AdaptationFailure is a subclass of TypeError and NotImplementedError, so code written to catch either of these errors will work.)

These differences are the result of experience using the protocols package with PEAK, and advances in the Python state-of-the-art since PEP 246 was written (over two years ago). We believe that they make the adaptation protocol more robust, more predictable, and easier to use for its most common applications.