1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
import sys
from collections.abc import Callable
from operator import not_
from typing import TYPE_CHECKING, Any, Protocol, TypeVar
import pytest
from propcache.api import cached_property
if sys.version_info >= (3, 11):
from typing import assert_type
_T_co = TypeVar("_T_co", covariant=True)
class APIProtocol(Protocol):
def cached_property(
self, func: Callable[[Any], _T_co]
) -> cached_property[_T_co]: ...
def test_cached_property(propcache_module: APIProtocol) -> None:
class A:
def __init__(self) -> None:
"""Init."""
@propcache_module.cached_property
def prop(self) -> int:
return 1
a = A()
if sys.version_info >= (3, 11):
assert_type(a.prop, int)
assert a.prop == 1
def test_cached_property_without_cache(propcache_module: APIProtocol) -> None:
class A:
__slots__ = ()
def __init__(self) -> None:
pass
@propcache_module.cached_property
def prop(self) -> None:
"""Mock property."""
a = A()
with pytest.raises(AttributeError):
a.prop = 123 # type: ignore[assignment]
def test_cached_property_check_without_cache(propcache_module: APIProtocol) -> None:
class A:
__slots__ = ()
def __init__(self) -> None:
"""Init."""
@propcache_module.cached_property
def prop(self) -> None:
"""Mock property."""
a = A()
with pytest.raises((TypeError, AttributeError)):
assert a.prop == 1
def test_cached_property_caching(propcache_module: APIProtocol) -> None:
class A:
def __init__(self) -> None:
"""Init."""
@propcache_module.cached_property
def prop(self) -> int:
"""Docstring."""
return 1
a = A()
assert a.prop == 1
def test_cached_property_class_docstring(propcache_module: APIProtocol) -> None:
class A:
def __init__(self) -> None:
"""Init."""
@propcache_module.cached_property
def prop(self) -> None:
"""Docstring."""
if TYPE_CHECKING:
assert isinstance(A.prop, cached_property)
else:
assert isinstance(A.prop, propcache_module.cached_property)
assert "Docstring." == A.prop.__doc__
def test_set_name(propcache_module: APIProtocol) -> None:
"""Test that the __set_name__ method is called and checked."""
class A:
@propcache_module.cached_property
def prop(self) -> None:
"""Docstring."""
A.prop.__set_name__(A, "prop")
match = r"Cannot assign the same cached_property to two "
with pytest.raises(TypeError, match=match):
A.prop.__set_name__(A, "something_else")
def test_get_without_set_name(propcache_module: APIProtocol) -> None:
"""Test that get without __set_name__ fails."""
cp = propcache_module.cached_property(not_)
class A:
"""A class."""
A.cp = cp # type: ignore[attr-defined]
match = r"Cannot use cached_property instance "
with pytest.raises(TypeError, match=match):
_ = A().cp # type: ignore[attr-defined]
def test_ensured_wrapped_function_is_accessible(propcache_module: APIProtocol) -> None:
"""Test that the wrapped function can be accessed from python."""
class A:
def __init__(self) -> None:
"""Init."""
@propcache_module.cached_property
def prop(self) -> int:
"""Docstring."""
return 1
a = A()
assert A.prop.func(a) == 1
|