python wsgi PEP333 中文翻译
PEP 333 中文翻译
首先说明一下,本人不是专门翻译的,英文水平也不敢拿来献丑。只是这是两年前用python的时候为了自己学习方便而翻译的,记录着笔记自己看看而已。最近翻出来看看觉得还是放出来吧。一个网络上还是没有找到别人完整的中文翻译,这个自己的烂翻译就姑且放出来吧。也方便其他英文不好的人了,总比没有光看英文好吧。
另外我翻译之前有朋友其实已经翻译了一半了(他的翻译点这里http://blog.sina.com.cn/s/blog_8a18c33d01019xqb.html),但是没全,所以我接上翻译的后面,只为自己学习看的。前面大部分是拷贝他的翻译来的,后面许多地方翻译的很不好,请大家见谅。
如果有朋友愿意指出错误来我很乐意修正过来。原文地址 http://www.python.org/dev/peps/pep-0333/
Preface 前言
这个版本更新的版本支持Python 3.x以及一些其他的勘误说明等。请参阅PEP 3333.
Abstract 简介
----------------------------------------------------------------------------------
本文档描述一份在web服务器与web应用/web框架之间的标准接口,此接口的目的是使得web应用在不同web服务器之间具有可移植性。
Rationale and Goals 基本原理与目标
----------------------------------------------------------------------------------
Specification Overview 概述
The Application/Framework Side 应用程序/框架 端
1 def simple_app(environ, start_response): 2 """Simplest possible application object""" 3 status = '200 OK' 4 response_headers = [('Content-type', 'text/plain')] 5 start_response(status, response_headers) 6 return ['Hello world!\n'] 7 8 9 class AppClass: 10 """Produce the same output, but using a class 11 12 (Note: 'AppClass' is the "application" here, so calling it 13 returns an instance of 'AppClass', which is then the iterable 14 return value of the "application callable" as required by 15 the spec. 16 17 If we wanted to use *instances* of 'AppClass' as application 18 objects instead, we would have to implement a '__call__' 19 method, which would be invoked to execute the application, 20 and we would need to create an instance for use by the 21 server or gateway. 22 """ 23 24 def __init__(self, environ, start_response): 25 self.environ = environ 26 self.start = start_response 27 28 def __iter__(self): 29 status = '200 OK' 30 response_headers = [('Content-type', 'text/plain')] 31 self.start(status, response_headers) 32 yield "Hello world!\n"
The Server/Gateway Side 服务器/网关 接口
----------------------------------------------------------------------------------
服务器/网关 为每一个http客户端发来的请求都会请求应用程序可调用者一次。为了说明这里有一个CGI gateway,以一个获取应用程序对象的函数实现,请注意,这个例子拥有有限的错误处理,因为默认情况下没有被捕获的异常都会被输出到sys.stderr并被服务器记录下来。
1 import os, sys 2 3 def run_with_cgi(application): 4 5 environ = dict(os.environ.items()) 6 environ['wsgi.input'] = sys.stdin 7 environ['wsgi.errors'] = sys.stderr 8 environ['wsgi.version'] = (1, 0) 9 environ['wsgi.multithread'] = False 10 environ['wsgi.multiprocess'] = True 11 environ['wsgi.run_once'] = True 12 13 if environ.get('HTTPS', 'off') in ('on', '1'): 14 environ['wsgi.url_scheme'] = 'https' 15 else: 16 environ['wsgi.url_scheme'] = 'http' 17 18 headers_set = [] 19 headers_sent = [] 20 21 def write(data): 22 if not headers_set: 23 raise AssertionError("write() before start_response()") 24 25 elif not headers_sent: 26 # Before the first output, send the stored headers 27 status, response_headers = headers_sent[:] = headers_set 28 sys.stdout.write('Status: %s\r\n' % status) 29 for header in response_headers: 30 sys.stdout.write('%s: %s\r\n' % header) 31 sys.stdout.write('\r\n') 32 33 sys.stdout.write(data) 34 sys.stdout.flush() 35 36 def start_response(status, response_headers, exc_info=None): 37 if exc_info: 38 try: 39 if headers_sent: 40 # Re-raise original exception if headers sent 41 raise exc_info[0], exc_info[1], exc_info[2] 42 finally: 43 exc_info = None # avoid dangling circular ref 44 elif headers_set: 45 raise AssertionError("Headers already set!") 46 47 headers_set[:] = [status, response_headers] 48 return write 49 50 result = application(environ, start_response) 51 try: 52 for data in result: 53 if data: # don't send headers until body appears 54 write(data) 55 if not headers_sent: 56 write('') # send headers now if body was empty 57 finally: 58 if hasattr(result, 'close'): 59 result.close()
Middleware: Components that Play Both Sides 中间件 : 同时扮演两种角色的组件
- 重写前面提到的 environ 之后,可以根据目标URL将请求传递到不同的应用程序对象
- 允许多个应用程序和框架在同一个进程中运行
- 通过在网络传递请求和响应,实现负载均衡和远程处理
- 对内容进行后加工,比如附加xsl样式表
1 from piglatin import piglatin 2 3 class LatinIter: 4 5 """Transform iterated output to piglatin, if it's okay to do so 6 7 Note that the "okayness" can change until the application yields 8 its first non-empty string, so 'transform_ok' has to be a mutable 9 truth value. 10 """ 11 12 def __init__(self, result, transform_ok): 13 if hasattr(result, 'close'): 14 self.close = result.close 15 self._next = iter(result).next 16 self.transform_ok = transform_ok 17 18 def __iter__(self): 19 return self 20 21 def next(self): 22 if self.transform_ok: 23 return piglatin(self._next()) 24 else: 25 return self._next() 26 27 class Latinator: 28 29 # by default, don't transform output 30 transform = False 31 32 def __init__(self, application): 33 self.application = application 34 35 def __call__(self, environ, start_response): 36 37 transform_ok = [] 38 39 def start_latin(status, response_headers, exc_info=None): 40 41 # Reset ok flag, in case this is a repeat call 42 del transform_ok[:] 43 44 for name, value in response_headers: 45 if name.lower() == 'content-type' and value == 'text/plain': 46 transform_ok.append(True) 47 # Strip content-length if present, else it'll be wrong 48 response_headers = [(name, value) 49 for name, value in response_headers 50 if name.lower() != 'content-length' 51 ] 52 break 53 54 write = start_response(status, response_headers, exc_info) 55 56 if transform_ok: 57 def write_latin(data): 58 write(piglatin(data)) 59 return write_latin 60 else: 61 return write 62 63 return LatinIter(self.application(environ, start_latin), transform_ok) 64 65 66 # Run foo_app under a Latinator's control, using the example CGI gateway 67 from foo_app import foo_app 68 run_with_cgi(Latinator(foo_app))
Specification Details 规格的详细说明
应用程序对象必须接受两个参数,为了方便说明我们不妨分别命名为 environ 和 start_response ,但并非必须取这个名字。服务器或gateway必须用这两个参数请求应用程序对象(比如象上面展示的,这样调用 result = application(environ,start_response) )
参数 environ 是个字典对象,包含CGI风格的环境变量。这个对象必须是一个python内建的字典对象(不能是子类、UserDict或其他对字典对象的模仿),应用程序可以以任何他愿意的方式修改这个字典, environ 还应该包含一些特定的WSGI需要的变量(在后面的节里会描述),有可以包含一些服务器特定的扩展变量,通过下面提高的约定命名。
start_response 参数是一个接受两个必须参数和一个可选参数的可调用者。方便说明,我们分别把他们命名为 status, response_headers ,和 exc_info 。应用程序必须用这些参数来请求可调用者 start_response (比如象这样 start_response(status,response_headers) )
参数 status 是一个形式象"999 Message here"的状态字符串。而 response_headers 参数是元组(header_name,header_value)的列表,描述http响应头。可选的 exc_info 参数会在下面的 `The start_response() Callable`_ 和 Error Handling 两节中描述,他只有在应用程序产生了错误并希望在浏览器上显示错误的时候才有用。
start_response 可调用者必须返回一个 write(body_data) 可调用者,他接受一个可选参数:一个将要被做为http响应体的一部分输出的字符串(注意:提供可调用者 write() 只是为了支持现有框架的必要的输出API,新的应用程序或框架尽量避免使用,详细情况请看 Buffering and Streaming 一节。)
当被服务器请求的时候,应用程序对象必须返回一个0或多个可迭代的字符串,这可以通过多种方法完成,比如返回一个字符串的列表,或者应用程序本身是一个生产字符串的函数,或者应用程序是一个类而他的实例是可迭代的,不管怎么完成,应用程序对象必须总是返回0或多个可迭代的字符串。
服务器必须将产生的字符串以一种无缓冲的方式传送到客户端,每次传完一个字符串再去获取下一个。(换句话说,应用程序应该实现自己的缓冲,更多关于应用程序输出必须如何处理的细节请阅读下面的 Buffering and Streaming 节。)
服务器或gateway应该把产生的字符串当字节流对待:特别地,他必须保证没修改行的结尾。应用程序负责确保字符串是以与客户端匹配的编码输出(服务器/gateway可能会附加HTTP传送编码,或者为了实现一些http的特性而进行一些转换比如byte-range transmission,更多细节请看下面的 Other HTTP Features )
如果调 len(iterable) 成功,服务器将认为结果是正确的。也就是说,应用程序返回的可迭代的字符串提供了一个有用 的__len__() 方法,么肯定返回了正确的结果(关于这个方法正常情况下如何被使用的请阅读 Handling the Content-Length Header )
如果应用程序返回的可迭代者有close()方法,则不管该请求是正常结束还是由于错误而终止,服务器/gateway都**必须**在结束该请求之前调用这个方法,(这是用来支持应用程序对资源的释放,This protocol is intended to complement PEP 325's generator support, and other common iterables with close() methods.)
(注意:应用程序必须在可迭代者产生第一个字符串之间请求 start_response() 可调用者,这样服务器才能在发送任何主体内容之前发送响应头,然而这一步也可以在可迭代者第一次迭代的时候执行,所以服务器不能假定开始迭代之前 start_response() 已经被调用过了)
最后,服务器或gateway不能应用程序返回的可迭代者的任何其他属性,除非是针对服务器或gateway特定类型的实例,比如wsgi.file_wrapper返回的“file wrapper”(阅读 Optional Platform-Specific File Handling )。通常情况下,只有在这里指定的属性,或者通过PEP 234 iteration APIs才是可以访问的。
environ Variables environ变量
Variable | Value |
---|---|
wsgi.version | The tuple (1, 0), representing WSGI version 1.0. |
wsgi.url_scheme | A string representing the "scheme" portion of the URL at which the application is being invoked. Normally, this will have the value "http" or "https", as appropriate. |
wsgi.input | An input stream (file-like object) from which the HTTP request body can be read. (The server or gateway may perform reads on-demand as requested by the application, or it may pre- read the client's request body and buffer it in-memory or on disk, or use any other technique for providing such an input stream, according to its preference.) |
wsgi.errors |
An output stream (file-like object) to which error output can be written, for the purpose of recording program or other errors in a standardized and possibly centralized location. This should be a "text mode" stream; i.e., applications should use "\n" as a line ending, and assume that it will be converted to the correct line ending by the server/gateway. For many servers, wsgi.errors will be the server's main error log. Alternatively, this may be sys.stderr, or a log file of some sort. The server's documentation should include an explanation of how to configure this or where to find the recorded output. A server or gateway may supply different error streams to different applications, if this is desired. |
wsgi.multithread | This value should evaluate true if the application object may be simultaneously invoked by another thread in the same process, and should evaluate false otherwise. |
wsgi.multiprocess | This value should evaluate true if an equivalent application object may be simultaneously invoked by another process, and should evaluate false otherwise. |
wsgi.run_once | This value should evaluate true if the server or gateway expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a gateway based on CGI (or something similar). |
最后 environ 字典也可以包含服务器定义的变量。这些变量的名字必须是小写字母、数字、点和下划线,并且应该带一个能唯一代表服务器或gateway的前缀。比如, mod_python 可能会定义象这样的一些变量:mod_python.some_variable.
input and Error Streams 输入和错误流
服务器提供的输入输出流必须提供以下的方法
Method | Stream | Notes |
---|---|---|
read(size) | input | 1 |
readline() | input | 1, 2 |
readlines(hint) | input | 1, 3 |
__iter__() | input | |
flush() | errors | 4 |
write(str) | errors | |
writelines(seq) | errors |
以上方法语义同Python Library Reference相同,处理下面列表指出的注意点之外。
The start_response() Callable
def start_response(status, response_headers, exc_info=None): if exc_info: try: # do stuff w/exc_info here finally: exc_info = None # Avoid circular ref.
示例CGI网关例子提供了这种技术的另一个说明。
Handling the Content-Length Header 处理Content-Length头信息
Buffering and Streaming 缓冲和流
Middleware Handling of Block Boundaries 中间件处理块边界
The write() Callable
Unicode Issues Unicode问题
Error Handling 错误处理
1 try: 2 # regular application code here 3 status = "200 Froody" 4 response_headers = [("content-type", "text/plain")] 5 start_response(status, response_headers) 6 return ["normal body goes here"] 7 except: 8 # XXX should trap runtime issues like MemoryError, KeyboardInterrupt 9 # in a separate handler before this bare 'except:'... 10 status = "500 Oops" 11 response_headers = [("content-type", "text/plain")] 12 start_response(status, response_headers, sys.exc_info()) 13 return ["error body goes here"]
如果当有异常发送的时候没有信息被写入,调用start_response将正常返回,并且应用程序返回的包含错误信息的body将被发送到浏览器。然而如果有任何的output被发送到浏览器,start_response会重新抛出提供的异常。此异常不应该被应用程序捕获,并且应用程序会终止。服务器/网关会捕获异常这个致命的异常并终止响应。
服务器应该捕获任何异常并记录日志当终止应用程序或反复迭代他们的返回值。如果当应用程序出错的时候已经有一部分response被发送到浏览器了,服务器或网关可以试图添加一个错误消息到output,如果已经发送了指明text/*的头信息,那么服务器就知道如何修改清理。
一些中间件或许希望提供额外的异常处理服务,或拦截并替换应用程序的错误信息。在此情况下,中间件可能选择不再重新抛出提供的exc_info到start_response,但换作抛出中间件特制的错误,或者在存贮了提供的参数后简单地返回且不包含错误。这将导致应用程序返回错误的body iterable(或调用write()).允许中间件来拦截并修改错误流,这些技术只会工作只要应用程序作者做到:
1.总是提供exc_info当开始一个错误的响应的时候。
2.当exc_info已经提供的情况下不要捕获来自start_response的异常。
HTTP 1.1 Expect/Continue Http1.1的长连接
实现了HTTP1.1的服务器/网关必须提供对HTTP1.1中"expect/continue"(长连接)的透明支持。可以用下面的几种方式完成。
1.用一个立即执行的"100 Continue"response相应一个包含Expect: 100-continue的请求,并且正常处理
2.正常处理请求,但提供给应用程序一个wsgi.input流,当应用程序首先尝试从输入流中读取的时候发送一个"100 Continue"响应。这个读取必须保持阻塞至到客户端的响应。
3.等待客户端判断服务器不支持expect/continue以及客户端自身发来的request body。(不好的方式,不推荐)
注意这些行为的限制不适用于HTTTP 1.O,或请求不是针对特定的应用程序对象。更多HTTP 1.1 Except/Continue的信息,请参阅RFC 2616的8.2.3段和10.1.1段。
Other HTTP Features 其他的HTTP特性
Thread Support 线程支持
线程的支持或缺乏,也是服务器依赖。服务器可以同时运行多个请求,也应当提供让应用程序运行在单一线程上的选项,因此让一些不是线程安全的应用程序或框架仍旧可以在这些服务器上使用。
Implementation/Application Notes 实现/应用 事项
Server Extension APIs 服务扩展API
Application Configuration 应用程序结构配置
from the_app import application def new_app(environ, start_response): environ['the_app.configval1'] = 'something' return application(environ, start_response)
但是,大多存在的应用程序和框架可能只需要从environ里面一个单一的配置值,用来指示它们的应用程序或框架专门的配置文件位置。(当然,应用程序应当缓存这些配置,以避免每次调用都重复读取)
URL Reconstruction URL的构建
如果应用程序希望改造请求的完整URL,可以使用如下的算法,由lan Bicking提供
1 from urllib import quote 2 url = environ['wsgi.url_scheme']+'://' 3 4 if environ.get('HTTP_HOST'): 5 url += environ['HTTP_HOST'] 6 else: 7 url += environ['SERVER_NAME'] 8 9 if environ['wsgi.url_scheme'] == 'https': 10 if environ['SERVER_PORT'] != '443': 11 url += ':' + environ['SERVER_PORT'] 12 else: 13 if environ['SERVER_PORT'] != '80': 14 url += ':' + environ['SERVER_PORT'] 15 16 url += quote(environ.get('SCRIPT_NAME', '')) 17 url += quote(environ.get('PATH_INFO', '')) 18 if environ.get('QUERY_STRING'): 19 url += '?' + environ['QUERY_STRING']
Supporting Older (<2.2) Versions of Python 支持老版本的Python(< 2.2)
略(没翻译,也没看)
Optional Platform-Specific File Handling 可选的特别的平台文件处理
一些操作环境提供高性能的文件传输设施,像Unix的sendfile()方法。服务器和网关可能会通过environ中的wsgi.file_wrapper选项来揭示这功能。用于程序可以使用这样的文件包装来转换文件或类文件对象到他们返回的迭代中去。例如:
if 'wsgi.file_wrapper' in environ: return environ['wsgi.file_wrapper'](filelike, block_size) else: return iter(lambda: filelike.read(block_size), '')
class FileWrapper: def __init__(self, filelike, blksize=8192): self.filelike = filelike self.blksize = blksize if hasattr(filelike, 'close'): self.close = filelike.close def __getitem__(self, key): data = self.filelike.read(self.blksize) if data: return data raise IndexError
这里是一个用它来访问特定平台API的服务器/网关代码片段。
environ['wsgi.file_wrapper'] = FileWrapper result = application(environ, start_response) try: if isinstance(result, FileWrapper): # check if result.filelike is usable w/platform-specific # API, and if so, use that API to transmit the result. # If not, fall through to normal iterable handling # loop below. for data in result: # etc. finally: if hasattr(result, 'close'): result.close()
Questions and AnswersQA问答
Proposed/Under Discussion 讨论
These items are currently being discussed on the Web-SIG and elsewhere, or are on the PEP author's "to-do" list:
- Should wsgi.input be an iterator instead of a file? This would help for asynchronous applications and chunked-encoding input streams.
- Optional extensions are being discussed for pausing iteration of an application's output until input is available or until a callback occurs.
- Add a section about synchronous vs. asynchronous apps and servers, the relevant threading models, and issues/design goals in these areas.
完。水平有限,如果有朋友发现文中许多翻译不当的地方,请随时指正。多谢。