matobaの学んだこと

とあるPythonエンジニアのブログ

DjangoのWSGI周りのコード読んだ

DjangoWSGIってどうなってるんだ、と思ったので、コード読んだ話です。説明ではありません。自分のための備忘録。

Djangoのバージョンは、2.0.5。

最初は、wsgi.py

myproject/wsgi.py から読み進める。

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")

application = get_wsgi_application()

とりあえず、applicationが呼ばれると思うので、get_wsgi_application() の中を読んでいく。

django.core.wsgi.get_wsgi_application

def get_wsgi_application():
    """
    The public interface to Django's WSGI support. Return a WSGI callable.
    Avoids making django.core.handlers.WSGIHandler a public API, in case the
    internal WSGI implementation changes or moves in the future.
    """
    django.setup(set_prefix=False)
    return WSGIHandler()
  • get_wsgi_application() では、 django.core.handlers.wsgi.WSGIHandler の呼び出し結果を返してる。
  • django.setup(set_prefix=False) と言う処理も読んでいる。これは、なんだろう。読んでみよう。

django.setup

def setup(set_prefix=True):
    """
    Configure the settings (this happens as a side effect of accessing the
    first setting), configure logging and populate the app registry.
    Set the thread-local urlresolvers script prefix if `set_prefix` is True.
    """
    from django.apps import apps
    from django.conf import settings
    from django.urls import set_script_prefix
    from django.utils.log import configure_logging

    configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)
    if set_prefix:
        set_script_prefix(
            '/' if settings.FORCE_SCRIPT_NAME is None else settings.FORCE_SCRIPT_NAME
        )
    apps.populate(settings.INSTALLED_APPS)

疑問が出た。

  • django.utils.log.configure_logging と言うロギングっぽい設定をしている。これは何の設定?
  • django.urls.set_script_prefix ってなんだろ?
  • django.apps.apps.populateってなんだろう?

django.utils.log.configure_logging は何?

def configure_logging(logging_config, logging_settings):
    if logging_config:
        # First find the logging configuration function ...
        logging_config_func = import_string(logging_config)

        logging.config.dictConfig(DEFAULT_LOGGING)

        # ... then invoke it with the logging settings
        if logging_settings:
            logging_config_func(logging_settings)
  • ロギングの設定があったら、ロギングの設定を読み込んでる。
  • デフォルトのロギング設定も書いてあった。
  • いい感じに、文字列からモジュールをインポートしてる様子。

django.urls.set_script_prefix ってなんだろ?

def set_script_prefix(prefix):
    """
    Set the script prefix for the current thread.
    """
    if not prefix.endswith('/'):
        prefix += '/'
    _prefixes.value = prefix

_prefixes って変数にプリフィックスを入れてる。

これは何だろ、と思ったらこんな感じに定義されてる。

from threading import local
...
_prefixes = local()

スレッドローカルなデータ。マルチスレッドにする時に使うのかな。あとで、調べる。 よくわかってないけど進む。

django.apps.apps.populateってなんだろう?

https://github.com/django/django/blob/2.0.5/django/apps/registry.py#L59

  • settings.INSTALLED_APPS に書いてあるアプリを上から読み込んで有効化してる。
  • 有効化する時に重複しないようにロックを取得してる。
  • 重複して読み込まないようにチェックしてる。(重複して読み込むケースがあるんだろうか?)

  • 以下の処理が気になる。各アプリのコンフィグと親アプリを相互参照できるようにリンクしてる。なるほど

  • 他の箇所で、全てのモデルを取得して、キャッシュをクリアするとかできるようになってる。へえ。
  • django.appsdjangoにインストールされているアプリ群を管理するアプリのイメージ
                self.app_configs[app_config.label] = app_config
                app_config.apps = self

django.core.handlers.wsgi.WSGIHandler とはなんぞや。

最初に戻って django.core.handlers.wsgi.WSGIHandler はなんだろう。

https://github.com/django/django/blob/2.0.5/django/core/handlers/wsgi.py#L135

  • base.BaseHandler を継承している.
  • request_class=WSGIRequest と言うプロパティを持ってる。
  • __init____call__ がメソッドとして定義されてるクラス。

base.BaseHandler ?

https://github.com/django/django/blob/2.0.5/django/core/handlers/base.py#L15

  • ミドルウェアを読み込むメソッドがある。
  • atomic_requestを設定してるメソッドがある
  • レスポンスを返すメソッドがある
  • Djangoのアプリのミドルウェアのチェーンを実行していくクラスっぽい。

request_class=WSGIRequest と言うプロパティ

WSGIRequest ってなんだろう

https://github.com/django/django/blob/2.0.5/django/core/handlers/wsgi.py#L66

  • HttpRequest クラスを継承してる。これは何?
  • 初期化処理の中で、環境の設定を読み込んでいる様子。
  • HTTPリクエストに設定されてる環境のヘッダーとかをDjangoのフォーマットに直している。
  • それを、WSGIHandlerがDjangoミドルウェアチェーンに渡して、responseを作る。

HttpRequest クラスを継承してる。これは何?

https://github.com/django/django/blob/2.0.5/django/http/request.py#L37

  • HttpRequestのベースになる情報を整理するところ。
  • WSGIRequestの場合は、WSGIHandler経由で渡ってきたenvironの情報を設定してほげほげしてる。

終わり

  • 何と無く雰囲気がわかった。
  • WSGIアプリのWebサーバ gunicorn とかは、多分environとかをいい感じに渡しながらマルチプロセスにして管理してくれるのかな、と思った。
  • そのうち gunicorn とか uWsgi のコードも読みたい。

よかったら、アンケートに答えてもらえると嬉しいです。