Python is a popular open source programming language used by data scientists, web application developers, systems administrators and more.

Google Cloud Functions is an event-driven serverless compute platform. Cloud Functions allows you to write your code without worrying about provisioning resources or scaling to handle changing requirements.

There are two types of Cloud Functions:

This codelab will walk you through creating your own Cloud Function in Python.

What you will build

In this codelab, you will publish a Cloud Function that, when invoked via HTTP, will display the "Python Powered" logo.

What you will learn

Self-paced environment setup

If you don't already have a Google Account (Gmail or Google Apps), you must create one. Sign-in to Google Cloud Platform console (console.cloud.google.com) and create a new project:

Screenshot from 2016-02-10 12:45:26.png

Remember the project ID, a unique name across all Google Cloud projects (the name above has already been taken and will not work for you, sorry!). It will be referred to later in this codelab as PROJECT_ID.

Next, you'll need to enable billing in the Cloud Console in order to use Google Cloud resources.

Running through this codelab shouldn't cost you more than a few dollars, but it could be more if you decide to use more resources or if you leave them running (see "cleanup" section at the end of this document).

New users of Google Cloud Platform are eligible for a $300 free trial.

Start Cloud Shell

While Google Cloud can be operated remotely from your laptop, in this codelab you will be using Google Cloud Shell, a command line environment running in the Cloud.

Activate Google Cloud Shell

From the GCP Console click the Cloud Shell icon on the top right toolbar:

Then click "Start Cloud Shell":

It should only take a few moments to provision and connect to the environment:

This virtual machine is loaded with all the development tools you'll need. It offers a persistent 5GB home directory, and runs on the Google Cloud, greatly enhancing network performance and authentication. Much, if not all, of your work in this lab can be done with simply a browser or your Google Chromebook.

Once connected to Cloud Shell, you should see that you are already authenticated and that the project is already set to your PROJECT_ID.

Run the following command in Cloud Shell to confirm that you are authenticated:

gcloud auth list

Command output

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

Command output

[core]
project = <PROJECT_ID>

If it is not, you can set it with this command:

gcloud config set project <PROJECT_ID>

Command output

Updated property [core/project].


Ensure the Cloud Functions API is enabled

Run the following command from Cloud Shell to make sure the Cloud Functions API is enabled. This will make sure we can deploy Cloud Functions later in the codelab.

gcloud services enable cloudfunctions.googleapis.com

Download and setup the code

  1. From the Cloud Shell terminal, use curl to download a zip with the code for this codelab:
curl -LO https://github.com/GoogleCloudPlatform/python-docs-samples/archive/master.zip
  1. Use unzip to unpack the code. This unpacks a directory (python-docs-samples-master), which contains sample Python code for cloud.google.com.
unzip master.zip
  1. Change to the directory containing the code for this codelab:
cd python-docs-samples-master/codelabs/functions/python_powered

The python_powered directory contains the following directories and files:

$ tree
.
├── main.py
├── python_powered.jpg
├── run.py
└── test_main.py

HTTP Cloud Functions in Python are written as regular Python functions. The function must accept a single argument, which is usually named request.

main.py

def hello_world(request):
    """HTTP Cloud Function.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """
    return "Hello World!"

You can open this file in Cloud Shell by opening the editor (click the pencil icon at the top right of Cloud Shell) then using the file tree on the left side of the editor to open the file, python-docs-samples-master/codelabs/functions/python_powered/main.py.

Let's deploy this function as an HTTP Cloud Function! Make sure you cd python-docs-samples-master/codelabs/functions/python_powered/, then you can deploy it using the function name and gcloud functions deploy. This may take a minute or two.

gcloud functions deploy hello_world --runtime python37 --trigger-http

Deploying function (may take a while - up to 2 minutes)...done.                
availableMemoryMb: 256
entryPoint: hello_world
httpsTrigger:
  url: https://region-my-project.cloudfunctions.net/hello_world
...

To test the hello_world function, copy the httpsTrigger URL that's displayed in the gcloud functions deploy output. It will have a form like this:

https://<REGION>-<GOOGLE_CLOUD_PROJECT>.cloudfunctions.net/hello_world

Then run the following command:

curl https://<REGION>-<GOOGLE_CLOUD_PROJECT>.cloudfunctions.net/hello_world

Hello World!

Functions are more useful when they can take arguments. We'll define a new function, hello_name, which says "Hello World!" by default, but can accept a name argument which causes it to say "Hello _____!" depending on the value of name.

main.py

import flask


def hello_name(request):
    """HTTP Cloud Function.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """
    request_args = request.args

    if request_args and "name" in request_args:
        name = request_args["name"]
    else:
        name = "World"
    return "Hello {}!".format(flask.escape(name))

Again, let's deploy this new function as an HTTP Cloud Function! Make sure you cd python-docs-samples-master/codelabs/functions/python_powered/, then you can deploy it using the function name and gcloud functions deploy. Note that this command is slightly different than the one you ran before: we're deploying hello_name, not hello_world.

