LoginSignup
5
8

More than 5 years have passed since last update.

Python でライブラリ関数のオーバーライド

Last updated at Posted at 2017-04-01

やりたいことはタイトルの通りなので、前置きは飛ばしても良いです。

背景、前置き

Google API Client Library を使ってGoogle Calendar いじるアプリを書いていた。Google Cloud Platform からOAuth2のクライアント情報を client_secret.json として落としてきて、その ファイル名oauth2client.client.flow_from_clientsecrets() の引数に指定する。

ここで問題。セキュリティ上の問題でgitのバージョン管理に client_secret.json を加えたくない。かといって Heroku でアプリを動かしているので、ファイルをアップロードできない。(Amazon S3などのオブジェクトストレージを使えば良いらしいのだが、使ったことがなかったのでめんどくさいのでやめた)

ようするに、 client_secret.json を指定しなければならないのに、そのファイルをHerokuサーバーに置けなくて困ったということである。

ということで、 oauth2client.client.flow_from_clientsecrets() 関数に ファイル名 ではなく辞書型データを入れるようにしたい。そしてHerokuの環境変数に client_secret.json の内容をそれぞれ格納して、それを辞書データに落とし込めば良い。
(環境変数に機密情報入れるの良くないってマジ?)

追記

普通に OAuth2WebServerFlow のインスタンスを作れば良かったということにコード書いてから気づきました。よって別にオーバーライドなんてしなくて良いのですが、オーバーライドこうやってできるんだねってことで知見になったので残しておきます。

やりたいこと

oauth2client.client.flow_from_clientsecrets() 関数に ファイル名 ではなく 辞書型 を入れるようにしたい。

どうしたか

ライブラリのソースコードはこんな感じだった。
(以降は、Google Inc. のApache license コードより抜粋)

oauth2client.client.py より一部省略。

@_helpers.positional(2)
def flow_from_clientsecrets(filename, scope, redirect_uri=None,
                            message=None, cache=None, login_hint=None,
                            device_uri=None, pkce=None, code_verifier=None):
    try:
        client_type, client_info = clientsecrets.loadfile(filename,
                                                          cache=cache)
        if client_type in (clientsecrets.TYPE_WEB,
        -----以下略------

注目するのは filenameclientsecrets.loadfile() に渡されているということ。
さらに clientsecrets.loadfile() を見てみた。

def loadfile(filename, cache=None):
    _SECRET_NAMESPACE = 'oauth2client:secrets#ns'

    if not cache:
        return _loadfile(filename)

    obj = cache.get(filename, namespace=_SECRET_NAMESPACE)
    -----以下略------

cache が存在しない場合、 _loadfile 関数に処理を移譲してる。 _loadfile を見てみよう。

def _loadfile(filename):
    try:
        with open(filename, 'r') as fp:
            obj = json.load(fp)
    except IOError as exc:
        raise InvalidClientSecretsError('Error opening file', exc.filename,
                                        exc.strerror, exc.errno)
    return _validate_clientsecrets(obj)

ファイルをロードしてる関数にたどり着いた。この関数をオーバーライドすれば filename 引数に辞書型をぶちこめるのではないか。

ということで以降は自分のコード

# `client.flow_from_clientsecrets` 関数が引数に辞書型を取れるように
# `clientsecrets._loadfile` 関数をオーバーライドする
def new_loadfile(data):
    return clientsecrets._validate_clientsecrets(data)
clientsecrets._loadfile = new_loadfile

こうすれば以降

flow = client.flow_from_clientsecrets(
    辞書型,
    scope='https://www.googleapis.com/auth/calendar',
    redirect_uri=flask.url_for('oauth2callback', _external=True))

のように 辞書型を引数に取る flow_from_clientsecrets が使えるようになった。

まとめ

def new_func(args):
    # いろんな処理
    return hoge
hoge_library.func = new_func

こうすれば簡単にオーバーライドできて、その関数を使う関数全てに影響するんだね。

ライブラリ関数のオーバーライド初めてやったけど、こんな簡単にちゃんと動くんだなー。怖いけど。

5
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
8