pytest-dev / pytest-dev/pytest

monkeypatch class bases fails with TypeError

Open
#1,938 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

plugin: monkeypatch type: bug
Dominant language
Python
Stars
14.5k
Forks
3.4k
Avg merge
2d 9h
Merged PRs (30d)
35

Description

In one of our test suites, I'm trying to patch a class' __bases__ (to demonstrate alternate but unexposed behavior), but when I do, monkeypatch fails on the teardown stage with a TypeError:

$ cat test-monkeypatch.py
import pytest


class Loud:
    def thing(self):
        return '!!!!'

class Quiet:
    def thing(self):
        return 'sssh...'


class ThingToTest(Loud):
    pass


class TestThing:
    @pytest.fixture(autouse=True)
    def make_thing_quiet(self, monkeypatch):
        new_bases = (
            Quiet,
        ) + ThingToTest.__bases__[1:]
        monkeypatch.setattr(ThingToTest, '__bases__', new_bases)

    def test_thing_is_quiet(self):
        assert ThingToTest().thing() == 'sssh...'
$ python -m rwt pytest -- -m pytest test-monkeypatch.py
Loading requirements using pytest
====================================== test session starts =======================================
platform darwin -- Python 3.6.0b1, pytest-3.0.2, py-1.4.31, pluggy-0.3.1
rootdir: /Users/jaraco/Dropbox/code/yg/gryphon.cases, inifile: pytest.ini
collected 1 items 

test-monkeypatch.py .E

============================================= ERRORS =============================================
_______________________ ERROR at teardown of TestThing.test_thing_is_quiet _______________________

self = <_pytest.monkeypatch.MonkeyPatch object at 0x1039610b8>

    def undo(self):
        """ Undo previous changes.  This call consumes the
            undo stack. Calling it a second time has no effect unless
            you do more monkeypatching after the undo call.

            There is generally no need to call `undo()`, since it is
            called automatically during tear-down.

            Note that the same `monkeypatch` fixture is used across a
            single test function invocation. If `monkeypatch` is used both by
            the test function itself and one of the test fixtures,
            calling `undo()` will undo all of the changes made in
            both functions.
            """
        for obj, name, value in reversed(self._setattr):
            if value is not notset:
                setattr(obj, name, value)
            else:
>               delattr(obj, name)
E               TypeError: can't delete ThingToTest.__bases__

/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/rwt-vduaqgcq/_pytest/monkeypatch.py:241: TypeError
=============================== 1 passed, 1 error in 0.08 seconds ================================

The same error happens on Python 3.5.2.

I have to monkeypatch the class' __bases__ because the target class (ThingToTest in this example) is already the base for other classes, and I want to avoid reconstructing all of those (plus the fixture which is already an instance of the class).

What's odd is that I'm not setting raises to True, so I would expect it to raise an exception if the class didn't have the __bases__ attribute. And when I invoke getattr or hasattr on the class, it does have the expected attribute.

I'll dig into the monkeypatch implementation to see if I can discover the cause.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the failure with test-monkeypatch.py, then inspect pytest's monkeypatch implementation around the undo method and the setattr handling shown at monkeypatch.py line 241. Check how bases is recorded and restored; done means the test still passes and fixture teardown completes without the TypeError.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
testing-qa
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.