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