Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: Python: Python

Getting lazy with decorators

 

 

Python python RSS feed   Index | Next | Previous | View Threaded


Joshua.R.English at gmail

Jun 23, 2012, 6:58 PM

Post #1 of 9 (732 views)
Permalink
Getting lazy with decorators

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?
--
http://mail.python.org/mailman/listinfo/python-list


__peter__ at web

Jun 24, 2012, 1:07 AM

Post #2 of 9 (707 views)
Permalink
Re: Getting lazy with decorators [In reply to]

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()


--
http://mail.python.org/mailman/listinfo/python-list


stefan at epy

Jun 24, 2012, 3:44 AM

Post #3 of 9 (712 views)
Permalink
Re: Getting lazy with decorators [In reply to]

On 24.06.2012, at 03:58, Josh English wrote:

> I'm creating a cmd.Cmd class, and I have developed a helper method to easily handle help_xxx methods.

When I need custom help processing I tend to simply override do_help().

Stefan

<plug>
http://pypi.python.org/pypi/kmd
</plug>

--
Stefan H. Holek
stefan [at] epy

--
http://mail.python.org/mailman/listinfo/python-list


Joshua.R.English at gmail

Jun 25, 2012, 1:04 PM

Post #4 of 9 (707 views)
Permalink
Re: Getting lazy with decorators [In reply to]

On Sunday, June 24, 2012 1:07:45 AM UTC-7, Peter Otten wrote:
>
> 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()


Okay. If I understand this, you are adding a help attribute to the class method. The help attribute is itself a function.

There is nothing in the documentation (that I have found) that points to this solution. Even after reading the do_help method in the cmd.Cmd source, I don't see this as working.

Yet it works.

How?
--
http://mail.python.org/mailman/listinfo/python-list


Joshua.R.English at gmail

Jun 25, 2012, 1:04 PM

Post #5 of 9 (705 views)
Permalink
Re: Getting lazy with decorators [In reply to]

On Sunday, June 24, 2012 1:07:45 AM UTC-7, Peter Otten wrote:
>
> 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()


Okay. If I understand this, you are adding a help attribute to the class method. The help attribute is itself a function.

There is nothing in the documentation (that I have found) that points to this solution. Even after reading the do_help method in the cmd.Cmd source, I don't see this as working.

Yet it works.

How?
--
http://mail.python.org/mailman/listinfo/python-list


__peter__ at web

Jun 25, 2012, 11:57 PM

Post #6 of 9 (707 views)
Permalink
Re: Getting lazy with decorators [In reply to]

Josh English wrote:

> On Sunday, June 24, 2012 1:07:45 AM UTC-7, Peter Otten wrote:
>>
>> 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()
>
>
> Okay. If I understand this, you are adding a help attribute to the class
> method. The help attribute is itself a function.
>
> There is nothing in the documentation (that I have found) that points to
> this solution.

That's because I "invented" it.

@deco
def func(...): ...

is equivalent to

def func(...): ...
func = deco(func)

so only one assignment is ever made in the enclosing namespace. If you have
more targets you have to put them somewhere else. Making the help_xxx()
function an attribute of do_xxx() seemed the obvious choice.

> Even after reading the do_help method in the cmd.Cmd
> source, I don't see this as working.

do_help(name)

looks up a "help_" + name method and invokes it if that lookup succeeds.

> Yet it works.

In the example above do_help() looks for a help_done attribute which doesn't
exist. As a fallback the __getattr__() method is invoked with the
"help_done" argument, finds that the name starts with "help_" and proceeds
with

>> helpfunc = getattr(self, "do_" + name[5:]).help

i. e. it looks for getattr(self, "do_help") which does exist and then stores
its help attribute (if that doesn't exist an AttributeError is implicitly
raised) in helpfunc. helpfunc is then added as "help_done" to the class

>> setattr(self.__class__, name, helpfunc)

and looked up again in the instance:

>> return getattr(self, name)

This time it exists and from the class attribute help_done an instance
method is created, returned from __getattr__() and invoked by do_help().

PS: Stefan is probably right that you should just override do_help()

--
http://mail.python.org/mailman/listinfo/python-list


__peter__ at web

Jun 26, 2012, 12:03 AM

Post #7 of 9 (712 views)
Permalink
Re: Getting lazy with decorators [In reply to]

Peter Otten wrote:

>>>helpfunc = getattr(self, "do_" + name[5:]).help
>
> i. e. it looks for getattr(self, "do_help") which does exist and then

Sorry that should be getattr(self, "do_done").


--
http://mail.python.org/mailman/listinfo/python-list


Joshua.R.English at gmail

Jun 27, 2012, 4:09 PM

Post #8 of 9 (701 views)
Permalink
Re: Getting lazy with decorators [In reply to]

On Monday, June 25, 2012 11:57:39 PM UTC-7, Peter Otten wrote:
> >
> > There is nothing in the documentation (that I have found) that points to
> > this solution.
>
> That's because I "invented" it.
>

Oh bother. The lines I completely overlooked were in your __getattr__ override.

Boy is my face red.

On further experimentation, adding a do_xxx command without the decorator still works...ish. The undecorated do_xxx is still considered to have a help function, and it prints the raw docstring (instead of using the show_help method to clean it up).

Josh
--
http://mail.python.org/mailman/listinfo/python-list


Joshua.R.English at gmail

Jun 27, 2012, 4:09 PM

Post #9 of 9 (702 views)
Permalink
Re: Getting lazy with decorators [In reply to]

On Monday, June 25, 2012 11:57:39 PM UTC-7, Peter Otten wrote:
> >
> > There is nothing in the documentation (that I have found) that points to
> > this solution.
>
> That's because I "invented" it.
>

Oh bother. The lines I completely overlooked were in your __getattr__ override.

Boy is my face red.

On further experimentation, adding a do_xxx command without the decorator still works...ish. The undecorated do_xxx is still considered to have a help function, and it prints the raw docstring (instead of using the show_help method to clean it up).

Josh
--
http://mail.python.org/mailman/listinfo/python-list

Python python RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact Gossamer Threads
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.