Auf gebündelte Dienste für Python 3 zugreifen

Auf dieser Seite wird beschrieben, wie gebündelte Legacy-Dienste mit der Python 3-Laufzeit für die Standardumgebung installiert und verwendet werden. Ihre Anwendung muss über das App Engine Services SDK für Python 3 auf die gebündelten Dienste zugreifen.

Hinweise

App Engine Services SDK installieren

So installieren Sie das App Engine Services SDK:

  1. Fügen Sie Ihrer App das SDK hinzu, indem Sie der Datei requirements.txt Folgendes hinzufügen:

    appengine-python-standard>=1.0.0
    

    Sie finden das SDK auf GitHub im Repository appengine-python-standard und unter PyPI.

  2. Fügen Sie Ihrem Haupt-Python-Skript den folgenden Code hinzu. Dieser Code erstellt WSGI-Middleware, mit der die für die Aktivierung Ihrer API-Aufrufe erforderlichen Variablen festgelegt werden.

    Flask

    from flask import Flask
    from google.appengine.api import wrap_wsgi_app
    
    app = Flask(__name__)
    app.wsgi_app = wrap_wsgi_app(app.wsgi_app)
    

    Django

    from DJANGO_PROJECT_NAME.wsgi import application
    from google.appengine.api import wrap_wsgi_app
    
    app = wrap_wsgi_app(application)
    

    Pyramide

    from pyramid.config import Configurator
    from google.appengine.api import wrap_wsgi_app
    
    config = Configurator()
    # make configuration settings
    app = config.make_wsgi_app()
    app = wrap_wsgi_app(app)
    

    WSGI

    import google.appengine.api
    
    def app(environ, start_response):
        start_response('200 OK', [('Content-Type', 'text/plain')])
        yield b'Hello world!\n'
    
    app = google.appengine.api.wrap_wsgi_app(app)
    
  3. Fügen Sie die folgende Zeile Ihrer app.yaml hinzu, bevor Sie die Anwendung bereitstellen:

    app_engine_apis: true
    
  4. Verwenden Sie zum Bereitstellen der Anwendung den Befehl gcloud app deploy.

Hinweise zur Migration

Beachten Sie die folgenden Überlegungen, wenn Sie zur Python 3-Laufzeit migrieren und Ihre Anwendung gebündelte Legacy-Dienste verwendet.

Test

Verwenden Sie den lokalen Entwicklungsserver, um die Legacy-Funktionen für gebündelte Dienste lokal in Ihrer Python 3-Anwendung zu testen. Wenn Sie den Befehl dev_appserver.py ausführen, müssen Sie das Argument --runtime_python_path so festlegen, dass ein Pfad zum Python 3-Interpreter enthalten ist. Beispiel:

   python3 CLOUD_SDK_ROOT/bin/dev_appserver.py --runtime_python_path=/usr/bin/python3

Sie können das Argument auch auf eine durch Kommas getrennte Liste an [RUNTIME_ID]=[PYTHON_INTERPRETER_PATH]-Paaren festlegen. Beispiel:

   python3 CLOUD_SDK_ROOT/bin/dev_appserver.py --runtime_python_path="python27=/user/bin/python2.7,python3=/usr/bin/python3"

Pickle-Kompatibilität

Freigegebene Dienste, einschließlich Memcache, Cloud NDB und Deffered, verwenden das Modul Pickle, um Python-Objekte zu serialisieren und freizugeben. Wenn Ihre App Engine-Umgebung sowohl Python 2 als auch Python 3 verwendet, was während einer Migration üblich ist, müssen Sie dafür sorgen, dass freigegebene serialisierte Objekte, die von einer Version von Python geschrieben wurden, von der anderen rekonstruiert werden können. Weitere Informationen zur Implementierung der Kompatibilität mit verschiedenen Pickle-Versionen finden Sie in der Anleitung.

