Migrazione a Python 2.7

Questa pagina illustra le misure necessarie per eseguire l'upgrade dell'applicazione Python al runtime di Python 2.7. Dopo aver seguito questa procedura dettagliata, la tua applicazione potrà sfruttare le numerose nuove funzionalità del runtime Python 2.7, tra cui il multithreading, il motore di creazione di modelli Jinja2, il caricamento e l'accesso al bytecode e diverse nuove librerie di terze parti incluse.

Prerequisiti e considerazioni

Per utilizzare Python 2.7, un'applicazione deve soddisfare i seguenti requisiti:

  • Se l'applicazione utilizza Django, deve utilizzare la versione 1.2 o successive. Per dettagli sull'upgrade, consulta la documentazione di Django.
  • Se vuoi utilizzare richieste concorrenti, l'applicazione deve utilizzare gestori di script WSGI (Web Server Gateway Interface), come descritto in Utilizzo di WSGI.

Oltre a soddisfare questi prerequisiti generali, devi utilizzare versioni specifiche di alcune funzionalità di App Engine e librerie di terze parti. Assicurati di aggiornare le versioni che includi e importi nella tua applicazione e di testare a fondo l'applicazione dopo l'upgrade. Il seguente elenco identifica i principali problemi di compatibilità e rimanda ad altre risorse per risolverli:

  • Prestazioni: le prestazioni dell'applicazione potrebbero cambiare dopo l'upgrade a Python 2.7. Se noti un aumento della latenza di risposta, puoi aumentare la classe di istanza frontend e attivare le richieste simultanee. Le richieste in parallelo consentono all'applicazione di funzionare più velocemente a un costo inferiore per le istanze più grandi. Per ulteriori informazioni, consulta la sezione Gestione delle richieste nelle istanze.
  • Django: devi utilizzare Django 1.2 o versioni successive con Python 2.7. Per informazioni sull'upgrade, consulta le note di rilascio di Django.
  • PyCrypto: Crypto.Util.randpool è stato ritirato in favore di Crypto.Random. Per ulteriori informazioni, consulta Che cosa fare in merito a RandomPool.
  • webapp: i modelli webapp sono deprecati in Python 2.7. In alternativa, puoi utilizzare direttamente i modelli Django, jinja2 o un altro motore di modelli a tua scelta.
  • WebOb: Python 2.7 supporta la versione 1.1 di WebOb. Questa versione non è completamente compatibile con la versione precedente (0.9). Se l'applicazione utilizza WebOb, devi testarla a fondo per rilevare eventuali errori derivanti dall'upgrade.
  • zipimport: Python 2.7 non supporta zipimport, ma può effettuare importazioni in modo nativo da file .zip.
  • simplejson: Python 2.7 non supporta simplejson, ma include il modulo json della libreria standard equivalente e molto più veloce.

Richieste simultanee e WSGI

La funzionalità di Python 2.7 che influisce maggiormente sul design e sulle prestazioni dell'applicazione è il supporto delle applicazioni multithread che possono gestire richieste in parallelo. La capacità di gestire richieste simultanee comporta un utilizzo migliorato che può migliorare notevolmente le prestazioni dell'applicazione, in particolare per le applicazioni che utilizzano classi di istanze più elevate che sfruttano più core della CPU.

Per abilitare il multithreading, le applicazioni devono passare dall'approccio basato su Common Gateway Interface (CGI) dei precedenti runtime Python a un approccio basato su Web Server Gateway Interface (WSGI). Questo perché gli script CGI, che sono stati progettati per gestire le richieste in modo seriale, si basano sulle variabili di ambiente per accedere agli stream di input e output.

Sebbene il modello WSGI per la gestione delle richieste offra alle applicazioni un accesso più diretto agli stream di input e output (consentendo le richieste concorrenti), la gestione di più richieste in parallelo può causare condizioni di gara quando la logica di un gestore delle richieste si basa su dati o interagisce con dati con un ambito maggiore di quello locale, ad esempio lo stato dell'applicazione. Per questo motivo, è importante scrivere codice in modo sicuro per gestire le condizioni di gara al fine di garantire che la nuova applicazione WSGI sia sicura per i thread.

Per maggiori dettagli, consulta la sezione Rendere l'applicazione a prova di thread.

Aggiornamento di app.yaml

Python 2.7 richiede un elemento di configurazione runtime speciale nell'intestazione di app.yaml. Tieni presente che l'elemento threadsafe: [ true | false ] è obbligatorio per le applicazioni Python 2.7. Se true, App Engine invia le richieste in modo concorrente; se false, le invia in sequenza. La seguente intestazione app.yaml consente le richieste in parallelo:

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

Utilizzo di WSGI

Il runtime Python 2.7 consente facoltativamente di eseguire direttamente un'applicazione WSGI (Web Server Gateway Interface), anziché utilizzare l'adattatore run_wsgi_app per eseguire il programma come script CGI. Per farlo, sostituisci il gestore CGI (ad es. myapp.py) in app.yaml con un nome di applicazione WSGI (ad es. myapp.app).

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

