Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

monkeypatch.setattr fails to undo on objects with a custom __setattr__ (regression from #14969)

Open Beginner friendly
#15,099 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

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

Research direction

Start at the new branch in MonkeyPatch.setattr and compare it with the previous behavior described in the issue. Add a regression case to test_proxy.py using the custom setattr/getattr object, then run that test to verify undo restores the original value and the patched value does not leak into later tests.

Written by the indexing model from the issue text.

Description

Since #14969 (0c601d510, not released yet), monkeypatch.setattr doesn't restore attributes on objects that store them somewhere other than __dict__ through a custom __setattr__/__getattr__. Undo raises AttributeError, and the patched value leaks into later tests.

class Config:
    """Stores attributes in a private dict instead of __dict__."""
    def __init__(self):
        object.__setattr__(self, "_data", {"debug": False})
    def __getattr__(self, name):
        try:
            return self._data[name]
        except KeyError:
            raise AttributeError(name) from None
    def __setattr__(self, name, value):
        self._data[name] = value

cfg = Config()

def test_patch(monkeypatch):
    monkeypatch.setattr(cfg, "debug", True)
    assert cfg.debug is True

def test_restored():
    assert cfg.debug is False

On main:

ERROR test_proxy.py::test_patch - AttributeError: 'Config' object has no attribute 'debug'
FAILED test_proxy.py::test_restored - assert True is False
1 failed, 1 passed, 1 error

Just before 0c601d510, the same file passes (2 passed).

The new branch in MonkeyPatch.setattr takes the old value from target.__dict__.get(name, NOTSET) whenever target has a __dict__. It assumes that a plain setattr() writes into that dict. With a custom __setattr__ it doesn't, so the old value is recorded as NOTSET. undo() then calls delattr(cfg, "debug") instead of setting it back to False. That delattr fails, and the value stays patched.

@Shriprasad-P pointed out this exact case in a review on #14969, but it wasn't addressed before the merge. I'm opening this issue so it gets fixed before the next release.

One possible direction: only use the __dict__ lookup when name is actually present in the instance __dict__, or when the type uses object.__setattr__. Otherwise fall back to the value from getattr(), as before.

pytest 9.2.0.dev345+g872117358, Python 3.13.5, Windows 11.

Dominant language
Python
Stars
14.6k
Forks
3.4k
Avg merge
2d 12h
Merged PRs (30d)
37

Getting set up

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.

More from pytest-dev/pytest

All issues in pytest-dev/pytest

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.