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.
In this codelab, you will publish a Cloud Function that, when invoked via HTTP, will display the "Python Powered" logo.
By using a kiosk at Google I/O, a test project has been created and can be accessed by using going to: https://console.cloud.google.com/.
These temporary accounts have existing projects that are set up with billing so that there are no costs associated for you with running this codelab.
Note that all these accounts will be disabled soon after the codelab is over.
Use these credentials to log into the machine or to open a new Google Cloud Console window https://console.cloud.google.com/. Accept the new account Terms of Service and any updates to Terms of Service.
When presented with this console landing page, please select the only project available. Alternatively, from the console home page, click on "Select a Project" :
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.
From the GCP Console click the Cloud Shell icon on the top right toolbar:
If you've never started Cloud Shell before, you'll be presented with an intermediate screen (below the fold) describing what it is. If that's the case, click "Continue" (and you won't ever see it again). Here's what that one-time screen looks like:
It should only take a few moments to provision and connect to the shell 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].
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
curl
to download a zip with the code for this codelab:curl -LO https://github.com/GoogleCloudPlatform/python-docs-samples/archive/master.zip
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
cd python-docs-samples-master/codelabs/functions/python_powered
Check out the content of the python_powered
directory:
ls -1 app.py main.py python_powered.jpg 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
.
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 \ --allow-unauthenticated
The --allow-unauthenticated
deploy option enables you to reach the function without authentication.
Command output
Deploying function (may take a while - up to 2 minutes)...done. availableMemoryMb: 256 entryPoint: hello_world httpsTrigger: url: https://region-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
.
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 \ --allow-unauthenticated
The --allow-unauthenticated
deploy option enables you to reach the function without authentication.
Command output
Deploying function (may take a while - up to 2 minutes)...done. availableMemoryMb: 256 entryPoint: hello_name httpsTrigger: url: https://region-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:
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)
class TestHello(TestCase)
. It must be a class that inherits from unittest.TestCase
.test_
, which represent individual test cases.request
parameter (i.e., replacing it with a fake object with the specific data we require for the test).As main.py
depends on flask
, make sure the Flask framework is installed in our test environment:
python3 -m pip install --user flask Collecting flask ... Successfully installed Jinja2-2.10.3 MarkupSafe-1.1.1 flask-1.1.1 itsdangerous-1.1.0
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.
import os
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_from_directory(os.getcwd(), "python_powered.jpg", mimetype="image/jpg")
flask
of a function we'll use to send files as responses.python_powered
function declaration.send_from_directory
function with a filename to send (python_powered.jpg
) and a mimetype.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 \ --allow-unauthenticated
The --allow-unauthenticated
deploy option enables you to reach the function without authentication.
To test the function, visit the function's URL, which again is displayed in the gcloud functions deploy
command output in your browser. If everything is working correctly, you will see the ‘Python Powered' 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:
import flask
import main
app = flask.Flask(__name__)
@app.route("/")
def index():
return main.python_powered(flask.request)
python_powered
from your function file, main.py
, and additional resources from flask
, a Python web framework.index()
.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 --user 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 them using gcloud
:
gcloud functions delete hello_world gcloud functions delete hello_name gcloud functions delete python_powered
You can also delete the functions from the Cloud Console UI.
We hope you enjoy using Cloud Functions in Python!