From 3b33f381885fb2294ec608cc5ad5b8ad665ae16c Mon Sep 17 00:00:00 2001 From: Jerjou Cheng Date: Thu, 20 Oct 2016 13:24:07 -0700 Subject: [PATCH 1/2] Use decorator to memoize. This is to avoid adding complexity unrelated to what's being explained, when the snippet of code is included in a doc. --- .../firebase/firetactoe/firetactoe.py | 43 +++++++++++-------- .../firebase/firetactoe/requirements.txt | 3 +- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/appengine/standard/firebase/firetactoe/firetactoe.py b/appengine/standard/firebase/firetactoe/firetactoe.py index d841d4bdf2d..9d9e8c99a49 100644 --- a/appengine/standard/firebase/firetactoe/firetactoe.py +++ b/appengine/standard/firebase/firetactoe/firetactoe.py @@ -15,6 +15,10 @@ """Tic Tac Toe with the Firebase API""" import base64 +try: + from functools import lru_cache +except ImportError: + from functools32 import lru_cache import json import os import re @@ -51,33 +55,36 @@ app = flask.Flask(__name__) -def _get_firebase_db_url(_memo={}): +# Memoize the value, to avoid parsing the code snippet every time +@lru_cache() +def _get_firebase_db_url(): """Grabs the databaseURL from the Firebase config snippet. Regex looks scary, but all it is doing is pulling the 'databaseURL' field from the Firebase javascript snippet""" - if 'dburl' not in _memo: - # Memoize the value, to avoid parsing the code snippet every time - regex = re.compile(r'\bdatabaseURL\b.*?["\']([^"\']+)') - cwd = os.path.dirname(__file__) + regex = re.compile(r'\bdatabaseURL\b.*?["\']([^"\']+)') + cwd = os.path.dirname(__file__) + try: with open(os.path.join(cwd, 'templates', _FIREBASE_CONFIG)) as f: url = next(regex.search(line) for line in f if regex.search(line)) - _memo['dburl'] = url.group(1) - return _memo['dburl'] + except StopIteration: + raise ValueError( + 'Error parsing databaseURL. Please copy Firebase web snippet ' + 'into templates/{}'.format(_FIREBASE_CONFIG)) + return url.group(1) +# Memoize the authorized http, to avoid fetching new access tokens +@lru_cache() # [START authed_http] -def _get_http(_memo={}): +def _get_http(): """Provides an authed http object.""" - if 'http' not in _memo: - # Memoize the authorized http, to avoid fetching new access tokens - http = httplib2.Http() - # Use application default credentials to make the Firebase calls - # https://firebase.google.com/docs/reference/rest/database/user-auth - creds = GoogleCredentials.get_application_default().create_scoped( - _FIREBASE_SCOPES) - creds.authorize(http) - _memo['http'] = http - return _memo['http'] + http = httplib2.Http() + # Use application default credentials to make the Firebase calls + # https://firebase.google.com/docs/reference/rest/database/user-auth + creds = GoogleCredentials.get_application_default().create_scoped( + _FIREBASE_SCOPES) + creds.authorize(http) + return http # [END authed_http] diff --git a/appengine/standard/firebase/firetactoe/requirements.txt b/appengine/standard/firebase/firetactoe/requirements.txt index 0ad4b627cd9..4a1839c0caa 100644 --- a/appengine/standard/firebase/firetactoe/requirements.txt +++ b/appengine/standard/firebase/firetactoe/requirements.txt @@ -1,4 +1,5 @@ flask==0.11.1 requests==2.11.1 requests_toolbelt==0.7.0 -oauth2client==4.0.0 +oauth2client==2.2.0 +functools32==3.2.3.post2 ; python_version < '3' From 88542eb5bb59cd050fc64971878e5324c7e27c00 Mon Sep 17 00:00:00 2001 From: Jerjou Cheng Date: Thu, 20 Oct 2016 13:51:36 -0700 Subject: [PATCH 2/2] Clear memoization before tests. --- appengine/bigquery/main_flask.py | 47 +++++++++++++++++++ .../standard/firebase/firetactoe/creds.json | 1 + .../firebase/firetactoe/firetactoe_test.py | 3 +- .../templates/_firebase_config.html.jerjou | 12 +++++ 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 appengine/bigquery/main_flask.py create mode 120000 appengine/standard/firebase/firetactoe/creds.json create mode 100644 appengine/standard/firebase/firetactoe/templates/_firebase_config.html.jerjou diff --git a/appengine/bigquery/main_flask.py b/appengine/bigquery/main_flask.py new file mode 100644 index 00000000000..db676f908c1 --- /dev/null +++ b/appengine/bigquery/main_flask.py @@ -0,0 +1,47 @@ +"""Sample appengine app demonstrating 3-legged oauth.""" +import cgi +import json +import os +import sys + +from flask import Flask + +from googleapiclient.discovery import build + +from oauth2client.appengine import OAuth2DecoratorFromClientSecrets + +# The project id whose datasets you'd like to list +PROJECTID = '' + +# Create the method decorator for oauth. +decorator = OAuth2DecoratorFromClientSecrets( + os.path.join(os.path.dirname(__file__), 'client_secrets.json'), + scope='https://www.googleapis.com/auth/bigquery') + +# Create the bigquery api client +service = build('bigquery', 'v2') + +# Create the Flask app +app = Flask(__name__) +app.config['DEBUG'] = True + +# Create the endpoint to receive oauth flow callbacks +app.route(decorator.callback_path)(decorator.callback_handler()) + + +@app.route('/') +@decorator.oauth_required +def list_datasets(): + """Lists the datasets in PROJECTID""" + http = decorator.http() + datasets = service.datasets() + + try: + response = datasets.list(projectId=PROJECTID).execute(http) + + return ('

Datasets.list raw response:

' + '
%s
' % json.dumps(response, sort_keys=True, + indent=4, separators=(',', ': '))) + except: + e = cgi.escape(sys.exc_info()[0], True) + return '

Error: %s

' % e diff --git a/appengine/standard/firebase/firetactoe/creds.json b/appengine/standard/firebase/firetactoe/creds.json new file mode 120000 index 00000000000..86538f43fcb --- /dev/null +++ b/appengine/standard/firebase/firetactoe/creds.json @@ -0,0 +1 @@ +../../../../../channels-api-deprecation-1e958c196e99.json \ No newline at end of file diff --git a/appengine/standard/firebase/firetactoe/firetactoe_test.py b/appengine/standard/firebase/firetactoe/firetactoe_test.py index b07b6111841..e00ca1a570f 100644 --- a/appengine/standard/firebase/firetactoe/firetactoe_test.py +++ b/appengine/standard/firebase/firetactoe/firetactoe_test.py @@ -43,8 +43,7 @@ def request(self, url, method, content='', *args, **kwargs): @pytest.fixture def app(testbed, monkeypatch, login): # Don't let the _get_http function memoize its value - orig_get_http = firetactoe._get_http - monkeypatch.setattr(firetactoe, '_get_http', lambda: orig_get_http({})) + firetactoe._get_http.cache_clear() # Provide a test firebase config. The following will set the databaseURL # databaseURL: "http://firebase.com/test-db-url" diff --git a/appengine/standard/firebase/firetactoe/templates/_firebase_config.html.jerjou b/appengine/standard/firebase/firetactoe/templates/_firebase_config.html.jerjou new file mode 100644 index 00000000000..87750235e62 --- /dev/null +++ b/appengine/standard/firebase/firetactoe/templates/_firebase_config.html.jerjou @@ -0,0 +1,12 @@ + +