Flask-Injector で試したあれこれ

Flask-Injector を試してる時に、これはどうしたら良いんだろうというのがあったので、色々試してみた。

コンストラクタに引数を渡したい

色々試していて以下のようにコンストラクタに引数を受け取りたい場合はどうしたら良いのか試行錯誤した所、binds でインスタンスを生成する際に渡してやればよさそう。

class RiakCSStorage(object):
    def __init__(self, config):
        # config に Flask の config を受け取りたい
        self.config = config
from injector import inject, singleton
from flask import Flask
from flask.ext.injector import request

@inject(app=Flask)
def configure(binder, app):
    riakcs = RiakCSStorage(app.config)
    binder.bind(Base, to=riakcs , scope=singleton)

インスタンスを生成して渡す。

request scope

Flask-Injector には request scope があり、これはリクエストごとにインスタンスが生成される。
が、上記のアプリケーションコンテキストである app.config を使ってインスタンスを生成した場合は singleton になるよう

from injector import inject
from flask import Flask
from flask.ext.injector import request

@inject(app=Flask)
def configure(binder, app):
    riakcs = RiakCSStorage(app.config)
    binder.bind(Base, to=riakcs , scope=request)
from injector import inject
from flask import Blueprint
from models.base import Base

app = Blueprint('index', __name__)


@app.route('/', strict_slashes=False, methods=['GET'])
@inject(service=Base)
def index(service):
    print service

    return service.save()
$ python app.py
 * Running on http://127.0.0.1:5000/
 * Restarting with reloader
<models.riakcs.RiakCSStorage object at 0x1071fdcd0>
127.0.0.1 - - [07/Jan/2014 23:35:57] "GET / HTTP/1.1" 200 -
<models.riakcs.RiakCSStorage object at 0x1071fdcd0>
127.0.0.1 - - [07/Jan/2014 23:35:58] "GET / HTTP/1.1" 200 -

オブジェクト ID が同じ事から singleton になってると思われる。

bind する際の tips

以下のような /local にアクセスがあったら FileStorage を /riakcs にアクセスがあったら RiakCSStorage のインスタンスを使いたいみたいな場合どうすれば良いのかなと悩んだ。

from injector import inject
from flask import Blueprint
from models.base import Base

app = Blueprint('index', __name__)


@app.route('/local', strict_slashes=False, methods=['GET'])
@inject(service=Base)
def index(service):
    print service

    return service.save()


@app.route('/riak', strict_slashes=False, methods=['GET'])
@inject(service=Base)
def riak(key, service):
    print service

    return service.save()
from flask import Flask
from injector import inject, singleton
from models.filestorage import FileStorage
from models.riakcs import RiakCSStorage

@inject(app=Flask)
def configure(binder, app):
    binder.bind(Base, to=FileStorage, scope=singleton)
    binder.bind(Base, to=RiakCSStorage, scope=singleton)

こうやって書いてみたが、/local にアクセスしても最後に bind した RiakCSStorage のインスタンスが生成された。
よくよく考えてみたら当然か。
Base という inject 先にどっちのインスタンスを渡せば良いかなんて分からない。
以下のように Key() を使えば実現できる。
binds.py

from flask import Flask
from injector import inject, singleton, Key
from models.filestorage import FileStorage as FileStorageProvider
from models.riakcs import RiakCSStorage as RiakCSStorageProvider

FileStorage = Key('filestorage')
RiakCSStorate = Key('riakcs')

@inject(app=Flask)
def configure(binder, app):
    binder.bind(FileStorage, to=FileStorageProvider, scope=singleton)
    binder.bind(RiakCSStorate, to=RiakCSStorageProvider, scope=singleton)

views/index.py

from injector import inject
from flask import Blueprint
from binds import FileStorage, RiakCSStorage

app = Blueprint('index', __name__)


@app.route('/local', strict_slashes=False, methods=['GET'])
@inject(service=FileStorage)
def index(service):
    print service

    return service.save()


@app.route('/riak', strict_slashes=False, methods=['GET'])
@inject(service=RiakCSStorage)
def riak(key, service):
    print service

    return service.save()

これで /local にアクセスすると FileStorage のインスタンスが、/riak にアクセスすると RiakCSStorage のインスタンスが service に inject される。

一つの変数に複数のインスタンスをインジェクションする(リスト)

from flask import Flask
from injector import inject, singleton, Key
from models.filestorage import FileStorage
from models.riakcs import RiakCSStorage

Names = Key('names')

@inject(app=Flask)
def configure(binder, app):
    binder.multibind(Names, to=[FileStorage(app.config)], scope=singleton)
    binder.multibind(Names, to=[RiakCSStorage], scope=singleton)
from injector import inject
from flask import Blueprint
from binds import Names

app = Blueprint('index', __name__)


@app.route('/local', strict_slashes=False, methods=['GET'])
@inject(service=Names)
def index(service):
    print service

    return ''
$ python app.py
 * Running on http://127.0.0.1:5000/
 * Restarting with reloader
[<models.filestorage.FileStorage object at 0x10b37ec90>, <class 'models.riakcs.RiakCSStorage'>]

service にはリストが格納される。
何の役に立つのだろうかと思ったけど、シーケンシャルにロジックを実行して行くというようなのに使えそう。

一つの変数に複数のインスタンスをインジェクトする(辞書)

from flask import Flask
from injector import inject, singleton, MappingKey
from models.localfs import LocalFS as LocalFSProvider
from models.riakcs import RiakCS as RiakCSProvider

Names = MappingKey('names')

@inject(app=Flask)
def configure(binder, app):
    to = {
        'local': LocalFSProvider(app.config),
        'riakcs': RiakCSProvider
    }
    binder.multibind(Names, to=to, scope=singleton)
$ python app.py
 * Running on http://127.0.0.1:5000/
 * Restarting with reloader
{'local': <models.filestorage.FileStorage object at 0x1066eec90>, 'riakcs': <class 'models.riakcs.RiakCSStorage'>}

こっちは service に辞書が入る。
ユースケースが思いつかない…。