Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Hint: You can adjust the default video playback speed in your account settings.
Hint: You can set your subtitle preferences in your account settings.
Sorry! Looks like there’s an issue with video playback 🙁 This might be due to a temporary outage or because of a configuration issue with your browser. Please see our video player troubleshooting guide to resolve the issue.

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:17 You ready? Set, go.

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.