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.
完。水平有限,如果有朋友發現文中許多翻譯不當的地方,請隨時指正。多謝。