Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #24378 > unrolled thread
| Started by | Josh English <Joshua.R.English@gmail.com> |
|---|---|
| First post | 2012-06-23 18:58 -0700 |
| Last post | 2012-06-24 12:44 +0200 |
| Articles | 9 — 3 participants |
Back to article view | Back to comp.lang.python
Getting lazy with decorators Josh English <Joshua.R.English@gmail.com> - 2012-06-23 18:58 -0700
Re: Getting lazy with decorators Peter Otten <__peter__@web.de> - 2012-06-24 10:07 +0200
Re: Getting lazy with decorators Josh English <Joshua.R.English@gmail.com> - 2012-06-25 13:04 -0700
Re: Getting lazy with decorators Peter Otten <__peter__@web.de> - 2012-06-26 08:57 +0200
Re: Getting lazy with decorators Josh English <Joshua.R.English@gmail.com> - 2012-06-27 16:09 -0700
Re: Getting lazy with decorators Josh English <Joshua.R.English@gmail.com> - 2012-06-27 16:09 -0700
Re: Getting lazy with decorators Peter Otten <__peter__@web.de> - 2012-06-26 09:03 +0200
Re: Getting lazy with decorators Josh English <Joshua.R.English@gmail.com> - 2012-06-25 13:04 -0700
Re: Getting lazy with decorators "Stefan H. Holek" <stefan@epy.co.at> - 2012-06-24 12:44 +0200
| From | Josh English <Joshua.R.English@gmail.com> |
|---|---|
| Date | 2012-06-23 18:58 -0700 |
| Subject | Getting lazy with decorators |
| Message-ID | <9d0c01f4-4430-4a45-8776-20d8dede9e14@googlegroups.com> |
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?
[toc] | [next] | [standalone]
| From | Peter Otten <__peter__@web.de> |
|---|---|
| Date | 2012-06-24 10:07 +0200 |
| Message-ID | <mailman.1442.1340525279.4697.python-list@python.org> |
| In reply to | #24378 |
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()
[toc] | [prev] | [next] | [standalone]
| From | Josh English <Joshua.R.English@gmail.com> |
|---|---|
| Date | 2012-06-25 13:04 -0700 |
| Message-ID | <513d25ea-e977-4b84-b388-bfb23c171aae@googlegroups.com> |
| In reply to | #24382 |
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?
[toc] | [prev] | [next] | [standalone]
| From | Peter Otten <__peter__@web.de> |
|---|---|
| Date | 2012-06-26 08:57 +0200 |
| Message-ID | <mailman.1510.1340693868.4697.python-list@python.org> |
| In reply to | #24442 |
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()
[toc] | [prev] | [next] | [standalone]
| From | Josh English <Joshua.R.English@gmail.com> |
|---|---|
| Date | 2012-06-27 16:09 -0700 |
| Message-ID | <mailman.1572.1340838595.4697.python-list@python.org> |
| In reply to | #24461 |
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
[toc] | [prev] | [next] | [standalone]
| From | Josh English <Joshua.R.English@gmail.com> |
|---|---|
| Date | 2012-06-27 16:09 -0700 |
| Message-ID | <3e726c11-f3ee-4cd7-983c-cd4ab923a0b1@googlegroups.com> |
| In reply to | #24461 |
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
[toc] | [prev] | [next] | [standalone]
| From | Peter Otten <__peter__@web.de> |
|---|---|
| Date | 2012-06-26 09:03 +0200 |
| Message-ID | <mailman.1511.1340694230.4697.python-list@python.org> |
| In reply to | #24442 |
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").
[toc] | [prev] | [next] | [standalone]
| From | Josh English <Joshua.R.English@gmail.com> |
|---|---|
| Date | 2012-06-25 13:04 -0700 |
| Message-ID | <mailman.1496.1340654681.4697.python-list@python.org> |
| In reply to | #24382 |
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?
[toc] | [prev] | [next] | [standalone]
| From | "Stefan H. Holek" <stefan@epy.co.at> |
|---|---|
| Date | 2012-06-24 12:44 +0200 |
| Message-ID | <mailman.1453.1340535958.4697.python-list@python.org> |
| In reply to | #24378 |
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@epy.co.at
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web