From dabaa14167799becbdce32027622a2fd6a598253 Mon Sep 17 00:00:00 2001
From: Bill Prin <waprin@google.com>
Date: Wed, 13 Apr 2016 17:21:40 -0700
Subject: [PATCH] Add Background Sample

---
 appengine/background/README.md    | 14 +++++
 appengine/background/app.yaml     | 10 ++++
 appengine/background/main.py      | 85 +++++++++++++++++++++++++++++++
 appengine/background/main_test.py | 53 +++++++++++++++++++
 4 files changed, 162 insertions(+)
 create mode 100644 appengine/background/README.md
 create mode 100644 appengine/background/app.yaml
 create mode 100644 appengine/background/main.py
 create mode 100644 appengine/background/main_test.py

diff --git a/appengine/background/README.md b/appengine/background/README.md
new file mode 100644
index 0000000000000..ea5f7589b3b0a
--- /dev/null
+++ b/appengine/background/README.md
@@ -0,0 +1,14 @@
+# Using Background Threads from Google App Engine
+
+This example shows how to use manual or basic scaling to start App Engine background threads.
+
+See the [documentation on modules](https://cloud.google.com/appengine/docs/python/modules/) for
+more information.
+
+
+## Setup
+
+1. Edit the `CLOUDSQL_INSTANCE` and `CLOUDSQL_PROJECT` values in `main.py`.
+
+1. Your app.yaml configuration must specify scaling as either manual or basic. The default 
+automatic scaling does not allow background threads.
diff --git a/appengine/background/app.yaml b/appengine/background/app.yaml
new file mode 100644
index 0000000000000..57cdb0bf57c33
--- /dev/null
+++ b/appengine/background/app.yaml
@@ -0,0 +1,10 @@
+runtime: python27
+api_version: 1
+threadsafe: yes
+
+manual_scaling:
+  instances: 1
+
+handlers:
+- url: .*
+  script: main.app
diff --git a/appengine/background/main.py b/appengine/background/main.py
new file mode 100644
index 0000000000000..8d904e7641d28
--- /dev/null
+++ b/appengine/background/main.py
@@ -0,0 +1,85 @@
+# Copyright 2016 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Sample application that demonstrates how to use the App Engine background
+threads.
+
+app.yaml scaling must
+"""
+
+# [START background-imp]
+from google.appengine.api import background_thread
+# [END background-imp]
+
+import webapp2
+
+val = 'Dog'
+
+
+class MainHandler(webapp2.RequestHandler):
+    def get(self):
+        self.response.headers['Content-Type'] = 'text/plain'
+        self.response.write(str(val))
+
+
+class SetDogHandler(webapp2.RequestHandler):
+    """ Resets the global val to 'Dog'"""
+
+    def get(self):
+        global val
+        val = 'Dog'
+        self.response.headers['Content-Type'] = 'text/plain'
+        self.response.write('Done')
+
+
+class SetCatBackgroundHandler(webapp2.RequestHandler):
+    """ Demonstrates two ways to start new background threads
+    """
+
+    def get(self):
+        """
+        Demonstrates using a background thread to change the global
+        val from 'Dog' to 'Cat'
+
+        The auto GET parameter determines whether to start the thread
+        automatically or manually
+        """
+        auto = self.request.get('auto')
+
+        # [START background-start]
+        # sample function to run in a background thread
+        def change_val(arg):
+            global val
+            val = arg
+
+        if auto:
+            # Start the new thread in one command
+            background_thread.start_new_background_thread(change_val, ['Cat'])
+        else:
+            # create a new thread and start it
+            t = background_thread.BackgroundThread(
+                target=change_val, args=['Cat'])
+            t.start()
+        # [END background-start]
+
+        self.response.headers['Content-Type'] = 'text/plain'
+        self.response.write('Done')
+
+app = webapp2.WSGIApplication([
+    ('/', MainHandler),
+    ('/dog', SetDogHandler),
+    ('/cat', SetCatBackgroundHandler),
+], debug=True)
+# [END all]
diff --git a/appengine/background/main_test.py b/appengine/background/main_test.py
new file mode 100644
index 0000000000000..55cf5bfe47f4d
--- /dev/null
+++ b/appengine/background/main_test.py
@@ -0,0 +1,53 @@
+# Copyright 2016 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import main
+
+from mock import patch
+import pytest
+import webtest
+
+
+@pytest.fixture
+def app(cloud_config, testbed):
+    main.PROJECTID = cloud_config.project
+    return webtest.TestApp(main.app)
+
+
+@patch("main.background_thread")
+def test_background(thread, app):
+    app.get('/dog')
+    response = app.get('/')
+    assert response.status_int == 200
+    assert response.body == 'Dog'
+    app.get('/cat')
+    # no stub for system so manually set
+    main.val = 'Cat'
+    response = app.get('/')
+    assert response.status_int == 200
+    assert response.body == 'Cat'
+
+
+@patch("main.background_thread")
+def test_background_auto_start(thread, app):
+    app.get('/dog')
+    response = app.get('/')
+    assert response.status_int == 200
+    assert response.body == 'Dog'
+    app.get('/cat?auto=True')
+    # no stub for system so manually set
+    main.val = 'Cat'
+    response = app.get('/')
+    assert response.status_int == 200
+    assert response.body == 'Cat'