358

I am trying to raise a Warning in Python without making the program crash / stop / interrupt.

I use the following simple function to check if the user passed a non-zero number to it. If so, the program should warn them, but continue as per normal. It should work like the code below, but should use class Warning(), Error() or Exception() instead of printing the warning out manually.

def is_zero(i):
   if i != 0:
     print "OK"
   else:
     print "WARNING: the input is 0!"
   return i

If I use the code below and pass 0 to the function, the program crashes and the value is never returned. Instead, I want the program to continue normally and just inform the user that he passed 0 to the function.

def is_zero(i):
   if i != 0:
     print "OK"
   else:
     raise Warning("the input is 0!")
   return i

I want to be able to test that a warning has been thrown testing it by unittest. If I simply print the message out, I am not able to test it with assertRaises in unittest.

pfabri
  • 885
  • 1
  • 9
  • 25
Tomas Novotny
  • 7,547
  • 9
  • 26
  • 23
  • 1
    How exactly do you want to notify the user? through email or SMS? cause that can be hooked up but you need to be specific. – aaronasterling Oct 08 '10 at 15:05
  • 2
    Why don't you just `print` the message? – sje397 Oct 08 '10 at 15:06
  • 5
    @sje397 The point is that I want to be able to test that a warning has been thrown testing it by unittest. If I simply print the message out, I am not able to do that with assertRaises in unittest. – Tomas Novotny Oct 08 '10 at 15:16

4 Answers4

531
import warnings
warnings.warn("Warning...........Message")

See the python documentation: here

necromancer
  • 6,477
  • 5
  • 18
  • 13
  • 40
    And `warnings.warn("blabla", DeprecationWarning)` for adding a class to the kind of warning being issued – Shadi Dec 30 '19 at 12:04
249

You shouldn't raise the warning, you should be using warnings module. By raising it you're generating error, rather than warning.

Mike
  • 19,114
  • 12
  • 59
  • 91
SilentGhost
  • 307,395
  • 66
  • 306
  • 293
  • 1
    Thank you very much. And how then do I test that the Warning has been thrown using unittest? I cannot use assertRaises() anymore. – Tomas Novotny Oct 08 '10 at 15:14
  • @Tomas Novotny you can capture stdout and stderr, then check that the strings issued by your warning are found within. – wheaties Oct 08 '10 at 15:33
  • 25
    @Tomas: I never heard of desire to test for warning, but there is available a [`warnings.catch_warnings`](http://docs.python.org/library/warnings.html#warnings.catch_warnings) context manager that will let you do this. – SilentGhost Oct 08 '10 at 15:33
  • 1
    @SilentGhost If it's in your code, you want to test it, no? https://docs.pytest.org/en/6.2.x/warnings.html#warns (though to be fair that comment is over 10 years old) – eric Dec 02 '21 at 17:58
  • 1
    This doesn't explain what to actually do. [This](https://stackoverflow.com/a/3891890/913098) should be the accepted answer. – Gulzar Apr 28 '22 at 07:29
111

By default, unlike an exception, a warning doesn't interrupt.

After import warnings, it is possible to specify a Warnings class when generating a warning. If one is not specified, it is literally UserWarning by default.

>>> warnings.warn('This is a default warning.')
<string>:1: UserWarning: This is a default warning.

To simply use a preexisting class instead, e.g. DeprecationWarning:

>>> warnings.warn('This is a particular warning.', DeprecationWarning)
<string>:1: DeprecationWarning: This is a particular warning.

Creating a custom warning class is similar to creating a custom exception class:

>>> class MyCustomWarning(UserWarning):
...     pass
... 
... warnings.warn('This is my custom warning.', MyCustomWarning)

<string>:1: MyCustomWarning: This is my custom warning.

For testing, consider assertWarns or assertWarnsRegex.


As an alternative, especially for standalone applications, consider the logging module. It can log messages having a level of debug, info, warning, error, etc. Log messages having a level of warning or higher are by default printed to stderr.

Asclepius
  • 57,944
  • 17
  • 167
  • 143
6

Just a small demonstration snippet

I was a bit puzzled how to use warnings.warn, so I provide here a short demo. Note how print("code running after warning") is executed / not executed after warnings.warn / raise UserWarning. Also warnings.warn prints the warning message only once.

>>> import warnings
>>> 
>>> warnings.warn("test warning"); print("code running after warning")
<stdin>:1: UserWarning: test warning
code running after warning
>>> 
>>> raise UserWarning('test warning'); print("code running after warning")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UserWarning: test warning
>>> 
>>> warnings.warn("test warning"); print("code running after warning")
code running after warning
Markus Dutschke
  • 9,341
  • 4
  • 63
  • 58