gabrielfalcao / HTTPretty

Intercept HTTP requests at the Python socket level. Fakes the whole socket module

Home Page:https://httpretty.readthedocs.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Version 1.1.1 causes Segfault if run with pytest-mypy on large codebase

gleusch opened this issue · comments

I'm using httpretty with pytest and pytest-mypy on a larger codebase (~ 404 tests, 97 times @httpretty.httprettified).

When running pytest --mypy on this code base, I am getting a Segmentation Fault after ~49% of all tests passed:

tests/tagger/test_sentence_tagger_factory.py .........                                                                                                                                                                                                                                                               [ 49%]
tests/tagger/test_tagger_factory.py ......FFatal Python error: Segmentation fault

Thread 0x000070000ed4b000 (most recent call first):
  File "/usr/local/Caskroom/miniconda/base/envs/[PROJECT]/lib/python3.8/threading.py", line 306 in wait
  File "/usr/local/Caskroom/miniconda/base/envs/[PROJECT]/lib/python3.8/threading.py", line 558 in wait
  File "/usr/local/Caskroom/miniconda/base/envs/[PROJECT]/lib/python3.8/site-packages/tqdm/_monitor.py", line 60 in run
  File "/usr/local/Caskroom/miniconda/base/envs/[PROJECT]/lib/python3.8/threading.py", line 932 in _bootstrap_inner
  File "/usr/local/Caskroom/miniconda/base/envs/[PROJECT]/lib/python3.8/threading.py", line 890 in _bootstrap

Current thread 0x000000011cd9edc0 (most recent call first):
  File "/usr/local/Caskroom/miniconda/base/envs/[PROJECT]/lib/python3.8/site-packages/httpretty/core.py", line 385 in __getattr__
  File "/usr/local/Caskroom/miniconda/base/envs/[PROJECT]/lib/python3.8/site-packages/httpretty/core.py", line 385 in __getattr__
[…line repeats 76 times in total…]
  File "/usr/local/Caskroom/miniconda/base/envs/[PROJECT]/lib/python3.8/site-packages/httpretty/core.py", line 385 in __getattr__
  ...
Segmentation fault: 11

With version 1.1.0, everything is fine.

Looking into the changes, and finding that said line 385 in core.py is

    def __getattr__(self, name):
        return getattr(self.file, name)

,

I tried undoing this change 1.1.0...1.1.1#diff-326a996e75c88c9fc8dabbfdca5029c2a409fac6a2dd9b7dffa9f3726bce7c55L367,

i.e. changed it back to

    def close(self):
        self.socket.close()
        self.file.close()

and lo, the segfault no longer happens; all tests pass.

Given that this only seems to appear quite a couple of unit tests in, into a larger test suite (selecting only the class in question with pytest --mypy -k test_tagger_factory does not reproduce the issue), I haven't been able to construct an artificial test case yet -- I suppose it would look, at the minimum, somethink like

class TestTaggerStubCreator(unittest.TestCase):
    @httpretty.httprettified(allow_net_connect=False)
    def test_001(self):
        pass

    @httpretty.httprettified(allow_net_connect=False)
    def test_002(self):
        pass

    # ...

        @httpretty.httprettified(allow_net_connect=False)
    def test_100(self):
        pass

possibly with some more tests going on in each test.

Please let me know if you want me to construct such a test file.

Thanks for the report @gleusch I'll get this fixed for the next release. Will keep you posted.

Awesome! Thank you for writing and maintaining such an extremely useful tool!

@gleusch I was able to reproduce the bug locally with this code:

class TestBug426MypySegfaultWithEmptyMethod(unittest.TestCase, metaclass=GenerateTests):
__generate_count__ = 1000
def __generate_method__(test_name):
@httpretty.httprettified(allow_net_connect=False)
def test_func(self):
pass
return test_func

which uses a metaclass to generate the tests when pytests is scanning the file:

class GenerateTests(type):
def __init__(cls, name, bases, attrs):
if name in ('GenerateTestMeta',):
return
count = getattr(cls, '__generate_count__', attrs.get('__generate_count__')) or 100
generate_method = getattr(cls, '__generate_method__', attrs.get('__generate_method__'))
if not generate_method:
raise SyntaxError(f'Metaclass requires def `__generate_method__(test_name):` to be implemented')
for x in range(count):
test_name = "test_{}".format(x)
def test_func(self, *args, **kwargs):
run_test = generate_method(test_name)
run_test(self, *args, **kwargs)
test_func.__name__ = test_name
attrs[test_name] = test_func
setattr(cls, test_name, test_func)

Now the challenge is to fix the bug in #428

@gleusch the new release 1.1.2 contains the changes from #428
Please let me know if the problem persists.

@gabrielfalcao Indeed, it does fix my problem. Thanks a ton for the super quick response!