Test for MonkeyPatch.setattr/delattr polluting vars(target)
This commit is contained in:
parent
3ad4344656
commit
b1b4469ae6
|
@ -5,12 +5,30 @@ import textwrap
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
|
from typing import Optional
|
||||||
|
from typing import overload
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.monkeypatch import MonkeyPatch
|
from _pytest.monkeypatch import MonkeyPatch
|
||||||
from _pytest.pytester import Pytester
|
from _pytest.pytester import Pytester
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
|
class SomeDescriptor:
|
||||||
|
@overload
|
||||||
|
def __get__(self, instance: None, owner: type) -> int:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def __get__(self, instance: T, owner: Optional[Type[T]] = ...) -> int:
|
||||||
|
...
|
||||||
|
|
||||||
|
def __get__(self, instance: Optional[T], owner: Optional[Type[T]] = None) -> int:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mp() -> Generator[MonkeyPatch, None, None]:
|
def mp() -> Generator[MonkeyPatch, None, None]:
|
||||||
|
@ -22,15 +40,21 @@ def mp() -> Generator[MonkeyPatch, None, None]:
|
||||||
|
|
||||||
|
|
||||||
def test_setattr() -> None:
|
def test_setattr() -> None:
|
||||||
|
import inspect
|
||||||
|
|
||||||
class A:
|
class A:
|
||||||
x = 1
|
x = 1
|
||||||
|
y = SomeDescriptor()
|
||||||
|
|
||||||
monkeypatch = MonkeyPatch()
|
monkeypatch = MonkeyPatch()
|
||||||
pytest.raises(AttributeError, monkeypatch.setattr, A, "notexists", 2)
|
pytest.raises(AttributeError, monkeypatch.setattr, A, "notexists", 2)
|
||||||
monkeypatch.setattr(A, "y", 2, raising=False)
|
monkeypatch.setattr(A, "w", 2, raising=False)
|
||||||
assert A.y == 2 # type: ignore
|
assert A.w == 2 # type: ignore
|
||||||
monkeypatch.undo()
|
monkeypatch.undo()
|
||||||
assert not hasattr(A, "y")
|
assert not hasattr(A, "w")
|
||||||
|
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
monkeypatch.setattr(A, "w") # type: ignore[call-overload]
|
||||||
|
|
||||||
monkeypatch = MonkeyPatch()
|
monkeypatch = MonkeyPatch()
|
||||||
monkeypatch.setattr(A, "x", 2)
|
monkeypatch.setattr(A, "x", 2)
|
||||||
|
@ -44,8 +68,23 @@ def test_setattr() -> None:
|
||||||
monkeypatch.undo() # double-undo makes no modification
|
monkeypatch.undo() # double-undo makes no modification
|
||||||
assert A.x == 5
|
assert A.x == 5
|
||||||
|
|
||||||
with pytest.raises(TypeError):
|
# Test that inherited attributes don't get written into the target's instance
|
||||||
monkeypatch.setattr(A, "y") # type: ignore[call-overload]
|
# dictionary.
|
||||||
|
a = A()
|
||||||
|
monkeypatch = MonkeyPatch()
|
||||||
|
assert "x" not in vars(a)
|
||||||
|
monkeypatch.setattr(a, "x", 2)
|
||||||
|
monkeypatch.undo()
|
||||||
|
assert "x" not in vars(a)
|
||||||
|
|
||||||
|
# Test that class/instance descriptors don't get bound and written into the target's
|
||||||
|
# dictionary.
|
||||||
|
for obj in (A, A()):
|
||||||
|
monkeypatch = MonkeyPatch()
|
||||||
|
assert isinstance(inspect.getattr_static(obj, "y"), SomeDescriptor)
|
||||||
|
monkeypatch.setattr(obj, "y", 2)
|
||||||
|
monkeypatch.undo()
|
||||||
|
assert isinstance(inspect.getattr_static(obj, "y"), SomeDescriptor)
|
||||||
|
|
||||||
|
|
||||||
class TestSetattrWithImportPath:
|
class TestSetattrWithImportPath:
|
||||||
|
@ -96,8 +135,11 @@ class TestSetattrWithImportPath:
|
||||||
|
|
||||||
|
|
||||||
def test_delattr() -> None:
|
def test_delattr() -> None:
|
||||||
|
import inspect
|
||||||
|
|
||||||
class A:
|
class A:
|
||||||
x = 1
|
x = 1
|
||||||
|
y = SomeDescriptor()
|
||||||
|
|
||||||
monkeypatch = MonkeyPatch()
|
monkeypatch = MonkeyPatch()
|
||||||
monkeypatch.delattr(A, "x")
|
monkeypatch.delattr(A, "x")
|
||||||
|
@ -106,14 +148,22 @@ def test_delattr() -> None:
|
||||||
assert A.x == 1
|
assert A.x == 1
|
||||||
|
|
||||||
monkeypatch = MonkeyPatch()
|
monkeypatch = MonkeyPatch()
|
||||||
|
pytest.raises(AttributeError, monkeypatch.delattr, A, "w")
|
||||||
|
monkeypatch.delattr(A, "w", raising=False)
|
||||||
monkeypatch.delattr(A, "x")
|
monkeypatch.delattr(A, "x")
|
||||||
pytest.raises(AttributeError, monkeypatch.delattr, A, "y")
|
|
||||||
monkeypatch.delattr(A, "y", raising=False)
|
|
||||||
monkeypatch.setattr(A, "x", 5, raising=False)
|
monkeypatch.setattr(A, "x", 5, raising=False)
|
||||||
assert A.x == 5
|
assert A.x == 5
|
||||||
monkeypatch.undo()
|
monkeypatch.undo()
|
||||||
assert A.x == 1
|
assert A.x == 1
|
||||||
|
|
||||||
|
# Test that (non-inherited) class descriptors don't get bound and written into the
|
||||||
|
# target's dictionary.
|
||||||
|
monkeypatch = MonkeyPatch()
|
||||||
|
assert isinstance(inspect.getattr_static(A, "y"), SomeDescriptor)
|
||||||
|
monkeypatch.delattr(A, "y")
|
||||||
|
monkeypatch.undo()
|
||||||
|
assert isinstance(inspect.getattr_static(A, "y"), SomeDescriptor)
|
||||||
|
|
||||||
|
|
||||||
def test_setitem() -> None:
|
def test_setitem() -> None:
|
||||||
d = {"x": 1}
|
d = {"x": 1}
|
||||||
|
|
Loading…
Reference in New Issue