Pedro Matiello

Implementing interface contracts in Python with class decorators

23 Mar 2012 interfaces python

Python decorators are quite useful and interesting. I’ve already written about function decorators in a previous post and class decorators are a worthy followup.

I order to illustrate this feature, we’ll implement support for interface contracts for Python classes. In a language like Java, for instance, a contract can be declared this way:

public interface Comparable<T> {
    public int compareTo(T o);
}

And a class declared as an implementation of this interface must provide the relevant methods (with the correct signature). Otherwise, a compilation error arises. For instance:

public final class Integer extends Number implements Comparable<Integer> {

    ...

    public int compareTo(Integer anotherInteger) {
        int thisVal = this.value;
        int anotherVal = anotherInteger.value;
        return (thisVal&lt;anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
    }

    ...

}

We’ll try to achieve a similar feature in Python, albeit with some limitations. First, as Python does not have an explicit compilation step, our errors will only arise in runtime. Second, in Python, the types of the arguments and return values aren’t present in the signature of methods, so we’ll leave it out too. This is a notable absence, but we can still verify the presence of desired methods in the implementer type and the following elements in the method’s signature:

This said, we now have to declare our interfaces. I picked the following (somewhat cumbersome) syntax:

class some_interface(interface):
    simple_method = method(['self'])
    method_with_args = method(['self', 'arg1', 'arg2'])
    method_with_varargs = method(['self'], varargs='varargs')
    method_with_kwargs = method(['self'], keywords='kwargs')
    method_with_default_argument = method(['self', 'default_arg'], defaults=1)

Here we have an ordinary class subtyping an interface class. The methods are defined as attributes of the some_interface class, holding instances of the method class.

The interface class isn’t really exciting or even necessary. Actually, it exists for the sole purpose of tagging an interface contract as such:

class interface(object):
    pass

The method class holds the method signature.

class method(object):
    def __init__(self, args=None, varargs=None, keywords=None, defaults=0):
        self.args = args or []
        self.varargs = varargs
        self.keywords = keywords
        self.defaults = defaults

Notice that the constructor takes a few arguments:

Now we can create a new type implementing some_interface. And it follows:

@implements(some_interface)
class some_class(object):

    def simple_method(self):
        pass

    def method_with_args(self, arg):
        pass

    def method_with_varargs(self, *varargs):
        pass

    def method_with_kwargs(self, **kwargs):
        pass

    def method_with_default_argument(self, default_arg=100):
        pass

Notice that we declare the class as implementing the contract through the implements class decorator.

First thing we have to observe is the initialization of the implements decorator. Once the decorated class is loaded by the interpreter, an instance of the decorator is produced. The argument some_interface is passed to the constructor during the initialization (and it’s stored as an attribute for future reference).

class implements(object):

    def __init__(self, interface):
        self.interface = interface

    ...

Then, the __call__ method of the decorator is invoked. The decorated class (some_class in this example) is passed as argument. In this method, it is verified whether the given class satisfies all of its contractual obligations. If no problem arises, the class is returned at the end of the call.

class implements(object):

    ...

    def __call__(self, clazz):
        methods = [each for each in dir(self.interface) if self._is_method(each)]
        for each in methods:
            self._assert_implements(clazz, each)
        return clazz

    ...

Notice that the _assert_implements method is called against each method declared in the interface. This method, listed ahead, verifies whether the given method, as implemented by the class, has the desired signature, as specified in the interface. In case it doesn’t, an exception is raised (effectively interrupting the program).

class implements(object):

    ...

    def _assert_implements(self, clazz, method_name):
        method_contract = object.__getattribute__(self.interface, method_name)
        method_impl = getargspec(object.__getattribute__(clazz, method_name))
        assert method_name in dir(clazz)
        assert method_contract.args == method_impl.args
        assert method_contract.varargs == method_impl.varargs
        assert method_contract.keywords == method_impl.keywords
        if (method_impl.defaults is not None):
            assert method_contract.defaults == len(method_impl.defaults)
        else:
            assert method_contract.defaults == 0

    ...

The object.__getattribute__() (documentation) call is one thing worth of note as it is part of Python’s introspection features and targeted at retrieving attributes from classes and objects. Here, it’s used both to retrieve method objects from the interface and and to obtain references to the actual methods in the concrete class (the clazz argument).

The getargspec function (documentation) is also an interesting introspection feature. Given a function, it will return an object with information about the given function’s signature.

So, having both the expected signature from the method object hosted in the interface and the signature of the method implemented in the concrete class, we can simply compare them both and hope for a match.

(Postscript: although the source code presented above works fine, it’s actually a shorter version of the code originally developed for the purpose and available here.)