Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conflict between Pandas and Unittest?

Consider the following block of code (developed inside a Jupyter notebook), for which it is expected that an AssertionError is raised because a UserWarning is not triggered:

%%writefile Game/tests/tests.py
import unittest
import pandas as pd

class TestGame(unittest.TestCase):
    def test_getters(self):
        print('Just before the critical line.')
        with self.assertWarns(UserWarning):
            print('Just testing...')

suite = unittest.TestLoader().loadTestsFromTestCase(TestGame)
unittest.TextTestRunner().run(suite)

For those unfamiliar with jupyter notebooks, the first line simply exports all following lines into the specified file.

Now if I execute the command:

python3 tests.py

from the terminal (I am using Python 3.5.1 on Ubuntu 14.04), I get a Runtime Error - the stack trace follows:

Just before the critical line:
E
======================================================================
ERROR: test_getters (__main__.TestGame)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests.py", line 8, in test_getters
    with self.assertWarns(UserWarning):
  File "/opt/anaconda3/lib/python3.5/unittest/case.py", line 225, in __enter__
    for v in sys.modules.values():
RuntimeError: dictionary changed size during iteration

----------------------------------------------------------------------
Ran 1 test in 0.004s

FAILED (errors=1)

Obviously the results are not as expected. However, I notice that either of the following options gets the expected results.

  1. Commenting out the first line with %%writefile ... and running the code snippet with the Jupyter notebook (which uses the same python interpreter).
  2. Commenting out the import pandas as pd line and running from terminal with the previously given command.

Does anyone understand what's going on here?

For reference, the relevant lines in case.py in the unittest module are

for v in sys.modules.values():
    if getattr(v, '__warningregistry__', None):
        v.__warningregistry__ = {}

which would seem to be benign code (which I would also presume is tested enough to say that it's not the source of the problem).

like image 308
Charlie Avatar asked Aug 03 '16 22:08

Charlie


Video Answer


1 Answers

This bug has been filed on Python's bug tracker.

The problem is in the iteration over sys.modules in unittest.case._AssertWarnsContext.__enter__() and accessing every module's __warningregistry__ attribute. On this access, the module-like objects may perform arbitrary actions including importing of further modules which extends sys.modules.

sys.modules is a dict mapping module names to loaded modules.

This problem is difficult to reproduce because there's "something platform dependent" about how the test runner orders the tests.

like image 91
hongsy Avatar answered Oct 24 '22 18:10

hongsy