概要

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

元記事はこちら

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