Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert Python 3 ResourceWarnings into exception

Is there a way to force a Python 3 unittest to fail, rather than simply print a warning to stderr, if it causes any ResourceWarning?

I've tried the following:

import warnings
warnings.simplefilter(action='error', category=ResourceWarning)

Which results in this output from unittest:

my_test (__main__.MyTest) ... Exception ignored in: <socket.socket fd=9, family=AddressFamily.AF_INET, type=SocketType.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 54065), raddr=('127.0.0.1', 27017)>
ResourceWarning: unclosed <socket.socket fd=9, family=AddressFamily.AF_INET, type=SocketType.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 54065), raddr=('127.0.0.1', 27017)>
ok

----------------------------------------------------------------------
Ran 1 test in 0.110s

Note the "Exception ignored" message. I'd rather the test failed, instead of requiring me to read its output looking for ResourceWarnings.

like image 238
A. Jesse Jiryu Davis Avatar asked Jul 12 '14 20:07

A. Jesse Jiryu Davis


People also ask

Can a Python program deliberately cause its own exception?

anywhere in your code for a run time error to occur, specifically in this case a ZeroDivisionError: integer division or modulo by zero . This is the simplest way to get an exception by embedding something in your code (as you mentioned in your post). You can of course raise your own Exceptions too ..

How do you hide errors in Python?

How do you hide errors in Python? Use the filterwarnings() Function to Suppress Warnings in Python. Use the -Wignore Option to Suppress Warnings in Python. Use the PYTHONWARNINGS Environment Variable to Suppress Warnings in Python.

What is str e in Python?

In Python 3. x, str(e) should be able to convert any Exception to a string, even if it contains Unicode characters.


2 Answers

Here's a unit test that fails if ResourceWarning is generated by the code in with catch_warning() statement:

#!/usr/bin/env python3
import gc
import socket
import unittest
import warnings

class Test(unittest.TestCase):
    def test_resource_warning(self):
        s = socket.socket()
        ####s.close() #XXX uncomment to pass the test

        # generate resource warning when s is deleted
        with warnings.catch_warnings(record=True) as w:
            warnings.resetwarnings() # clear all filters
            warnings.simplefilter('ignore') # ignore all
            warnings.simplefilter('always', ResourceWarning) # add filter
            del s        # remove reference
            gc.collect() # run garbage collection (for pypy3)
            self.assertFalse(w and str(w[-1])) # test fails if there
                                               # are warnings

if __name__=="__main__":
    unittest.main()
like image 143
jfs Avatar answered Oct 11 '22 19:10

jfs


Unfortunately, it doesn't appear to be possible. That "Exception ignored in:" message is produced by the CPython function PyErr_WriteUnraisable in Python/errors.c. The comment before that function states:

/* Call when an exception has occurred but there is no way for Python
   to handle it.  Examples: exception in __del__ or during GC. */

The ResourceWarning is indeed being generated during garbage collection, and Python prints a message because it doesn't have a way to raise an exception at that point. This has to do with the core CPython implementation and there is nothing that unittest can do to override it.

Update: while the above is a correct technical analysis, there is another approach that will actually solve the OP's problem. See the answer by J.F. Sebastian for more details.

like image 21
Daniel Harding Avatar answered Oct 11 '22 17:10

Daniel Harding