14.3.5.2 Adding new actions

Python 2.5

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 in ALWAYS_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 to extend actions, we put the extend action in ALWAYS_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 as
    values.ensure_value(attr, value)
    

    If the attr attribute of values doesn't exist or is None, then ensure_value() first sets it to value, and then returns 'value. This is very handy for actions like extend, append, and count, 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.

See About this document... for information on suggesting changes.