gcloud functions deploy hello_name --runtime python37 --trigger-http

Deploying function (may take a while - up to 2 minutes)...done.                
availableMemoryMb: 256
entryPoint: hello_name
httpsTrigger:
  url: https://region-my-project.cloudfunctions.net/hello_name
...

To test the hello_name function, copy the httpsTrigger URL that's displayed in the gcloud functions deploy output. It will have a form like this:

https://<REGION>-<GOOGLE_CLOUD_PROJECT>.cloudfunctions.net/hello_name

Then run the following command:

curl https://<REGION>-<GOOGLE_CLOUD_PROJECT>.cloudfunctions.net/hello_name
Hello World!

You'll see that we're getting the default result because we haven't set the name argument. To set it, we'll add a URL parameter name=YOURNAME to the URL. Call it like so, replacing YOURNAME with your name:

curl https://<REGION>-<GOOGLE_CLOUD_PROJECT>.cloudfunctions.net/hello_name?name=YOURNAME
Hello YOURNAME!

The next step is to add some tests to make sure your functions continue to work.

HTTP Cloud Functions in Python are tested using the unittest module from the standard library. There is no need to run an emulator or other simulation to test your function—just normal Python code.

Here is what a test looks like for the hello_world and hello_name functions:

test_main.py

import unittest
import unittest.mock

import main


class TestHello(unittest.TestCase):
    def test_hello_world(self):
        req = unittest.mock.Mock()

        # Call tested function
        assert main.hello_world(req) == "Hello World!"

    def test_hello_name_no_name(self):
        req = unittest.mock.Mock(args={})

        # Call tested function
        assert main.hello_name(req) == "Hello World!"

    def test_hello_name_with_name(self):
        name = "test"
        req = unittest.mock.Mock(args={"name": name})

        # Call tested function
        assert main.hello_name(req) == "Hello {}!".format(name)
  1. Python tests are written the same way as other Python files. They start with a set of imports, then define classes and functions.
  2. The test declaration is of the form class TestHello(TestCase). It must be a class that inherits from unittest.TestCase.
  3. The test class has methods, each of which must start with test_, which represent individual test cases.
  4. Each test case tests one of our functions by mocking the request parameter (i.e., replacing it with a fake object with the specific data we require for the test).
  5. After invoking each function, the test checks the HTTP response to be sure it was what we were expecting.

To run these tests locally, cd to the directory with the files you're testing then use the python3 -m unittest command:

python3 -m unittest
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

Next, you'll create a new function which returns the ‘Python Powered' logo.

Let's make the "Hello World!" function a bit more entertaining by printing the ‘Python Powered' image for every request:

The following listing shows the code to make it happen. You can see this code in Cloud Shell at python-docs-samples-master/codelabs/functions/python_powered/main.py. After the code block, there are notes about each portion.

main.py

import flask


def python_powered(request):
    """HTTP Cloud Function.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response file, a JPG image that says "Python Powered"
    """
    return flask.send_file("python_powered.jpg", mimetype="image/jpg")
  1. The file starts with an import statement from flask of a function we'll use to send files as responses.
  2. Next is the python_powered function declaration.
  3. The function calls the send_file function with a filename to send (python_powered.jpg) and a mimetype.
  4. If there was no error when writing the response, the function returns normally.

Deploy this function as you did the "Hello World!" function from before, using gcloud functions deploy and the name of the function, python_powered:

gcloud functions deploy python_powered --runtime python37 --trigger-http

To test the function, visit the function's URL, which again is displayed in the glcoud functions deploy command output in your browser. If everything is working correctly, you will see the ‘Python Powerd' logo in your browser!

Next, you'll create an app so that you can run your function locally and try it in a browser.

You can run an HTTP function locally by creating an HTTP server and calling your function in a route.

You can write an HTTP server for your function in the same directory as your function. Create a file named app.py with the following contents:

app.py

import flask

import main


app = flask.Flask(__name__)


@app.route("/")
def index():
    return main.python_powered(flask.request)
  1. This file imports python_powered from your function file, main.py, and additional resources from flask , a Python web framework.
  2. It creates a Flask application
  3. It registers a route at the base URL which is handled with a function named index().
  4. The index() function then calls our python_powered function, passing it the current request.

To run this application locally, run the following commands:

python3 -m pip install flask
python3 -m flask run

Now use the Cloud Shell Web Preview to test the server in your browser. Click the Web Preview button , select "Change Port" and then enter port number 5000 in the displayed input. Cloud Shell opens the preview URL on its proxy service in a new browser window. The web preview restricts access over HTTPS to your user account only. If everything is working properly, you should see the ‘Python Powered' logo!

Cloud Functions pricing is based on how often your function is invoked, including a free tier for functions that don't run often.

Once you're done testing your Cloud Functions, you can delete it using gcloud:

gcloud functions delete hello_world
gcloud functions delete hello_name
gcloud functions delete python_powered

You can also delete the function from the Cloud Console UI.

We hope you enjoy using Cloud Functions in Python!