From f2571fe7b62abfa67f4b805c266869058647afff Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Fri, 5 Oct 2007 13:01:53 -0300 Subject: [PATCH] Use a handler object as the root dispatcher handler instead of a dict. Now the dispatcher use a handler object as the root handler. This way you can inherit your root handler from Handler and get the useful 'help' and 'coommands' commands for free. --- config.py | 26 +++++++----------------- pymin/dispatcher.py | 48 ++++++++++++++++++++------------------------ pymin/pymindaemon.py | 39 +++++++++++++++++++---------------- pymind | 2 +- 4 files changed, 52 insertions(+), 63 deletions(-) diff --git a/config.py b/config.py index 059faae..5ae043d 100644 --- a/config.py +++ b/config.py @@ -1,36 +1,24 @@ # vim: set et sts=4 sw=4 encoding=utf-8 : from pymin.services import * -from pymin.dispatcher import handler +from pymin.dispatcher import Handler -# XXX for testing only -@handler -def test_func(*args): - print 'func:', args - -routes = dict \ -( +class Root(Handler): dhcp = DhcpHandler( pickle_dir = 'var/lib/pymin/pickle/dhcp', - config_dir = 'var/lib/pymin/config/dhcp', - ), + config_dir = 'var/lib/pymin/config/dhcp') dns = DnsHandler( pickle_dir = 'var/lib/pymin/pickle/dns', - config_dir = 'var/lib/pymin/config/dns', - ), + config_dir = 'var/lib/pymin/config/dns') firewall = FirewallHandler( pickle_dir = 'var/lib/pymin/pickle/firewall', - config_dir = 'var/lib/pymin/config/firewall', - ), + config_dir = 'var/lib/pymin/config/firewall') ip = IpHandler( pickle_dir = 'var/lib/pymin/pickle/ip', - config_dir = 'var/lib/pymin/config/ip', - ), + config_dir = 'var/lib/pymin/config/ip') proxy = ProxyHandler( pickle_dir = 'var/lib/pymin/pickle/proxy', - config_dir = 'var/lib/pymin/config/proxy', - ), -) + config_dir = 'var/lib/pymin/config/proxy') bind_addr = \ ( diff --git a/pymin/dispatcher.py b/pymin/dispatcher.py index c7d260b..7e9ef60 100644 --- a/pymin/dispatcher.py +++ b/pymin/dispatcher.py @@ -294,15 +294,13 @@ def parse_command(command): return (seq, dic) class Dispatcher: - r"""Dispatcher([routes]) -> Dispatcher instance :: Command dispatcher + r"""Dispatcher([root]) -> Dispatcher instance :: Command dispatcher. This class provides a modular and extensible dispatching mechanism. You - can specify root 'routes' (as a dict where the key is the string of the - root command and the value is a callable object to handle that command, - or a subcommand if the callable is an instance and the command can be - sub-routed). + specify a root handler (probably as a pymin.dispatcher.Handler subclass), - The command can have arguments, separated by (any number of) spaces. + The command can have arguments, separated by (any number of) spaces and + keyword arguments (see parse_command for more details). The dispatcher tries to route the command as deeply as it can, passing the other "path" components as arguments to the callable. To route the @@ -311,25 +309,26 @@ class Dispatcher: Example: >>> d = Dispatcher(dict(handler=some_handler)) - >>> d.dispatch('handler attribute method arg1 arg2 "third argument"') + >>> d.dispatch('handler attribute method arg1 "arg 2" arg=3') If 'some_handler' is an object with an 'attribute' that is another object which has a method named 'method', then - some_handler.attribute.method('arg1', 'arg2') will be called. If - some_handler is a function, then some_handler('attribute', 'method', - 'arg1', 'arg2') will be called. The handler "tree" can be as complex - and deep as you want. - - If some command can't be dispatched (because there is no root handler or - there is no matching callable attribute), a CommandNotFoundError is raised. + some_handler.attribute.method('arg1', 'arg 2', arg=3) will be called. + If some_handler is a function, then some_handler('attribute', 'method', + 'arg1', 'arg 2', arg=3) will be called. The handler "tree" can be as + complex and deep as you want. + + If some command can't be dispatched (because there is no root handler + or there is no matching callable attribute), a CommandNotFoundError + is raised. """ - def __init__(self, routes=dict()): + def __init__(self, root): r"""Initialize the Dispatcher object. See Dispatcher class documentation for more info. """ - self.routes = routes + self.root = root def dispatch(self, route): r"""dispatch(route) -> None :: Dispatch a command string. @@ -342,11 +341,7 @@ class Dispatcher: (route, kwargs) = parse_command(route) if not route: raise CommandNotFoundError(command) - command.append(route[0]) - handler = self.routes.get(route[0], None) - if handler is None: - raise CommandNotFoundError(command) - route = route[1:] + handler = self.root while not is_handler(handler): if len(route) is 0: raise CommandNotFoundError(command) @@ -378,12 +373,11 @@ if __name__ == '__main__': print 'class.cmd2:', args subclass = TestClassSubHandler() - test_class = TestClass() + class RootHandler(Handler): + func = staticmethod(test_func) + inst = TestClass() - d = Dispatcher(dict( - func=test_func, - inst=test_class, - )) + d = Dispatcher(RootHandler()) d.dispatch(r'''func arg1 arg2 arg3 "fourth 'argument' with \", a\ttab and\n\\n"''') print 'inst commands:', tuple(d.dispatch('inst commands')) @@ -404,6 +398,8 @@ if __name__ == '__main__': d.dispatch('inst cmd3 arg1 arg2 arg3') except CommandNotFoundError, e: print 'Not found:', e + print + print # Parser tests p = parse_command('hello world') diff --git a/pymin/pymindaemon.py b/pymin/pymindaemon.py index be2c975..ee5f805 100644 --- a/pymin/pymindaemon.py +++ b/pymin/pymindaemon.py @@ -11,28 +11,33 @@ command-line. import signal import socket +from pymin.dispatcher import handler +from pymin import dispatcher from pymin import eventloop from pymin import serializer class PyminDaemon(eventloop.EventLoop): - r"""PyminDaemon(bind_addr, routes) -> PyminDaemon instance + r"""PyminDaemon(root, bind_addr) -> PyminDaemon instance This class is well suited to run as a single process. It handles signals for controlled termination (SIGINT and SIGTERM), as well as a user signal to reload the configuration files (SIGUSR1). - bind_addr - is a tuple of (ip, port) where to bind the UDP socket to. + root - the root handler. This is passed directly to the Dispatcher. - routes - is a dictionary where the key is a command string and the value - is the command handler. This is passed directly to the Dispatcher. + bind_addr - is a tuple of (ip, port) where to bind the UDP socket to. Here is a simple usage example: - >>> def test_handler(*args): print 'test:', args - >>> PyminDaemon(('', 9999), dict(test=test_handler)).run() + >>> from pymin import dispatcher + >>> class Root(dispatcher.Handler): + @handler('Test command.') + def test(self, *args): + print 'test:', args + >>> PyminDaemon(Root(), ('', 9999)).run() """ - def __init__(self, routes=dict(), bind_addr=('', 9999)): + def __init__(self, root, bind_addr=('', 9999)): r"""Initialize the PyminDaemon object. See PyminDaemon class documentation for more info. @@ -44,7 +49,7 @@ class PyminDaemon(eventloop.EventLoop): # Create EventLoop eventloop.EventLoop.__init__(self, sock) # Create Dispatcher - self.dispatcher = Dispatcher(routes) + self.dispatcher = dispatcher.Dispatcher(root) # Signal handling def quit(signum, frame): print "Shuting down ..." @@ -87,14 +92,14 @@ class PyminDaemon(eventloop.EventLoop): if __name__ == '__main__': - @handler(u"Print all the arguments, return nothing.") - def test_handler(*args): - print 'test:', args - - @handler(u"Echo the message passed as argument.") - def echo_handler(message): - print 'echo:', message - return message + class Root(dispatcher.Handler): + @handler(u"Print all the arguments, return nothing.") + def test(self, *args): + print 'test:', args + @handler(u"Echo the message passed as argument.") + def echo(self, message): + print 'echo:', message + return message - PyminDaemon(dict(test=test_handler, echo=echo_handler)).run() + PyminDaemon(Root()).run() diff --git a/pymind b/pymind index 27a9722..a04307d 100755 --- a/pymind +++ b/pymind @@ -4,5 +4,5 @@ from pymin.pymindaemon import PyminDaemon import config -PyminDaemon(config.routes, config.bind_addr).run() +PyminDaemon(config.Root(), config.bind_addr).run() -- 2.43.0