Migration vers Python 2.7

Cette page présente les mesures nécessaires pour mettre à niveau une application Python vers l'environnement d'exécution Python 2.7. Lorsque vous aurez terminé ce tutoriel, votre application pourra exploiter toutes les nouvelles fonctionnalités de Python 2.7, telles que le multithreading, le moteur de création de modèles Jinja2, l'accès au bytecode et la possibilité d'en importer, ainsi que plusieurs nouvelles bibliothèques tierces incluses.

Conditions préalables et points à noter

Pour pouvoir utiliser Python 2.7, une application doit remplir les conditions suivantes :

  • Si l'application exploite Django, elle doit utiliser la version 1.2 ou ultérieure. Consultez la documentation de Django pour en savoir plus sur la mise à niveau.
  • Si vous souhaitez exécuter des requêtes simultanées, l'application doit utiliser des gestionnaires de scripts WSGI (Web Server Gateway Interface), comme décrit dans la section Utiliser WSGI.

En plus de satisfaire ces conditions, vous devez également employer des versions spécifiques de certaines fonctionnalités App Engine et bibliothèques tierces. Veillez à mettre à jour les versions que vous intégrez et importez dans votre application, et assurez-vous de tester cette dernière de manière approfondie après la mise à niveau. La liste suivante répertorie les principaux problèmes de compatibilité et fournit des liens vers des ressources supplémentaires permettant les résoudre :

  • Performances : les performances des applications peuvent changer après la mise à niveau vers Python 2.7. Si vous subissez une latence de réponse accrue, vous pouvez augmenter la taille de la classe d'instance frontend et activer les requêtes simultanées. Les requêtes simultanées permettent d'accélérer l'application tout en réduisant le coût associé aux grandes instances. Pour en savoir plus, consultez la page relative au traitement des requêtes dans les instances.
  • Django : vous devez utiliser Django 1.2 ou version ultérieure avec Python 2.7. Pour en savoir plus sur la mise à niveau, consultez les notes de version relatives à Django.
  • PyCrypto : Crypto.Util.randpool a été abandonné au profit de Crypto.Random. Pour en savoir plus, consultez la page What to do about RandomPool (Que faire au sujet de RandomPool ?).
  • webapp : les modèles webapp sont désormais obsolètes dans l'environnement d'exécution Python 2.7. Vous pouvez directement utiliser les modèles Django, jinja2 ou un autre moteur de création de modèles.
  • WebOb : Python 2.7 est compatible avec WebOb version 1.1. Cette version n'est pas totalement rétrocompatible avec l'ancienne version (0.9). Si l'application utilise WebOb, vous devez la tester de manière approfondie pour identifier toute erreur résultant de la mise à niveau.
  • zipimport : l'environnement d'exécution Python 2.7 n'est pas compatible avec zipimport, mais permet d'effectuer des importations de manière native depuis des fichiers ZIP.
  • simplejson : l'environnement d'exécution Python 2.7 n'est pas compatible avec simplejson, mais inclut un équivalent beaucoup plus rapide : le module JSON de la bibliothèque standard.

Requêtes simultanées et WSGI

La fonctionnalité Python 2.7 qui affecte le plus la conception et les performances de votre application est sa compatibilité avec les applications multithread capables de traiter des requêtes simultanées. Cette fonctionnalité permet en effet d'accroître l'efficacité de l'application et d'en améliorer considérablement les performances, en particulier pour les applications qui utilisent des classes d'instance plus élevées exploitant plusieurs cœurs de processeur.

Pour activer le multithreading, les applications doivent passer de l'approche basée sur le standard CGI (Common Gateway Interface) des précédents environnements d'exécution Python à celle basée sur le standard WSGI (Web Server Gateway Interface). Ceci est dû au fait que les scripts CGI, qui ont été conçus pour traiter les requêtes en série, s'appuient sur des variables d'environnement pour accéder aux flux d'entrée et de sortie.

Bien que le modèle WSGI pour le traitement des requêtes offre aux applications un accès plus direct aux flux d'entrée et de sortie (ce qui permet d'exécuter des requêtes simultanées), le traitement de plusieurs requêtes en parallèle peut entraîner des conditions de concurrence lorsque la logique d'un gestionnaire de requêtes s'appuie sur (ou interagit avec) des données ayant un champ d'application allant au-delà de l'échelle locale, telles que l'état de l'application. Il est donc important que vous écriviez votre code de manière défensive afin de résoudre les conditions de concurrence dans l'ordre et de vous assurer que votre nouvelle application WSGI est threadsafe.

Consultez la section Rendre l'application threadsafe pour en savoir plus.

Mettre à jour le fichier app.yaml

Python 2.7 nécessite un élément de configuration runtime spécial dans l'en-tête de app.yaml. Notez que l'élément threadsafe: [ true | false ] est requis pour les applications Python 2.7. S'il est défini sur true, App Engine envoie les requêtes de manière simultanée. S'il est défini sur false, le service les envoie en série. L'en-tête app.yaml suivant active les requêtes simultanées :

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

Utiliser WSGI

L'environnement Python 2.7 permet éventuellement d'exécuter directement une application WSGI plutôt que de recourir à l'adaptateur run_wsgi_app pour exécuter le programme en tant que script CGI. Pour ce faire, remplacez le gestionnaire CGI (par exemple, myapp.py) du fichier app.yaml par un nom d'application WSGI (par exemple, myapp.app).

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

