lab04 : input, scripts, command line arguments

num ready? description assigned due
lab04 true input, scripts, command line arguments Thu 08/23 11:00AM Thu 08/30 06:30PM

This lab may be done solo, or in pairs

Please decide before you begin whether you want to work solo or in a pair.

If you do decide to work in a pair, keep in mind that the following is NOT OK:

That is a joint effort, but it is NOT pair programming.

What this lab is about

In this lab, we begin to move in the direction of writing real Python programs that resemble the ones created by professional software developers to solve real world problems.

Our first programs will still be very simple, and will not necessarily seem very authentic. They will do very simple tasks that, quite honestly, can be done more easily with a simple calculator, for example.

I’ll ask you to trust me that we are taking small steps that will get larger as we move forward.

More specifically, in this lab, we want to learn four things:

  1. Reusing a file we’ve already created by importing it as a module.
  2. Writing an interactive program using input and print
  3. Using the if __name__=="__main__": idiom so our programs can serve more than one purpose.
  4. Using command line arguments (e.g. sys.argv)

Here’s a bit more detail about each of these:

  1. Reusing a file we’ve already created by importing it as a module.

Up until now, when we typed import we were always importing something that was part of the Python standard library, for example:

   import math
   import pytest

etc.

However, it is not only possibly, but very common, for programmers to create their own modules and import them.

In fact, in this lab, we are going to take the functions you wrote for lab02 in the file convert.py and import those so that they can be reused in this lab. Since those functions already have test cases, and those test cases are passing, we have confidence that we can build software on top of those functions.

  1. Writing an interactive program using input and print.

    Up until now, we’ve provided input to our functions in one of two ways:

    • Hard coded values.

      For example, in lab01, we used hard coded function calls such as drawA(100,50). We call the values 100 and 50 “hard coded”, because they are the same every time we run the program.

    • Typing function calls directly at the Python Prompt (>>>).

      For example, in lab02, we typed things like fToC(32) at the >>> prompt and looked to see what was returned by the function. We could change the value passed to the fToC function by putting different argument values inside the parentheses.

    Most real world users of software don’t work with a system such as the Python prompt. In real software input values are usually provided through a mobile app, a web form, a graphical user interface (GUI).

    In old-school console apps, though, which is what intro students usually learn first, there are two ways of providing input.

    The first of these is using something like the input function which was covered in Chapter 3 of the textbook.

    While the author suggest the use of the eval function for getting non-string input from the user, I strongly recommend against getting into this habit. We’ll explore better ways of doing that in this lab.

    (Using any kind of eval function on user input, whether in Python or any other language, is a very risky practice, and a classic vector for malware to infect your system. And, its not even the best approach. Just say no to eval unless you REALLY know what you are doing.)

  2. The use of command line arguments via sys.argv

    I said above that there two “old school console app” ways of getting input. The way that most Unix commands get their input is through command line arguments. You may not have thought much about it, but every time you use a command such as any of these, you are running a program:

    • cd cs8
    • mkdir lab04
    • python3 -m pytest lab03.py

    The first thing on the line, e.g. cd, mkdir, or python3, is the name of the program you are running. The rest of it is provided as input to the program. We’ll learn how you can do the same thing in your own Python code.

  3. The use of the if __name__=="__main__": idiom in Python.

    This is an issue that can be a little confusing for students, but it really is quite simple. This little strange looking piece of code allows us to use any Python file for two different purposes:

    • It does one thing when we run it
    • It can do something else entirely, when we import

