From fa0688cb4e26149f078b883555bfc47f9b77e0b4 Mon Sep 17 00:00:00 2001 From: Thomas MK Date: Sun, 19 Feb 2023 16:52:46 +0100 Subject: [PATCH 1/4] Add `__set__` to functools.cached_property --- stdlib/functools.pyi | 2 ++ test_cases/stdlib/check_functools.py | 26 ++++++++++++++++++++++++++ tests/stubtest_allowlists/py310.txt | 1 + tests/stubtest_allowlists/py311.txt | 1 + tests/stubtest_allowlists/py38.txt | 1 + tests/stubtest_allowlists/py39.txt | 1 + 6 files changed, 32 insertions(+) create mode 100644 test_cases/stdlib/check_functools.py diff --git a/stdlib/functools.pyi b/stdlib/functools.pyi index 1214e349f605..a0457bc910ce 100644 --- a/stdlib/functools.pyi +++ b/stdlib/functools.pyi @@ -148,6 +148,8 @@ if sys.version_info >= (3, 8): @overload def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... def __set_name__(self, owner: type[Any], name: str) -> None: ... + # __set__ is not defined at runtime, but @cached_property is designed to be settable + def __set__(self, instance: object, value: _T) -> None: ... if sys.version_info >= (3, 9): def __class_getitem__(cls, item: Any) -> GenericAlias: ... diff --git a/test_cases/stdlib/check_functools.py b/test_cases/stdlib/check_functools.py new file mode 100644 index 000000000000..5d9c70115b44 --- /dev/null +++ b/test_cases/stdlib/check_functools.py @@ -0,0 +1,26 @@ +import sys + +if sys.version_info >= (3, 8): + from typing import cast + from typing_extensions import assert_type + from functools import cached_property + + class A: + def __init__(self, x: int): + self.x = x + + @cached_property + def x(self) -> int: + return 0 + + assert_type(A(x=1).x, int) + + class B: + @cached_property + def x(self) -> int: + return 0 + + b = B() + assert_type(b.x, int) + b.x = cast(int, 4) + assert_type(b.x, int) diff --git a/tests/stubtest_allowlists/py310.txt b/tests/stubtest_allowlists/py310.txt index a3c77a1b91e1..00de048a0873 100644 --- a/tests/stubtest_allowlists/py310.txt +++ b/tests/stubtest_allowlists/py310.txt @@ -16,6 +16,7 @@ builtins.property.__set_name__ # Doesn't actually exist contextlib.AbstractAsyncContextManager.__class_getitem__ contextlib.AbstractContextManager.__class_getitem__ fractions.Fraction.__new__ # overload is too complicated for stubtest to resolve +functools.cached_property.__set__ # Stub is a while lie; see comments in the stub gettext.install gettext.translation hmac.new # Stub is a white lie; see comments in the stub diff --git a/tests/stubtest_allowlists/py311.txt b/tests/stubtest_allowlists/py311.txt index 12adfda655ca..2af032c20668 100644 --- a/tests/stubtest_allowlists/py311.txt +++ b/tests/stubtest_allowlists/py311.txt @@ -20,6 +20,7 @@ enum.auto.__init__ enum.auto.value fractions.Fraction.__new__ # overload is too complicated for stubtest to resolve ftplib.FTP.trust_server_pasv_ipv4_address +functools.cached_property.__set__ # Stub is a while lie; see comments in the stub ipaddress.IPv4Interface.hostmask ipaddress.IPv6Interface.hostmask ipaddress._BaseNetwork.broadcast_address diff --git a/tests/stubtest_allowlists/py38.txt b/tests/stubtest_allowlists/py38.txt index 7066216fb76d..94ee60f64036 100644 --- a/tests/stubtest_allowlists/py38.txt +++ b/tests/stubtest_allowlists/py38.txt @@ -46,6 +46,7 @@ dummy_threading.Thread.native_id dummy_threading.local.__new__ fractions.Fraction.__new__ # overload is too complicated for stubtest to resolve ftplib.FTP.trust_server_pasv_ipv4_address # Dangerous to use, intentionally undocumented, intentionally missing from typeshed. #6154 +functools.cached_property.__set__ # Stub is a while lie; see comments in the stub gettext.install # codeset default value is ['unspecified'] so can't be specified gettext.translation # codeset default value is ['unspecified'] so can't be specified hmac.new # Stub is a white lie; see comments in the stub diff --git a/tests/stubtest_allowlists/py39.txt b/tests/stubtest_allowlists/py39.txt index a7937b3b931e..c45ed510d19c 100644 --- a/tests/stubtest_allowlists/py39.txt +++ b/tests/stubtest_allowlists/py39.txt @@ -40,6 +40,7 @@ distutils.core.extension_keywords distutils.core.gen_usage distutils.core.setup_keywords fractions.Fraction.__new__ # overload is too complicated for stubtest to resolve +functools.cached_property.__set__ # Stub is a while lie; see comments in the stub gettext.install gettext.translation hmac.new # Stub is a white lie; see comments in the stub From 9efa9be1c425ad3400efb36ec566fb42e0e44451 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 19 Feb 2023 15:55:42 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks --- test_cases/stdlib/check_functools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_cases/stdlib/check_functools.py b/test_cases/stdlib/check_functools.py index 5d9c70115b44..cff6409240c2 100644 --- a/test_cases/stdlib/check_functools.py +++ b/test_cases/stdlib/check_functools.py @@ -1,14 +1,14 @@ import sys if sys.version_info >= (3, 8): + from functools import cached_property from typing import cast from typing_extensions import assert_type - from functools import cached_property class A: def __init__(self, x: int): self.x = x - + @cached_property def x(self) -> int: return 0 From ab68a0b6b417cb0a0c302724e021289ed049e29f Mon Sep 17 00:00:00 2001 From: Thomas MK Date: Sun, 19 Feb 2023 16:57:37 +0100 Subject: [PATCH 3/4] Add `from __future__ import annotations` to test file --- test_cases/stdlib/check_functools.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test_cases/stdlib/check_functools.py b/test_cases/stdlib/check_functools.py index cff6409240c2..d47884bffff9 100644 --- a/test_cases/stdlib/check_functools.py +++ b/test_cases/stdlib/check_functools.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys if sys.version_info >= (3, 8): From f1e3e8f47fa0a6c803c2dff579e2a83b846f91f9 Mon Sep 17 00:00:00 2001 From: Thomas MK Date: Sun, 19 Feb 2023 18:20:47 +0100 Subject: [PATCH 4/4] Better work-around for pyright's type narrowing --- test_cases/stdlib/check_functools.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test_cases/stdlib/check_functools.py b/test_cases/stdlib/check_functools.py index d47884bffff9..327e60faa07d 100644 --- a/test_cases/stdlib/check_functools.py +++ b/test_cases/stdlib/check_functools.py @@ -4,7 +4,6 @@ if sys.version_info >= (3, 8): from functools import cached_property - from typing import cast from typing_extensions import assert_type class A: @@ -22,7 +21,8 @@ class B: def x(self) -> int: return 0 - b = B() - assert_type(b.x, int) - b.x = cast(int, 4) - assert_type(b.x, int) + def check_cached_property_settable(x: int) -> None: + b = B() + assert_type(b.x, int) + b.x = x + assert_type(b.x, int)