6.20.5.2 Adding new actions

###
6.20.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:

``store'' actions
  actions that result in `optparse` storing a value to an attribute of the OptionValues instance; these options require a dest attribute to be supplied to the Option constructor
``typed'' actions
  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.

Some default ``store'' actions are store, store_const, append, and count. The default ``typed'' actions are store, append, and callback.

When you add an action, you need to decide if it's a ``store'' action, a ``typed'', neither, or both. Three class attributes of `Option` (or your `Option` subclass) control this:

`ACTIONS`
  All actions must be listed as strings in ACTIONS.

`STORE_ACTIONS`
  ``store'' actions are additionally listed here.

`TYPED_ACTIONS`
  ``typed'' actions are additionally listed here.

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",)

        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`.

- `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 `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.