a8e965c3c2f24d31f0841f4d499129a1b7950f29 — Stephen Brennan 1 year, 8 months ago 6c68d0f
Add docstring updates and README.rst update
2 files changed, 78 insertions(+), 22 deletions(-)

M subc.py
M README.rst => README.rst +32 -3
@@ 36,11 36,14 @@ and running your application:
.. code:: python

    if __name__ == '__main__':
        MyCmd.main('description of app')

Advanced Use

Intermediate Base Classes

You may find yourself wanting to create intermediate subclasses for your
application, in order to share common functionality. For example, you might
create a class for all commands which handle a single file as an argument:

@@ 65,8 68,34 @@ example, given the following class hierarchy:
The non-leaf commands (marked with an asterisk) will not be included as
executable commands. Only leaf classes will be executable.

``subc`` is a very simple library. If you have other advanced uses, read the
Default Command

When the user does not provide any argument on the command-line, the default
action is to raise an Exception which states "you must select a sub-command".
You can provide a default command to run instead, via the ``default`` argument
to ``main()`` (or ``add_subcommands()``). For example:

.. code:: python

    if __name__ == '__main__':
        MyCmd.main('description', default='help')

The above code will run the ``help`` subcommand when no subcommand is specified.
Note that in this case, the default sub-command may not receive all of its
expected arguments.

Shortest Prefix Aliasing

``subc`` has an optional feature which allows the user to specify a subcommand
by the shortest prefix which uniquely identifies the subcommand, or any longer
prefix thereof. As an example, imagine a git command with the following
sub-commands: clone, checkout, commit, cherry-pick. The shortest prefix aliasing
would allow you to run "git clone" by executing ``git cl``, since only "clone"
begins with "cl". You could also execute "git clone" with a longer prefix like
``git clo``. The feature can be enabled by setting ``shortest_prefix`` to true
in ``main()`` or ``add_subcommands()``.


M subc.py => subc.py +46 -19
@@ 103,6 103,29 @@ class Command(ABC):
        default: t.Optional[str] = None,
        shortest_prefix: bool = False
    ) -> argparse.ArgumentParser:
        Add all subcommands which are descendents of this class to parser.

        This call is required in order to setup an argument parser before
        parsing args and executing sub-command. Each sub-command must be a
        sub-class (or a further descendent) of this class. Only leaf subclasses
        are considered commands -- internal "nodes" in the hierarchy are skipped
        as they are assumed to be helpers.

        A default command to run may be set with 'default'. When the argument
        parser is called without a sub-command, this command will automatically
        execute (rather than simply raising an Exception).

        Shortest prefix sub-command matching allows the user to select a
        sub-command by using any string which is a prefix of exactly one
        command, e.g. "git cl" rather than "git clone".

        :param parser: Argument parser which is already created for this app
        :param default: Name of the command which should be executed if none is
        :param shortest_prefix: Enable shortest prefix command matching
        :returns: the modified parser (this can be ignored)
        default_set = False
        subparsers = parser.add_subparsers(title='sub-command')
        subclasses = collections.deque(cls.__subclasses__())

@@ 139,20 162,6 @@ class Command(ABC):
        return parser

    def parse_args(
            default: t.Optional[str] = None,
            args: t.Optional[t.List[str]] = None,
            shortest_prefix: bool = False,
    ) -> argparse.Namespace:
        parser = argparse.ArgumentParser(description=description)
            parser, default=default, shortest_prefix=shortest_prefix,
        return parser.parse_args(args)

    def main(
            description: str,

@@ 160,10 169,28 @@ class Command(ABC):
            args: t.Optional[t.List[str]] = None,
            shortest_prefix: bool = False,
    ) -> t.Any:
        ns = cls.parse_args(
        Parse arguments and run the selected sub-command.

        This helper function is expected to be the main, most useful API for
        subc, although you could directly call the add_commands() method.
        Creates an argument parser, adds every discovered sub-command, parses
        the arguments, and executes the selected sub-command, returning its
        return value.

        Custom arguments (rather than sys.argv) can be specified using "args".
        Details on the arguments "default" and "shortest_prefix" can be found
        in the docstring for add_commands().

        :param description: Description of the application (for help output)
        :param default: Default command name
        :param args: If specified, a list of args to use in place of sys.argv
        :param shortest_prefix: whether to enable prefix matching
        :returns: Return value of the selected command's run() method
        parser = argparse.ArgumentParser(description=description)
            parser, default=default, shortest_prefix=shortest_prefix,
        ns = parser.parse_args(args=args)
        return ns.func(ns)