web-dev-qa-db-fra.com

Faire du `assert` de Python une exception que je choisis

Puis-je faire assert lève une exception que je choisis au lieu de AssertionError?

METTRE À JOUR:

J'expliquerai ma motivation: jusqu'à présent, j'ai eu des tests de type assertion qui ont soulevé mes propres exceptions; Par exemple, lorsque vous créez un objet Node avec certains arguments, il vérifie si les arguments sont utiles à la création d'un nœud et sinon, il déclenche NodeError.

Mais je sais que Python a un mode -o dans lequel les assertions sont ignorées, ce que je souhaiterais que le système soit disponible, car cela rendrait mon programme plus rapide. Mais j'aimerais quand même avoir mes propres exceptions. C'est pourquoi je veux utiliser assert avec mes propres exceptions.

36
Ram Rachum

Cela fonctionnera. Mais c'est un peu fou.

try:
    assert False, "A Message"
except AssertionError, e:
    raise Exception( e.args )

Pourquoi pas ce qui suit? C'est moins fou.

if not someAssertion: raise Exception( "Some Message" )

C'est seulement un peu plus verbeux que la déclaration assert, mais cela ne contrevient pas à notre attente selon laquelle les échecs d'assertion lèvent AssertionError.

Considère ceci.

def myAssert( condition, action ):
    if not condition: raise action

Ensuite, vous pouvez plus ou moins remplacer vos assertions existantes par quelque chose comme ceci.

myAssert( {{ the original condition }}, MyException( {{ the original message }} ) )

Une fois que vous avez fait cela, vous êtes maintenant libre de vous mêler d'activer ou de désactiver ou quoi que vous essayiez de faire.

Lisez également le module warnings . Cela peut être exactement ce que vous essayez de faire.

48
S.Lott

Que dis-tu de ça?


>>> def myraise(e): raise e
... 
>>> cond=False
>>> assert cond or myraise(RuntimeError)
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 1, in myraise
RuntimeError

23
John La Rooy

Python ignore également les blocs if __debug__: lorsqu’il est exécuté avec l’option -o. Le code suivant est plus détaillé, mais fait ce dont vous avez besoin sans piratage:

def my_assert(condition, message=None):
    if not condition:
        raise MyAssertError(message)

if __debug__: my_assert(condition, message)

Vous pouvez le raccourcir en déplaçant la condition if __debug__: dans my_assert(), mais elle sera ensuite appelée (sans aucune action à l'intérieur) lorsque l'optimisation sera activée.

7
Denis Otkidach

Ne jamais utiliser une affirmation pour la logique! Seulement pour les tests optionnels. N'oubliez pas que si Python est en cours d'exécution avec les optimisations activées, les assertions ne sont même pas compilées dans le bytecode. Si vous faites cela, vous vous souciez évidemment de l'exception levée et si vous vous souciez, vous utilisez des affirmations fausses au départ.

5
ironfroggy

Vous pouvez laisser un context manager faire la conversion pour vous, à l’intérieur d’un bloc with (qui peut contenir plusieurs assertions, ou plusieurs appels de code et de fonction ou ce que vous voulez.

from __future__ import with_statement
import contextlib

@contextlib.contextmanager
def myassert(exctype):
    try:
        yield
    except AssertionError, exc:
        raise exctype(*exc.args)

with myassert(ValueError):
    assert 0, "Zero is bad for you"

Voir une version précédente de cette réponse pour substituer directement des objets d'exception construits (KeyError("bad key")) au lieu de réutiliser les arguments des assertions.

4
u0b34a0f6ae

Pour voir si j'essaie d'avoir des frais généraux, j'ai essayé cette expérience

voici myassert.py


def myassert(e):
    raise e

def f1(): #this is the control for the experiment cond=True

def f2(): cond=True try: assert cond, "Message" except AssertionError, e: raise Exception(e.args)

def f3(): cond=True assert cond or myassert(RuntimeError)

def f4(): cond=True if __debug__: raise(RuntimeError)


$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f1()'
100 loops, best of 1000: 0.42 usec per loop
$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f2()'
100 loops, best of 1000: 0.479 usec per loop
$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f3()'
100 loops, best of 1000: 0.42 usec per loop
$ python -O -mtimeit -n100 -r1000 -s'import myassert' 'myassert.f4()'
100 loops, best of 1000: 0.42 usec per loop

3
John La Rooy

En Python 2.6.3 au moins, cela fonctionnera aussi:

class MyAssertionError (Exception):
    pass

AssertionError = MyAssertionError

assert False, "False"

Traceback (most recent call last):
  File "assert.py", line 8, in <module>
    assert False, "False"
__main__.MyAssertionError: False
3
Ber

Si vous voulez utiliser des assertions pour cela, cela semble plutôt bien fonctionner:

>>> def raise_(e): raise e
...
>>> x = -2
>>> assert x >= 0, raise_(ValueError('oops'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in raise_
ValueError: oops

Notez que dans l'assertion, les éléments figurant après la virgule ne seront évalués que si la condition est fausse. Par conséquent, la variable ValueError ne sera créée et levée que lorsque cela sera nécessaire.

0
uryga