遷移至 Python 2.7

本頁面提供將 Python 應用程式升級為 Python 2.7 執行階段的必要措施。在依照這個逐步操作說明執行之後,應用程式可利用 Python 2.7 執行階段的許多新功能,包括多執行緒處理Jinja2 範本引擎、位元組程式碼存取和上傳功能,另外還有多個新加入的第三方程式庫

必備條件與注意事項

如要使用 Python 2.7,應用程式必須符合以下必備條件:

  • 如果應用程式使用 Django,則必須使用 1.2 以上的版本。如需升級詳細資訊,請參閱 Django 說明文件
  • 如要使用並行要求,應用程式必須使用網路伺服器閘道介面 (WSGI) 指令碼處理常式,如使用 WSGI所述。

除了滿足這些一般必備條件外,您還必須使用部分 App Engine 功能與第三方程式庫的特定版本。請務必更新您納入並匯入到應用程式的版本,並在升級後對應用程式進行全面測試。下列清單說明主要的相容性問題,並提供解決這些問題的進階資源。

  • 效能:應用程式的效能在升級到 Python 2.7 後可能會發生變更。如果發生嚴重的回應延遲情形,您可以增加前端執行個體類別並啟用並行要求。並行要求可讓應用程式在較大的執行個體上以較低的費用執行,加快執行速度。詳情請參閱「在執行個體中處理要求」。
  • Django:Python 2.7 必須與 Django 1.2 以上的版本搭配使用。如要瞭解升級程序,請參閱 Django 版本資訊
  • PyCryptoCrypto.Util.randpool 已淘汰,改用 Crypto.Random。詳情請參閱「RandomPool 的處置方式」。
  • webapp:webapp 範本已在 Python 2.7 中淘汰。您可以直接改用 Django 範本jinja2,或自訂其他範本引擎。
  • WebOb:Python 2.7 支援 WebOb 1.1 版。這個版本與舊版 (0.9) 並非完全回溯相容。如果應用程式使用 WebOb,您需要進行全面測試,才能掌握升級作業會導致的任何錯誤狀況。
  • zipimport:Python 2.7 不支援 zipimport,但 Python 2.7 能以原生方式從 .zip 檔案匯入
  • simplejson:Python 2.7 不支援 simplejson,但 Python 2.7 包含與其功能相當、速度更快的標準程式庫 json 模組。

並行要求與 WSGI

Python 2.7 功能對於應用程式的設計與效能影響最大的部分,在於 Python 2.7 功能可支援能處理並行要求的多執行緒應用程式。處理並行要求的能力可提高使用率,進而大幅提升應用程式的效能,特別是對於使用較高層級執行個體類別 (可利用多個 CPU 核心) 的應用程式。

為啟用多執行緒功能,應用程式必須將先前 Python 執行階段的共用閘道介面 (CGI) 方法移到網路伺服器閘道介面 (WSGI) 方法。這是因為設計用來依序列處理要求的 CGI 指令碼必須依賴環境變數才能存取輸入與輸出串流。

雖然用於處理要求的 WSGI 模型能讓應用程式以更直接的方式存取輸入與輸出串流 (啟用並行要求),但如果要求處理常式的邏輯依賴超出本機範圍 (如應用程式狀態) 的資料或與這類資料互動,那麼平行處理多個要求可能會引發競爭狀況。因此,請從防範觀點撰寫程式碼來處理競爭狀況,以確保新的 WSGI 應用程式的執行緒安全。

詳情請參閱確保應用程式執行緒安全

更新 app.yaml

Python 2.7 需要在 app.yaml 的標頭中加入特殊的 runtime 設定元素。請注意,Python 2.7 應用程式必須使用 threadsafe: [ true | false ] 元素。如果為 true,App Engine 會同時傳送要求;如果為 false,App Engine 會依序傳送要求。下列 app.yaml 標頭可啟用並行要求:

application: myapp
version: 1
runtime: python27
api_version: 1
threadsafe: true
...

使用 WSGI

Python 2.7 執行階段可選擇性地讓您直接執行網路伺服器閘道介面 (WSGI) 應用程式,而非使用 run_wsgi_app 轉接器將程式當做 CGI 指令碼執行。為執行此操作,請以 WSGI 應用程式名稱 (例如 myapp.app) 取代 app.yaml 中的 CGI 處理常式 (例如 myapp.py)。

...
handlers:
- url: /.*
  script: myapp.app
...

您也需要將 WSGI 應用程式物件移到全域範圍:

import webapp2

class MainPage(webapp2.RequestHandler):
  def get(self):
    self.response.headers['Content-Type'] = 'text/plain'
    self.response.out.write('Hello, WebApp World!')

