monkeypatch.setattr fails to undo on objects with a custom __setattr__ (regression from #14969)
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
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from pytest-dev/pytest
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
pytest-dev/pytest#14514 · 6 comments ·
Maintainers usually reply within 1 day
-
type: enhancement type: feature-branch
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
pytest-dev/pytest#14186 · 2 comments ·
Maintainers usually reply within 1 day
-
type: docs
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
pytest-dev/pytest#9825 · 7 comments ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 35/100
pytest-dev/pytest#15097 · 1 comment ·
Maintainers usually reply within 1 day
-
topic: reporting topic: tracebacks type: proposal
Difficulty 5/5 Over a week Newbie friendliness 25/100
pytest-dev/pytest#15072 ·
Maintainers usually reply within 1 day
All issues in pytest-dev/pytest
Similar issues
-
bug frontend
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
PedestrianDynamics/pyFDS-Evac#552 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
resend/resend-skills#144 ·
Maintainers usually reply within 1 day
-
good first issue
Difficulty 1/5 Under an hour Newbie friendliness 68/100
-
Bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
GNS3/gns3-server#2935 · 1 comment ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
TauricResearch/TradingAgents#1476 ·
Maintainers usually reply within 2 days