Test MonkeyPatch.setattr/delattr descriptor checking and short-circuiting

This commit is contained in:
Ganden Schaffner 2023-01-09 00:00:00 -08:00
parent 1b53b0083b
commit d5f04b526b
No known key found for this signature in database
GPG Key ID: AAF2420F20D8B553
1 changed files with 38 additions and 2 deletions

View File

@ -5,6 +5,7 @@ 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 NoReturn
from typing import Optional from typing import Optional
from typing import overload from typing import overload
from typing import Type from typing import Type
@ -30,6 +31,21 @@ class SomeDescriptor:
return 1 return 1
class RaisingDescriptor:
@overload
def __get__(self, instance: None, owner: type) -> NoReturn:
...
@overload
def __get__(self, instance: T, owner: Optional[Type[T]] = ...) -> NoReturn:
...
def __get__(
self, instance: Optional[T], owner: Optional[Type[T]] = None
) -> NoReturn:
assert False, "descriptor was bound"
@pytest.fixture @pytest.fixture
def mp() -> Generator[MonkeyPatch, None, None]: def mp() -> Generator[MonkeyPatch, None, None]:
cwd = os.getcwd() cwd = os.getcwd()
@ -45,6 +61,7 @@ def test_setattr() -> None:
class A: class A:
x = 1 x = 1
y = SomeDescriptor() y = SomeDescriptor()
z = RaisingDescriptor()
monkeypatch = MonkeyPatch() monkeypatch = MonkeyPatch()
pytest.raises(AttributeError, monkeypatch.setattr, A, "notexists", 2) pytest.raises(AttributeError, monkeypatch.setattr, A, "notexists", 2)
@ -77,15 +94,24 @@ def test_setattr() -> None:
monkeypatch.undo() monkeypatch.undo()
assert "x" not in vars(a) 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()): for obj in (A, A()):
# Test that class/instance descriptors don't get bound and written into the
# target's dictionary.
monkeypatch = MonkeyPatch() monkeypatch = MonkeyPatch()
assert isinstance(inspect.getattr_static(obj, "y"), SomeDescriptor) assert isinstance(inspect.getattr_static(obj, "y"), SomeDescriptor)
monkeypatch.setattr(obj, "y", 2) monkeypatch.setattr(obj, "y", 2)
monkeypatch.undo() monkeypatch.undo()
assert isinstance(inspect.getattr_static(obj, "y"), SomeDescriptor) assert isinstance(inspect.getattr_static(obj, "y"), SomeDescriptor)
# Test that the `raising=True` check binds descriptors (to check if they raise
# AttributeError).
monkeypatch = MonkeyPatch()
with pytest.raises(AssertionError, match="descriptor was bound"):
monkeypatch.setattr(obj, "z", 2)
# Test that descriptors don't get bound if `raising=False`.
monkeypatch.setattr(obj, "z", 2, raising=False)
monkeypatch.undo()
class TestSetattrWithImportPath: class TestSetattrWithImportPath:
def test_string_expression(self, monkeypatch: MonkeyPatch) -> None: def test_string_expression(self, monkeypatch: MonkeyPatch) -> None:
@ -140,6 +166,7 @@ def test_delattr() -> None:
class A: class A:
x = 1 x = 1
y = SomeDescriptor() y = SomeDescriptor()
z = RaisingDescriptor()
monkeypatch = MonkeyPatch() monkeypatch = MonkeyPatch()
monkeypatch.delattr(A, "x") monkeypatch.delattr(A, "x")
@ -164,6 +191,15 @@ def test_delattr() -> None:
monkeypatch.undo() monkeypatch.undo()
assert isinstance(inspect.getattr_static(A, "y"), SomeDescriptor) assert isinstance(inspect.getattr_static(A, "y"), SomeDescriptor)
# Test that the `raising=True` check binds descriptors (to check if they raise
# AttributeError).
monkeypatch = MonkeyPatch()
with pytest.raises(AssertionError, match="descriptor was bound"):
monkeypatch.delattr(A, "z")
# Test that descriptor's don't get bound if `raising=False`.
monkeypatch.delattr(A, "z", raising=False)
monkeypatch.undo()
def test_setitem() -> None: def test_setitem() -> None:
d = {"x": 1} d = {"x": 1}