+ r"is_handler(handler) -> bool :: Tell if a object is a handler."
+ return callable(handler) and hasattr(handler, '_dispatcher_help')
+
+def get_help(handler):
+ r"get_help(handler) -> unicode :: Get a handler's help string."
+ if not is_handler(handler):
+ raise TypeError("'%s' should be a handler" % handler.__name__)
+ return handler._dispatcher_help
+
+class Handler:
+ r"""Handler() -> Handler instance :: Base class for all dispatcher handlers.
+
+ All dispatcher handlers should inherit from this class to have some extra
+ commands, like help.
+ """
+
+ @handler(u'List available commands.')
+ def commands(self):
+ r"""commands() -> generator :: List the available commands."""
+ return (a for a in dir(self) if is_handler(getattr(self, a)))
+
+ @handler(u'Show available commands with their help.')
+ def help(self, command=None):
+ r"""help([command]) -> unicode/dict :: Show help on available commands.
+
+ If command is specified, it returns the help of that particular command.
+ If not, it returns a dictionary which keys are the available commands
+ and values are the help strings.
+ """
+ if command is None:
+ return dict((a, get_help(getattr(self, a)))
+ for a in dir(self) if is_handler(getattr(self, a)))
+ if not hasattr(self, command):
+ raise CommandNotFoundError(command)
+ handler = getattr(self, command)
+ if not is_handler(handler):
+ raise CommandNotFoundError(command)
+ return get_help(handler)