Adding code and methods to a class dynamically

S

Sarir Khamsi

I have a class (Command) that derives from cmd.Cmd and I want to add
methods to it dynamically. I've added a do_alias() method and it would
be nice if I could turn an alias command into a real method of Command
(that way the user could get help and name completion). The code would
be generated dynamically from what gets passed to the do_alias()
method. I've tried looking in the Python cookbook and have tried:

def funcToMethod(func, clas, method_name=None):
setattr(clas, method_name or func.__name__, func)

class Command(object, cmd.Cmd):
# ...
def do_f1(self, rest): print 'In Command.do_f1()'
def do_alias(self, rest):
rest.strip() # remove leading and trailing whitespace
pat = re.compile(r'^(\w+)\s+(\w+)$')
mo = pat.search(rest)
if mo:
newName = mo.group(1)
existingName = mo.group(2)
code = 'def do_' + newName + '(self, rest):\n'
code += ' self.do_' + existingName + '(rest)\n'
exec code
funcToMethod(getattr(newModule, 'do_' + existingName),
self,
'do_' + 'existingName')
else:
print 'Invalid alias command'

but this does not seem to work. What I get is:

$ importDynamic.py
(Cmd) do alias x f1
*** Unknown syntax: do alias x f1
(Cmd)

Any suggestions? Thanks.

Sarir
 
P

Peter Hansen

Sarir said:
I have a class (Command) that derives from cmd.Cmd and I want to add
methods to it dynamically. I've added a do_alias() method and it would
be nice if I could turn an alias command into a real method of Command
(that way the user could get help and name completion). The code would
be generated dynamically from what gets passed to the do_alias()
method. I've tried looking in the Python cookbook and have tried:

def funcToMethod(func, clas, method_name=None):
setattr(clas, method_name or func.__name__, func)

class Command(object, cmd.Cmd):
# ...
def do_f1(self, rest): print 'In Command.do_f1()'
def do_alias(self, rest):
rest.strip() # remove leading and trailing whitespace
pat = re.compile(r'^(\w+)\s+(\w+)$')
mo = pat.search(rest)
if mo:
newName = mo.group(1)
existingName = mo.group(2)
code = 'def do_' + newName + '(self, rest):\n'
code += ' self.do_' + existingName + '(rest)\n'
exec code
funcToMethod(getattr(newModule, 'do_' + existingName),
self,
'do_' + 'existingName')
else:
print 'Invalid alias command'

but this does not seem to work.

If it's truly just an alias you want, then something like this should
work better. Replace everything in the if mo: section with this:

if mo:
newName = mo.group(1)
existingName = mo.group(2)
existingMethod = getattr(self, 'do_' + existingName)
setattr(self, 'do_' + newName, existingMethod)


-Peter
 
S

Sarir Khamsi

Peter Hansen said:
If it's truly just an alias you want, then something like this should
work better. Replace everything in the if mo: section with this:

if mo:
newName = mo.group(1)
existingName = mo.group(2)
existingMethod = getattr(self, 'do_' + existingName)
setattr(self, 'do_' + newName, existingMethod)

Thanks, this is amazingly more simple than what I had. This works
fine but I'm looking at the code for cmd and don't really see how you
get completion to work on the new alias.

Also, I would like the alias to have arguments:

alias newName existingName arg1 arg2 ...

Currying comes to mind, but I haven't tried it yet. Thanks again for
the help.

Sarir
 
P

Peter Hansen

Sarir said:
Thanks, this is amazingly more simple than what I had. This works
fine but I'm looking at the code for cmd and don't really see how you
get completion to work on the new alias.

I'm not sure what "completion" means in this case, and I'm not aware of
any "command-line completion" support in cmd.Cmd though it may well be
there, so I can't say. Certainly there is nothing in any way different
about the new attribute created by the alias code, as both it and the
original attribute are bound to exactly the same method.
Also, I would like the alias to have arguments:
alias newName existingName arg1 arg2 ...

Currying comes to mind, but I haven't tried it yet. Thanks again for
the help.

It sounds like currying, but I'd suggest implementing such a thing with
a completely separate layer on top of the existing Cmd stuff, probably
through overriding .default() and doing a lookup in a dictionary, then
building a command line with the original command and maybe executing it
with .onecmd() if that can work from within the .cmdloop(). Lots of
possibilities there; haven't tried any myself.

-Peter
 
S

Sarir Khamsi

Peter Hansen said:
I'm not sure what "completion" means in this case, and I'm not aware
of any "command-line completion" support in cmd.Cmd though it may well
be there, so I can't say. Certainly there is nothing in any way
different about the new attribute created by the alias code, as both
it and the original attribute are bound to exactly the same method.

If you have the readline module, you get TAB command completion for
free (a la bash, emacs, etc)...or maybe readline only gives you
emacs-style command history editing, or both...not sure.
 
P

Peter Hansen

Sarir said:
If you have the readline module, you get TAB command completion for
free (a la bash, emacs, etc)...or maybe readline only gives you
emacs-style command history editing, or both...not sure.

Cool. The answer to whether a particular approach preserves command
completion ability then, unfortunately, depends on how it's implemented.
If it's totally dynamic, then the aliases my approach created will be
supported, but if it's done at Cmd creation time, or in some other
static manner, the dynamically created aliases won't be found.

-Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,239
Members
46,828
Latest member
LauraCastr

Latest Threads

Top