]> 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 *
 # vim: set et sts=4 sw=4 encoding=utf-8 :
 
 from services import *
+from dispatcher import handler
 
 # XXX for testing only
 
 # XXX for testing only
+@handler
 def test_func(*args):
     print 'func:', args
 
 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.
 
     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):
     """
 
     def __init__(self, command):
@@ -26,7 +27,7 @@ class Error(RuntimeError):
         self.command = command
 
     def __str__(self):
         self.command = command
 
     def __str__(self):
-        return repr(self.command)
+        return ' '.join(self.command)
 
 class CommandNotFoundError(Error):
     r"""
 
 class CommandNotFoundError(Error):
     r"""
@@ -37,6 +38,14 @@ class CommandNotFoundError(Error):
     """
     pass
 
     """
     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
 
 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.
         """
         "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:
         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)
         handler = self.routes.get(route[0], None)
+        if handler is None:
+            raise CommandNotFoundError(command)
         route = route[1:]
         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]):
             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)
             handler = getattr(handler, route[0])
             route = route[1:]
         handler(*route)
@@ -99,16 +113,20 @@ class Dispatcher:
 
 if __name__ == '__main__':
 
 
 if __name__ == '__main__':
 
+    @handler
     def test_func(*args):
           print 'func:', args
 
     class TestClassSubHandler:
     def test_func(*args):
           print 'func:', args
 
     class TestClassSubHandler:
+        @handler
         def subcmd(self, *args):
             print 'class.subclass.subcmd:', args
 
     class TestClass:
         def subcmd(self, *args):
             print 'class.subclass.subcmd:', args
 
     class TestClass:
+        @handler
         def cmd1(self, *args):
             print 'class.cmd1:', args
         def cmd1(self, *args):
             print 'class.cmd1:', args
+        @handler
         def cmd2(self, *args):
             print 'class.cmd2:', args
         subclass = TestClassSubHandler()
         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:
     except CommandNotFoundError, e:
         print 'Not found:', e
     try:
-        d.dispatch('sucutrule')
+        d.dispatch('sucutrule piquete culete')
     except CommandNotFoundError, e:
         print 'Not found:', e
     try:
     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
 
     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
     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',)
 
 
 __ALL__ = ('DhcpHandler',)
 
@@ -45,6 +49,7 @@ class HostHandler:
         r"Initialize HostHandler object, see class documentation for details."
         self.hosts = hosts
 
         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? :)
     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)
 
         # 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:
     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
 
         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]
 
     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.
 
     def list(self):
         r"""list() -> CSV string :: List all the hostnames.
 
@@ -74,6 +82,7 @@ class HostHandler:
         """
         return ','.join(self.hosts)
 
         """
         return ','.join(self.hosts)
 
+    @handler
     def show(self):
         r"""show() -> CSV string :: List all the complete hosts information.
 
     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)
 
             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
 
     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.
 
     def list(self):
         r"""list() -> CSV string :: List all the parameter names.
 
@@ -134,6 +145,7 @@ class DhcpHandler:
         """
         return ','.join(self.vars)
 
         """
         return ','.join(self.vars)
 
+    @handler
     def show(self):
         r"""show() -> CSV string :: List all the parameters (with their values).
 
     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()))
 
         """
         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
 
     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
 
     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
 
     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
 
     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
     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()
 
         self._write_config()
         self.reload()
 
+    @handler
     def rollback(self):
         r"rollback() -> None :: Discard the changes not yet commited."
         self._load()
     def rollback(self):
         r"rollback() -> None :: Discard the changes not yet commited."
         self._load()