Python
cloudfunctions
google-api-python-client

Google Cloud Functionsでgoogle-api-python-clientを利用してfile_cache is unavailableエラーになったときの対応方法


概要

Google Cloud FunctionsでPython(Beta)を利用して、他のGCPサービスを利用する場合、google-api-python-clientを利用するのが便利です。

googleapis / google-api-python-client

https://github.com/googleapis/google-api-python-client

ただし現状、ImportError が発生するケースがあるので、その対応方法です。(2018/10/31時点)


発生するエラー

ImportError: file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth


回避策: cache_discovery を無効化する


requirements.txt

google-api-python-client

oauth2client


main.py

from googleapiclient import discovery

from httplib2 import Http

from oauth2client.client import GoogleCredentials

def check_discovery_error(request):
# Cloud Functions環境から認証情報を取得する
credentials = GoogleCredentials.get_application_default()

cloudBuild = discovery.build(
'cloudbuild', 'v1', # Cloud Buildじゃなく、他のサービスでもおk
http=credentials.authorize(Http()),
cache_discovery=False)# cache_discovery=Falseしてエラーを回避する

request = cloudBuild.projects().builds().list(projectId='[GCPのプロジェクトID]')
response = request.execute()
print(response)



再現してみる


前提


  • GCPプロジェクトが利用可能


  • gcloud beta が端末にインストール済みで利用可


実装の用意

> mkdir 任意のディレクトリ

> cd 任意のディレクトリ
> touch requirements.txt
> touch main.py


requirements.txt

google-api-python-client


今回はCloud Buildからジョブリストを取得する実装にしていますが、他のサービスでもおkです。


main.py(回避前)

from googleapiclient import discovery

def check_discovery_error(request):
cloudBuild = discovery.build('cloudbuild', 'v1')
request = cloudBuild.projects().builds().list(projectId='GCPのプロジェクトID')
response = request.execute()
print(response)


これをCloud Functionsにデプロイして実行します。

> gcloud beta functions deploy check_discovery_error \

--trigger-http \
--runtime=python37

> gcloud beta functions call check_discovery_error

実行すると、以下のようにエラーが出力されます。


ログ(一部抜粋)

> gcloud functions logs read check_discovery_error

E from oauth2client.contrib.locked_file import LockedFile
E ModuleNotFoundError: No module named 'oauth2client'
E
E During handling of the above exception, another exception occurred:
E
E Traceback (most recent call last):
E File "/env/local/lib/python3.7/site-packages/googleapiclient/discovery_cache/file_cache.py", line 37, in <module>
E from oauth2client.locked_fileimport LockedFile
E ModuleNotFoundError: No module named 'oauth2client'
E
E During handling of the above exception, another exception occurred:
E
E Traceback (most recent call last):
E File "/env/local/lib/python3.7/site-packages/googleapiclient/discovery_cache/__init__.py", line 41, in autodetect
E from . import file_cache
E File "/env/local/lib/python3.7/site-packages/googleapiclient/discovery_cache/file_cache.py", line 41, in <module>
E 'file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth')
E ImportError: file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth
I URL being requested: GET https://www.googleapis.com/discovery/v1/apis/cloudbuild/v1/rest
I URL being requested: GET https://cloudbuild.googleapis.com/v1/projects/[GCPのプロジェクトID]/builds?alt=json
I {'builds': [{'id': ()
D Function execution took 138 ms, finished with status code: 200



原因

調べて見るとあちこちでissueが上がっていますが、いまのところ、根本的な解決はされてないようです。

file_cache is unavailable when using oauth2client >= 4.0.0

https://github.com/googleapis/google-api-python-client/issues/299

File caching for the oauth2client >= 4.0.0

https://github.com/googleapis/google-api-python-client/issues/325


回避策

issueのコメントに回避策がコメントされていました。ありがたや^^


cache_discovery を無効化する

ライブラリのインポート時にキャッシュを利用すると、oauth2clientのバージョンチェックに引っかかる?みたいなので、無効化して回避すればよいみたいです。

https://github.com/googleapis/google-api-python-client/issues/299#issuecomment-268915510


It seems to be the better way to silence this error, if one does not care about the cache, is to simply specify cache_discovery=False when performing discovery.build() -- this avoids the broken code path.


以下のコメントの実装方法を参考にしました。

https://github.com/googleapis/google-api-python-client/issues/299#issuecomment-427118293

discovery.buildcache_discovery パラメータ指定する場合、http パラメータも指定する必要があったので、oauth2client を用いてCredentials を取得するようにしました。


requirements.txt

google-api-python-client

oauth2client


main.py(対応版)

from googleapiclient import discovery

from httplib2 import Http

from oauth2client.client import GoogleCredentials

def check_discovery_error(request):
# Cloud Functions環境から認証情報を取得する
credentials = GoogleCredentials.get_application_default()

cloudBuild = discovery.build(
'cloudbuild', 'v1', # Cloud Buildじゃなく、他のサービスでもおk
http=credentials.authorize(Http()),
cache_discovery=False)# cache_discovery=Falseしてエラーを回避する

request = cloudBuild.projects().builds().list(projectId='[GCPのプロジェクトID]')
response = request.execute()
print(response)


再びデプロイと実行して、エラー出力がなくなっていることが確認できました。

やったぜ。


一部抜粋

D      Function execution started

I URL being requested: GET https://www.googleapis.com/discovery/v1/apis/cloudbuild/v1/rest
I URL being requested: GET https://cloudbuild.googleapis.com/v1/projects/[GCPのプロジェクトID]/builds?alt=json
I {'builds': [{'id': ()
D Function execution took 2352 ms, finished with status code: 200


参考

file_cache is unavailable when using oauth2client >= 4.0.0

https://github.com/googleapis/google-api-python-client/issues/299

File caching for the oauth2client >= 4.0.0

https://github.com/googleapis/google-api-python-client/issues/325

Google Driveにpythonでデータを登録する & Siderを使ってチェックしてみる

https://qiita.com/akiko-pusu/items/f05a5dcba544e97c057c

[GoogleCloudPlatform] API Client Libraryを用いてGoogle Cloud APIを利用する

https://qiita.com/j-un/items/dc46b3b766a7afb4080c