This week we'll look at ways to take user input on the command line and in the web environment. We'll also talk about import and see how to “catch” errors.

User Input

  • Last week I introduced the 'input()' function.
  • One important thing that I didn't mention is that it's important to prompt the user when you take input.
  • For example:
print ('First Name:')
first = input()
print ('Last Name:') 
last = input()
print (f'Welcome {first} {last}')

When the above code runs it looks like this:

$ python3.6 ./input.py 
First Name:
Mike
Last Name:
Matera
Welcome Mike Matera
  • The input() function takes an argument. The argument is a prompt.
  • When you give an argument to input() the prompt is drawn on the same line.

Here's an example of using input() with a prompt:

first = input('First Name: ')
last = input('Last Name: ')
print (f'Welcome {first} {last}')

Notice the change in the way the prompt works:

$ python3.6 ./input.py 
First Name: Mike
Last Name: Matera
Welcome Mike Matera

Exception Handling

  • Python uses exceptions like many modern programming languages.
  • When problems with your program cause Python to exit it's because of an unhandled exception
  • Exceptions can be handled with a try/except block

Here's an example of checking user input:

try:
  a = float(input('Type a number: '))
  print (f"You entered {a}")
except:
  print ("That's not a number!")
  • If the numerical conversion using float() causes an error control flows to the except: phrase
  • Notice that the contents of the try: and except: block are indented
  • Indentation is syntax in Python
    • The indentation is how code appears “inside” of a block
    • Indentation can have spaces or tabs, but not both
There is a lot more to know about exceptions, we will discuss them again in weeks 8 and 12.

Command Line Input

  • Programs can take input when they are started
  • Input from the command line is common for UNIX programs
    • It's sometimes more convenient than taking input interactively
    • It can be more secure

Here's an example of running a Python program with arguments:

import sys
 
program, arg1, arg2 = sys.argv
print (f'Hello, I am {program} and my arguments are "{arg1}" and "{arg2}"')

The program now requires two arguments. Here's and example of how to call the program:

$ python3.6 ./args.py Hello World
Hello, I am ./args.py and my arguments are "Hello" and "World"

If the wrong number of arguments is given the program encounters an error:

$ python3.6 ./args.py Hello
Traceback (most recent call last):
  File "./args.py", line 3, in <module>
    program, arg1, arg2 = sys.argv 
ValueError: not enough values to unpack (expected 3, got 2)
  • The variable sys.argv is a Python list.
  • The syntax in the book unpacks the list in a way that's easy to see
  • The following two code snippets are identical:
program, arg1, arg2 = sys.argv

Is the same as:

program = sys.argv[0]
arg1 = sys.argv[1]
arg2 = sys.argv[2]

You can also use sys.argv directly in your code without creating a variable. For example:

print (f'Hello, I am {sys.argv[0]} and my arguments are "{sys.argv[1]}" and "{sys.argv[2]}"')

Using Environment Variables

  • Another way to pass input to a program is through the environment.
  • Environment variables are used on Windows, Mac and Linux
    • We'll see how to set them in Linux/Mac
  • Environment variables are frequently used to pass input to web applications
  • The environment is a dictionary in Python

Here's how to set and environment variable in the BASH shell:

$ export MY_NAME="Mike Matera"
$ export MY_JOB=Instructor 
  • Bash is a very difficult programming language, so the pay close attention to the following
    • There can *never* be a space between the name of the variable, the equal sign (=) and the value of the variable
    • When a value contains a space it needs to be in quotes ()
    • When a value doesn't contain a space no quote is necessary.
    • It's a tradition to make environment variables upper case (but not required)
  • Environment variables are accessible to Python.
import os
 
name = os.environ['MY_NAME']
job = os.environ['MY_JOB']
 
print (f"Hello, I'm {name} and I'm a {job}")

The following code prints:

$ export MY_NAME="Mike Matera"
$ export MY_JOB=Instructor 
$ python3.6 /tmp/env.py 
Hello, I'm Mike Matera and I'm a Instructor

The program will have an error if an environment variable is not set:

$ unset MY_JOB 
$ python3.6 /tmp/env.py 
Traceback (most recent call last):
  File "/tmp/env.py", line 4, in <module>
    job = os.environ['MY_JOB']
  File "/usr/local/lib/python3.6/os.py", line 669, in __getitem__
    raise KeyError(key) from None
KeyError: 'MY_JOB'

Debugging Flask

  • Flask has a debug mode that is very useful while you're developing.
  • Debug mode is accessed using an environment variable.
  • Run this command on your UNIX prompt to enable debugging:
export FLASK_DEBUG=1

Imports

  • Using the command line and the environment required us to use the import statement.
  • import does just what it says, it imports Python code into your program.
  • The import statement does two things
    • It looks for a module with the supplied name
    • If found, it binds the module to the current namespace
  • In programming a namespace is the place where variable and function names live.
    • The scope of a variable or function name is the namespace where it lives.

