argparse documentation Help

Subcommands

Sophisticated command line tools, like git, have many subcommands (e.g., git clone, git commit, git push, etc.), each with its own set of arguments. There are few ways how to use subcommands with argparse.

Subcommand type

General approach to declare subcommands is to use SubCommand type. This type is behaving like a SumType from standard library with few additions:

  • It allows no command to be chosen.

  • It supports at most one default subcommand (see below for details).

import argparse; struct cmd1 {} struct cmd2 {} struct cmd3 {} struct T { // name of the subcommand is the same as a name of the type by default SubCommand!(cmd1, cmd2, cmd3) cmd; } T t; assert(CLI!T.parseArgs(t, [])); assert(t == T.init); assert(CLI!T.parseArgs(t, ["cmd1"])); assert(t == T(typeof(T.cmd)(cmd1.init))); assert(CLI!T.parseArgs(t, ["cmd2"])); assert(t == T(typeof(T.cmd)(cmd2.init))); assert(CLI!T.parseArgs(t, ["cmd3"])); assert(t == T(typeof(T.cmd)(cmd3.init)));

Subcommands with shared common arguments

In some cases command line tool has arguments that are common across all subcommands. They can be specified as regular arguments in a struct that represents the whole program:

import argparse; struct min {} struct max {} struct sum {} struct T { int[] n; // common argument for all subcommands // name of the subcommand is the same as a name of the type by default SubCommand!(min, max, sum) cmd; } T t; assert(CLI!T.parseArgs(t, ["min","-n","1","2","3"])); assert(t == T([1,2,3],typeof(T.cmd)(min.init))); t = T.init; assert(CLI!T.parseArgs(t, ["max","-n","4","5","6"])); assert(t == T([4,5,6],typeof(T.cmd)(max.init))); t = T.init; assert(CLI!T.parseArgs(t, ["sum","-n","7","8","9"])); assert(t == T([7,8,9],typeof(T.cmd)(sum.init)));

Subcommand name and aliases

Using type name as a subcommand name in command line might not be convenient, moreover, the same subcommand might have multiple names in command line (e.g. short and long versions). Command UDA can be used to list all acceptable names for a subcommand:

import argparse; @Command("minimum", "min") struct min {} @Command("maximum", "max") struct max {} struct T { int[] n; // common argument for all subcommands SubCommand!(min, max) cmd; } T t; assert(CLI!T.parseArgs(t, ["minimum","-n","1","2","3"])); assert(t == T([1,2,3],typeof(T.cmd)(min.init))); t = T.init; assert(CLI!T.parseArgs(t, ["max","-n","4","5","6"])); assert(t == T([4,5,6],typeof(T.cmd)(max.init)));

Default subcommand

Default subcommand is one that is selected when user does not specify any subcommand in the command line. To mark a subcommand as default, use Default template:

import argparse; struct min {} struct max {} struct sum {} struct T { int[] n; // common argument for all subcommands // name of the subcommand is the same as a name of the type by default SubCommand!(min, max, Default!sum) cmd; } T t; assert(CLI!T.parseArgs(t, ["-n","7","8","9"])); assert(t == T([7,8,9],typeof(T.cmd)(sum.init))); t = T.init; assert(CLI!T.parseArgs(t, ["max","-n","4","5","6"])); assert(t == T([4,5,6],typeof(T.cmd)(max.init)));

Enumerating subcommands in CLI mixin

One of the possible ways to use subcommands with argparse is to list all subcommands in CLI mixin. Although this might be a useful feature, it is very limited: CLI mixin only allows overriding of the main function for this case:

import argparse; struct cmd1 {} struct cmd2 {} struct cmd3 {} // This mixin defines standard main function that parses command line and calls the provided function: mixin CLI!(cmd1, cmd2, cmd3).main!((cmd) { import std.stdio; cmd.writeln; });
Last modified: 07 August 2024