web-dev-qa-db-fra.com

Comment gérer avec élégance les futures importations de fonctionnalités (__future__) ayant échoué en raison de l'ancienne version de l'interpréteur?

Comment gérez-vous avec élégance les futures importations de fonctionnalités ayant échoué? Si un utilisateur s'exécute en utilisant Python 2.5 et la première instruction de mon module est:

from __future__ import print_function

La compilation de ce module pour Python 2.5 échouera avec un:

  File "__init__.py", line 1
    from __future__ import print_function
SyntaxError: future feature print_function is not defined

Je voudrais informer l'utilisateur qu'il doit réexécuter le programme avec Python> = 2.6 et peut-être fournir des instructions sur la façon de le faire. Cependant, pour citer PEP 236 :

Les seules lignes qui peuvent apparaître avant une future_statement sont:

  • Le module docstring (le cas échéant).
  • Commentaires.
  • Lignes vides.
  • Autres déclarations_ futures.

Je ne peux donc pas faire quelque chose comme:

import __future__

if hasattr(__future__, 'print_function'):
    from __future__ import print_function
else:
    raise ImportError('Python >= 2.6 is required')

Parce qu'elle donne:

  File "__init__.py", line 4
    from __future__ import print_function
SyntaxError: from __future__ imports must occur at the beginning of the file

Cet extrait du PEP semble donner l'espoir de le faire en ligne:

Q: Je veux encapsuler les futures déclarations dans des blocs try/except, donc je peux utiliser un code différent selon la version de Python que j'utilise. Pourquoi pas?

R: Désolé! try/except est une fonctionnalité d'exécution; futures_statements sont principalement des gadgets au moment de la compilation, et votre try/except se produit longtemps après la fin du compilateur. Autrement dit, au moment où vous essayez/sauf, la sémantique en vigueur pour le module est déjà terminée. Puisque le try/except n'accomplirait pas ce qu'il ressemble à ce qu'il devrait accomplir, ce n'est tout simplement pas autorisé. Nous voulons également garder ces déclarations spéciales très faciles à trouver et à reconnaître.

Notez que vous pouvez importer directement __future__ et utiliser les informations qu'il contient, ainsi que sys.version_info, pour déterminer où se trouve la version sous laquelle vous exécutez est en relation avec le statut d'une entité donnée.

Des idées?

68
cdleary

"Je voudrais informer l'utilisateur qu'il doit réexécuter le programme avec Python> = 2.6 et peut-être fournir des instructions sur la façon de le faire."

N'est-ce pas à cela que sert un fichier README?

Voici votre alternative. Un "wrapper": un petit blob de Python qui vérifie l'environnement avant d'exécuter votre cible aop.

Fichier: appwrapper.py

import sys
major, minor, micro, releaselevel, serial = sys.version_info
if (major,minor) <= (2,5):
    # provide advice on getting version 2.6 or higher.
    sys.exit(2)
import app
app.main()

Que signifie "importation directe". Vous pouvez examiner le contenu de __future__. Vous êtes toujours lié par le fait qu'un from __future__ import print_function est une information pour le compilateur, mais vous pouvez fouiller avant d'importer le module qui fait le vrai travail.

import __future__, sys
if hasattr(__future__, 'print_function'): 
    # Could also check sys.version_info >= __future__. print_function.optional
    import app
    app.main()
else:
    print "instructions for upgrading"
59
S.Lott

Une méthode plutôt hacky mais simple que j'ai utilisée auparavant consiste à exploiter le fait que les littéraux d'octet ont été introduits dans Python 2.6 et à utiliser quelque chose comme ça au début du fichier:

b'This module needs Python 2.6 or later. Please do xxx.'

Ceci est inoffensif dans Python 2.6 ou version ultérieure, mais un SyntaxError dans toutes les versions antérieures. Quiconque essaie de compiler votre fichier recevra toujours une erreur, mais il recevra également le message que vous veux donner.

Vous pourriez penser que vous devrez avoir cette ligne après votre from __future__ import print_function alors ce sera l'import qui générera le SyntaxError et vous ne verrez pas le message d'erreur utile, mais curieusement, l'erreur la plus récente a la priorité. Je soupçonne que comme l'erreur de l'importation n'est pas vraiment une erreur de syntaxe en soi, elle n'est pas déclenchée lors de la première passe de compilation, et donc les vraies erreurs de syntaxe sont déclenchées en premier (mais je suppose).

Cela pourrait ne pas répondre à vos critères pour être "gracieux", et c'est très Python 2.6 spécifique, mais c'est rapide et facile à faire.

46
Scott Griffiths

Mettez simplement un commentaire sur la même ligne avec le "from __future__ import ...", comme ça:

from __future__ import print_function, division  # We require Python 2.6 or later

Puisque Python affiche la ligne contenant l'erreur, si vous essayez d'exécuter le module avec Python 2.5 vous obtiendrez une belle erreur descriptive:

    from __future__ import print_function, division  # We require Python 2.6 or later
SyntaxError: future feature print_function is not defined
40
Dave Burton