Consider the following example. Here the Python interpreter has just been started:

>>> os.environ['USER'] 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'os' is not defined

The problem with the code is that the os module has not been found. Now see what happens when os is imported:

>>> import os 
>>> os.environ['USER'] 
'mikem'
  • After the import the contents of the os namespace are bound to the name os
  • It's possible to change name that a namespace is bound to using import.

Here's an example of choosing the name for a namespace:

>>> import os as blahblah
>>> blahblah.environ['USER'] 
'mikem'
  • Notice that instead of os.environ it's now blahblah.environ
  • Changing the import name binding is useful when the library has a long name

Example of shortening a long import name:

import cloudstorage as gcs
  • When you import a module like os or sys all of the functions and variables in the module become available.
  • They are all accessible via the name binding
  • You can access imported names without a name binding if you ask for the name specifically
>>> from os import environ 
>>> environ['USER'] 
'mikem'
  • You've seen this from version of import before.
  • Flask programs use the from syntax

The from type is used to help readability:

from flask import Flask 
app = Flask(__name__)

This is also valid:

import flask
app = flask.Flask(__name__)
  • It's possible to import multiple names into the current namespace
  • This was from the template code last week:
from flask import Flask, render_template

HTML Forms

  • An HTML form is an HTML document that takes input from the user.
  • When the user presses “Submit” the form data is sent to the web server
  • Flask can handle form data
  • If you're new to HTML forms read this tutorial with examples

Here's an example of a simple form that could have been used for Project 3:

<html>
    <head>
        <title>Simple Form</title>
    </head>
    <body>
        <form action="/result" method="post">
          Value for the variable a:<br>
          <input type="text" name="a_value"><br>
          Value for the variable b:<br>
          <input type="text" name="b_value"><br><br>
          <input type="submit" value="Submit">
        </form>
    </body>
</html>
  • Notice the following about the form:
    • It's action field is set to /result
      • The forms action will be directed to <the-form-url>/result
      • The application must have a route to this URL
    • The method is set to post.
      • Submitting this form will create an HTTP POST request.
    • The text inputs are named a_value and b_value. Those will appear in the Python program
    • The last input is the Submit button.

The form is handled by the following Python function:

@app.route('/result', methods=['POST'])
def do_table():
    a = float(request.form['a_value'])
    b = float(request.form['b_value'])
    data = []
    data.append(['a', a])
    data.append(['b', b])
    data.append([f'{a}+{b}', a+b])
    return render_template('basic_answer.html', table_data=data)
  • Notice the following.
    • The form field values are accessible by the variable request.form which is a dictionary
    • The @app.route decorator has an additional argument methods=['POST']
      • That allows the function to handle post data.
      • The form would be rejected otherwise.

You need a function to render the form. That's shown below:

@app.route('/', methods=['GET'])
def do_form():
    return render_template('basic_form.html')

The code simply renders the form template, which is not parameterizable.

HTML Form Security Warning

The forms in this section aren't secure. Secure forms should:

  1. Validate user input
  2. Contain a random number to prevent cross site request forgery (XSRF)

I will show you how to do forms securely in the coming weeks!

Routing GET Requests

  • POST forms are “hide” the information they contain inside the HTTP request.
  • GET request can use the URL to encode data.
  • For example to find the Reddit user foobar you can go to their URL
  • Flask can bind parts of a URL to function arguments

Here's a function that gets it's arguments from the URL

@app.route('/math/<a>/<b>')
def do_table(a, b):
    data = []
    data.append(['a', a])
    data.append(['b', b])
    data.append([f'{a}+{b}', a+b])
    return render_template('basic_answer.html', table_data=data)
  • Taking input this way is recommended in situations where
    • You want browser bookmarks to work
    • You want the forward and back buttons on the browser to do the right thing

Introduction to Google App Engine

  • Google App Engine (GAE) is a platform for executing programs
  • GAE is known as Platform as a Service (PaaS)
    • The platform is the set of Python APIs you get to use
    • Other languages are supported too!
  • GAE applications aren't concerned with servers
  • You pay for them by API call
    • There's a very extensive free tier

To get started with GAE goto the Python quickstart:

https://cloud.google.com/appengine/docs/standard/python/quickstart

  • GAE standard environment uses Python 2.7 only
  • Here are the instructions for installing the gcloud tool in your workspace:
export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)"
echo "deb http://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
sudo apt-get update && sudo apt-get install google-cloud-sdk google-cloud-sdk-app-engine-python
gcloud init
  • gcloud init will give you a URL you must direct your browser to the URL to connect your workspace to your GAE account

Example Code

You can find all the example code in this week's lesson here: