質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
uWSGI

uWSGIは、PythonでWebサービスを動かすアプリケーションサーバの一つです。WSGI(Web Server Gateway Interface)アプリケーションコンテナの一種で、WSGIに則ったDjangoやFlaskなどで動かすことができます。

import

自身のプラットフォーム・プログラム・データセットに対して、外部ソースを取り込むプロセスをimportと呼びます。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

1873閲覧

引数のenviron, start_responseは、どのモジュールのどこの記述箇所でセットされているのか

sequelanonymous

総合スコア123

uWSGI

uWSGIは、PythonでWebサービスを動かすアプリケーションサーバの一つです。WSGI(Web Server Gateway Interface)アプリケーションコンテナの一種で、WSGIに則ったDjangoやFlaskなどで動かすことができます。

import

自身のプラットフォーム・プログラム・データセットに対して、外部ソースを取り込むプロセスをimportと呼びます。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/07/02 06:56

編集2021/07/02 07:45

下記のようにデバックしていて、気になりました。引数のenviron, start_responseは、どのモジュールのどこの記述箇所でセットされているのか
start_responseの定義がある箇所はわかっています。

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 import pdb;pdb.set_trace() 8 print(environ) 9 setup_testing_defaults(environ) 10 print(environ) 11 print(start_response) 12 status = '200 OK' 13 headers = [('Content-type', 'text/plain; charset=utf-8')] 14 15 start_response(status, headers) 16 17 ret = [("%s: %s\n" % (key, value)).encode("utf-8") 18 for key, value in environ.items()] 19 return ret 20 21with make_server('', 8000, simple_app) as httpd: 22 print("Serving on port 8000...") 23 httpd.serve_forever() 24

デバッカーでの確認結果

python

1bound method BaseHandler.start_response of <wsgiref.simple_server.ServerHandler object

各関数の定義の場所

text

1start_responseの定義の場所 2wsgiref > handers.py > BaseHandler > def start_response 3 4make_serverの定義の場所 5wsgiref > simple_server.py > def make_server

start_responseの定義

python

1def start_response(self, status, headers,exc_info=None): 2 """'start_response()' callable as specified by PEP 3333""" 3 4 if exc_info: 5 try: 6 if self.headers_sent: 7 # Re-raise original exception if headers sent 8 raise exc_info[0](exc_info[1]).with_traceback(exc_info[2]) 9 finally: 10 exc_info = None # avoid dangling circular ref 11 elif self.headers is not None: 12 raise AssertionError("Headers already set!") 13 14 self.status = status 15 self.headers = self.headers_class(headers) 16 status = self._convert_string_type(status, "Status") 17 assert len(status)>=4,"Status must be at least 4 characters" 18 assert status[:3].isdigit(), "Status message must begin w/3-digit code" 19 assert status[3]==" ", "Status message must have a space after code" 20 21 if __debug__: 22 for name, val in headers: 23 name = self._convert_string_type(name, "Header name") 24 val = self._convert_string_type(val, "Header value") 25 assert not is_hop_by_hop(name),\ 26 f"Hop-by-hop header, '{name}: {val}', not allowed" 27 28 return self.write

make_serverの定義

python

1def make_server( 2 host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler 3): 4 """Create a new WSGI server listening on `host` and `port` for `app`""" 5 server = server_class((host, port), handler_class) 6 server.set_app(app) 7 return server

set_appの定義の箇所

python

1class WSGIServer(HTTPServer): 2 3 """BaseHTTPServer that implements the Python WSGI protocol""" 4 5 application = None 6 7 def server_bind(self): 8 """Override server_bind to store the server name.""" 9 HTTPServer.server_bind(self) 10 self.setup_environ() 11 12 def setup_environ(self): 13 # Set up base environment 14 env = self.base_environ = {} 15 env['SERVER_NAME'] = self.server_name 16 env['GATEWAY_INTERFACE'] = 'CGI/1.1' 17 env['SERVER_PORT'] = str(self.server_port) 18 env['REMOTE_HOST']='' 19 env['CONTENT_LENGTH']='' 20 env['SCRIPT_NAME'] = '' 21 22 def get_app(self): 23 return self.application 24 25 def set_app(self,application): 26 self.application = application

uwsgiの場合は、setup_pypy.pyに定義があり、同じ読み込み方をしているのかどうかは気になります。
該当コード

関連する質問はこちら

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2021/07/02 06:59

> 明示的にimport文をかいていないのに引数に指定して利用することができるのでしょうか? 渡されてくる引数にimportも何も無関係だと思うのですが、何が疑問なのでしょうか?
sequelanonymous

2021/07/02 07:16

ありがとうございます! 下記のコードで引数なしで読んでいるのにいきなりstart_response関数を呼べるのってどうなってるのだろう、という疑問になります。 ``` with make_server('', 8000, simple_app) as httpd: print("Serving on port 8000...") httpd.serve_forever() ```
maisumakun

2021/07/02 07:19

「何を」引数無しで呼んでいる、と主張したいのですか?
sequelanonymous

2021/07/02 07:24 編集

simple_appです。呼んでいるというより、callableオブジェクトを引数として渡してる、と言いたかったです。
guest

回答2

0

下記のコードで引数なしで読んでいるのに

simple_appは、当該のコードで「引数なしで呼び出されている」わけではありません

いったん呼ばずに関数そのものを引数として渡して、make_server(あるいはそれで作られたインスタンスの何かしらのメソッド)が、適切な引数をセットしてsimple_appを呼び出しています。

投稿2021/07/02 07:23

maisumakun

総合スコア145184

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sequelanonymous

2021/07/02 07:36

つまり、上記のコードの場合、make_server関数の定義内のserver.set_app関数の引数にsimpe_app関数オブジェクトがわたり、set_app関数内でもとのsimple_appの引数であるenviron, start_responseがセットされるという理解であっていますでしょうか? 結局、environ, start_responseは、どこの記述箇所で引数がセットされているのか、が気になっています。質問文を正確なものに変更します。
maisumakun

2021/07/02 07:46 編集

> 結局、environ, start_responseは、どこの記述箇所で引数がセットされているのか、が気になっています。 根本的な疑問ですが、それを「なぜ」気にする必要があるのでしょうか? WSGIアプリケーションを作る側としては、「当該引数には必要な関数が渡ってくる」ことだけ知っておけば、どこで呼び出しを実行しているかを知ることもなく、自分のアプリケーションコードを書くことに専念できます。 サーバの種類やコンフィギュレーションによって実際の実行の流れは違いうるものですので、具体的に調べて何を得たいのかが疑問です。
maisumakun

2021/07/02 07:55

実際のWebアプリケーションでは、「WSGIミドルウェア」と呼ばれるような、「他のWSGIアプリケーションを呼び出すWSGIアプリケーション」を多数組み合わせて、多様な機能性を積み重ねる、という構成が取られます。 https://gihyo.jp/dev/feature/01/wsgi/0003 あるWSGIアプリケーションからすれば、自分を呼び出しているのが「本物のサーバ」であることも「WSGIミドルウェア」であることも考えられるわけですが、どちらでも「適切な引数を渡して返り値を受け取る」という意味では共通なので、どちらが呼んでいるかは「全く考えずに」WSGIアプリケーションを実装できます。
sequelanonymous

2021/07/02 07:57

wsgiプロトコルができた理由がおっしゃるように気にしなくてもすむようにするためにできたと思うんですが、人に各サーバーやアプリケーションがどうやって動いるかの仕組みを説明するときに理解していないとちゃんと説明できないと思ったからです。あとは、今後サーバーやフレームワークを作ってみたいからです。
sequelanonymous

2021/07/02 07:59

更新の時差でレスポンスがずれました。ありがとうございます。よんでみます。
sequelanonymous

2021/07/02 08:04

すみません、この記事で言及されているWebサーバー、ミドルウェア、WSGIアプリケーションっていう言葉は、python(.pyモジュール), uwsgi, nginx,flaskの構成があったとして、それぞれどれのことを言ってるとおもいますでしょうか?
guest

0

自己解決

引数のenviron, start_responseは、基本的にwsgiアプリケーションを呼び出すwsgiサーバー側に定義されている。