app = webapp2.WSGIApplication([('/', MainPage)])

""" Old code:
def main():
  run_wsgi_app(app)

if __name__ == '__main__':
  main()
"""

您仍可在 app.yaml 中指定 CGI 指令碼處理常式;不過,CGI 指令碼處理的要求會依序列處理,而非並行處理。此外,您無法在 app.yaml 檔案中結合 CGI 指令碼與 WSGI 應用程式,而且如果您有定義任何 CGI 處理常式,您就無法將 threadsafe 設為 true

先前的 Python 執行階段使用的部分慣例 (例如使用 main() 以及檢查 __name__ == 'main') 現在已淘汰。這些措施有助確保先前的 CGI 指令碼維持快取狀態,但如果您目前是直接執行 WSGI 應用程式,就不需要這些步驟。

使用應用程式根目錄

在 Python 2.5 中,CGI 指令碼會與包含該指令碼的目錄所設定的目前工作目錄一併執行。此方法在 Python 2.7 中已變更。在使用 WSGI 的情況下,要求處理常式生命週期在開始時所使用的當前工作目錄,屬於應用程式的根目錄。

請查看應用程式的程式碼,確保已將所有處理常式撰寫為:預期當前工作目錄為應用程式的根目錄。

設定程式庫

Python 2.7 執行階段包含一些第三方模組。在這些程式庫中,有些程式庫是預設可使用的,但有些則需要經過設定才能使用。您可以指定要使用的版本。

libraries:
- name: PIL
  version: "1.1.7"
- name: webob
  version: "1.1.1"

您可以指定應用程式使用最新版本的模組。如果您在開發的應用程式還沒有任何使用者,這項功能就非常實用,因為您不需要追蹤新的版本。但請注意,如果應用程式的使用頻率很高,您可能會意外發現應用程式開始使用不具回溯相容性的新程式庫版本。如要使用最新版本:

libraries:
- name: PIL
  version: latest

如需支援的程式庫清單,請參閱「第三方程式庫」。

確保應用程式的執行緒安全

如果每個處理常式都是與其範圍內的變數互動,則並行要求的處理作業會很簡單,但如果有個處理常式在其他處理常式讀取資源時修改這些資源,情況就會立即變得複雜。在可能有多個要求正在使用相同的資料且要求彼此干擾的情況下,確保應用程式還能依照預期運作,即為確保應用程式的「執行緒安全」。

如要設計出能夠確保執行緒安全的應用程式,主要規則在於盡可能限制共用資源 (例如狀態資訊或全域變數) 的使用情形,但通常還是無法完全排除共用資源的使用情形,此時就是同步機制 (例如鎖定物件) 的最佳使用時機。

在 Python 2.7 中,您可以存取 Python 的執行緒程式庫,讓您在強制內含程式碼依序列執行 (而非並行執行) 的邏輯區塊上宣告鎖定。請考慮使用以下程式碼:

class Configuration(ndb.Model):
  some_config_data = ndb.StringProperty()
  _config_cache = None
  _config_lock = threading.Lock()
  @classmethod
  def get_config(cls):
    with cls._config_lock:
      if not cls._config_cache:
        cls._config_cache = cls.get_by_id('config')
    return cls._config_cache

這段程式碼會示範如何將某些全域設定變數的快取建立到名為 _config_cache 的變數中。這裡使用名為 _config_lock 的鎖定物件,可確保對現有 _config_cache 的檢查作業可靠運作。否則,這個變數可能會浪費時間,多次前往 Datastore 並使用相同資料多次設定相同的變數,因為競爭要求都發現 _config_cache 為空白。

請勿輕率地選擇使用鎖定。鎖定功能會強制封鎖任何其他執行此方法的執行緒。這可能會造成效能的瓶頸。

將應用程式更新為 webapp2

注意:如果您並非使用 webapp 做為要求處理常式,可以略過這個小節。

Python 2.7 執行階段包含的網路架構已從 webapp 升級為 webapp2webapp2 除了其他功能外,還新增了改良版 URI 路由和例外狀況處理機制、全功能的回應物件,以及更靈活的調度機制。

webapp 範本現已淘汰。取而代之的是 Jinja2Django,或您選擇的範本系統 (只要純以 Python 撰寫即可)。

在 App Engine 中,webapp2 已設為 webapp 別名,且 webapp2 可回溯相容。不過在升級之後,您還是需要對應用程式進行徹底的測試,並自行熟悉 webapp2新語法與功能,而非繼續依賴回溯相容性。

現在,升級作業已完成!

上傳應用程式後,您需要進行全面測試,確保回溯相容性。如果您遇到任何問題,請查看論壇內容。