multiple inheritance of a dynamic list of classes?

Michele Simionato michele.simionato at gmail.com
Mon Feb 12 13:56:15 EST 2007


On Feb 12, 4:48 pm, deviceran... at gmail.com wrote:
> - is there a better way than using multiple inheritance to plug-in
> dynamically commands in a Cmd command line?

I had to solve the same problem recently, and I decided to avoid
multiple
inheritance by using delegation. My idea was to make the Cmd class a
wrapper
around an object with 'do_' methods. I give you the code as it is,
without
much explanations: just run it and give "help" on the command line.
You can cut the part about the 'do_input' method which I needed in
order
to pass lists of strings to the inner object.
Hope you can find some useful trick for solving your problem.

  Michele Simionato

import sys, cmd, traceback, inspect
try: # Python 2.5
    from functools import update_wrapper
except ImportError:
    def update_wrapper(wrapper,
                   wrapped,
                   assigned=('__module__', '__name__', '__doc__'),
                   updated=('__dict__',)):
        for attr in assigned:
            setattr(wrapper, attr, getattr(wrapped, attr))
        for attr in updated:
            getattr(wrapper, attr).update(getattr(wrapped, attr))
        return wrapper

def makewrapper(meth):
    if inspect.ismethod(meth):
        func = meth.im_func
    elif inspect.isfunction(meth):
        func = meth
    else:
        raise TypeError('%r must be a regular function or method' %
meth)
    return update_wrapper(lambda self, arg: meth(arg), func)

# dispatch the input dictionary to self.innerobj
def do_input(self, arg):
    if arg.split(): # passed some argument of type 'name <args>'
        try:
            name, args = arg.split(' ', 1)
        except ValueError:
            print 'Wrong format: use the format input <name> <values>'
        else:
            self.inputdict[name] = args.split(self.inputsep)
    else:
        self.innerobj.input(self.inputdict)
        self.inputdict = {}

def copy_interface_methods(innerobj, _CLI):
    for attr in dir(innerobj):
        if attr.startswith('do_'):
            setattr(_CLI, attr, makewrapper(getattr(innerobj, attr)))
        elif attr == 'input':
            do_input.__doc__ = innerobj.input.__doc__
            _CLI.do_input = do_input

class CLI(cmd.Cmd, object):
    """
    Wraps an object with 'do_' methods with a command line interface.
    """
    def __new__(cls, innerobj, completekey='tab', stdin=None,
stdout=None,
             nonblocking=False):
        class _CLI(cls):
            prompt = 'CLI> '
            if stdin is not None:
                use_rawinput = False
        copy_interface_methods(innerobj, _CLI)
        return super(CLI, cls).__new__(
            _CLI, innerobj, completekey, stdin, stdout)

    def __init__(self, innerobj, completekey='tab', stdin=None,
stdout=None):
        self.innerobj = innerobj
        if hasattr(self, 'do_input'):
            self.inputdict = {}
            self.inputsep = '|'
        cmd.Cmd.__init__(self, completekey, stdin, stdout)

    def onecmd(self, line): # enable comments
        if not line.startswith('#'):
            return super(CLI, self).onecmd(line)

    def emptyline(self): # does not repeat the last command
        pass

    def do_EOF(self, arg):
        "Called when you enter CTRL-D, it stops the command loop"
        return 1

if __name__ == '__main__': # test
    class PrintableObj(object):
        def __init__(self, x, y):
            self.x = x
            self.y = y
        def do_print(self, arg):
            "example"
            print self.x, self.y
        def input(self, dict_of_lists):
            'just print the entered value'
            vars(self).update(dict_of_lists)
            print dict_of_lists

    cli = CLI(PrintableObj([], []))
    cli.cmdloop()




More information about the Python-list mailing list