Sub-parsers
00:00 In the previous lesson, I showed you how to write your own action. In this lesson, I’ll be covering sub-parsers.
00:08
A sub-parser gives you the ability to write commands within your script. This allows you to write scripts that have different actions. depending on the arguments, like git
does with checkout
, status
, and its other commands.
00:22
A common pattern is to associate a function with each subcommand. Rather than writing a giant if
… else
block of all the possible commands, you can use the .set_defaults()
trick. .set_defaults()
allows you to set values onto the namespace explicitly.
00:37 You can use it to set a value that points to your command function and then just invoke the value. Confused? Yeah, it takes a little. Let’s go look at it in some code.
00:50 This script supports three subcommands, One for each of the Indiana Jones movies. There were only three. Again, I’ll fight you. I’ve defined a function for each of the subcommands.
01:02
The first one is raiders()
, and it takes args
as a parameter. This isn’t required by the sub-parser, but it is good practice when using the methodology that I’m going to be showing you. By passing in the namespace object, your subcommand can get at any parameters that were passed to it.
01:19
My raiders()
subcommand prints out a quote and uses the animal
argument from the args
namespace. The doom()
command is similar. As I’m passing in args
, I can use any of the argument definitions you’ve seen up until now. The doom()
subcommand uses a repeat
flag.
01:38 And for the final movie—you heard me—I use an argument to specify just how many camels Sallah brought. With the commands defined, let’s see how to wire this into the parser.
01:49 Scrolling down … This should be familiar, nothing different with this line. But here’s the new bit. Similar to creating mutually exclusive groups, you use the parser to create a sub-parser.
02:03
The title
parameter is used in the help message, and the dest
attribute indicates where to store the subcommand name. As I only want the user using the sub-parser and nothing else, I’ve set it to be required. Now to add the subcommands.
02:20
You take the sub-parser and add a parser. I’ve called mine sub
. The name of the subcommand gets passed into the .add_parser()
call.
02:30
This is the argument that goes after the subcommand. If you invoke the Indy script with the raiders
subcommand, you have to give it the name of an animal. Spoiler alert. It’ll be snakes.
02:42
It’s always snakes. Here’s that func
trick I so badly attempted to explain before. The .set_defaults()
method on any parser, sub-parser or otherwise, sets one or more name-value pairs on the namespace. Here, I’m setting something called func
with a value of the raiders
function.
03:02
That’s a reference to the function itself. In a few lines, I’ll be using this default argument to invoke my subcommand function. For the doom
command, I do something similar: another sub-parser, a new argument—this time, for repeating—and I use the .set_defaults()
to point the func
argument at the doom
function. I also snuck something extra in here.
03:25
The metavar
parameter to the .add_argument()
method changes the name of the variable in the help message. Without this, you’d see -r
and then REPEAT
in all caps. With it, you see -r
and NUM
in all caps.
03:41
And the third subcommand, in the same vein: new parser, new argument, new association for the func
argument, this time pointing to the crusade
function. Scrolling down … Line 37 is no different than any other parser you’ve built.
03:58
The magic is on line 38. Line 37 creates the namespace like usual. depending on what subcommand was invoked, a different .set_defaults()
method will have been called, changing the value of func
inside of the namespace.
04:14
This overwriting mechanism means you can take the value of func
and invoke it as a function. Each subcommand was associated with a different function at the top of the script. So this call will invoke the corresponding function. Tricky, huh?
04:30
You’ll recall that I wrote each of the subcommands to take the args
namespace as a parameter. This is why on line 38, when I invoke the function, I pass in the args
namespace.
04:41 This gives the subcommands access to the entire argument space. It’s that a swelling orchestra I hear? Let’s try this out.
04:55
There’s my first subcommand, which required an animal
positional argument.
05:03
My doom
subcommand, using its default value …
05:10
or the same subcommand, setting its repeat flag to 2
.
05:19
And there’s the crusade
command.
05:25
Like our other scripts, if you don’t have any positional arguments, you get the help message. It even shows what the choices for the subcommands are in the usage
statement.
05:39
Let me just scroll back a little. As usual, using -h
, you get the full help message. There’s the usage
statement, then a list of the options, then the subcommands.
05:53 The subcommands themselves also support help.
06:00
By putting the -h
after the doom
subcommand, you get the doom
subcommand’s help info. This is where that metavar
parameter comes into play. Without it, the NUM
here would say REPEAT
in all caps. That probably have been a better choice, but then I wouldn’t have been able to demonstrate metavar
. Don’t forget, you can also set the help
parameter in the .add_argument()
method to put more detailed information on this line, as well.
06:31 Wow. That was a lot of info on command line argument paassing. In the last lesson, I’ll summarize the course and point you at some other tools you can use to accomplish similar things.
Become a Member to join the conversation.