Standardmäßig verwendet Python 3 Pickling-Protokolle, die in Python 2 nicht unterstützt werden. Dies kann zu Fehlern führen, wenn Ihre Anwendung versucht, ein Python-Objekt in einer Python 2-Umgebung zu rekonstruieren, die in einer Python 3-Umgebung geschrieben wurde. Um dieses Problem zu vermeiden, legen Sie in der app.yaml-Datei folgende Umgebungsvariablen für Ihre Python 3-Anwendung nach Bedarf fest:

  • Legen Sie für Anwendungen, die Memcache verwenden, einschließlich Anwendungen, die NDB verwenden, Folgendes fest: MEMCACHE_USE_CROSS_COMPATIBLE_PROTOCOL: 'True'
  • Legen Sie für Anwendungen, die NDB für die Verbindung zu Datastore verwenden, Folgendes fest: NDB_USE_CROSS_COMPATIBLE_PICKLE_PROTOCOL: 'True'
  • Legen Sie für Anwendungen, die Deferred verwenden, Folgendes fest: DEFERRED_USE_CROSS_COMPATIBLE_PICKLE_PROTOCOL: 'True'

In Python 2 enthalten string-Objekte eine Sequenz von 8-Bit-Bytewerten. In Python 3 enthalten string-Objekte eine Sequenz an Unicode-Zeichen. Standardmäßig Pickle-übersetzt Python 3 ein Python 2-string in Unicode, wobei Python 3 string als ASCII interpretiert wird. Dies kann zu Fehlern bei Werten außerhalb des ASCII-Zeichenbereichs von 0 bis 127 führen. Memcache unterstützt das Überschreiben dieser Standardzuordnung.

from google.appengine.api import memcache
import six.moves.cPickle as pickle

def _unpickle_factory(file):
    return pickle.Unpickler(file, encoding='latin1')

memcache.setup_client(memcache.Client(unpickler=_unpickle_factory))

Die latin1-Codierung definiert eine Zuordnung für jeden der 256 möglichen Werte jedes Byte in einem Python 2-string. Dadurch werden Decodierungsfehler verhindert. Wenn Ihr Python 2-string jedoch tatsächliche Unicode-Daten außerhalb des latin1-Bereichs enthält, z. B. Daten, die aus einer Datei gelesen werden, ordnet cPickle die Daten nicht korrekt zu. Daher ist es wichtig, dass Sie Ihren Python 2-Code aktualisieren, um Unicode-Daten mit unicode-Objekten und nicht mit string-Objekten für von Ihnen ausgewählte Objekte zu speichern. Der Kompatibilitätsleitfaden enthält Details zu den erforderlichen Updates.

Die zuvor beschriebene Methode zum Aktualisieren des Python 2-Codes zum Erstellen von Python 3-kompatiblen Serialisierungen bezieht sich auf kurzlebige Serialisierungen, z. B. die in Memcache gespeicherten. Möglicherweise müssen Sie langlebige Python 2-Serialisierungen aktualisieren oder neu schreiben, z. B. diejenigen, die im Rahmen Ihrer Migration in Datastore gespeichert sind. Beispiel: Serialisierungen, die mit google.appengine.ext.ndb.model.PickleProperty geschrieben wurden, erfordern möglicherweise ein Upgrade.

Weitere Informationen zu Einschränkungen und weniger häufigen Problemen finden Sie im Leitfaden zur Kompatibilität.

Web-Frameworks

webapp2 ist in Python 3 nicht gebündelt oder wird nicht unterstützt. Daher muss jede Anwendung neu geschrieben werden, um ein WSGI-kompatibles Framework wie Flask zu nutzen.

Es wird empfohlen, zuerst die Verwendung vonwebapp2 in Ihrer Python 2.7-Anwendung mit Flask (oder einem anderen Web-Framework wieDjango, Pyramid, Bottle oderweb.py) zu ersetzen, während Sie noch Python 2.7 verwenden. Wenn die aktualisierte Anwendung stabil ist, migrieren Sie den Code zu Python 3 und stellen Sie die Anwendung mit App Engine für Python 3 bereit.

Beispiele für die Konvertierung von Python 2.7-Anwendungen, die webapp2 für die Verwendung des Flask-Frameworks verwenden, finden Sie in diesen zusätzlichen Ressourcen.

Handler verwenden

Einer Python 3-Anwendung kann nur ein Skript zugeordnet sein. Wenn Ihre app.yaml also mehrere script-Handler enthält, die URLs verschiedenen Skripts zuordnen, müssen Sie diese Skripts in einem Skript zusammenfassen, das URL-Routing verwendet.

