Accedere ai servizi in bundle legacy per Python 3

Questa pagina descrive come installare e utilizzare i servizi in bundle legacy con il runtime Python 3 per l'ambiente standard. La tua app deve accedere ai servizi in bundle tramite l'SDK dei servizi App Engine per Python 3.

appengine-python-standard>=1.0.0

Prima di iniziare

Installazione dell'SDK dei servizi App Engine

Per installare l'SDK dei servizi App Engine:

  1. Includi l'SDK nella tua app aggiungendo la seguente riga al file requirements.txt:

    appengine-python-standard>=1.0.0
    

    Puoi trovare l'SDK su GitHub nel appengine-python-standard repository, e su PyPI.

  2. Aggiungi il seguente codice nello script Python principale. Questo codice crea un middleware WSGI che imposta le variabili necessarie per abilitare le chiamate API.

    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)
    

    Pyramid

    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. Aggiorna il file app.yaml per specificare uno o più servizi in bundle legacy. Ad esempio:

    app_engine_bundled_services:
    - datastore_v3
    - memcache
    - user
    

    Se utilizzi funzionalità come login: admin nella sezione handlers del file app.yaml, abilita l'API Users configurando l'impostazione user nell'elenco app_engine_bundled_services.

  4. Per eseguire il deployment dell'app, utilizza il gcloud app deploy comando.

Considerazioni sulla migrazione

Se esegui la migrazione al runtime Python 3 e la tua app utilizza i servizi in bundle legacy, devi tenere presente le seguenti considerazioni.

Test

Per testare localmente la funzionalità dei servizi in bundle legacy nella tua app Python 3, utilizza il server di sviluppo locale. Quando esegui il comando dev_appserver.py, devi impostare l'argomento --runtime_python_path in modo da includere un percorso all'interprete Python 3. Ad esempio:

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

Puoi anche impostare l'argomento su un elenco separato da virgole di [RUNTIME_ID]=[PYTHON_INTERPRETER_PATH] coppie. Ad esempio:

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

Compatibilità con Pickle

I servizi condivisi, tra cui Memcache, Cloud NDB e Deferred utilizzano il modulo pickle per serializzare e condividere gli oggetti Python. Se il tuo ambiente App Engine utilizza sia Python 2 sia Python 3, cosa comune durante una migrazione, devi assicurarti che gli oggetti serializzati condivisi scritti da una versione di Python possano essere ricostituiti dall'altra. Puoi trovare indicazioni sull'implementazione della compatibilità con pickle tra le versioni nel la guida.

Per impostazione predefinita, Python 3 utilizza protocolli di pickling non supportati in Python 2. Ciò può causare errori quando l'app tenta di ricostituire un oggetto Python in un ambiente Python 2 scritto in un ambiente Python 3. Per evitare questo problema, imposta le seguenti variabili di ambiente nel file app.yaml per l'app Python 3, se necessario:

  • Per le app che utilizzano Memcache, incluse quelle che utilizzano NDB, imposta: MEMCACHE_USE_CROSS_COMPATIBLE_PROTOCOL: 'True'
  • Per le app che utilizzano NDB per connettersi a Datastore, imposta: NDB_USE_CROSS_COMPATIBLE_PICKLE_PROTOCOL: 'True'
  • Per le app che utilizzano Deferred, imposta: DEFERRED_USE_CROSS_COMPATIBLE_PICKLE_PROTOCOL: 'True'

In Python 2, gli oggetti string contengono una sequenza di valori di byte a 8 bit. In Python 3, gli oggetti string contengono una sequenza di caratteri Unicode. Per impostazione predefinita, pickle di Python 3 traduce una string di Python 2 in Unicode interpretando la string di Python 3 come ASCII. Ciò può causare errori per i valori al di fuori dell'intervallo di caratteri ASCII da 0 a 127. Memcache supporta la sostituzione di questo mapping predefinito.

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))

