Désactiver les assertions en Python

Réponses:

75

Comment désactiver les assertions en Python?

Il existe plusieurs approches qui affectent un seul processus, l'environnement ou une seule ligne de code.

Je démontre chacun.

Pour l'ensemble du processus

L'utilisation de l' -Oindicateur (O majuscule) désactive toutes les instructions d'assertion dans un processus.

Par exemple:

$ python -Oc "assert False"

$ python -c "assert False"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AssertionError

Notez que par désactiver, je veux dire qu'il n'exécute pas non plus l'expression qui le suit:

$ python -Oc "assert 1/0"

$ python -c "assert 1/0"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

Pour l'environnement

Vous pouvez également utiliser une variable d'environnement pour définir cet indicateur.

Cela affectera chaque processus qui utilise ou hérite de l'environnement.

Par exemple, sous Windows, définir puis effacer la variable d'environnement:

C:\>python -c "assert False"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AssertionError
C:\>SET PYTHONOPTIMIZE=TRUE

C:\>python -c "assert False"

C:\>SET PYTHONOPTIMIZE=

C:\>python -c "assert False"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AssertionError

Idem sous Unix (en utilisant set et unset pour les fonctionnalités respectives)

Point unique dans le code

Vous continuez votre question:

si une assertion échoue, je ne veux pas qu'elle lève une AssertionError, mais qu'elle continue.

Si vous souhaitez que le code qui échoue à s'exercer, vous pouvez soit vous assurer que le flux de contrôle n'atteint pas l'assertion, par exemple:

if False:
    assert False, "we know this fails, but we don't get here"

ou vous pouvez attraper l'erreur d'assertion:

try:
    assert False, "this code runs, fails, and the exception is caught"
except AssertionError as e:
    print(repr(e))

qui imprime:

AssertionError('this code runs, fails, and the exception is caught')

et vous continuerez à partir du moment où vous avez géré le fichier AssertionError.

Références

De la assertdocumentation :

Une déclaration d'assert comme celle-ci:

assert expression #, optional_message

Est équivalent à

if __debug__:
    if not expression: raise AssertionError #(optional_message)

Et,

la variable intégrée __debug__est Truedans des circonstances normales, Falselorsqu'une optimisation est demandée (option de ligne de commande -O).

et plus loin

Les attributions à __debug__sont illégales. La valeur de la variable intégrée est déterminée au démarrage de l'interpréteur.

À partir des documents d'utilisation:

-O

Activez les optimisations de base. Cela change l'extension de nom de fichier pour les fichiers compilés (bytecode) de .pyc à .pyo. Voir aussi PYTHONOPTIMIZE.

et

PYTHONOPTIMISATION

Si cela est défini sur une chaîne non vide, cela équivaut à spécifier l' -Ooption. S'il est défini sur un entier, cela équivaut à spécifier -Oplusieurs fois.

Salle Aaron
la source
serait-il possible de sauter le code qui échoue en cas de «point unique dans le code»? J'ai essayé de régler __debug__sur False mais ce n'est pas autorisé.
Matthijs le
1
@Matthijs vous pouvez soit vous assurer que le flux de contrôle ne l'atteint pas (par exemple if False: assert False), soit attraper l'erreur d'assertion. Ce sont vos choix. Mise à jour de la réponse pour répondre à votre question.
Aaron Hall
Merci pour la réponse, mais pas encore complètement à quoi je pensais. Je voudrais désactiver une fonction à l' intérieur affirme lors de l' exécution, idéalement avec une sorte de gestionnaire de contexte: l' affirmation est évaluée: foo()et assertions mise hors circuit: with skip_assertion(): foo(). L'avantage de ceci étant que je n'ai pas à ajouter un autre drapeau sur la fonction
Matthijs
2
Vous pouvez réécrire le bytecode de la fonction, réécrire l'AST ou réécrire la fonction elle-même. (manuellement ou par programme, pour l'un ou l'autre). Réécrire l'AST serait probablement l'approche la plus fiable (remplacer "simplement" les Assertobjets par des Passobjets). Un gestionnaire de contexte ne fonctionnerait pas directement pour cela, mais vous pourriez avoir une sorte de mécanisme qui utilise des fonctions décorées de cette manière. Quoi qu'il en soit, je ne le recommande pas. Je soupçonne que votre raison de vouloir le faire est que vous appelez du code que vous ne contrôlez pas et que vous obtenez des AssertionErrors. Si tel est le cas, vous devrez probablement trouver un correctif différent.
Aaron Hall
59

Appelez Python avec l'indicateur -O:

test.py:

assert(False)
print 'Done'

Production:

C:\temp\py>C:\Python26\python.exe test.py
Traceback (most recent call last):
  File "test.py", line 1, in <module>
    assert(False)
AssertionError

C:\temp\py>C:\Python26\python.exe -O test.py
Done
Mark Rushakoff
la source
8
Assert n'est pas une fonction, donc les parenthèses sont superflues.
Aaron Hall
15

Les deux réponses déjà données sont valides (appelez Python avec l'un -Oou l' autre -OOsur la ligne de commande).

Voici la différence entre eux:

  • -OActivez les optimisations de base. Cela change l'extension de nom de fichier pour les fichiers compilés (bytecode) de .pyc à .pyo.

  • -OOSupprimez les docstrings en plus des -Ooptimisations.

(De la documentation Python )

Michael Currie
la source
7

Utilisez python -O:

$ python -O
>>> assert False
>>> 
John Millikin
la source
3

Vous ne devez PAS désactiver (la plupart) des assertions. Ils détectent des erreurs imprévues lorsque l'attention est ailleurs. Voir la règle 5 dans «Le pouvoir de dix» .

Au lieu de cela, protégez certains contrôles d'assertions coûteux par quelque chose comme:

import logging
logger = logging.getLogger(__name__)

if logger.getEffectiveLevel() < logging.DEBUG:
    ok = check_expensive_property()
    assert ok, 'Run !'

Une façon de conserver les assertions importantes et d'autoriser assertl'optimisation des instructions consiste à les lever dans une instruction de sélection:

if foo_is_broken():
    raise AssertionError('Foo is broken!')
Ioannis Filippidis
la source
1
//, Le problème est, cependant, que l'instruction ajoute encore à la complexité cyclomatique et que la gestion des erreurs devrait gérer le reste?
Nathan Basanese
1
Les assertions qui seraient protégées comme ci-dessus sont des appels coûteux qui ralentissent considérablement l'exécution. Pour certains algorithmes, les vérifications de ce type peuvent prendre des ordres de grandeur plus longs que l'ensemble du programme. Pensez à exécuter une implémentation naïve mais plus simple (donc moins susceptible de contenir des erreurs) du même algorithme pour vérifier l'exactitude. Ou une vérification par énumération exhaustive de quelque chose qui est hors de question pour un fonctionnement normal.
Ioannis Filippidis
Je ne vois pas beaucoup de problème de lisibilité, car une telle déclaration n'ajoute pas d'imbrication au code. L'extraction en tant qu'appel de fonction peut le déplacer hors du chemin, si c'est un problème (et je pense qu'un tel refactoring devrait réduire la complexité cyclomatique). En tout état de cause, la complexité cyclomatique ne devrait pas régir les contrôles de sécurité.
Ioannis Filippidis
2

L'exécution en mode optimisé devrait le faire:

python -OO module.py
FogleBird
la source