Im folgenden Beispiel werden die Handler-Unterschiede in der Datei app.yaml für die entsprechenden Laufzeiten dargestellt.

Python 2

runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /
  script: home.app

- url: /index\.html
  script: home.app

- url: /stylesheets
  static_dir: stylesheets

- url: /(.*\.(gif|png|jpg))$
  static_files: static/\1
  upload: static/.*\.(gif|png|jpg)$

- url: /admin/.*
  script: admin.app
  login: admin

- url: /.*
  script: not_found.app

Python 3

runtime: python312
app_engine_apis: true

handlers:
- url: /stylesheets
  static_dir: stylesheets

- url: /(.*\.(gif|png|jpg))$
  static_files: static/\1
  upload: static/.*\.(gif|png|jpg)$

- url: /admin/.*
  script: auto
  login: admin

Ihre Python 3-Anwendung muss URL-Routing verarbeiten (beispielsweise mit Flask-Decorators).

Wenn Sie mehrere script-Handler mit unterschiedlichen URL-Mustern oder andere Attribute in Ihren Handlern verwenden möchten, muss jeder Handler script: auto angeben.

Sie können das Standardstartverhalten auch überschreiben. Geben Sie dazu in der Datei app.yaml das Feld entrypoint an.

Weitere Informationen zur Verwendung bestimmter Handler finden Sie in den Übersichten Blobstore, Deferred und Mail.

Threadsicherheit

Es wird davon ausgegangen, dass Apps threadsicher sind. API-Aufrufe müssen im Anfrage-Thread erfolgen. Wenn Sie beim Start der Anwendung eine Legacy-Bundle-Dienst-API verwenden, kann dies zu Sicherheitsfehlern führen.

Weitere Informationen finden Sie unter Sicherheitsfehler bei der Verwendung von gebündelten Legacy-Diensten für Python.

URL Fetch verwenden

Wenn Sie URL Fetch für Python verwenden möchten, müssen Sie die URL-Abrufbibliothek explizit aufrufen.

Wenn Ihre Python 3-Anwendung die URL Fetch API verwendet, wird der Anfrageheader X-Appengine-Inbound-Appid hinzugefügt, wenn Ihre App eine Anfrage an eine andere App Engine-Anwendung sendet. Dies ermöglicht der empfangenden Anwendung, die Identität der aufrufenden Anwendung zu überprüfen. Weitere Informationen finden Sie unter Ausgehende Anfragen migrieren.

Beispiel (App Engine ndb)

Nachfolgend sehen Sie eine einfache Python 2-App, die Seitenbesuche mit App Engine ndb für den Zugriff auf Datastore registriert. Ihr Gegenstück ist eine Python 3 äquivalente Anwendung, bei der die Verwendung von webapp2 durch Flask ersetzt wurde und die oben beschriebenen notwendigen Änderungen für den Zugriff auf gebündelte Dienste in Python 3 implementiert wurden.

Python 2 (webapp2)

import os
import webapp2
from google.appengine.ext import ndb
from google.appengine.ext.webapp import template

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    'get most recent visits'
    return Visit.query().order(-Visit.timestamp).fetch(limit)

class MainHandler(webapp2.RequestHandler):
    'main application (GET) handler'
    def get(self):
        store_visit(self.request.remote_addr, self.request.user_agent)
        visits = fetch_visits(10)
        tmpl = os.path.join(os.path.dirname(__file__), 'index.html')
        self.response.out.write(template.render(tmpl, {'visits': visits}))

app = webapp2.WSGIApplication([
    ('/', MainHandler),
], debug=True)

Python 3 (Flask)

from flask import Flask, render_template, request
from google.appengine.api import wrap_wsgi_app
from google.appengine.ext import ndb

app = Flask(__name__)
app.wsgi_app = wrap_wsgi_app(app.wsgi_app)


class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    'get most recent visits'
    return Visit.query().order(-Visit.timestamp).fetch(limit)


@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10)
    return render_template('index.html', visits=visits)

Beide Anwendungen finden Sie im Open-Source-Repository für den Python App Engine-Migrationsinhalt (Codebeispiele, Videos, codelabs), insbesondere in den Ordnern mod0 bzw. mod1b.