From 6cb3857054c7d310529b3b2823f7e922511cfbd3 Mon Sep 17 00:00:00 2001 From: Matiiss <83066658+Matiiss@users.noreply.github.com> Date: Tue, 13 Aug 2024 02:22:10 +0300 Subject: [PATCH 1/9] Add runtime support for `pygame.sprite.AbstractGroup` subscripts --- src_py/sprite.py | 6 ++++++ test/sprite_test.py | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src_py/sprite.py b/src_py/sprite.py index 6f7f9e32dd..b1fe39272d 100644 --- a/src_py/sprite.py +++ b/src_py/sprite.py @@ -371,6 +371,12 @@ class AbstractGroup: """ + def __class_getitem__(cls, generic): + # switch to `types.GenericAlias` once Python 3.8 support is dropped + import typing + + return typing._GenericAlias(cls, generic) # type: ignore[attr-defined] + # protected identifier value to identify sprite groups, and avoid infinite recursion _spritegroup = True diff --git a/test/sprite_test.py b/test/sprite_test.py index 25415fce19..47d8da289d 100644 --- a/test/sprite_test.py +++ b/test/sprite_test.py @@ -660,6 +660,19 @@ def update(self, *args, **kwargs): self.assertEqual(test_sprite.sink, [1, 2, 3]) self.assertEqual(test_sprite.sink_kwargs, {"foo": 4, "bar": 5}) + def test_type_subscript(self): + try: + group_generic_alias = sprite.Group[sprite.Sprite] + except TypeError as e: + self.fail(e) + + # switch to `types.GenericAlias` once Python 3.8 support is dropped + import typing + + self.assertIsInstance(group_generic_alias, typing._GenericAlias) # type: ignore[attr-defined] + self.assertIs(typing.get_origin(group_generic_alias), sprite.Group) + self.assertEqual(typing.get_args(group_generic_alias), (sprite.Sprite,)) + ################################################################################ From 793a5ee93a975bcbfad6d30bbca894ecf3555727 Mon Sep 17 00:00:00 2001 From: Matiiss <83066658+Matiiss@users.noreply.github.com> Date: Tue, 13 Aug 2024 02:29:32 +0300 Subject: [PATCH 2/9] Add `__class_getitem__` to type stubs --- buildconfig/stubs/pygame/sprite.pyi | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/buildconfig/stubs/pygame/sprite.pyi b/buildconfig/stubs/pygame/sprite.pyi index 8220c36ba9..804779166b 100644 --- a/buildconfig/stubs/pygame/sprite.pyi +++ b/buildconfig/stubs/pygame/sprite.pyi @@ -1,3 +1,4 @@ +import typing from typing import ( Any, Callable, @@ -13,7 +14,7 @@ from typing import ( TypeVar, Union, ) -from typing_extensions import deprecated # added in 3.13 +from typing_extensions import deprecated # added in 3.13 from pygame.rect import FRect, Rect from pygame.surface import Surface @@ -149,6 +150,7 @@ _TDirtySprite = TypeVar("_TDirtySprite", bound=_DirtySpriteSupportsGroup) class AbstractGroup(Generic[_TSprite]): spritedict: Dict[_TSprite, Optional[Union[FRect, Rect]]] lostsprites: List[Union[FRect, Rect]] + def __class_getitem__(cls, generic: Any) -> typing._GenericAlias: ... # type: ignore[attr-defined] def __init__(self) -> None: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[_TSprite]: ... @@ -185,10 +187,12 @@ class Group(AbstractGroup[_TSprite]): # these are aliased in the code too @deprecated("Use `pygame.sprite.Group` instead") class RenderPlain(Group): ... + @deprecated("Use `pygame.sprite.Group` instead") class RenderClear(Group): ... class RenderUpdates(Group[_TSprite]): ... + @deprecated("Use `pygame.sprite.RenderUpdates` instead") class OrderedUpdates(RenderUpdates[_TSprite]): ... @@ -200,7 +204,7 @@ class LayeredUpdates(AbstractGroup[_TSprite]): AbstractGroup[_TSprite], Iterable[Union[_TSprite, AbstractGroup[_TSprite]]], ], - **kwargs: Any + **kwargs: Any, ) -> None: ... def add( self, @@ -209,7 +213,7 @@ class LayeredUpdates(AbstractGroup[_TSprite]): AbstractGroup[_TSprite], Iterable[Union[_TSprite, AbstractGroup[_TSprite]]], ], - **kwargs: Any + **kwargs: Any, ) -> None: ... def get_sprites_at(self, pos: Point) -> List[_TSprite]: ... def get_sprite(self, idx: int) -> _TSprite: ... @@ -238,10 +242,10 @@ class LayeredDirty(LayeredUpdates[_TDirtySprite]): def set_timing_threshold( self, time_ms: SupportsFloat ) -> None: ... # This actually accept any value - @deprecated("since 2.1.1. Use `pygame.sprite.LayeredDirty.set_timing_threshold` instead") - def set_timing_treshold( - self, time_ms: SupportsFloat - ) -> None: ... + @deprecated( + "since 2.1.1. Use `pygame.sprite.LayeredDirty.set_timing_threshold` instead" + ) + def set_timing_treshold(self, time_ms: SupportsFloat) -> None: ... class GroupSingle(AbstractGroup[_TSprite]): sprite: _TSprite From f0955503bcf6cecdcc837650a52fa91573f2a5de Mon Sep 17 00:00:00 2001 From: Matiiss <83066658+Matiiss@users.noreply.github.com> Date: Tue, 13 Aug 2024 02:35:31 +0300 Subject: [PATCH 3/9] Apparently I was using the wrong ignore code --- buildconfig/stubs/pygame/sprite.pyi | 2 +- src_py/sprite.py | 2 +- test/sprite_test.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/buildconfig/stubs/pygame/sprite.pyi b/buildconfig/stubs/pygame/sprite.pyi index 804779166b..8b092008e8 100644 --- a/buildconfig/stubs/pygame/sprite.pyi +++ b/buildconfig/stubs/pygame/sprite.pyi @@ -150,7 +150,7 @@ _TDirtySprite = TypeVar("_TDirtySprite", bound=_DirtySpriteSupportsGroup) class AbstractGroup(Generic[_TSprite]): spritedict: Dict[_TSprite, Optional[Union[FRect, Rect]]] lostsprites: List[Union[FRect, Rect]] - def __class_getitem__(cls, generic: Any) -> typing._GenericAlias: ... # type: ignore[attr-defined] + def __class_getitem__(cls, generic: Any) -> typing._GenericAlias: ... # type: ignore[name-defined] def __init__(self) -> None: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[_TSprite]: ... diff --git a/src_py/sprite.py b/src_py/sprite.py index b1fe39272d..51116ebdfc 100644 --- a/src_py/sprite.py +++ b/src_py/sprite.py @@ -375,7 +375,7 @@ def __class_getitem__(cls, generic): # switch to `types.GenericAlias` once Python 3.8 support is dropped import typing - return typing._GenericAlias(cls, generic) # type: ignore[attr-defined] + return typing._GenericAlias(cls, generic) # type: ignore[name-defined] # protected identifier value to identify sprite groups, and avoid infinite recursion _spritegroup = True diff --git a/test/sprite_test.py b/test/sprite_test.py index 47d8da289d..d0f95ea2d1 100644 --- a/test/sprite_test.py +++ b/test/sprite_test.py @@ -669,7 +669,7 @@ def test_type_subscript(self): # switch to `types.GenericAlias` once Python 3.8 support is dropped import typing - self.assertIsInstance(group_generic_alias, typing._GenericAlias) # type: ignore[attr-defined] + self.assertIsInstance(group_generic_alias, typing._GenericAlias) # type: ignore[name-defined] self.assertIs(typing.get_origin(group_generic_alias), sprite.Group) self.assertEqual(typing.get_args(group_generic_alias), (sprite.Sprite,)) From 7f3b58f63c795f7c103788ba2badd5c4aac55d04 Mon Sep 17 00:00:00 2001 From: Matiiss <83066658+Matiiss@users.noreply.github.com> Date: Thu, 5 Sep 2024 22:32:14 +0300 Subject: [PATCH 4/9] import appropriate GenericAlias based on the version --- src_py/sprite.py | 16 +++++++++------- test/sprite_test.py | 16 +++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src_py/sprite.py b/src_py/sprite.py index 51116ebdfc..a71e868efa 100644 --- a/src_py/sprite.py +++ b/src_py/sprite.py @@ -84,14 +84,19 @@ # specific ones that aren't quite so general but fit into common # specialized cases. -from warnings import warn +import sys from typing import Optional +from warnings import warn -import pygame +if sys.version_info[:3] >= (3, 9, 0): + from types import GenericAlias +else: + from typing import _GenericAlias as GenericAlias # type: ignore[name-defined] +import pygame +from pygame.mask import from_surface from pygame.rect import Rect from pygame.time import get_ticks -from pygame.mask import from_surface class Sprite: @@ -372,10 +377,7 @@ class AbstractGroup: """ def __class_getitem__(cls, generic): - # switch to `types.GenericAlias` once Python 3.8 support is dropped - import typing - - return typing._GenericAlias(cls, generic) # type: ignore[name-defined] + return GenericAlias(cls, generic) # protected identifier value to identify sprite groups, and avoid infinite recursion _spritegroup = True diff --git a/test/sprite_test.py b/test/sprite_test.py index d0f95ea2d1..f1d55c285a 100644 --- a/test/sprite_test.py +++ b/test/sprite_test.py @@ -1,12 +1,17 @@ #################################### IMPORTS ################################### - +import sys +import typing import unittest +if sys.version_info[:3] >= (3, 9, 0): + from types import GenericAlias +else: + from typing import _GenericAlias as GenericAlias # type: ignore[name-defined] + import pygame from pygame import sprite - ################################# MODULE LEVEL ################################# @@ -666,10 +671,7 @@ def test_type_subscript(self): except TypeError as e: self.fail(e) - # switch to `types.GenericAlias` once Python 3.8 support is dropped - import typing - - self.assertIsInstance(group_generic_alias, typing._GenericAlias) # type: ignore[name-defined] + self.assertIsInstance(group_generic_alias, GenericAlias) self.assertIs(typing.get_origin(group_generic_alias), sprite.Group) self.assertEqual(typing.get_args(group_generic_alias), (sprite.Sprite,)) @@ -1375,8 +1377,8 @@ def test_memoryleak_bug(self): # For memory leak bug posted to mailing list by Tobias Steinrücken on 16/11/10. # Fixed in revision 2953. - import weakref import gc + import weakref class MySprite(sprite.Sprite): def __init__(self, *args, **kwargs): From 5d8e11a26bbf040ad70141cbab2f0fbbed35b477 Mon Sep 17 00:00:00 2001 From: Matiiss <83066658+Matiiss@users.noreply.github.com> Date: Sun, 24 Nov 2024 18:15:17 +0200 Subject: [PATCH 5/9] remove 3.8 compat --- src_py/sprite.py | 9 ++------- test/sprite_test.py | 9 ++------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src_py/sprite.py b/src_py/sprite.py index a71e868efa..103788ab54 100644 --- a/src_py/sprite.py +++ b/src_py/sprite.py @@ -84,15 +84,10 @@ # specific ones that aren't quite so general but fit into common # specialized cases. -import sys +import types from typing import Optional from warnings import warn -if sys.version_info[:3] >= (3, 9, 0): - from types import GenericAlias -else: - from typing import _GenericAlias as GenericAlias # type: ignore[name-defined] - import pygame from pygame.mask import from_surface from pygame.rect import Rect @@ -377,7 +372,7 @@ class AbstractGroup: """ def __class_getitem__(cls, generic): - return GenericAlias(cls, generic) + return types.GenericAlias(cls, generic) # protected identifier value to identify sprite groups, and avoid infinite recursion _spritegroup = True diff --git a/test/sprite_test.py b/test/sprite_test.py index f1d55c285a..783ea1fabe 100644 --- a/test/sprite_test.py +++ b/test/sprite_test.py @@ -1,14 +1,9 @@ #################################### IMPORTS ################################### -import sys +import types import typing import unittest -if sys.version_info[:3] >= (3, 9, 0): - from types import GenericAlias -else: - from typing import _GenericAlias as GenericAlias # type: ignore[name-defined] - import pygame from pygame import sprite @@ -671,7 +666,7 @@ def test_type_subscript(self): except TypeError as e: self.fail(e) - self.assertIsInstance(group_generic_alias, GenericAlias) + self.assertIsInstance(group_generic_alias, types.GenericAlias) self.assertIs(typing.get_origin(group_generic_alias), sprite.Group) self.assertEqual(typing.get_args(group_generic_alias), (sprite.Sprite,)) From dd698a75493ef11cda5d4e0c9baa0d541bdbda2e Mon Sep 17 00:00:00 2001 From: Matiiss <83066658+Matiiss@users.noreply.github.com> Date: Sun, 24 Nov 2024 18:22:19 +0200 Subject: [PATCH 6/9] remove 3.8 compat --- buildconfig/stubs/pygame/sprite.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildconfig/stubs/pygame/sprite.pyi b/buildconfig/stubs/pygame/sprite.pyi index 8b092008e8..8fc4bf1d0c 100644 --- a/buildconfig/stubs/pygame/sprite.pyi +++ b/buildconfig/stubs/pygame/sprite.pyi @@ -1,4 +1,4 @@ -import typing +import types from typing import ( Any, Callable, @@ -150,7 +150,7 @@ _TDirtySprite = TypeVar("_TDirtySprite", bound=_DirtySpriteSupportsGroup) class AbstractGroup(Generic[_TSprite]): spritedict: Dict[_TSprite, Optional[Union[FRect, Rect]]] lostsprites: List[Union[FRect, Rect]] - def __class_getitem__(cls, generic: Any) -> typing._GenericAlias: ... # type: ignore[name-defined] + def __class_getitem__(cls, generic: Any) -> types.GenericAlias: ... def __init__(self) -> None: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[_TSprite]: ... From 35fffadda539543bf236e6f0ca07d69e8e34c528 Mon Sep 17 00:00:00 2001 From: Matiiss <83066658+Matiiss@users.noreply.github.com> Date: Sun, 24 Nov 2024 18:23:56 +0200 Subject: [PATCH 7/9] minimize diff --- buildconfig/stubs/pygame/sprite.pyi | 16 +++++++--------- src_py/sprite.py | 4 ++-- test/sprite_test.py | 4 +++- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/buildconfig/stubs/pygame/sprite.pyi b/buildconfig/stubs/pygame/sprite.pyi index 8fc4bf1d0c..f475b4f663 100644 --- a/buildconfig/stubs/pygame/sprite.pyi +++ b/buildconfig/stubs/pygame/sprite.pyi @@ -14,7 +14,7 @@ from typing import ( TypeVar, Union, ) -from typing_extensions import deprecated # added in 3.13 +from typing_extensions import deprecated # added in 3.13 from pygame.rect import FRect, Rect from pygame.surface import Surface @@ -187,12 +187,10 @@ class Group(AbstractGroup[_TSprite]): # these are aliased in the code too @deprecated("Use `pygame.sprite.Group` instead") class RenderPlain(Group): ... - @deprecated("Use `pygame.sprite.Group` instead") class RenderClear(Group): ... class RenderUpdates(Group[_TSprite]): ... - @deprecated("Use `pygame.sprite.RenderUpdates` instead") class OrderedUpdates(RenderUpdates[_TSprite]): ... @@ -204,7 +202,7 @@ class LayeredUpdates(AbstractGroup[_TSprite]): AbstractGroup[_TSprite], Iterable[Union[_TSprite, AbstractGroup[_TSprite]]], ], - **kwargs: Any, + **kwargs: Any ) -> None: ... def add( self, @@ -213,7 +211,7 @@ class LayeredUpdates(AbstractGroup[_TSprite]): AbstractGroup[_TSprite], Iterable[Union[_TSprite, AbstractGroup[_TSprite]]], ], - **kwargs: Any, + **kwargs: Any ) -> None: ... def get_sprites_at(self, pos: Point) -> List[_TSprite]: ... def get_sprite(self, idx: int) -> _TSprite: ... @@ -242,10 +240,10 @@ class LayeredDirty(LayeredUpdates[_TDirtySprite]): def set_timing_threshold( self, time_ms: SupportsFloat ) -> None: ... # This actually accept any value - @deprecated( - "since 2.1.1. Use `pygame.sprite.LayeredDirty.set_timing_threshold` instead" - ) - def set_timing_treshold(self, time_ms: SupportsFloat) -> None: ... + @deprecated("since 2.1.1. Use `pygame.sprite.LayeredDirty.set_timing_threshold` instead") + def set_timing_treshold( + self, time_ms: SupportsFloat + ) -> None: ... class GroupSingle(AbstractGroup[_TSprite]): sprite: _TSprite diff --git a/src_py/sprite.py b/src_py/sprite.py index 103788ab54..6bad8f53d6 100644 --- a/src_py/sprite.py +++ b/src_py/sprite.py @@ -85,13 +85,13 @@ # specialized cases. import types -from typing import Optional from warnings import warn +from typing import Optional import pygame -from pygame.mask import from_surface from pygame.rect import Rect from pygame.time import get_ticks +from pygame.mask import from_surface class Sprite: diff --git a/test/sprite_test.py b/test/sprite_test.py index 783ea1fabe..56ce86a4a5 100644 --- a/test/sprite_test.py +++ b/test/sprite_test.py @@ -1,5 +1,6 @@ #################################### IMPORTS ################################### + import types import typing import unittest @@ -7,6 +8,7 @@ import pygame from pygame import sprite + ################################# MODULE LEVEL ################################# @@ -1372,8 +1374,8 @@ def test_memoryleak_bug(self): # For memory leak bug posted to mailing list by Tobias Steinrücken on 16/11/10. # Fixed in revision 2953. - import gc import weakref + import gc class MySprite(sprite.Sprite): def __init__(self, *args, **kwargs): From 03870377f21c2f2bf7c07e0fb75dcd3e5e5a298d Mon Sep 17 00:00:00 2001 From: Matiiss <83066658+Matiiss@users.noreply.github.com> Date: Sun, 24 Nov 2024 18:24:49 +0200 Subject: [PATCH 8/9] further minimize the diff --- src_py/sprite.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src_py/sprite.py b/src_py/sprite.py index 6bad8f53d6..e3865194bb 100644 --- a/src_py/sprite.py +++ b/src_py/sprite.py @@ -89,6 +89,7 @@ from typing import Optional import pygame + from pygame.rect import Rect from pygame.time import get_ticks from pygame.mask import from_surface From e3816c532730118499292bcf281bb3ffc3b9ab82 Mon Sep 17 00:00:00 2001 From: Matiiss <83066658+Matiiss@users.noreply.github.com> Date: Sun, 24 Nov 2024 18:29:38 +0200 Subject: [PATCH 9/9] change type hints --- buildconfig/stubs/pygame/sprite.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildconfig/stubs/pygame/sprite.pyi b/buildconfig/stubs/pygame/sprite.pyi index e28fd77306..dc3ce78c53 100644 --- a/buildconfig/stubs/pygame/sprite.pyi +++ b/buildconfig/stubs/pygame/sprite.pyi @@ -143,8 +143,8 @@ _TDirtySprite = TypeVar("_TDirtySprite", bound=_DirtySpriteSupportsGroup) # b = Group(MySprite()) class AbstractGroup(Generic[_TSprite]): - spritedict: Dict[_TSprite, Optional[Union[FRect, Rect]]] - lostsprites: List[Union[FRect, Rect]] + spritedict: dict[_TSprite, Optional[Union[FRect, Rect]]] + lostsprites: list[Union[FRect, Rect]] def __class_getitem__(cls, generic: Any) -> types.GenericAlias: ... def __init__(self) -> None: ... def __len__(self) -> int: ...