wsgiサーバーとは、WSGIの仕様に則ったサーバー。

  • uwsgiの場合、ここ
  • gunicornの場合、ここ
  • flask runで起動するwerkzeug(simple_runをつかう)の場合、ここ
  • CPythonにあるwsgirefのsimple_serverのmake_serverをつかってPython xx.pyで起動する場合、ここ

=======
flask runで起動する場合の補足:

  • host, port指定していない場合、127.0.0.1と5000が設定されるとこ、ここ

  • werkzeugからsimple_runをimportしてるのここ

  • clickコマンドとしてflask runにrun関数を定義しているの、ここ -> なぜこっちでもsimple_runをimportしてつかってるけど、まーいいや。

  • FLASK_APPで設定し忘れのとき、ここ

  • werkzeugでCPython内で定義されたHTTPServerがつかわれてる箇所、ここ

  • CPythonでHTTPServerが定義されている箇所、ここ

simple_serverをつかってpython xx.pyで起動する場合の補足:

  • werkzeugと同様、simple_serverでもCPython内で定義されたHTTPServerが利用されてる

=======
混乱しそうなポイント:
app.run()は、フレームワークを利用してappをつくらないといけない。
ex) app = Flask(name)
pythonのみで起動するには、標準モジュールのwsgirefのutility郡を利用する。

もしくは、wsgiサーバーで下記のような感じでよんであげる。
wsgi_appというcallableなオブジェクトを指定してgunicorn側でよぶ。

python

1# app.py 2# gunicorn app:wsgi_app 3# curl http://localhost:8000 4def wsgi_app(environ, start_response): 5 start_response('200 OK', [('Content-Type', 'text/plain')]) 6 body = f"Method was {environ['REQUEST_METHOD']}" 7 return [body.encode('utf-8')]

=======
参考になったURL

WSGIには二つの側 — サーバ側とアプリケーション側が存在する。WSGIは、リクエスト情報・レスポンスヘッダ・レスポンス本文を、両者の間でどのようにやりとりするかをPythonのAPIとして定義している。

Webサーバにリクエストが来ると、次のような流れでやりとりが行なわれる:

サーバ側が、クライアントからリクエストを受ける。
サーバ側は、アプリケーション側がエントリポイントとして提供するcallableオブジェクト(関数やクラスインスタンスなど call が定義されたオブジェクト)を呼び出して、その引数として環境変数と1つのコールバック用callableオブジェクトを渡す。
アプリケーション側は、このコールバック用callableオブジェクトを呼び出すことでステータスコードとレスポンスヘッダをサーバ側に伝え、さらに本文を生成するiterableオブジェクト(イテレータやリストなど)を戻り値として返す。
サーバ側は、これらを用いてクライアントへのレスポンスを生成する。

PythonのWebアプリケーションは WSGI という仕様に則って開発されています。 WSGIに則って作られたWebアプリケーション(WSGIアプリケーション)は、WSGIの仕様に則ったサーバー(WSGIサーバー)上で動作させられます。

environ: HTTPリクエストの情報(ヘッダーの値など)
start_response: HTTPレスポンスのコード、返すヘッダーを指定して呼び出すための関数
戻り値: HTTPのボディーになるバイト型のイテラブル

ブラウザー: HTTPでサーバーと通信する
WSGIサーバー: HTTPの解釈、WSGIアプリケーションを呼び出し、結果をHTTPで返すサーバー
WSGIアプリケーション: 上記仕様でHTTPの内容を返すPythonの呼び出し可能オブジェクト
(Python)Webフレームワーク: Viewなど一部の処理を書くだけでWebアプリケーションが作れるPythonパッケージ
ユーザーの書いたViewなどを読み込んで、WSGIアプリケーションとして動作する

FlaskでWebアプリケーションを作った場合、 app = Flask(...) でインスタンス化した app 自体がWSGIアプリケーションです。 例えば myapp.py というファイル内に app という名前でFlaskアプリケーションがある場合、以下のように gunicorn で起動できます。

投稿2021/07/02 15:50

編集2021/07/02 16:13
sequelanonymous

総合スコア123

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問