Test MonkeyPatch.setattr/delattr descriptor checking and short-circuiting
This commit is contained in:
parent
1b53b0083b
commit
d5f04b526b
|
@ -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}
|
||||||
|
|
Loading…
Reference in New Issue