Code Main and Arguments
00:00
In the previous lesson, I politely introduced you to curses
. In this lesson, I’ll start the process of turning your code into a command line program.
00:10
If what you’re hoping to build is a command line program, you need a way of specifying all those arguments to the curses
view class. I’ll be using argparse
to handle the following command line arguments.
00:22 This displays the program’s version, allows you to choose a pattern or loop through all the patterns. This specifies the view class. I’m not going to implement other views, but this future-proofs it for your homework.
00:36
This specifies the number of generations to evolve, and this is for the frame rate in frames per second. I’m going to put all the code for these arguments in the cli.py
file.
00:49 Let’s go take a look at it.
00:53 This file is responsible for specifying the arguments that will be acceptable to the program.
00:59 It currently defines a single function that gets called by the program to register all the args. The first thing this function does is create the parser by instantiating an instance of argument parser.
01:13
This takes two parameters, the name of the program and the description string used in the help message. Once the parser is in place, I call add_argument()
for each command line argument to be supported by the program.
01:27
The first argument is version. This uses argparse
’s version action, which knows to print info to the screen and exit. The version information here is loaded from the __version__
variable, which I’ll add to __init__
when I’m done here.
01:44 Providing version information for your program is good practice. If you ever get to the point where you have multiple versions and someone reports a bug, it’s good to know where it happened.
01:55 The next argument allows the user to specify the initial pattern for the grid. This argument takes the name of a pattern and the choices parameter here restricts the allowed names to those returned by the get all patterns method in our patterns file.
02:11 Note that at the moment there is no way to specify an external data file. If you want to add that feature later, you’ll have to muck with this call as you’d have a chicken and egg problem of defining the choices and saying which file to look for them in. Ignoring our chickens and moving on.
02:28
This argument says to run all the patterns instead of specifying only one. You could get fancier with argparse
and make this exclusive of the --pattern
argument.
02:39
Or you can be lazy like I am, and if --all
is present, just ignore if --pattern
is there as well. Using the store_true
action here specifies this flag acts as a Boolean and doesn’t require any arguments after it.
02:56
The next argument specifies the view to use to to display the grid. It also populates a choices
value based on the classes in __all__
.
03:05
in the v
class. Remind me to add __all__
to the v
class. Okay, if this argument isn’t given, the curses
view gets used by default.
03:16 That’s good, as it’s the only one currently implemented. Spoiler alert: homework.
03:23
Next is the argument to determine how many iterations per pattern. The metvar
here gives the value a better name than gen
in the code.
03:32 The argument to this flag must be an integer, and if it’s not given, defaults to 10.
03:40
Finally, the fps
argument is for frames per second, specified in a similar fashion to --gen
before it. The version argument loads __version__
from __init__
.
03:52
So I need to add something to __init__
.
03:59 Nothing earth shattering here. Just a version label declaration. There’s no standard in Python for where version info goes or what the semantics of the values mean.
04:08
But using __version__
in the module’s __init__
file is a very common pattern. This value serves another purpose as well. When it comes time to package this program up, this variable can also be used to give the package number.
04:24
What were you supposed to remind me of? Oh yeah. Adding __all__
to the v
file. __all__
is a special value that states what parts of this file are publicly exported.
04:37
For now, that’s just our curses
view class, but if you added other views in this file, you’d stick them in __all__
as well.
04:46
Okay, arguments have been prepared. Let’s actually wrap this sucker up in a program script. To do so, you need an entry point for the program. Then inside there you use that function I just showed you in cli.py
.
05:00
And finally, you construct a view based on the arguments the user put on the command line. All of this goes in the module’s __main__
file, and here it is.
05:13
The main method is what the module calls when it is loaded. More on that in a second. Its first task is to process the command line args. That was all taken care of in cli.py
, and now using the view argument from the argument parser, load that given view class.
05:32 I’ve got a good guess which one it’ll be using.
05:36 Next, I call a helper function here that shows the given pattern in that view class. The name of that pattern to use is specified also as a command line argument.
05:47
I skipped something here. That’s the check if __all__
was invoked. If so, instead of calling show_pattern()
once, you call it once for every pattern in the data file.
05:59
And what does show_pattern()
do? I thought you’d never ask. Similar to what was done in the REPL at the end of the last lesson, it instantiates the view, passes in the pattern and the remaining command line arguments, and calls show()
. Code in a module gets run as soon as the module gets loaded.
06:17
As such, it’s best practice to have scripts use this if
__name__ == "__main__"
clause. This if
statement is only true if the module is called as a program.
06:29 This way you could load the module without the script aspects running as a side effect. You don’t really need it here, but best practice is, well best practice.
06:39
Naming the entry point __main__
is pretty common. If this module is run as a script, our main()
function gets called. Yep. That’s the main()
function in the file called __main__
, under a __name__
check for __main__
.
06:53 It’s turtles all the way down.
06:56 Let me head to my shell and try this out.
07:06
The -m
argument to Python says to invoke this module as a callable script, and the -a
is the short form of --all
in the command line arguments handled by cli.py
.
07:33 Still pretty. Let’s take this one step further and allow our program to be called directly without invoking the Python interpreter.
Become a Member to join the conversation.