From 3855297bb55e0b9abcafc23c7e6f68f72ba4020d Mon Sep 17 00:00:00 2001 From: Pierre LeMoine Date: Sun, 14 Jan 2024 08:18:01 +0100 Subject: [PATCH 1/4] Adding descriptor methods to _SimpleCData This allows adding type annotations to structures and unions that resolve to the underlying types when accessed inside structures --- stdlib/_ctypes.pyi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stdlib/_ctypes.pyi b/stdlib/_ctypes.pyi index 1e0188b2599f..0f3130ce51df 100644 --- a/stdlib/_ctypes.pyi +++ b/stdlib/_ctypes.pyi @@ -82,6 +82,13 @@ class _SimpleCData(_CData, Generic[_T]): # The TypeVar can be unsolved here, # but we can't use overloads without creating many, many mypy false-positive errors def __init__(self, value: _T = ...) -> None: ... # pyright: ignore[reportInvalidTypeVarUse] + @overload + def __get__(self, __instance: None, __owner: type[Any] | None) -> Self: ... + @overload + def __get__(self, __instance: Any, __owner: type[_StructUnionBase]) -> _T: ... + @overload + def __get__(self, __instance: Any, __owner: type[Any] | None) -> Self: ... + def __set__(self, __instance: Any, __value: _T) -> None: ... class _CanCastTo(_CData): ... class _PointerLike(_CanCastTo): ... From 4a4820e330e69d579a38f78557d3da47de5fbdc8 Mon Sep 17 00:00:00 2001 From: Pierre LeMoine Date: Sun, 14 Jan 2024 08:51:12 +0100 Subject: [PATCH 2/4] Not sure if this is the right way to go about it. Would be happy with feedback from reviewers/maintainers --- tests/stubtest_allowlists/py3_common.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/stubtest_allowlists/py3_common.txt b/tests/stubtest_allowlists/py3_common.txt index 1ddc4771f634..5b1053f6d8f4 100644 --- a/tests/stubtest_allowlists/py3_common.txt +++ b/tests/stubtest_allowlists/py3_common.txt @@ -13,6 +13,8 @@ _collections_abc.KeysView.__reversed__ _collections_abc.ValuesView.__reversed__ _csv.Dialect.__init__ # C __init__ signature is inaccurate _ctypes.CFuncPtr # stubtest erroneously thinks it can't be subclassed +_ctypes._SimpleCData.__get__ # Does not exist on runtime object +_ctypes._SimpleCData.__set__ # Does not exist on runtime object _threading_local.local.__new__ ast.Bytes.__new__ ast.Ellipsis.__new__ @@ -37,6 +39,8 @@ ctypes.memmove # CFunctionType ctypes.memset # CFunctionType ctypes.string_at # docstring argument name is wrong ctypes.wstring_at # docstring argument name is wrong +ctypes._SimpleCData.__get__ # Does not exist on runtime object +ctypes._SimpleCData.__set__ # Does not exist on runtime object 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 white lie; see comments in the stub From 135c4437351138d1392110333c737a9ee4dbcf35 Mon Sep 17 00:00:00 2001 From: Pierre LeMoine Date: Sun, 14 Jan 2024 22:22:18 +0100 Subject: [PATCH 3/4] Adding regression test for ctypes --- test_cases/stdlib/check_ctypes.py | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 test_cases/stdlib/check_ctypes.py diff --git a/test_cases/stdlib/check_ctypes.py b/test_cases/stdlib/check_ctypes.py new file mode 100644 index 000000000000..ffae266b7e01 --- /dev/null +++ b/test_cases/stdlib/check_ctypes.py @@ -0,0 +1,42 @@ +# pyright: reportUninitializedInstanceVariable=false + +from __future__ import annotations + +import ctypes +from typing_extensions import assert_type +from typing import Any + +class Annotated(ctypes.Structure): + _fields_ = [("i", ctypes.c_int), ("f", ctypes.c_float)] + i: ctypes.c_int + f: ctypes.c_float + +class NoAnnotation(ctypes.Structure): + _fields_ = [("i", ctypes.c_int), ("f", ctypes.c_float)] + +class NonCType: + i: ctypes.c_int + f: ctypes.c_float + + +assert_type(ctypes.c_int().value, int) +ctypes.c_int() + 2 # type: ignore + +assert_type(ctypes.c_float().value, float) +ctypes.c_float() + 2 # type: ignore + +# All passes; all access are Any +assert_type(NoAnnotation().x + 2, Any) +assert_type(NoAnnotation().vec.x + 2, Any) + +Annotated.x.value + 2 # type: ignore +Annotated.x + 2 # type: ignore +assert_type(Annotated().i, int) +Annotated().i.value + 2 # type: ignore +assert_type(Annotated().f, float) +Annotated().f.value + 3.14 # type: ignore + +assert_type(NonCType.i.value, int) +NonCType.i + 2 # type: ignore +NonCType().i + 2 # type: ignore +assert_type(NonCType().i.value, int) From 1d340f41e3ebc7f24ad202f2110a616e5505666d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 14 Jan 2024 21:53:13 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks --- test_cases/stdlib/check_ctypes.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test_cases/stdlib/check_ctypes.py b/test_cases/stdlib/check_ctypes.py index ffae266b7e01..de94fd525190 100644 --- a/test_cases/stdlib/check_ctypes.py +++ b/test_cases/stdlib/check_ctypes.py @@ -3,17 +3,20 @@ from __future__ import annotations import ctypes -from typing_extensions import assert_type from typing import Any +from typing_extensions import assert_type + class Annotated(ctypes.Structure): _fields_ = [("i", ctypes.c_int), ("f", ctypes.c_float)] i: ctypes.c_int f: ctypes.c_float + class NoAnnotation(ctypes.Structure): _fields_ = [("i", ctypes.c_int), ("f", ctypes.c_float)] + class NonCType: i: ctypes.c_int f: ctypes.c_float