La codifica latin1 definisce un mapping per ognuno dei 256 valori possibili di ogni byte in una string di Python 2. In questo modo si evitano errori di decodifica. Tuttavia, se la string di Python 2 contiene dati Unicode effettivi al di fuori dell'intervallo latin1, ad esempio dati letti da un file, cPickle non mapperà correttamente i dati. Pertanto, è importante aggiornare il codice Python 2 in modo che contenga dati Unicode con oggetti unicode e non oggetti string per gli oggetti che esegui con pickle. La guida alla compatibilità include dettagli sugli aggiornamenti necessari.

Il metodo descritto in precedenza per aggiornare il codice Python 2 in modo da produrre serializzazioni compatibili con Python 3 riguarda le serializzazioni di breve durata, ad esempio quelle archiviate in Memcache. Potresti dover aggiornare o riscrivere le serializzazioni Python 2 di lunga durata, ad esempio quelle archiviate in Datastore nell'ambito della migrazione. Ad esempio, la serializzazione scritta utilizzando google.appengine.ext.ndb.model.PickleProperty potrebbe richiedere un upgrade.

Per saperne di più sulle limitazioni e sui problemi meno comuni, consulta la guida alla compatibilità.

Framework web

webapp2 non è in bundle né supportato in Python 3, quindi qualsiasi applicazione deve essere riscritta per utilizzare qualsiasi framework compatibile con WSGI (come Flask).

Una strategia di migrazione consigliata consiste nel sostituire prima l'utilizzo di webapp2 in nell'app Python 2.7 con Flask (o un framework web alternativo come Django, Pyramid, Bottle o web.py), rimanendo su Python 2.7. Poi, quando l'app aggiornata è stabile, esegui la migrazione del codice a Python 3, esegui il deployment e testa utilizzando App Engine per Python 3.

Per esempi di come convertire le app Python 2.7 che utilizzano webapp2 per utilizzare il framework Flask, puoi consultare queste risorse aggiuntive.

Utilizzo dei gestori

Un'app Python 3 può avere un solo script associato, quindi se app.yaml ha più gestori script che mappano gli URL a script diversi, dovrai combinarli in uno che gestisca il routing degli URL.

L'esempio seguente mostra le differenze tra i gestori nel file app.yaml per i rispettivi runtime.

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: python314

app_engine_bundled_services:
- datastore_v3
- memcache
- user
...

#list your bundled services

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

L'app Python 3 deve gestire il routing degli URL (ad esempio, con i decoratori Flask).

Se vuoi utilizzare più script gestori con pattern URL diversi o se vuoi utilizzare altri attributi nei gestori, ogni gestore deve specificare script: auto.

Puoi anche sostituire il comportamento di avvio predefinito by specificando un entrypoint field in your app.yaml file.

Per saperne di più su come utilizzare gestori specifici, consulta le panoramiche di Blobstore, Deferred, e Mail.

Thread safety

Si presume che le app siano thread-safe. Le chiamate API devono essere effettuate sul thread della richiesta. Se utilizzi un'API dei servizi in bundle legacy all'avvio dell'app, possono verificarsi errori di sicurezza.

Per saperne di più, consulta Errori di sicurezza durante l'utilizzo dei servizi in bundle legacy per Python.

Utilizzo di URL Fetch

Per utilizzare URL Fetch per Python, devi chiamare esplicitamente la libreria URL Fetch.

Se l'app Python 3 utilizza l'API URL Fetch, l'intestazione della richiesta X-Appengine-Inbound-Appid viene aggiunta quando l'app invia una richiesta a un'altra app App Engine. In questo modo, l'app ricevente può verificare l'identità dell'app chiamante. Per saperne di più, consulta Eseguire la migrazione delle richieste in uscita.

Esempio (App Engine ndb)

Di seguito è riportata un'app Python 2 di base che registra le visite alle pagine utilizzando App Engine ndb per accedere a Datastore. La sua controparte è un'app Python 3 equivalente in cui l'utilizzo di webapp2 è stato sostituito da Flask e sono state implementate le modifiche richieste descritte sopra per accedere ai servizi in bundle in Python 3.

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)

Entrambe queste app sono disponibili nel repository open source per i contenuti di migrazione di Python App Engine (esempi di codice, video, codelab), in particolare nelle cartelle mod0 e mod1b rispettivamente.