It’s a very simple, yet powerful feature, and we’ll explore why its a really good habit to develop in your Python coding.

  1. The use of the shebang to run Python programs as standalone programs.

    In the real world, when folks run a program, they run it to get something done.

    For example, earlier this week, I wrote a script that takes a class roster and creates a randomized seating chart for giving an exam in Phelps 1924.

    Suppose another faculty member is teaching in Phelps 1924 and wants to use my script. What they care about is running the program and getting the result. They really don’t care whether its written in Python, or FORTRAN, or Java. They just want to run it and get the output.

    On Unix/Linux and on MacOS, there is a easy technique called the “shebang” that we can use to turn any Python file into a “standalone program” that can be run at the terminal prompt.

    (Note: This shebang thing isn’t applicable to old school Windows systems, though Windows 10 users probably can do it if they install the new Windows 10’s bash shell. Getting that to work is left as an exercise to the student though. In any case, for Windows users, I strongly encourage you to do this particular lab on CSIL, through MobaXTerm if needed.)

Ok, we are ready to get started.

Step 1: Make a ~/cs8/lab04 folder

The easiest way to create this is to do the following, which will work from any directory:

mkdir -p ~/cs8/lab04

That form of the mkdir command, with the -p has these advantages:

Ok, once you’ve created this directory, to get yourself into it, type:

cd ~/cs8/lab04

Again, since that’s an absolute path, it works from any directory.

Step 2: Create a file called lab04.py in your ~/cs8/lab04 directory

To start out lab04, write the line:

import convert

That line is going to import the convert.py file that we wrote in lab02.

Of course, that will only work if the convert.py file is in our current working directory. So, next, we’ll get that file where it should be.

First, though, use the “Save” command to save your lab04.py file.

Step 3: Copy ‘convert.py’ from your lab02 directory to your lab04 directory

I realize that you may be working on the same computer, or a different one from the one where you did lab02. And, it’s also possible that you didn’t put your convert.py file in the “right place”, i.e. in your ~/cs8/lab02 folder.

No worries. We are going to proceed as follows:

(NOTE: It may be tempting to just click and drag the file, using your mouse to do the copying, and skip all command line stuff. I don’t recommend that, because he process of copying files from one Unix/Linux directory to another is likely to be on the next exam in this course. So it might be a good idea to actually practice these steps. Entirely up to you.)

Alright, let’s dive in. First, try this:

cd ~/cs8/lab02

If it didn’t work, because that directory doesn’t exist on this computer, or in this account, then create it like this, and try the cd command again:

mkdir -p ~/cs8/lab02
cd ~/cs8/lab02

(For an explanation of the -p flag, see the discussion earlier in this lab.)

Then, use the ls command to see if the file convert.py is in that directory:

ls

If the file is NOT THERE, then:

It’s not working—what do I do?

A situation in which this will NOT work is if you are logged into CSIL via MobaXTerm, or ssh -X on MacOS.

In this case, your web browser downloads the file to your computer’s hard drive, not to your CSIL directory.

There are two work arounds:

(1) Just copy paste the contents from the web page into a new file.

(2) Bring up a web browser running on CSIL by typing either firefox or google-chrome in at the CSIL prompt. Note: This method is not fun. It typically runs very slowly, and you may see lots of error messages on the screen, and it will take a long time. But eventually the Chrome or Firefox browser should appear. It will hopefully work well enough for you to log into Gradescope and download your convert.py file into your ~/cs8/lab02 directory.

