]> git.llucax.com Git - software/pymin.git/commitdiff
Add a decorator to mark which callables are exported by the dispatcher.
authorLeandro Lucarella <llucarella@integratech.com.ar>
Mon, 24 Sep 2007 17:09:37 +0000 (14:09 -0300)
committerLeandro Lucarella <llucarella@integratech.com.ar>
Mon, 24 Sep 2007 17:09:37 +0000 (14:09 -0300)
All callables which should handle a command should be maked with the
@handler decorator. This is for the sake of security, so users can't call
arbitrary python code (like a constructor).

config.py
dispatcher.py
services/dhcp/__init__.py

index f433170dae6664ee393ad2e20a2f7bc232a33bcf..2addc6823caeee0252b641511d616dd24e30cf08 100644 (file)
--- a/config.py
+++ b/config.py
@@ -1,8 +1,10 @@
 # vim: set et sts=4 sw=4 encoding=utf-8 :
 
 from services import *
+from dispatcher import handler
 
 # XXX for testing only
+@handler
 def test_func(*args):
     print 'func:', args
 
index 2f545cd6e91fb7351c65b223092b253863b5a0a6..addd45f716a3446b5fa0078875328eb2cdf37d5c 100644 (file)
@@ -15,7 +15,8 @@ class Error(RuntimeError):
     All exceptions raised by the Dispatcher inherits from this one, so you can
     easily catch any dispatching exception.
 
-    command - is the command that raised the exception.
+    command - is the command that raised the exception, expressed as a list of
+              paths (or subcommands).
     """
 
     def __init__(self, command):
@@ -26,7 +27,7 @@ class Error(RuntimeError):
         self.command = command
 
     def __str__(self):
-        return repr(self.command)
+        return ' '.join(self.command)
 
 class CommandNotFoundError(Error):
     r"""
@@ -37,6 +38,14 @@ class CommandNotFoundError(Error):
     """
     pass
 
+def handler(f):
+    f._dispatcher_handler = True
+    return f
+
+def is_handler(handler):
+    return callable(handler) and hasattr(handler, '_dispatcher_handler') \
+            and handler._dispatcher_handler
+
 class Dispatcher:
     r"""Dispatcher([routes]) -> Dispatcher instance :: Command dispatcher
 