Vous devez également déplacer votre objet d'application WSGI vers le champ d'application global :

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

Vous pouvez toujours spécifier des gestionnaires de scripts CGI dans app.yaml ; toutefois, les requêtes traitées par des scripts CGI sont exécutées en série et non simultanément. En outre, vous ne pouvez pas disposer d'un fichier app.yaml combinant des scripts CGI et des applications WSGI. Vous ne pouvez pas non plus définir threadsafe sur true si vous définissez des gestionnaires CGI.

Certaines conventions des anciens environnement d'exécution Python, telles que l'utilisation de main() et la vérification de __name__ == 'main', sont désormais obsolètes. Ces étapes aidaient les anciens scripts CGI à rester en cache, mais ne sont plus nécessaires dans la mesure où vous exécutez maintenant des applications WSGI directement.

Utiliser le répertoire racine de l'application

Dans l'environnement d'exécution Python 2.5, le répertoire de travail actuel des scripts CGI était défini sur le répertoire contenant le script. Cet aspect a été modifié dans Python 2.7. Avec WSGI, le répertoire de travail actuel au début de la durée de vie du gestionnaire de requêtes correspond au répertoire racine de l'application.

Examinez votre code d'application et assurez-vous que tous les gestionnaires définissent le répertoire de travail actuel sur la racine de l'application.

Configurer les bibliothèques

L'environnement d'exécution Python 2.7 inclut des modules tiers. Certains d'entre eux sont disponibles par défaut, tandis que d'autres ne sont disponibles qu'une fois configurés. Vous pouvez spécifier la version que vous souhaitez utiliser.

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

Vous pouvez indiquer à l'application d'utiliser la dernière version du module. Cette démarche est utile si vous développez une application qui n'a pas encore d'utilisateurs : vous n'avez alors pas besoin de suivre les nouvelles versions. En revanche, soyez prudent si votre application est utilisée activement. Vous pourriez être surpris de constater que votre application commence à utiliser une nouvelle version de bibliothèque non rétrocompatible avec les versions antérieures. Pour utiliser la dernière version, procédez comme suit :

libraries:
- name: PIL
  version: latest

Pour obtenir la liste des bibliothèques compatibles, consultez la page relative aux bibliothèques tierces.

Rendre l'application threadsafe

Le traitement des requêtes simultanées est très simple lorsque chaque gestionnaire n'interagit qu'avec des variables de son champ d'application. Toutefois, les choses se compliquent rapidement si un gestionnaire modifie des ressources pendant qu'un autre les lit. Lorsqu'on s'assure qu'une application se comporte bien comme prévu (bien que plusieurs requêtes puissent manipuler les mêmes données et interférer les unes avec les autres), on parle de la rendre "threadsafe".

La règle principale à observer lorsque vous développez une application threadsafe est de limiter le plus souvent possible l'utilisation des ressources partagées (telles que les informations d'état ou les variables globales). Cependant, il n'est généralement pas possible de les exclure complètement. C'est là que des mécanismes de synchronisation tels que les objets de verrouillage entrent en jeu.

Dans l'environnement d'exécution Python 2.7, vous avez accès à la bibliothèque threading Python, qui permet de déclarer un verrou sur un bloc de logique, obligeant le code qu'il contient à s'exécuter en série plutôt que simultanément. Prenons l'exemple de code suivant :

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

Ce code montre la création d'un cache de certaines variables de configuration globales dans une variable appelée _config_cache. Ici, l'utilisation d'un verrou nommé _config_lock garantit que la variable _config_cache préexistante sera vérifiée de manière fiable. Sans ce verrou, la variable pourrait perdre du temps à effectuer plusieurs trajets au sein du Datastore, afin de définir la même variable plusieurs fois avec les mêmes données, car les requêtes concurrentes auraient détecté que _config_cache était vide.

Ne prenez pas l'utilisation des verrous à la légère. Un verrou force tous les autres threads qui exécutent cette méthode à bloquer l'accès, ce qui peut alors brider les performances.

Mettre à jour l'application vers webapp2

Remarque : Si vous n'utilisez pas le gestionnaire de requêtes webapp, vous pouvez ignorer cette section.

Le framework Web fourni avec l'environnement d'exécution Python 2.7 a été mis à niveau de webapp vers webapp2. Le framework webapp2 intègre un routage URI et un traitement des exceptions plus performants, un objet de réponse complet et un mécanisme de distribution plus flexible, entre autres.

Les modèles webapp sont désormais obsolètes. À la place, vous pouvez utiliser Jinja2, Django ou un système de création de modèles de votre choix (tant qu'il est écrit en langage Python pur).

Dans App Engine, webapp2 est un alias de webapp, et webapp2 est rétrocompatible. Toutefois, après la mise à niveau, vous devez toujours tester votre application de manière approfondie et vous familiariser avec la nouvelle syntaxe et les nouvelles fonctionnalités de webapp2, plutôt que de continuer à vous fier à la rétrocompatibilité.

La mise à niveau est maintenant terminée !

Une fois l'application importée, vous devez la tester de manière approfondie pour garantir la rétrocompatibilité. Si vous rencontrez des problèmes, veuillez consulter les forums.