Ok, assuming the `convert.py file is there, you can use one of these two commands to look at its contents. We want to make sure that is looks like the “finished product” from your

Try both of those now. You should see that convert.py file is your finished product from lab02.

Note that the cat convert.py file lists the entire contents of the file. The thing is, if the file is long, it just spews out the entire thing all at once.

The command more convert.py file also lists the contents of the file, but if the file is longer than a single screen of text, it gives it to us one screen full at a time, with the word More at the bottom of the screen. We can press either Enter, or the space bar, to go through the file one line or one screen full at a time. Try it a few times.

(There are other keys you can press as well, including q to quit in the middle of the file, as well as keys to search for particular text, and many other fun goodies. To learn more about more, see this tutorial and this wikipedia article).

Assuming that file is where it should be, i.e. in the directory ~/cs8/lab02, we can now go back to your ~/cs8/lab04 directory with a cd command:

cd ~/cs8/lab04

Now, copy the convert.py file into your current directory with this command. This is the unix cp command which stands for “copy”:

cp ~/cs8/lab02/convert.py ~/cs8/lab04

After running this command, use ls to see if the convert.py file is there:

ls

If so, you are almost ready for the next step.

Before you go on though, a reminder that you can make a preliminary submission to Gradescope anytime you are “done for the day” (i.e. if you aren’t completely finished with the lab, and are planning to come back to it later.)

The benefit is that this provides a way to move your files between CSIL and your own computer, and/or to share files between pair partners.

It’s also a way you can put code in a place where the instructors can see it in case you want to ask a question on Piazza via private instructor note. Upload your code first, then ask the question, and include your username/Gradescope details. While you can only see your own submissions on Gradescope (and those of your pair partner, not no one elses), instructors have access to see all submissions for the class.

To make a submission now, you can visit the corresponding Gradescope submission page.

Step 4: Go back to your lab04.py and run it

If you’ve followed the instructions up to this point, your lab04.py file (which should be located in your ~/cs8/lab04 folder) only has these contents:

import convert

That should be enough that we can run the file and see one of two things:

What we want to see:

>>> 
================= RESTART: /Users/pconrad/cs8/lab04/lab04.py =================
>>>

If that’s what you see, good! Move on to step 5.

What we don’t want to see:

>>> 
================= RESTART: /Users/pconrad/cs8/lab04/lab04.py =================

Traceback (most recent call last):
  File "/Users/pconrad/cs8/lab04/lab04.py", line 1, in <module>
    import convert
ImportError: No module named convert
>>> 

If you see the second thing (the error message), it suggests that you ran idle3 from some directory other than your ~/cs8/lab04 directory where your convert.py program is located.

If you see the error, do this to get back on track (otherwise, just go on to th next step):

Step 5: Learning about the __name__=="__main__" block

Ok, now we are ready to put some code in our lab04.py.

This is an important step to work through slowly and carefully, to get the learning—not just to get the result. There isn’t much code to write here, but there is a lot of important reading, and if you gloss over it, you’ll have trouble later.

The first thing we are going to put in there is this funny looking code right here. Just copy this in there for now, and I’ll explain it in a moment.

(I’ve repeated the import convert that was already there, just for context.)

import convert

print("This is outside the main block of lab04.py")

if __name__=="__main__":
   print("This is the main block of lab04.py")


Note: make sure you type that line with the if exactly as shown:

What the heck does __name__=="__main__" mean?

The if test is checking whether a funny looking variable, __name__ is equal to some other funny looking string "__main__". But why?

Basically, this funny variable equals this funny string whenever you run a Python program directly, as opposed to importing it.

We can see the effect by doing the following. Let’s load up your convert.py file in idle, and put this code at the very bottom of the file:

print("This is outside the main block of convert.py")

if __name__=="__main__":
   print("This is main block of convert.py")

Then, try choosing the “Run Module” command from the menu in idle, from the window where your file convert.py is open. You should see this:

>>> 
================ RESTART: /Users/pconrad/cs8/lab04/convert.py ================
This is outside the main block of convert.py
This is main block of convert.py
>>>

Then, run your lab04.py file using “Run Module”. You should see this:

================= RESTART: /Users/pconrad/cs8/lab04/lab04.py =================
This is outside the main block of convert.py
This is outside the main block of lab04.py
This is the main block of lab04.py
>>> 

Ok, what just happened? Here’s what:

With this as background, we can now clearly state the purpose of a __name__=="__main__" block:

What is a __name__=="__main__" block for?

It is good practice in Python to put ALL code other than function definitions and initial assignments of global variables into a __name__=="__main__" block.

That is, the main code for a Python program that does the work should ideally always be in a __name__=="__main__" block.

That allows us to reuse all of the other function definitions in our program by importing them into another program without any unwanted side effects.

Step 6: Remove the print statements outside the main block

So, our next step will be to remove the print statements in convert.py and lab04.py that are outside the __name__=="__main__" block. (We only put those in temporarily in this step so that you could understand how the __name__=="__main__" block works.)

Specifically, remove these lines:

From lab04.py, delete this line:

print("This is outside the main block of lab04.py")

From convert.py, delete this line:

print("This is outside the main block of convert.py")

Run your two files again, and see if your results match those shown below.

Note that when switching between two file windows in idle3 you must actully click inside the window in idle3 before you run—just bringing the window to the front isn’t enough (you might still running convert.py when you think you are runnnig lab04.py, or vice-versa.)

For convert.py:

================ RESTART: /Users/pconrad/cs8/lab04/convert.py ================
This is main block of convert.py
>>> 

For lab04.py:

================= RESTART: /Users/pconrad/cs8/lab04/lab04.py =================
This is the main block of lab04.py
>>> 

If so, you are ready for the next step.

Step 7: Using the input function

Now, we’ll show how your program can have a conversation with the user.

Enter this into the main block of your lab04.py file, as shown below:


if __name__=="__main__":
   print("This is the main block of lab04.py")

   fTempStr = input("Please enter a Fahrenheit temperature: ")
   print("You entered: ",fTempStr)

Run the program. You should see something like this:

>>> 
================= RESTART: /Users/pconrad/cs8/lab04/lab04.py =================
This is the main block of lab04.py
Please enter a Fahrenheit temperature:

Enter a temperature, e.g. 45 and press enter. It should then look like this:

>>> 
================= RESTART: /Users/pconrad/cs8/lab04/lab04.py =================
This is the main block of lab04.py
Please enter a Fahrenheit temperature: 45
You entered:  45
>>>

Try this again, but this time enter something else. You should see that whatever you type in gets echoed back.

When that’s working, we’ll try the next step, which is to convert the user input into a number.

Step 8: Converting input data to a number

As you know from the reading in Chapter 3, the input function returns string data (Python type str). If we want to use this as a number, we need to do a conversion. The textbook suggest using eval. DON’T DO THAT. Here’s what do to instead:

if __name__=="__main__":
   print("This is the main block of lab04.py")

   fTempStr = input("Please enter a Fahrenheit temperature: ")
   print("You entered: ",fTempStr)

   fTemp = float(fTempStr)
   cTemp = convert.fToC(fTemp)

   print("{} degrees F = {} degrees C".format(fTemp,cTemp))

Try this code, and try entering a few values for the Fahrenheit temperature.

First try some legit numbers. For example:

>>> 
================= RESTART: /Users/pconrad/cs8/lab04/lab04.py =================
This is the main block of lab04.py
Please enter a Fahrenheit temperature: 68
You entered:  68
68.0 degrees F = 20.0 degrees C
>>>

Well, that’s nice! But what if we put in something invalid, for example “potato”:

>>> 
================= RESTART: /Users/pconrad/cs8/lab04/lab04.py =================
This is the main block of lab04.py
Please enter a Fahrenheit temperature: potato
You entered:  potato
Traceback (most recent call last):
  File "/Users/pconrad/cs8/lab04/lab04.py", line 10, in <module>
    fTemp = float(fTempStr)
ValueError: could not convert string to float: 'potato'
>>>

Well, that’s unfortunate. But, no worries: we can take care of it.

We’ll do that in the next step.

Step 9: Gracefully handling errors

Change these three lines of code:

   fTemp = float(fTempStr)
   cTemp = convert.fToC(fTemp)

   print("{} degrees F = {} degrees C".format(fTemp,cTemp))

Into these line of code:


   try:
      fTemp = float(fTempStr)
      cTemp = convert.fToC(fTemp)
      print("{} degrees F = {} degrees C".format(fTemp,cTemp))
   except ValueError:
      print("Sorry, I could not convert {} to a number".format(fTempStr))

Now, try again with both the valid and invalid input. For valid input such as 68 there should be no difference. But for invalid, input we should now see something like this:

>>> 
================= RESTART: /Users/pconrad/cs8/lab04/lab04.py =================
This is the main block of lab04.py
Please enter a Fahrenheit temperature: potato
You entered:  potato
Sorry, I could not convert potato to a number
>>> 

Ok, this is great! But we can still do more!

Step 9: Adding a shebang

Ok, now we are going to add another strange bit of code to our file.

The following line must be the VERY first line of the file, before any imports, any other comments, any blank lines, and it must be truly EXACTLY as shown. Put this at the very top of your lab04.py file.

#!/usr/bin/env python3

Be sure to SAVE this change after making it in the idle3 file window.

This is a bit of magic sauce that allows us to run the program directly from the shell. It is called a “shebang”, and you can read more about it here, including why it’s called a “shebang”:

This ONLY works on Linux and MacOS, so if you are working on Windows, you need to do this part on CSIL if you want to see it work.

Bring up a terminal window. If your terminal window is running idle3, you can use the special trick of pressing CTRL-Z, then bg followed by enter to get your cursor back.

Then use pwd to make sure you are in ~/cs8/lab04. If not, put yourself there. Make sure, also, that you can see the lab04.py file (use ls to list the files.)

If you can, then use this Unix command. This makes your lab04.py executable:

chmod u+x lab04.py

Finally, type this at the Unix prompt (make sure you saved the file in idle3 first):

./lab04.py

You should see the following:

[your unix prompt here]$ ./lab04.py
This is the main block of lab04.py
Please enter a Fahrenheit temperature: 68
You entered:  68
68.0 degrees F = 20.0 degrees C
[your unix prompt here]$

Whoa! You just ran a Python program that:

This is where it actually starts to look like “real programming”.

But wait, it gets even better. We still have one more trick.

Step 10: Command line arguments with sys.arg

Wouldn’t it be cool if instead of having to type ./lab04.py on one line and then typing in temperature after we are prompted, if we could just type:

./lab04.py 68

And it would just print the result?

Well, we can. Here’s how:

First, add this import near the top of the file, right before or after your import convert (but not the very top, because the shebang has to be the first line in the file.)

import sys

Replace this line of code:

  fTempStr = input("Please enter a Fahrenheit temperature: ")

With this:

  if len(sys.argv) >= 2:
     fTempStr = sys.argv[1]
  else:
     fTempStr = input("Please enter a Fahrenheit temperature: ")

Save this change. Now try this at the linux shell prompt:

./lab04.py 68

Then try this at the linux shell prompt:

./lab04.py

What you should see is a result like that shown below:

[your unix prompt here] $ ./lab04.py 68
This is the main block of lab04.py
You entered:  68
68.0 degrees F = 20.0 degrees C
[your unix prompt here] $ ./lab04.py
This is the main block of lab04.py
Please enter a Fahrenheit temperature: 32
You entered:  32
32.0 degrees F = 0.0 degrees C
[your unix prompt here] $ 

If your output looks similar, you are almost done. A bit of reading in Step 11, then submit at Step 12.

Step 11: Taking stock of what we’ve done.

Ok! That’s a lot of stuff.

This lab was pretty “cookbook”—for the most part, you were led by the hand through it.

If you did it all correctly, you should be able to submit your convert.py and lab04.py files and get a perfect score right away.

If you don’t get a perfect score, it’s likely either because:

But most likely, you’ll get a perfect score on the first try.

Now: you will have a follow up lab where you’ll be asked to do all of the same things you did in this lab, but without the detailed instructions. You’ll need to refer back to this lab and understand what you did.

So, please make sure that you read back through this, and if any part of it doesn’t make sense to you, ask questions.

Step 12: See perfect score on Gradescope; profit.

To submit your file, navigate to the corresponding Gradescope submission page.

At this point, you should see that you have a perfect 100 points on Gradescope, and you are finished with the lab!