#!/usr/bin/env python # coding: utf-8 """ Defines a class for parsing objects for special command methods. """ import inspect from chatwisted import debug class CommandParser(object): """ Class to manage command methods, ie. methods that can be called over the network. """ def __init__(self, **kwargs): """ Set some attributes """ # all known commands # format: {"commandname":([minargs], [maxargs], func)} self.commands = {} self.cmd_prefix = kwargs.get("prefix", "/") self.default_func = kwargs.get("default_func", None) self.last_error = "" def add(self, func, name=None): """ Add a command to the parser. The name of the given function will become the commandname, if no name is given. """ name = name or func.func_name # dummy = unused kwargs args, more_args, dummy, defaults = inspect.getargspec(func) args = filter(lambda s: s!= "self", args) defaults = defaults or [] min_args = len(args) - len(defaults) max_args = None if more_args else len(args) self.commands[name] = (min_args, max_args, func) def get_cmds_from_obj(self, obj, method_prefix="cmd_", cmd_prefix=""): """ get command functions by introspecting the given object and taking all methods that start with `method_prefix`. The command name is cmd_prefix + (method_name without leading method_prefix) """ for name, method in inspect.getmembers(obj, inspect.ismethod): if name.startswith(method_prefix): self.add(method, cmd_prefix + name[len(method_prefix):]) def unregister(self, prefix): """ remove all commands that start with `prefix` """ for cmd_name in self.commands.keys(): if cmd_name.startswith(prefix): del self.commands[cmd_name] def parse(self, line, *extra_args): """ Parse `line` and try to execute it (command with arguments) :Parameters: line : string the line to parse extra_args extra arguments that will be passed to the command function as first arguments """ self.last_error = "" debug("'"*30, 2) debug("parsing line: >%s< extraargs: >%s<" % (line, str(extra_args)), 2) debug("self.commands: %s" % str(self.commands.keys()), 2) if line.startswith(self.cmd_prefix): values = line[len(self.cmd_prefix):].split(" ") cmd, args = values[0], list(extra_args) + values[1:] debug("command: %s args: %s" % (cmd, str(args)), 2) if cmd in self.commands: min_args, max_args, func = self.commands[cmd] # debug("found command min_args %s max_args %s" % (str(min_args),) # str(max_args)) if max_args is None: max_args = len(args) # debug("max_args", max_args, len(args)) if min_args <= len(args) <= max_args: # debug("launch command") func(*tuple(args)) elif len(args) > max_args and max_args-len(extra_args)>=1: # debug(">>", max_args, args, "<<") #! bug here! # debug(":->", tuple(args[:max_args-1]+ # [(" ".join(args[max_args-1:]))])) func(*tuple(args[:max_args-1]+ [(" ".join(args[max_args-1:]))])) else: debug("bad ars"+" ".join(map(str, (args,len(args), min_args, max_args))), 2) self.last_error = ("wrong number of args: " + "%i (min_args: %i max_args: %i" % ((len(args), min_args, max_args))) else: debug("command not found", 2) self.last_error = "Command not found: %s" % cmd elif self.default_func: # debug("launch default func") self.default_func(*(extra_args+(line, ))) #! improve, +extra_args else: self.last_error = "no default function given" #debug("debug(","*30) return not self.last_error