From 9dd119b95afc64303e286ad47feb363d1aa1a507 Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Wed, 9 Oct 2024 16:34:30 -0700 Subject: [PATCH 1/2] Experiment: decouple types.DynamicClassAttribute from property I'm curious to see what the tests look like. --- stdlib/@tests/stubtest_allowlists/py310.txt | 1 - stdlib/@tests/stubtest_allowlists/py311.txt | 1 - stdlib/@tests/stubtest_allowlists/py312.txt | 1 - stdlib/@tests/stubtest_allowlists/py313.txt | 1 - stdlib/types.pyi | 21 +++++++++++++++++++-- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/stdlib/@tests/stubtest_allowlists/py310.txt b/stdlib/@tests/stubtest_allowlists/py310.txt index 8d0631cdd291..4588ae80986f 100644 --- a/stdlib/@tests/stubtest_allowlists/py310.txt +++ b/stdlib/@tests/stubtest_allowlists/py310.txt @@ -150,7 +150,6 @@ _collections_abc.Generator.send _collections_abc.Generator.throw # typing.SupportsRound.__round__ # pos-or-kw at runtime, but we pretend it's pos-only in the stub so that e.g. float.__round__ satisfies the interface -types.DynamicClassAttribute..* # In the stub we pretend it's an alias for property, but it has positional-only differences # These three have a pos-or-keyword first parameter at runtime, but deliberately have a pos-only first parameter in the stub. #6812 posixpath.join diff --git a/stdlib/@tests/stubtest_allowlists/py311.txt b/stdlib/@tests/stubtest_allowlists/py311.txt index 308855f8f00b..585854c6fe82 100644 --- a/stdlib/@tests/stubtest_allowlists/py311.txt +++ b/stdlib/@tests/stubtest_allowlists/py311.txt @@ -95,7 +95,6 @@ _collections_abc.Generator.send _collections_abc.Generator.throw # typing.SupportsRound.__round__ # pos-or-kw at runtime, but we pretend it's pos-only in the stub so that e.g. float.__round__ satisfies the interface -types.DynamicClassAttribute..* # In the stub we pretend it's an alias for property, but it has positional-only differences # These three have a pos-or-keyword first parameter at runtime, but deliberately have a pos-only first parameter in the stub. #6812 posixpath.join diff --git a/stdlib/@tests/stubtest_allowlists/py312.txt b/stdlib/@tests/stubtest_allowlists/py312.txt index 14500a678fdf..473bc5b18aa9 100644 --- a/stdlib/@tests/stubtest_allowlists/py312.txt +++ b/stdlib/@tests/stubtest_allowlists/py312.txt @@ -75,7 +75,6 @@ _collections_abc.Generator.send _collections_abc.Generator.throw # typing.SupportsRound.__round__ # pos-or-kw at runtime, but we pretend it's pos-only in the stub so that e.g. float.__round__ satisfies the interface -types.DynamicClassAttribute..* # In the stub we pretend it's an alias for property, but it has positional-only differences # These three have a pos-or-keyword first parameter at runtime, but deliberately have a pos-only first parameter in the stub. #6812 posixpath.join diff --git a/stdlib/@tests/stubtest_allowlists/py313.txt b/stdlib/@tests/stubtest_allowlists/py313.txt index 8ddbb8fe3089..48d65b135488 100644 --- a/stdlib/@tests/stubtest_allowlists/py313.txt +++ b/stdlib/@tests/stubtest_allowlists/py313.txt @@ -73,7 +73,6 @@ _collections_abc.Generator.send _collections_abc.Generator.throw # typing.SupportsRound.__round__ # pos-or-kw at runtime, but we pretend it's pos-only in the stub so that e.g. float.__round__ satisfies the interface -types.DynamicClassAttribute..* # In the stub we pretend it's an alias for property, but it has positional-only differences # These three have a pos-or-keyword first parameter at runtime, but deliberately have a pos-only first parameter in the stub. #6812 posixpath.join diff --git a/stdlib/types.pyi b/stdlib/types.pyi index 0c5a43617040..dcd1d70e1f39 100644 --- a/stdlib/types.pyi +++ b/stdlib/types.pyi @@ -576,8 +576,25 @@ def prepare_class( if sys.version_info >= (3, 12): def get_original_bases(cls: type, /) -> tuple[Any, ...]: ... -# Actually a different type, but `property` is special and we want that too. -DynamicClassAttribute = property +class DynamicClassAttribute: + fget: Callable[[Any], Any] | None + fset: Callable[[Any, Any], None] | None + fdel: Callable[[Any], None] | None + overwrite_doc: bool + __isabstractmethod__: bool + def __init__( + self, + fget: Callable[[Any], Any] | None = ..., + fset: Callable[[Any, Any], None] | None = ..., + fdel: Callable[[Any], None] | None = ..., + doc: str | None = ..., + ) -> None: ... + def __get__(self, instance: Any, ownerclass: type | None = None) -> Any: ... + def __set__(self, instance: Any, value: Any) -> None: ... + def __delete__(self, instance: Any) -> None: ... + def getter(self, fget: Callable[[Any], Any]) -> property: ... + def setter(self, fset: Callable[[Any, Any], None]) -> property: ... + def deleter(self, fdel: Callable[[Any], None]) -> property: ... _Fn = TypeVar("_Fn", bound=Callable[..., object]) _R = TypeVar("_R") From 86e763e62418a62d4317428e8c5ae9256858066f Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Wed, 9 Oct 2024 17:06:06 -0700 Subject: [PATCH 2/2] what if DynamicClassAttribute subclasses property? --- stdlib/types.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/types.pyi b/stdlib/types.pyi index dcd1d70e1f39..a03f845decdf 100644 --- a/stdlib/types.pyi +++ b/stdlib/types.pyi @@ -576,7 +576,7 @@ def prepare_class( if sys.version_info >= (3, 12): def get_original_bases(cls: type, /) -> tuple[Any, ...]: ... -class DynamicClassAttribute: +class DynamicClassAttribute(property): fget: Callable[[Any], Any] | None fset: Callable[[Any, Any], None] | None fdel: Callable[[Any], None] | None