Migrazione a Python 2.7

Questa pagina illustra le misure necessarie per eseguire l'upgrade dell'applicazione Python al runtime Python 2.7. Dopo aver seguito questa procedura dettagliata, l'applicazione può sfruttare le numerose nuove funzionalità del runtime Python 2.7, tra cui il multithreading, il motore di modelli Jinja2, l'accesso e il caricamento in 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 successiva. Per i dettagli dell'upgrade, consulta la documentazione di Django.
  • Se vuoi utilizzare richieste in parallelo, 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à e librerie di terze parti di App Engine. Assicurati di aggiornare le versioni che includi e importi nell'applicazione e di testare l'applicazione a fondo dopo l'upgrade. Il seguente elenco identifica i principali problemi di compatibilità e rimanda a ulteriori risorse per risolverli:

  • Prestazioni: le prestazioni dell'applicazione possono cambiare dopo l'upgrade a Python 2.7. Se riscontri una latenza di risposta maggiore, puoi aumentare la classe dell'istanza di frontend e abilitare le richieste in parallelo. Le richieste in parallelo consentono all'applicazione di essere eseguite più velocemente con un costo di istanza inferiore sulle istanze più grandi. Per saperne di più, consulta 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 deprecato e sostituito con Crypto.Random. Per ulteriori informazioni, vedi Cosa fare su RandomPool.
  • webapp: i modelli di app web sono deprecati in Python 2.7. Puoi invece utilizzare direttamente i modelli Django, jinja2 o un altro motore di modelli a tua scelta.
  • WebOb: Python 2.7 supporta WebOb versione 1.1. Questa versione non è completamente compatibile con le versioni precedenti (0.9). Se l'applicazione utilizza WebOb, devi testarla a fondo per individuare eventuali errori derivanti dall'upgrade.
  • zipimport: Python 2.7 non supporta zipimport, ma Python 2.7 può importare in modo nativo da file .zip.
  • simplejson: Python 2.7 non supporta il file simplejson, ma Python 2.7 include il modulo JSON equivalente e molto più veloce della libreria standard.

Richieste in parallelo e WSGI

La funzionalità Python 2.7 che influisce maggiormente sulla progettazione e sulle prestazioni della tua applicazione è il supporto di applicazioni multithread in grado di gestire richieste in parallelo. La capacità di gestire le richieste in parallelo comporta un migliore utilizzo che può migliorare notevolmente le prestazioni dell'applicazione, in particolare per le applicazioni che utilizzano classi di istanza più elevate che sfruttano più core della CPU.

Per abilitare il multi-threading, le applicazioni devono passare dall'approccio basato su Common Gateway Interface (CGI) dei runtime Python precedenti a un approccio basato su Web Server Gateway Interface (WSGI). Questo perché gli script CGI, progettati per gestire le richieste in modo seriale, si basano su variabili di ambiente per l'accesso ai flussi di input e di output.

Mentre il modello WSGI per la gestione delle richieste conferisce alle applicazioni un accesso più diretto ai flussi di input e di output (consentendo richieste in parallelo), la gestione di più richieste in parallelo può causare condizioni di gara quando la logica di un gestore di richieste si basa su dati o interagisce con questi dati con un ambito superiore a locale, ad esempio lo stato dell'applicazione. Per questo motivo, è importante programmare in modo difensivo per gestire le condizioni di gara e garantire che la nuova applicazione WSGI sia threadsafe.

Per maggiori dettagli, consulta Rendere l'applicazione Threadsafe.

Aggiornamento del file 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 richieste in contemporanea; se false, App Engine le invia in modo seriale. La seguente intestazione app.yaml abilita le richieste in parallelo:

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

Utilizzo di WSGI

Il runtime Python 2.7 facoltativamente consente 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 dell'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 i gestori di script CGI in app.yaml; tuttavia, le richieste gestite dagli script CGI vengono elaborate in serie, non in contemporanea. Inoltre, non puoi avere un file app.yaml che combina script CGI e applicazioni WSGI e non puoi impostare threadsafe su true se definisci dei gestori CGI.

Alcune convenzioni dei runtime Python precedenti, come l'utilizzo di main() e il controllo di __name__ == 'main', sono ora deprecate. Queste misure hanno consentito agli script CGI precedenti di rimanere memorizzati nella cache, ma ora che esegui direttamente le applicazioni WSGI, questi passaggi non sono più necessari.

Utilizzo della directory root dell'app

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

Rivedi il codice dell'applicazione e assicurati che tutti i gestori siano scritti in modo che la directory di lavoro corrente sia la radice dell'applicazione.

Configurazione delle librerie

Il runtime Python 2.7 include alcuni moduli di terze parti. Alcune di queste 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 l'ultima versione del modulo. Ciò è utile se stai sviluppando un'applicazione che non ha ancora utenti: non è necessario monitorare le nuove versioni. Tuttavia, se la tua applicazione viene utilizzata attivamente, fai attenzione: potrebbe sorprenderti il fatto che la tua applicazione inizi 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 Threadsafe

La gestione delle richieste in parallelo è semplice se ogni gestore interagisce solo con le variabili all'interno del suo ambito. Ma la situazione si fa rapidamente difficile se un gestore modifica le risorse mentre un altro le legge. Per assicurarti che l'applicazione funzioni come previsto, anche se più richieste potrebbero manipolare gli stessi dati e interferire tra loro, è noto come rendere l'applicazione "threadsafe".

Quando si progetta un'applicazione sicura per i thread, la regola principale è limitare il più spesso possibile l'uso di risorse condivise (ad esempio, informazioni sullo stato o variabili globali). Tuttavia, in genere non è possibile escluderne del tutto l'utilizzo ed è qui che entrano in gioco i meccanismi di sincronizzazione come gli oggetti blocco.

In Python 2.7, hai accesso alla libreria di thread di Python, che ti consente di dichiarare un blocco su un blocco di logica che forza l'esecuzione del codice al suo interno in modo seriale anziché contemporaneamente. 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'uso di un oggetto blocco denominato _config_lock garantisce che il controllo di un _config_cache preesistente si comporti in modo affidabile. In caso contrario, questa variabile potrebbe perdere tempo a eseguire più viaggi in Datastore per impostare la stessa variabile più volte con gli stessi dati, perché tutte le richieste concorrenti hanno rilevato che _config_cache era vuoto.

Non scegliere con leggerezza di utilizzare le serrature. Un blocco forza il blocco di qualsiasi altro thread che esegue questo metodo. Questo può rappresentare un collo di bottiglia per il rendimento.

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 URI migliorato e la gestione delle eccezioni, un oggetto risposta completo e un meccanismo di invio più flessibile.

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

In App Engine, webapp2 ha un alias che rimanda a webapp e webapp2 è compatibile con le versioni precedenti. Tuttavia, dopo l'upgrade, dovrai comunque testare l'applicazione in modo approfondito e acquisire familiarità con la nuova sintassi e le nuove funzionalità di webapp2, anziché continuare a fare affidamento sulla compatibilità con le versioni precedenti.

E il tuo upgrade è completo!

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