@@ -82,16 +91,21 @@ class Dispatcher:
         "tree" and call it, or raises a CommandNotFoundError if the command
         can't be dispatched.
         """
+        command = list()
         route = route.split() # TODO support "" and keyword arguments
         if not route:
-            raise CommandNotFoundError('') # TODO better error reporting
+            raise CommandNotFoundError(command)
+        command.append(route[0])
         handler = self.routes.get(route[0], None)
+        if handler is None:
+            raise CommandNotFoundError(command)
         route = route[1:]
-        while not callable(handler):
-            if not route:
-                raise CommandNotFoundError('XXX') # TODO better error reporting
+        while not is_handler(handler):
+            if len(route) is 0:
+                raise CommandNotFoundError(command)
+            command.append(route[0])
             if not hasattr(handler, route[0]):
-                raise CommandNotFoundError(route[0]) # TODO better error rep.
+                raise CommandNotFoundError(command)
             handler = getattr(handler, route[0])
             route = route[1:]
         handler(*route)
@@ -99,16 +113,20 @@ class Dispatcher:
 
 if __name__ == '__main__':
 
+    @handler
     def test_func(*args):
           print 'func:', args
 
     class TestClassSubHandler:
+        @handler
         def subcmd(self, *args):
             print 'class.subclass.subcmd:', args
 
     class TestClass:
+        @handler
         def cmd1(self, *args):
             print 'class.cmd1:', args
+        @handler
         def cmd2(self, *args):
             print 'class.cmd2:', args
         subclass = TestClassSubHandler()
@@ -126,11 +144,11 @@ if __name__ == '__main__':
     except CommandNotFoundError, e:
         print 'Not found:', e
     try:
-        d.dispatch('sucutrule')
+        d.dispatch('sucutrule piquete culete')
     except CommandNotFoundError, e:
         print 'Not found:', e
     try:
-        d.dispatch('inst cmd3')
+        d.dispatch('inst cmd3 arg1 arg2 arg3')
     except CommandNotFoundError, e:
         print 'Not found:', e
 
index 46b7d816e60848c685b41d6d42ea445bf9fe8563..3d956f94a7152c895c5057a6f2fe3e92a88eef62 100644 (file)
@@ -7,6 +7,10 @@ try:
     import cPickle as pickle
 except ImportError:
     import pickle
+try:
+    from dispatcher import handler
+except ImportError:
+    def handler(f): return f # NOP for testing
 
 __ALL__ = ('DhcpHandler',)
 
@@ -45,6 +49,7 @@ class HostHandler:
         r"Initialize HostHandler object, see class documentation for details."
         self.hosts = hosts
 
+    @handler
     def add(self, name, ip, mac):
         r"add(name, ip, mac) -> None :: Add a host to the hosts list."
         # XXX deberia indexar por hostname o por ip? o por mac? :)
@@ -52,6 +57,7 @@ class HostHandler:
         # nombres? Una MAC con muchas IP? una MAC con muchos nombre? Etc...
         self.hosts[name] = Host(name, ip, mac)
 
+    @handler
     def update(self, name, ip=None, mac=None):
         r"update(name[, ip[, mac]]) -> None :: Update a host of the hosts list."
         if not name in self.hosts:
@@ -61,12 +67,14 @@ class HostHandler:
         if mac is not None:
             self.hosts[name].mac = mac
 
+    @handler
     def delete(self, name):
         r"delete(name) -> None :: Delete a host of the hosts list."
         if not name in self.hosts:
             raise KeyError('Host not found')
         del self.hosts[name]
 
+    @handler
     def list(self):
         r"""list() -> CSV string :: List all the hostnames.
 
@@ -74,6 +82,7 @@ class HostHandler:
         """
         return ','.join(self.hosts)
 
+    @handler
     def show(self):
         r"""show() -> CSV string :: List all the complete hosts information.
 
@@ -121,12 +130,14 @@ class DhcpHandler:
             self._write_config()
         self.host = HostHandler(self.hosts)
 
+    @handler
     def set(self, param, value):
         r"set(param, value) -> None :: Set a DHCP parameter."
         if not param in self.vars:
             raise KeyError('Parameter ' + param + ' not found')
         self.vars[param] = value
 
+    @handler
     def list(self):
         r"""list() -> CSV string :: List all the parameter names.
 
@@ -134,6 +145,7 @@ class DhcpHandler:
         """
         return ','.join(self.vars)
 
+    @handler
     def show(self):
         r"""show() -> CSV string :: List all the parameters (with their values).
 
@@ -143,30 +155,35 @@ class DhcpHandler:
         """
         return '\n'.join(('%s,%s' % (k, v) for (k, v) in self.vars.items()))
 
+    @handler
     def start(self):
         r"start() -> None :: Start the DHCP service."
         #esto seria para poner en una interfaz
         #y seria el hook para arrancar el servicio
         pass
 
+    @handler
     def stop(self):
         r"stop() -> None :: Stop the DHCP service."
         #esto seria para poner en una interfaz
         #y seria el hook para arrancar el servicio
         pass
 
+    @handler
     def restart(self):
         r"restart() -> None :: Restart the DHCP service."
         #esto seria para poner en una interfaz
         #y seria el hook para arrancar el servicio
         pass
 
+    @handler
     def reload(self):
         r"reload() -> None :: Reload the configuration of the DHCP service."
         #esto seria para poner en una interfaz
         #y seria el hook para arrancar el servicio
         pass
 
+    @handler
     def commit(self):
         r"commit() -> None :: Commit the changes and reload the DHCP service."
         #esto seria para poner en una interfaz
@@ -176,6 +193,7 @@ class DhcpHandler:
         self._write_config()
         self.reload()
 
+    @handler
     def rollback(self):
         r"rollback() -> None :: Discard the changes not yet commited."
         self._load()