Getting lazy with decorators

Peter Otten __peter__ at web.de
Sun Jun 24 10:07:45 CEST 2012


Josh English wrote:

> I'm creating a cmd.Cmd class, and I have developed a helper method to
> easily handle help_xxx methods.
> 
> I'm trying to figure out if there is an even lazier way I could do this
> with decorators.
> 
> Here is the code:
> *********************
> import cmd
> 
> 
> def add_help(func):
>     if not hasattr(func, 'im_class'):
>         return func #probably should raise an error
>     cls = func.im_class
>     setattr(cls, func.im_func.__name__.replace("do","help"), None)
> 
>     return func
> 
> 
> class BaseCmd(cmd.Cmd):
>     def __init__(self, *args, **kwargs):
>         cmd.Cmd.__init__(self, *args, **kwargs)
> 
>     def show_help(self, func):
>         print "\n".join((line.strip() for line in
>         func.__doc__.splitlines()))
> 
>     @add_help
>     def do_done(self, line):
>         """done
>         Quits this and goes to higher level or quits the application.
>         I mean, what else do you expect?
>         """
>         return True
> 
> if __name__=='__main__':
>     c = BaseCmd()
> 
>     print c.help_done
> 
> 
> *********************
> 
> This generates "AttributeError: BaseCmd instance has no attribute
> 'help_done'"
> 
> The show_help method is the shortcut I want to use (I'm pretty sure it's
> from Doug Hellman's site). I'm wondering if it's possible to use a
> decorator such as add_help to automatically create the appropriate
> help_xxx function.
> 
> In the decorator, I can get the function and the name of the class, but I
> can't find the instance of  the class that the method is attached to.
> Maybe this is just one step of lazy too far.
> 
> 
> Am I right in thinking that I can't do this? There is no way to access the
> class instance from the method?

You cannot access a class instance because even the class itself doesn't 
exist yet. You could get hold of the class namespace with sys._getframe(),

def add_help(f):
    exec """\
def help_%s(self):
    f = getattr(self, %r)
    self.show_help(f)
""" % (f.__name__[3:], f.__name__) in sys._getframe(1).f_locals
    return f

but here's a simpler approach:

import cmd

def add_help(f):
    def help(self):
        self.show_help(f)
    f.help = help
    return f


class BaseCmd(cmd.Cmd):
    def __init__(self, *args, **kwargs):
        cmd.Cmd.__init__(self, *args, **kwargs)

    def show_help(self, func):
        print "\n".join((line.strip() for line in 
func.__doc__.splitlines()))

    def __getattr__(self, name):
        if name.startswith("help_"):
            helpfunc = getattr(self, "do_" + name[5:]).help
            setattr(self.__class__, name, helpfunc)
            return getattr(self, name)
        raise AttributeError

    @add_help
    def do_done(self, line):
        """done
        Quits this and goes to higher level or quits the application.
        I mean, what else do you expect?
        """
        return True

if __name__=='__main__':
    c = BaseCmd()
    c.cmdloop()




More information about the Python-list mailing list