Needed: Real-world examples for Python's Cooperative Multiple Inheritance

Kirill Simonov xi at gamma.dn.ua
Sun Nov 28 14:22:54 EST 2010


Hi Raymond,

We've been using cooperative inheritance to implement stacked utilities 
such as WSGI middleware or connecting to a database.

An example of a WSGI middleware stack:

# Declare the interface and provide the default implementation.
class WSGI(Utility):
     def __call__(self, environ, start_response):
         # The main WSGI application is implemented here.
         start_response("200 OK", [('Content-Type', 'text/plain')])
         return ["Hello World!"]

# GZip middleware (may be defined in a different module or a plugin)
class GZIP(WSGI):
     # To indicate relative position in the middleware stack
     weights(100)
     def __call__(self, environ, start_response):
         # Call the next middleware in the stack to generate data.
         # Also, need to wrap start_response here...
         generator = super(GZIP, self).__call__(environ, start_response)
         # Pack the output...

# Error handling middleware (defined in a different module or a plugin)
class LogErrors(WSGI):
     weights(1000)
     def __call__(self, environ, start_response):
         # Call the next middleware in the stack, catch any errors.
         try:
             generator = super(LogErrors, self).__call__(environ,
                                                  start_response)
         except:
             # Log errors...

# Now glue them all together
def wsgi(environ, start_response):
     wsgi = WSGI() # !!!
     return wsgi(environ, start_response)

The trick here is that the constructor of `WSGI` (actually, 
`Utility.__new__()`) is overridden.  Instead of producing a new instance 
of `WSGI` , it does the following:
- use `__subclasses__()` to find all components of the utility;
- order the components by their weights;
- create a new class: `type(name, list_of_components, {})`;
- return an instance of the class.

Here is another example, database connection.

# The interface, no default implementation.
class Connect(Utility):
     def __init__(self, host, port, user, password, database):
         self.host = host
         self.port = port
         self.user = user
         self.password = password
         self.database = database
     def __call__(self):
         raise NotImplementedError()

# Public API
def connect(host, port, user, password, database):
     # Same trick here.
     connect = Connect(host, port, user, password, database)
     return connect()

# PostgreSQL implementation (defined in a plugin)
import psycopg2
class PGSQLConnect(Connect):
     def __call__(self):
         return psycopg2.connect(...)

# Connection pooling (defined in a plugin)
class Pooling(Connect):
     weights(100)
     def __call__(self):
         # Check if we could reuse an existing connection
         # ...
         # If no free connections available
         connection = super(Pooling, self).__call__()
         # Save it somewhere and return it...

Note that utility instances are short-lived so any persistent state must 
be kept elsewhere.


We also use the same pattern to implement Cecil/Diesel-style 
multimethods and general predicate dispatch, but that's probably outside 
the scope of your question.

A public version of the code lives here:
https://bitbucket.org/prometheus/htsql
Unfortunately it doesn't exactly match my examples above: connection 
pooling and most of the wsgi middleware are still to be ported, 
`weights()` is missing, etc.


Hope it helps.

Thanks,
Kirill

On 11/24/2010 03:08 PM, Raymond Hettinger wrote:
> I'm writing-up more guidance on how to use super() and would like to
> point at some real-world Python examples of cooperative multiple
> inheritance.
>
> Google searches take me to old papers for C++ and Eiffel, but that
> don't seem to be relevant to most Python programmers (i.e. a
> WalkingMenu example where a submenu is both a Entry in a Menu and a
> Menu itself).  Another published example is in a graphic library where
> some widgets inherit GraphicalFeature methods such as location, size
> and NestingGroupingFeatures such as finding parents, siblings, and
> children.  I don't find either of those examples compelling because
> there is no particular reason that they would have to have overlapping
> method names.
>
> So far, the only situation I can find where method names necessarily
> overlap is for the basics like __init__(), close(), flush(), and
> save() where multiple parents need to have their own initialization
> and finalization.
>
> If you guys know of good examples, I would appreciate a link or a
> recap.
>
> Thanks,
>
>
> Raymond
>



More information about the Python-list mailing list