Thread safe client lib on app engine (python) - python

I found a little sample code about bigquery insert in one of Google's git repositories.
https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/appengine/standard/bigquery/main.py
If you see the app.yaml it says this code should be thread safe, but if I'm lokking at the client lib's documentation (https://developers.google.com/api-client-library/python/guide/thread_safety) it should not be thread safe. I'm a little bit confused now, that my following code is thread safe or not?
It's running on app engine standard env.
import pprint
from googleapiclient.discovery import build
from oauth2client.client import GoogleCredentials
credentials = GoogleCredentials.get_application_default()
# Create the bigquery api client
service = build('bigquery', 'v2', credentials=credentials)
response = service.datasets().list(projectId='PROJECTID').execute()
pprint.pprint(response)
---- UPDATE ----
After Tim's answer I changed my code to the following. This should be good now:
import pprint
from googleapiclient.discovery import build
from oauth2client.contrib.appengine import AppAssertionCredentials
import httplib2
credentials = AppAssertionCredentials(scope='https://www.googleapis.com/auth/bigquery')
# Create the bigquery api client
service = build('bigquery', 'v2')
def get():
# authorize http object with client credentials
http = credentials.authorize(httplib2.Http())
response = service.datasets().list(projectId='PROJECTID').execute(http=http)
pprint.pprint(response)

If you read the docs you reference it says
The google-api-python-client library is built on top of the httplib2
library, which is not thread-safe. Therefore, if you are running as a
multi-threaded application, each thread that you are making requests
from must have its own instance of httplib2.Http().
They then go on to show you how to do this. If you follow the instructions then yes it will be.
You sample code is too simple and isn't attempting what was outlined in the docs
# Create a new Http() object for every request
def build_request(http, *args, **kwargs):
new_http = httplib2.Http()
return apiclient.http.HttpRequest(new_http, *args, **kwargs)
service = build('api_name', 'api_version', requestBuilder=build_request)
# Pass in a new Http() manually for every request
service = build('api_name', 'api_version')
http = httplib2.Http()
service.stamps().list().execute(http=http)
So if you tried your code in a threaded situation it would not be thread safe.
If you are just trying that code from REPL then I doubt you are in a threaded situation.

Related

How do I execute Google app script function from Python script via API? Not able to locate credentials.json for download in order to execute appscript

I'm trying to run a google app script function remotely from a python flask app. This function creates google calendar events with inputs from a google sheet. I referred to this documentation from Google in order to set up the python script to run the appscript function. I followed every step required to deploy the app script project as an executable API and connected it to a google developer project and made OAuth 2.0 ID credentials as well.
From the API executable documentation, I got the following code and modified it to run as an object which can be called from the main server file.
from __future__ import print_function
from googleapiclient import errors
from googleapiclient.discovery import build
from httplib2 import Http
from oauth2client import file as oauth_file, client, tools
class CreateGCalEvent:
def main(self):
"""Runs the sample.
"""
SCRIPT_ID = 'my app script deployment ID was put here'
# Set up the Apps Script API
SCOPES = [
'https://www.googleapis.com/auth/script.scriptapp',
'https://www.googleapis.com/auth/drive.readonly',
'https://www.googleapis.com/auth/drive',
]
store = oauth_file.Storage('token.json')
creds = store.get()
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('app_script_creds.json', SCOPES)
creds = tools.run_flow(flow, store)
service = build('script', 'v1', credentials=creds)
# Create an execution request object.
request = {"function": "getFoldersUnderRoot"}
try:
# Make the API request.
response = service.scripts().run(body=request,
scriptId=SCRIPT_ID).execute()
if 'error' in response:
# The API executed, but the script returned an error.
# Extract the first (and only) set of error details. The values of
# this object are the script's 'errorMessage' and 'errorType', and
# an list of stack trace elements.
error = response['error']['details'][0]
print("Script error message: {0}".format(error['errorMessage']))
if 'scriptStackTraceElements' in error:
# There may not be a stacktrace if the script didn't start
# executing.
print("Script error stacktrace:")
for trace in error['scriptStackTraceElements']:
print("\t{0}: {1}".format(trace['function'],
trace['lineNumber']))
else:
# The structure of the result depends upon what the Apps Script
# function returns. Here, the function returns an Apps Script Object
# with String keys and values, and so the result is treated as a
# Python dictionary (folderSet).
folderSet = response['response'].get('result', {})
if not folderSet:
print('No folders returned!')
else:
print('Folders under your root folder:')
for (folderId, folder) in folderSet.items():
print("\t{0} ({1})".format(folder, folderId))
except errors.HttpError as e:
# The API encountered a problem before the script started executing.
print(e.content)
Here is where the error comes. It can neither locate token.json nor the app_script_creds.json.
Now with a service account and any normal OAuth2.0 ID, when I create it, I will be given the option to download the credentials.json but here, this is all I seem to be getting, an App Script ID with no edit access or credentials to download as JSON. I created another OAuth ID in the same project as shown in the screenshot which has the edit access and json ready for download. When I used that json file inside the python script, It told me that it was expecting redirect uris, which I don't know for what it is or where to redirect to.
What do I need to do to get this working?
I adapted some code that I used for connecting to the App Scripts API. I hope it works for you too. The code is pretty much the same thing as this.
You can use from_client_secrets_file since you're already loading these credentials from the file. So, what the code does is look for a token file first. If the token file is not there, it logs in the user (prompting using the Google authorization screen) and stores the new token in the file as pickle.
Regarding the credentials in the Google console you need to pick the Desktop application when creating them because that is basically what a server is.
Note: with this, you can only have one user that will be doing all of these actions. This is because the server script will start a local server on the server machine to authenticate you, your client code will not see any of this.
import logging
import pickle
from pathlib import Path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
class GoogleApiService:
def __init__(self, , scopes):
"""
Args:
scopes: scopes required by the script. There needs to be at least
one scope specified.
"""
self.client_secrets= Path('credentials/credentials.json')
self.token_path = Path('credentials/token.pickle')
self.credentials = None
self.scopes = scopes
def get_service(self):
self.__authenticate()
return build('script', 'v1', credentials=self.credentials)
def __authenticate(self):
log.debug(f'Looking for existing token in {self.token_path}')
if self.token_path.exists():
with self.token_path.open('rb') as token:
self.credentials = pickle.load(token)
if self.__token_expired():
self.credentials.refresh(Request())
# If we can't find any token, we log in and save it
else:
self.__log_in()
self.__save_token()
def __log_in(self):
flow = InstalledAppFlow.from_client_secrets_file(
self.client_secrets,
self.scopes
)
self.credentials = flow.run_local_server(port=0)
def __save_token(self):
with self.token_path.open('wb') as token:
pickle.dump(self.credentials, token)
def __token_expired(self):
return self.credentials and self.credentials.expired and \
self.credentials.refresh_token
# Example for Google Apps Scripts
def main():
request = {'function': 'some_function', 'parameters': params}
gapi_service = GoogleApiService()
with gapi_service.get_service() as service:
response = service.scripts().run(
scriptId=self.script_id,
body=request
).execute()
if response.get('error'):
message = response['error']['details'][0]['errorMessage']
raise RuntimeError(message)
else:
return response['response']['result']

How to authorize a Python Google API client service given an OAuth2 access_token?

I have implemented the python-social-auth library for Google OAuth2 in my Django project, and am successfully able to log users in with it. The library stores the access_token received in the response for Google's OAuth2 flow.
My question is: use of the google-api-python-client seems to rely on creating and authorizing a credentials object, then using it to build an API service like so:
...
# send user to Google consent URL, get auth_code in response
credentials = flow.step2_exchange(auth_code)
http_auth = credentials.authorize(httplib2.Http())
from apiclient.discovery import build
service = build('gmail', 'v1', http=http_auth)
# use service for API calls...
Since I'm starting with an access_token provided by python-social-auth, how do I create & authorize the API client service for future API calls?
Edit: to clarify, the code above is from the examples provided by Google.
Given you already have the OAuth2 access token you can use the AccessTokenCredentials class.
The oauth2client.client.AccessTokenCredentials class is used when you have already obtained an access token by some other means. You can create this object directly without using a Flow object.
Example:
import httplib2
from googleapiclient.discovery import build
from oauth2client.client import AccessTokenCredentials
credentials = AccessTokenCredentials(access_token, user_agent)
http = httplib2.Http()
http = credentials.authorize(http)
service = build('gmail', 'v1', http=http)
You can check examples provided by Google Guide API, for example: sending email via gmail application, https://developers.google.com/gmail/api/guides/sending
If you have an already refreshed latest access token which is not expired, then you can get service variable as:
from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials
creds = Credentials("<ACCESS_TOKEN>")
service = build('gmail', 'v1', credentials=creds)
# Call the Gmail API

GMAIL API: <HttpError 400 when requesting https://www.googleapis.com/gmail/v1/users/myemail%40gmail.com/labels?alt=json returned "Bad Request">

I am trying to write a python program that will access my gmail account through the gmail API using account services. I followed all the steps in the Using OAuth 2.0 for Server to Server Applications
here is my code:
from __future__ import print_function
import httplib2
import os
from apiclient.discovery import build
from httplib2 import Http
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage
from oauth2client.service_account import ServiceAccountCredentials
try:
import argparse
flags = argparse.ArgumentParser(parents=[ tools.argparser]).parse_args()
except ImportError:
flags = None
def main():
SCOPES = ['https://www.googleapis.com/auth/gmail.modify']
APPLICATION_NAME = 'Gmail Application'
credentials = ServiceAccountCredentials.from_json_keyfile_name(
'MyKeyFile.json', scopes=SCOPES)
http_auth = credentials.authorize(Http())
service = build('gmail', 'v1', http=http_auth)
results = service.users().labels().list(userId='myemail#gmail.com').execute()
labels = results.get('labels', [])
if not labels:
print('No labels found.')
else:
print('Labels:')
for label in labels:
print(label['name'])
if __name__ == '__main__':
main()
When I run this i get a an error 400 Bad Request.
Anyone encountered this issue. I have been changing things around foro a couple of days now and nothing has worked: I used the service ID instead of my email, I used p12 credentials api, created new projects and new account services with new keys... you name it. I'm hitting a wall. Can anyone help?
Thanks
You should to Enable your API on Dashboard in your API Manager menu here
Then, choose you interested in API
Finally, you'll see something like this

Error 404 on creating a calendar with Google Calendar Api

I just wrote this code that is supposed to check if calendar exists and if not create one. Well it returns error 404 when I try to create a calendar and the calendar does NOT appear. Any ideas? I blanked out clientid, secret, app key.
import gflags
import httplib2
import sys, traceback
from apiclient.discovery import build
from oauth2client.file import Storage
from oauth2client.client import OAuth2WebServerFlow
from oauth2client.tools import run
FLAGS = gflags.FLAGS
# Set up a Flow object to be used if we need to authenticate. This
# sample uses OAuth 2.0, and we set up the OAuth2WebServerFlow with
# the information it needs to authenticate. Note that it is called
# the Web Server Flow, but it can also handle the flow for native
# applications
# The client_id and client_secret are copied from the API Access tab on
# the Google APIs Console
FLOW = OAuth2WebServerFlow(
client_id='MY_CLIENT_ID',
client_secret='MY_SECRET',
scope='https://www.googleapis.com/auth/calendar',
user_agent='KUDOS_CALENDAR/v1')
# To disable the local server feature, uncomment the following line:
# FLAGS.auth_local_webserver = False
# If the Credentials don't exist or are invalid, run through the native client
# flow. The Storage object will ensure that if successful the good
# Credentials will get written back to a file.
storage = Storage('calendar.dat')
credentials = storage.get()
if credentials is None or credentials.invalid == True:
credentials = run(FLOW, storage)
# Create an httplib2.Http object to handle our HTTP requests and authorize it
# with our good Credentials.
http = httplib2.Http()
http = credentials.authorize(http)
# Build a service object for interacting with the API. Visit
# the Google APIs Console
# to get a developerKey for your own application.
service = build(serviceName='calendar', version='v3', http=http,
developerKey='MY_DEV_KEY')
kudos_calendar = None
try:
kudos_calendar = service.calendarList().get(calendarId='KudosCalendar').execute()
except:
print 'Calendar KudosCalendar does not exist!'
print 'Creating one right now...'
kudos_calendar_entry = {
'id': 'KudosCalendar'
}
kudos_calendar = service.calendarList().insert(body=kudos_calendar_entry).execute()
OK, I found a way around. I am not sure what exactly are google abstractions reflecting, but I am pretty sure one cannot just create calendar list. However if you just create a calendar then everything goes fine and then one can use calendar id to access calendarlist entry corresponding to that calendar.
Ufff.. Horribly confusing. Also while trying to do that I found at least two bugs in example python codes given in docs. I think they still did not properly rolled out v3.

Google Analytics Core Reporting API Python library

I'm starting looking at Google Analytics core reporting API, which is now in version 3.
According to the documentation, I could use one of the client libraries listed in the link http://code.google.com/apis/analytics/docs/gdata/v3/gdataLibraries.html.
I'm using python, so I was looking for an example of using the core reporting API in python, but I could not find one using this library. None of the examples at http://code.google.com/p/google-api-python-client/wiki/SampleApps include an example of the Core Reporting API.
One other option seems to be using the library at http://code.google.com/p/gdata-python-client/ but I'm not sure this library is using the lastest version of the core reporting API (v3.0).
I'm looking for a python library (with documentation / examples) that is compliant to http://code.google.com/apis/analytics/docs/gdata/v3/reference.html
Thanks
I did not find any example or good documentation, but I was able to mix general oauth2 authentication with the JAVA example and the python library source code to find an answer. So, here it goes:
Authentication:
from oauth2client.file import Storage
from oauth2client.client import AccessTokenRefreshError
from oauth2client.client import OAuth2WebServerFlow
from oauth2client.tools import run
import httplib2
FLOW = OAuth2WebServerFlow(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
scope='https://www.googleapis.com/auth/analytics.readonly')
storage = Storage('file_name.dat')
credentials = storage.get()
if credentials is None or credentials.invalid:
credentials = run(FLOW, storage)
http = credentials.authorize(httplib2.Http())
Connecting to the Core Reporting API (I'm not sure the verb "connect" is adequate)
from apiclient.discovery import build
service = build('analytics', 'v3', http=http)
Making a query:
query = service.data().ga().get(ids='ga:%d' % PROFILE_ID, start_date=START_DATE, end_date=END_DATE,metrics='ga:pageviews')
results = query.execute()
The full list of parameters to pass to the get method when creating the query can be found at http://api-python-client-doc.appspot.com/analytics/v3/data/ga.
The results come in a python dict exactly as described in http://code.google.com/apis/analytics/docs/gdata/v3/reference.html#data_response

Categories