14.3.5.2 Adding new actions
Adding new actions is a bit trickier, because you have to understand that optparse has a couple of classifications for actions:
- actions that result in optparse storing a value to an attribute of the current OptionValues instance; these options require a dest attribute to be supplied to the Option constructor
- actions that take a value from the command line and expect it to be of a certain type; or rather, a string that can be converted to a certain type. These options require a type attribute to the Option constructor.
These are overlapping sets: some default ``store'' actions are store
,
store_const
, append
, and count
, while the default ``typed''
actions are store
, append
, and callback
.
When you add an action, you need to categorize it by listing it in at least one of the following class attributes of Option (all are lists of strings):
- all actions must be listed in ACTIONS
- ``store'' actions are additionally listed here
- ``typed'' actions are additionally listed here
- actions that always take a type (i.e. whose options always take a
value) are additionally listed here. The only effect of this is
that optparse assigns the default type,
string
, to options with no explicit type whose action is listed inALWAYS_TYPED_ACTIONS
.
In order to actually implement your new action, you must override Option's take_action() method and add a case that recognizes your action.
For example, let's add an extend
action. This is similar to the
standard append
action, but instead of taking a single value from
the command-line and appending it to an existing list, extend
will
take multiple values in a single comma-delimited string, and extend an
existing list with them. That is, if "-names"
is an extend
option of type string
, the command line
--names=foo,bar --names blah --names ding,dong
would result in a list
["foo", "bar", "blah", "ding", "dong"]
Again we define a subclass of Option:
class MyOption (Option): ACTIONS = Option.ACTIONS + ("extend",) STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",) TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",) ALWAYS_TYPED_ACTIONS = Option.ALWAYS_TYPED_ACTIONS + ("extend",) def take_action(self, action, dest, opt, value, values, parser): if action == "extend": lvalue = value.split(",") values.ensure_value(dest, []).extend(lvalue) else: Option.take_action( self, action, dest, opt, value, values, parser)
Features of note:
-
extend
both expects a value on the command-line and stores that value somewhere, so it goes in both STORE_ACTIONS and TYPED_ACTIONS -
to ensure that optparse assigns the default type of
string
toextend
actions, we put theextend
action inALWAYS_TYPED_ACTIONS
as well -
MyOption.take_action() implements just this one new action, and
passes control back to Option.take_action() for the standard
optparse actions
-
values
is an instance of the optparse_parser.Values class, which provides the very useful ensure_value() method. ensure_value() is essentially getattr() with a safety valve; it is called asvalues.ensure_value(attr, value)
If the
attr
attribute ofvalues
doesn't exist or is None, then ensure_value() first sets it tovalue
, and then returns 'value. This is very handy for actions likeextend
,append
, andcount
, all of which accumulate data in a variable and expect that variable to be of a certain type (a list for the first two, an integer for the latter). Using ensure_value() means that scripts using your action don't have to worry about setting a default value for the option destinations in question; they can just leave the default as None and ensure_value() will take care of getting it right when it's needed.