Problem
Sometimes it may be useful to create and reuse things like database connections, proxies for XML-RPC and SOAP client access, etc. This recipe shows how to do so.
Solution
The solution requires that, in your Quixote driver script, you do the following:
- Implement a subclass of class quixote.Publisher.
- Instruct the handler to use your publisher subclass.
Your subclass of quixote.Publisher will have the following:
A constructor (__init__) which will create an instance of your resource (or connection or proxy or whatever) and save it in an instance variable.
A start_request method which will attach your resource to the request object. This method is called just before your application code is called to handle the request.
A cleanup method which will do any clean-up on the resource (close it, delete it, etc).
Implement a Publisher subclass
Here is an example of a subclass of quixote.Publisher that creates a database connection, several XML-RPC proxies, and a SOAP proxy:
from quixote.publish import Publisher from pyPgSQL import PgSQL import xmlrpclib from quixote import enable_ptl enable_ptl() # Warning: Must import SOAPpy *after* enable_ptl. I don't know why. from SOAPpy import SOAPProxy class PersistentContainer: pass class MyPublisher(Publisher): def __init__(self, root_namespace, config=None): Publisher.__init__(self, root_namespace, config) self.container = PersistentContainer() self.container.dbConnection = PgSQL.connect(CONNECTION_ARGS) self.container.xmlrpcProxy = xmlrpclib.ServerProxy('http://localhost:8082') self.container.soapProxy = SOAPProxy(str("http://localhost:8083/")) meerkatURI = "http://www.oreillynet.com/meerkat/xml-rpc/server.php" self.container.meerkatServer = xmlrpclib.Server(meerkatURI) def cleanup(self): self.container.dbConnection.close() def start_request(self, request): Publisher.start_request(self, request) request.container = self.container
Explanation:
The __init__ method creates a database connection and several proxies. It then packs these resources in an instance of class PersistentContainer, which it saves as an instance variable. This method will be called (and a new instances of our resources will be created) each time the handler creates a new process.
The cleanup method cleans up resources that need it. In this case, it closes the database connection. This method will be called when the handler kills a process. (Note, __del__ is not appropriate because if Python is shutting down when it is called, the environment is unstable.)
The start_request method will be called each time a request is to be handled. It inserts the object containing our resources into the request object, where our application code can find it.
Start the Server
SCGI Initialization
If you use Apache and SCGI, the following will start up the SCGI request handler with your Publisher subclass:
from scgi.quixote_handler import QuixoteHandler, main class MyAppHandler(QuixoteHandler): publisher_class = MyPublisher root_namespace = "myapplication.ui" prefix = "" # QuixoteHandler.serve will raise SystemExit when we should shutdown. The finally: # clause will handle this case and make sure we get a chance to cleanup before # the interpreter starts to shut down. def serve(self): try: QuixoteHandler.serve(self) finally: self.publisher.cleanup() if __name__ == '__main__': main(MyAppHandler)
Medusa Initialization
If you use Medusa, the following will start up the Medusa request handler with your Publisher subclass:
from quixote.publish import Publisher from quixote.server import medusa_http def start_server(port): print 'Now serving the myapplication application on port %i' % port server = http_server.http_server('', port) publisher = MyPublisher('iptables_firewall_config.ui') publisher.read_config('config.py') publisher.setup_logs() dh = medusa_http.QuixoteHandler(publisher, 'myapplication on medusa server', server) server.install_handler(dh) try: asyncore.loop() finally: publisher.cleanup() def main(): port = 8081 start_server(port) if __name__ == "__main__": main()
Discussion
This recipe can be employed whenever you wish to reuse a resource or object that has a cost (time or space) when you create it. This recipe ensures that only one request handler can use the resource or object at any one time. Furthermore, it ensures that any one request handler will be finished with handling its request before the resource is given to another request handler.
Because of the way in which requests are handled, under some Web server configurations, more than one instance of the resource may be created. With a Web server configuration which creates multiple processes, as is the case with Apache and SCGI, the Quixote SCGI handler may create more than one process to handle requests concurrently. When this happens, there will be one instance of your resource in each process.