python / python/cpython

patch does not correctly restore state on objects without __dict__

Open
#126,886 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

stdlib type-bug
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Bug report

Bug description:
from unittest.mock import patch
class Proxy(object):
    config = {}
    def __getattr__(self, name):
        return self.config[name]
    def __setattr__(self, name, value):
        if name == 'config':
            object.__setattr__(self, name, value)
            return
        self.config[name] = value
    def __delattr__(self, name):
        self.config[name] = 'DEFAULT'

p = Proxy()
p.a = True
print(p.a)
with patch('__main__.p.a', False):
    print(p.a)
print(p.a)

Expected

True
False
True

Actual

True
False
DEFAULT

Note that this is a relatively niche issue, but for example in a config system (i.e. https://github.com/pytorch/pytorch/pull/140779), if you don't want to have a delete work, (i.e. have it set it back to a default value), the code in https://github.com/python/cpython/blob/94a7a4e22fb8f567090514785c69e65298acca42/Lib/unittest/mock.py#L1637 will call delattr, and then not call setattr, because it still has the attr.

Possible fixes:
If delattr throws (maybe NotImplementederror), call setattr instead (this is nice and explicit + removes the hacky workaround I have to do).

If the value after it's been deleted doesn't equal the original value, reassign it? (this might require objects to be comparable, not sure if that is already an issue here).

Either way I wanted to see if there was any interest in fixing this, and if anyone saw a solution that seemed reasonable enough.

CPython versions tested on:

3.12

Operating systems tested on:

Linux

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

Start with the reproduction in the issue and inspect the restoration logic at Lib/unittest/mock.py around line 1637. Compare how patch handles attributes on objects without dict when deletion changes the observed value. Done means the example restores p.a to True while preserving existing behavior for ordinary objects, with regression coverage added in the relevant unittest.mock tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.