# Copyright (c) 2002 Sean R. Lynch # # This file is part of PythonVerse. # # PythonVerse is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # PythonVerse is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with PythonVerse; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import re, string, asyncore, asynchat, socket class HandlerError(Exception): pass class InputHandler: def __init__(self, callbacks): """Callbacks is a sequence of (command, callback, regular expression, sequence of thunks)""" self.dict = {} for command, callback, r, thunks in callbacks: self.dict[command] = (callback, re.compile(r), thunks) def handle(self, s): try: # Extract the command cmd, args = string.split(s, ' ', 1) except ValueError: # Assume a command with no arguments cmd = s args = '' try: func, r, conv = self.dict[cmd] except KeyError: raise HandlerError, 'Unknown: %s' % cmd else: # Parse the arguments match = r.match(args) if match is None: raise HandlerError, 'Failed parse: %s' % args elif match.end() != len(args): raise HandlerError, 'Not all arguments parsed: %s' % \ (len(args), match.end(), args) else: # Convert the arguments to the correct types apply(func, map(lambda x: x[0](x[1]), zip(conv, match.groups()))) class Connection(asynchat.async_chat): """Connection object that can handle a client or server""" def __init__(self, handler, sock=None): asynchat.async_chat.__init__(self, sock) self.buffer = '' self.outbuf = '' self.handler = handler self.set_terminator('\r\n') # asynchat/asyncore handlers def debug(self): pass def handle_connect(self): # Should override this pass def collect_incoming_data(self, data): self.buffer = self.buffer + data def found_terminator(self): self.debug('-> %s' % string.strip(self.buffer)) try: self.handler.handle(self.buffer) except HandlerError, info: self.debug(str(info)) self.buffer = '' def writable(self): return len(self.outbuf) > 0 def handle_write(self): bytes = self.send(self.outbuf) self.outbuf = self.outbuf[bytes:] def write(self, data): self.debug("<- %s" % string.strip(data)) self.outbuf = self.outbuf + data