Implement an Interface

There are several ways to indicate that instances of a class provide an interface. These are listed here, and for performance reasons, should typically be considered in the order that they appear.

Register a class that implements the interface

For classes that define all attributes of an interface, decorate the class with jute.implements.

@jute.implements(BufferedWritable)
class OutputWriter:
    def write(self, buf):
        sys.stdout.write(buf)
    def flush(self):
        sys.stdout.flush()

If it is not possible to decorate the class, use the interface’s register_implementation method to specify a class as an implementation of the interface.

BufferedWritable.register_implementation(file)

In either of the above cases, the interface is verified once, during class definition or registration.

Register a class whose instances provide the interface

Sometimes a class does not define all interface attributes, but instances of the class will, typically through the __init__ or __getattr__ methods. In this case, decorate the class with jute.provides.

@jute.provides(BufferedWritable)
class ErrorWriter:
    def __init__(self):
        self.write = sys.stderr.write
    def __getattr__(self, name):
        if name == 'flush':
            def flush(buf):
                sys.stderr.flush(buf)
            return flush
        raise AttributeError(name)

If it is not possible to decorate the class, use the interface’s register_provider method to specify that class instances will provide the interface. Note that register_provider takes the class whose instances will provide the interface, not a class instance.

Writable.register_provider(ssl.SSLSocket)

This particular class could also have been registered using register_implementation.

The claim to provide an interface is verified during each conversion to the interface, and hence is slower than registering an implementation.

Dynamically indicate that an instance provides the interface

Sometimes, especially for wrapper classes, it is useful to declare support for an interface dynamically. Dynamic implementations are declared using the jute.Dynamic interface, which provides a single method provides_interface:

@implements(jute.Dynamic)
class PrintAttributeAccessWrapper:
    def __init__(self, wrapped):
        self.wrapped = wrapped

    def __getattr__(self, name):
        # return the wrapped object's attributes
        print('Accessing attribute {}'.format(name))
        return getattr(self.wrapped, name)

    def provides_interface(self, interface):
        # Check wrapped object's support for interface.
        return interface.provided_by(self.wrapped)

Note, this object may print “Accessing attribute write” twice. The first time is during interface verification, which will not actually call the function. This may be an issue if __getattr__ performs non-trivial work to resolve the attribute.