I'm trying to implement a "subcommand" system as an inheritable class in Python. My expected use case is something like:
from command import Command
import sys
class MyCommand(Command):
@Command.subcommand
def foo(self):
print "this can be run as a subcommand"
def bar(self):
print "but this is a plain method and isn't exposed to the CLI"
MyCommand()(*sys.argv)
# at the command line, the user runs "mycommand.py foo"
I implemented Command.subcommand
as a static method and everything worked fine until I tried to add a subcommand to the parent class, which got me TypeError: 'staticmethod' object is not callable
. In hindsight, it's obvious that this won't work:
class Command(object):
@staticmethod
def subcommand(method):
method.is_subcommand = True
return method
@subcommand
def common(self):
print "this subcommand is available to all child classes"
The only alternative I've found so far is to declare the subcommand
decorator outside the parent class, then inject it after the class definition is complete.
def subcommand(method):
method.is_subcommand = True
return method
class Command(object):
@subcommand
def common(self):
print "this subcommand is available to all child classes"
Command.subcommand = staticmethod(subcommand)
del subcommand
However, as someone who never used Python before decorators were added, this feels very clunky to me. Is there a more elegant way to accomplish this?
There are two solutions to this problem that I can think of. The simplest is to make it a static method after you're done using it in the parent class:
class Command(object):
def subcommand(method): # Regular function in class definition scope.
method.is_subcommand = True
return method
@subcommand
def common(self):
print "this subcommand is available to all child classes"
subcommand = staticmethod(subcommand)
# Now a static method. Can no longer be called during class definition phase.
This is somewhat fragile in that you can't use it in the parent class after you make it a static method. The more robust way to do this is to add an intermediate class:
class Command(object):
@staticmethod
def subcommand(method):
method.is_subcommand = True
return method
class CommandBase(Command):
@Command.subcommand
def common(self):
print "this subcommand is available to all child classes"
You can now inherit all of your classes from CommandBase
instead of Command
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With