とりあえず、wsgiref
モジュールを使って、簡単なWebアプリを作ってみてはいかがですか。
マニュアルのサンプルコードを引用すると、
python
1from wsgiref.util import setup_testing_defaults
2from wsgiref.simple_server import make_server
3
4# A relatively simple WSGI application. It's going to print out the
5# environment dictionary after being updated by setup_testing_defaults
6def simple_app(environ, start_response):
7 setup_testing_defaults(environ)
8
9 status = '200 OK'
10 headers = [('Content-type', 'text/plain; charset=utf-8')]
11
12 start_response(status, headers)
13
14 ret = [("%s: %s\n" % (key, value)).encode("utf-8")
15 for key, value in environ.items()]
16 return ret
17
18with make_server('', 8000, simple_app) as httpd:
19 print("Serving on port 8000...")
20 httpd.serve_forever()
の、simple_app
関数がWSGIアプリケーション、make_server
関数以下の処理がWebサーバの役割になります。
WSGIアプリケーションとは、上記のように「クライアントから送られてきた情報が格納されている辞書オブジェクト」と「レスポンスを開始するために呼び出すcallableオブジェクト」の2つの引数を持ち、レスポンス本文を戻り値とするcallableなオブジェクトでしかありません。
WSGIに対応したWebサーバは、クライアントからの接続があるたびに、上記のcallableオブジェクトを適切な引数を持って呼び出し、「レスポンスを開始するために呼び出すcallableオブジェクト」の呼び出しと上記のcallableオブジェクトの戻り値を元に、クライアントにレスポンスを返します。
Flask
のソースコードで言えば、以下の点です。
python
1 def __call__(self, environ: dict, start_response: t.Callable) -> t.Any:
2 """The WSGI server calls the Flask application object as the
3 WSGI application. This calls :meth:`wsgi_app`, which can be
4 wrapped to apply middleware.
5 """
6 return self.wsgi_app(environ, start_response)
__call__
という名のメソッドは特殊メソッドで、そのクラスのインスタンスがcallableオブジェクトとして扱われた際に呼び出されるメソッドです。
python
1class Hoge:
2 def __call__(self):
3 print('Hello.')
4
5a = Hoge()
6a() # Hoge.__call__が呼ばれ、「Hello.」と出力される。
Flask.__call__
が定義されているということは、Flask
クラスのインスタンスはcallableなオブジェクトであり、Flask
クラスのインスタンス自体がWSGIアプリケーションとなりうるわけです。
先程のコードも、simple_app
の代わりにFlask
クラスのインスタンスを使用することができることになります。
python
1from wsgiref.util import setup_testing_defaults
2from wsgiref.simple_server import make_server
3
4from flask import Flask
5
6app = Flask(__name__)
7
8@app.route('/')
9def index():
10 return 'Hello, flask!'
11
12
13with make_server('', 8000, app) as httpd:
14 print("Serving on port 8000...")
15 httpd.serve_forever()
コメントを受けて。
WSGIに対応したWebサーバ
具体的になんのことをいっていますでしょうか?uwsgiやビルトインサーバーのことをいっていますでしょうか?であれば、WEBサーバーではなくアプリケーションサーバーのことだと思いますが、あっていますでしょうか?
おおよそその認識で結構です。
そもそも、WEBサーバもアプリケーションサーバも、どこかの企画で決められた名称ではありませんから、意味合いも多少のズレがあると思います。
(自分も、サーバは本職ではないので、多少言葉が間違っている点もあるかもしれませんが)
自分の場合、クライアントからのリクエストやレスポンスまで記述したので「Webサーバ」という言葉を使ったまでです。
こちらも具体的に言うと、start_responseにはいるのは、'name'であっていますでしょうか?
start_response
は、以前の回答でも書いたとおり「レスポンスを開始するために呼び出すcallableオブジェクト」です。
この説明では分かりづらいかもしれませんが、もうちょっと言い換えると「WSGIアプリケーションで、レスポンスを返すために必要な情報を渡して呼び出さなければならないcallableオブジェクト」です。
先に挙げたサンプルコードでも呼び出していますし、flaskなどを使わないシンプルなWSGIアプリケーションのサンプルを探せば、同様に呼び出しているはずです。(それを確認する意味でも、「wsgiref
モジュールを使って、簡単なWebアプリを作ってみては」と提案したのですが)
もし可能でしたら、python+flask+nginx+uwsgiの構成でクライアントからリクエストがきたときにどういう処理の順番でflaskが起動するか教えて頂けますと助かります。
くり返し言いますが、「WSGIアプリケーションは、単なるcallableオブジェクトであり、(uwsgiのような)WSGIアプリケーションを起動する側は、そのcallableオブジェクトを呼び出している」だけです。
自分はuwsgiを使ったことはありませんが、例えばここを参考に、
uwsgi --http=0.0.0.0:8080 --wsgi-file=run.py --callable=app
と起動したとします。
起動時に指定しているのは、pythonのスクリプト名とWSGIアプリケーションのcallableオブジェクトとなる名前(flaskのインスタンスがcallableオブジェクトになるのは先に説明したとおり)ですね。
uwsgiは、これらの情報を元に、WSGIアプリケーションを呼び出すだけです。
python
1
2environ = {}
3# environに、WSGIの仕様に従った情報を設定
4def start_response(status, headers):
5 # 渡されたstatusとヘッダ情報を元に、httpのヘッダを送信
6 pass
7
8# こんな単純なimportではないと思いますが、そこは長くなるし、WSGIとは関係のないことなので省略
9import run
10response = run.app(environ, start_response)
11
12# responseを元に、httpのボディを送信
これだけのことを知っていれば十分だと思いますが、environ
の中身や、start_response
の中の処理を知りたければ、WSGIの仕様をよく理解すべきかと思います。(この辺りかな?)
あと、httpのプロトコルのことも理解していないのであれば、それも理解すべきです。
python
1app.run(environ, start_response(a, b))
と、
python
1app.run(environ, start_response)
とでは、全く違います。
前者は、run
メソッドを呼び出す前にstart_response
関数を呼び出し、その戻り値をrun
メソッドに渡します。
後者は、start_response
関数を呼び出していません。
start_response
関数そのものを渡し、run
メソッドの中でstart_response
関数を呼び出せるようにしています。
また、start_responseがcallableなオブジェクトってことであれば、どの箇所をコードからcallableだといっていますでしょうか?つまり、どこに__call__
がありますでしょうか?
def
キーワードで定義された関数・メソッドは、callableオブジェクトになります。
Pythonの内部で勝手に__call__
が定義される、と思ってください。
>>> def a():
... pass
...
>>> a.__call__
<method-wrapper '__call__' of function object at 0x7f64299eab80>
>>>
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/06/29 17:21
2021/06/29 23:34
2021/06/30 13:49
2021/07/02 05:21 編集
2021/07/02 06:58