Devi anche spostare l'oggetto dell'applicazione WSGI nell'ambito globale:

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

Puoi comunque specificare gestori di script CGI in app.yaml; tuttavia, le richieste gestite dagli script CGI vengono elaborate in sequenza, non in modo contemporaneo. Inoltre, non puoi avere un app.yaml file che mescola script CGI e applicazioni WSGI e non puoi impostare threadsafe su true se definisci gestori CGI.

Alcune convenzioni dei precedenti runtime di Python, come l'utilizzo di main() e il controllo di __name__ == 'main', sono ora deprecate. Queste misure hanno contribuito a mantenere in cache gli script CGI del passato, ma ora che esegui direttamente le applicazioni WSGI, questi passaggi non sono più necessari.

Utilizzare la directory principale dell'app

In Python 2.5, gli script CGI venivano eseguiti con la directory di lavoro attuale impostata sulla directory che conteneva lo script. Questo è cambiato in Python 2.7. Con WSGI, la directory di lavoro corrente all'inizio del ciclo di vita del gestore delle richieste è la directory principale dell'applicazione.

Esamina il codice dell'applicazione e assicurati che tutti gli handler siano scritti in modo da prevedere che la directory di lavoro corrente sia la radice dell'applicazione.

Configurazione delle librerie

Il runtime di Python 2.7 include alcuni moduli di terze parti. Alcune sono disponibili per impostazione predefinita, mentre altre sono disponibili solo se configurate. Puoi specificare la versione che vuoi utilizzare.

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

Puoi specificare che l'applicazione debba utilizzare la versione più recente del modulo. Questo è utile se stai sviluppando un'applicazione che non ha ancora utenti: non devi monitorare le nuove versioni. Tuttavia, se la tua applicazione è in uso attivo, attenzione: potresti rimanere sorpreso nel vedere che inizia a utilizzare una nuova versione della libreria non compatibile con le versioni precedenti. Per utilizzare la versione più recente:

libraries:
- name: PIL
  version: latest

Per un elenco delle librerie supportate, consulta Librerie di terze parti.

Rendere l'applicazione sicura per i thread

La gestione delle richieste simultanee è semplice se ogni gestore interagisce solo con le variabili all'interno del suo ambito. Tuttavia, la situazione si complica rapidamente se un gestore modifica le risorse mentre un altro le sta leggendo. Assicurarsi che l'applicazione si comporti come previsto, anche se più richieste potrebbero manipolare gli stessi dati e interferire tra loro, è nota come applicazione "a prova di thread".

La regola principale per progettare un'applicazione sicura per i thread è limitare l'uso delle risorse condivise (come le informazioni sullo stato o le variabili globali) il più spesso possibile. Tuttavia, in genere non è possibile escluderne completamente l'utilizzo ed è qui che entrano in gioco i meccanismi di sincronizzazione come gli oggetti di blocco.

In Python 2.7, hai accesso alla libreria di threading di Python, che ti consente di dichiarare un blocco su un blocco di logica che forza l'esecuzione del codice al suo interno in sequenza anziché in contemporanea. Considera il seguente codice:

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

Questo codice mostra la creazione di una cache di alcune variabili di configurazione globali in una variabile denominata _config_cache. In questo caso, l'utilizzo di un oggetto di blocco denominato _config_lock garantisce che il controllo di un _config_cache preesistente funzioni in modo affidabile. In caso contrario, questa variabile potrebbe farti perdere tempo con più accessi a Datastore per impostare la stessa variabile più volte con gli stessi dati, perché le richieste concorrenti hanno tutte trovato che _config_cache era vuoto.

Non scegliere a cuor leggero di utilizzare le serrature. Un blocco forza tutti gli altri thread che eseguono questo metodo a bloccarsi. Questo può rappresentare un collo di bottiglia per le prestazioni.

Aggiornamento dell'applicazione a webapp2

Nota: se non utilizzi webapp come gestore delle richieste, puoi saltare questa sezione.

È stato eseguito l'upgrade del framework web incluso nel runtime Python 2.7 da webapp a webapp2. Tra le altre cose, webapp2 aggiunge il routing degli URI e la gestione delle eccezioni migliorati, un oggetto di risposta completo e un meccanismo di invio più flessibile.

I modelli webapp sono stati ritirati. Al loro posto, puoi utilizzare Jinja2, Django o un sistema di creazione di modelli a tua scelta (purché sia scritto in Python puro).

In App Engine, a webapp2 è stato associato l'alias webapp e webapp2 è compatibile con le versioni precedenti. Tuttavia, dopo l'upgrade, devi comunque verificare attentamente l'applicazione e familiarizzare con le nuove sintassi e funzionalità di webapp2, anziché continuare a fare affidamento sulla compatibilità con le versioni precedenti.

L'upgrade è stato completato.

Dopo aver caricato l'applicazione, devi testarla a fondo per garantire la compatibilità con le versioni precedenti. Se riscontri problemi, consulta i forum.