1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

転職黙示録 (7) FastAPIのソースを読む 第1回 RouterにRouteを登録する

Last updated at Posted at 2019-09-20

FastAPIから学ぶWebフレームワークの内部

事前準備

前提条件

 作業環境は以下です.

  • Python (3.7.4)
  • Poetry 0.12.17
  • VSCode (1.38.1)
  • macOS Mojava (10.14.6)

プロジェクトの作成

poetry new --src fast-001
cd fast-001
poetry add uvicorn fastapi

依存パッケージ

Uvicorn

 軽量なASGIサーバーらしいです. つまりサーバーです. Webフレームワークで作ったWebアプリを動かすソフトウェアということですね.

WSGI

 ASGIはWSGIを非同期な処理にも拡張した仕様らしいです. WSGIはサーバーとWebアプリケーションの標準インターフェースを定めたもののようです.

The Web Server Gateway Interface (WSGI) is a standard interface between web server software and web applications written in Python. 1

 こうやって共通のインタフェースを決めておくと開発が別々にできますねということでしょうか.

Starlette

 ASGIに対応したWebフレームワーク(ツールキット)らしいです. UvicornとStarletteは両方ASGIに対応しているので一緒に使えますねということです. WebsocketやGraphQLにも対応しているようです.

対象コード

fast-001
import uvicorn
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Hello World"}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

コード・リーディング

Q. FastAPIクラスとは?

A. Starletteを継承したクラス

 つまりWebフレームワークということになります. Starletteがツールキットとも呼ばれるのはこのような拡張を意識した作りになっているからのようです.

application.py
# ...
class FastAPI(Starlette):
# ...

 アプリのインスタンスでルーティングの機能などを提供します. ルートの設定はデコレータを使います. よく見るとコンストラクタの引数にroutesというのがありここに実際のルートが格納されるようです. 意外にもリスト型です.2

application.py
class FastAPI(Starlette):
    def __init__(
        ...
        routes: List[BaseRoute] = None,
        ...
    ) -> None:

 BaseRouteはStarletteで定義されている抽象クラスです.

starlette/routing.py
class BaseRoute:
    def matches(self, scope: Scope) -> typing.Tuple[Match, Scope]:
        raise NotImplementedError()  # pragma: no cover

    def url_path_for(self, name: str, **path_params: str) -> URLPath:
        raise NotImplementedError()  # pragma: no cover

    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
        raise NotImplementedError()  # pragma: no cover

 またroutingモジュールのAPIRouterインスタンスをプロパティに持ちます. インスタンス化するときにroutesを渡します. このAPIRouterインスタンスが実際のルーティングの処理担うようです.

fastapi/application.py
self.router: routing.APIRouter = routing.APIRouter(
    routes, dependency_overrides_provider=self
)

Q. getは何をしているの?

A. 関数の再定義

実際にadd_api_routeはAPIRouterクラスのメソッドです. ルーターにルートを追加するという処理をしているわけです.

routing.py
def api_route(
        # ...
    ) -> Callable:
        def decorator(func: Callable) -> Callable:
            self.add_api_route(
                path,
                func,
                # ...
            )
            return func

        return decorator

 add_api_routeの引数を前二つ見るとpath, endPointという聞き慣れた名前になっています. 両者はほぼ同じ意味です.

add_api_route
def add_api_route(self, path: str, endPoint: Callable, ...):
# ...

 getの処理の流れをまとめておきましょう

  • パス文字列をapp.getに渡す
  • routerインスタンスのgetに引数を引き継ぐ
  • routerインスタンスのapi_route関数に引数を引き継ぐ
  • api_route関数からdecorator関数の定義を返す
routing.py
def api_route(
    self,
    path: str,
    ...
) -> Callable:
    def decorator(func: Callable) -> Callable:
        self.add_api_route(
            path,
            func,
            ...
        )
        return func

    return decorator

Q. デコレータとは?

A. 高階関数です.

 高階関数は引数として関数をとったり戻り値として関数を返す関数のことです. 関数をとり関数を返しますが, デコレータは渡した関数プラスアルファ何かします. 例を見るのが早いでしょう.

def higher_order_function(func):
    def wrapped(*arg, **kwarg):
        print('🐶')
        func()
        print('🦜')
    return wrapped

@higher_order_function
def saru():
    print("🐵")

saru()

 デコレータは高階関数の一種ですが, デコレータがやっているのは関数の定義を返すことです. つまり関数の再定義がデコレータが実際にしていることです. api_route関数は関数を再定義して返しています. しかしこの再定義した関数はadd_api_route関数でエンドポイントを登録する以外はもらった関数をそのまま返しています.3

 getデコレータによって, @app.get("/")以下の行がapi_routeの内部で定義されたdecoratorで置き換えられていると思えばいいでしょう.

Q. APIRouterはどのように複数のルートを管理しているの?

A. リストに入れてる

 つまりAPIRouterはコレクション型のラッパーです. add_api_routeでpathとendPointを登録しているのはすでに示した通りです.

routing.py
def add_api_route(self, path: str, endpoint: Callable, ...) -> None:
    route = self.route_class(path, endpoint=endpoint, ...)
    self.routes.append(route)

まとめ

  • FastAPIはStarletteを継承したクラス
  • APIRouterはAPIRouteクラスのインスタンスを管理するコレクション型のラッパー
  1. wsgiref

  2. てっきり辞書型かなと思っていました.

  3. 面白いけどなぜ?

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?