from typing import ( Any, Callable, Dict, Generic, Iterable, Iterator, List, Optional, SupportsFloat, Tuple, TypeVar, Union, ) # Protocol added in python 3.8 from typing_extensions import Protocol from pygame.rect import Rect from pygame.surface import Surface from pygame.mask import Mask from ._common import RectValue, Coordinate # non-generic Group, used in Sprite _Group = AbstractGroup[_SpriteSupportsGroup] # protocol helps with structural subtyping for typevars in sprite group generics class _SupportsSprite(Protocol): @property def layer(self) -> int: ... @layer.setter def layer(self, value: int) -> None: ... def __init__(self, *groups: _Group) -> None: ... def add_internal(self, group: _Group) -> None: ... def remove_internal(self, group: _Group) -> None: ... def update(self, *args: Any, **kwargs: Any) -> None: ... def add(self, *groups: _Group) -> None: ... def remove(self, *groups: _Group) -> None: ... def kill(self) -> None: ... def alive(self) -> bool: ... def groups(self) -> List[_Group]: ... # also a protocol class _SupportsDirtySprite(_SupportsSprite, Protocol): dirty: int blendmode: int source_rect: Rect visible: int _layer: int def _set_visible(self, val: int) -> None: ... def _get_visible(self) -> int: ... # concrete sprite implementation class class Sprite(_SupportsSprite): @property def layer(self) -> int: ... @layer.setter def layer(self, value: int) -> None: ... def __init__(self, *groups: _Group) -> None: ... def add_internal(self, group: _Group) -> None: ... def remove_internal(self, group: _Group) -> None: ... def update(self, *args: Any, **kwargs: Any) -> None: ... def add(self, *groups: _Group) -> None: ... def remove(self, *groups: _Group) -> None: ... def kill(self) -> None: ... def alive(self) -> bool: ... def groups(self) -> List[_Group]: ... # concrete dirty sprite implementation class class DirtySprite(_SupportsDirtySprite): dirty: int blendmode: int source_rect: Rect visible: int _layer: int def _set_visible(self, val: int) -> None: ... def _get_visible(self) -> int: ... # used as a workaround for typing.Self because it is added in python 3.11 _TGroup = TypeVar("_TGroup", bound=AbstractGroup) # define some useful protocols first, which sprite functions accept # sprite functions don't need all sprite attributes to be present in the # arguments passed, they only use a few which are marked in the below protocols class _HasRect(Protocol): rect: Rect # image in addition to rect class _HasImageAndRect(_HasRect, Protocol): image: Surface # mask in addition to rect class _HasMaskAndRect(_HasRect, Protocol): mask: Mask # radius in addition to rect class _HasRadiusAndRect(_HasRect, Protocol): radius: float class _SpriteSupportsGroup(_SupportsSprite, _HasImageAndRect, Protocol): ... class _DirtySpriteSupportsGroup(_SupportsDirtySprite, _HasImageAndRect, Protocol): ... # typevar bound to Sprite, _SpriteSupportsGroup Protocol ensures sprite # subclass passed to group has image and rect attributes _TSprite = TypeVar("_TSprite", bound=_SpriteSupportsGroup) _TSprite2 = TypeVar("_TSprite2", bound=_SpriteSupportsGroup) # almost the same as _TSprite but bound to DirtySprite _TDirtySprite = TypeVar("_TDirtySprite", bound=_DirtySpriteSupportsGroup) # Below code demonstrates the advantages of the _SpriteSupportsGroup protocol # typechecker should error, regular Sprite does not support Group.draw due to # missing image and rect attributes # a = Group(Sprite()) # typechecker should error, other Sprite attibutes are also needed for Group # class MySprite: # image: Surface # rect: Rect # # b = Group(MySprite()) # typechecker should pass # class MySprite(Sprite): # image: Surface # rect: Rect # # b = Group(MySprite()) class AbstractGroup(Generic[_TSprite]): spritedict: Dict[_TSprite, Optional[Rect]] lostsprites: List[Rect] def __init__(self) -> None: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[_TSprite]: ... def __bool__(self) -> bool: ... def __contains__(self, item: Any) -> bool: ... def add_internal(self, sprite: _TSprite, layer: None = None) -> None: ... def remove_internal(self, sprite: _TSprite) -> None: ... def has_internal(self, sprite: _TSprite) -> bool: ... def copy(self: _TGroup) -> _TGroup: ... # typing.Self is py3.11+ def sprites(self) -> List[_TSprite]: ... def add( self, *sprites: Union[_TSprite, AbstractGroup[_TSprite], Iterable[_TSprite]] ) -> None: ... def remove( self, *sprites: Union[_TSprite, AbstractGroup[_TSprite], Iterable[_TSprite]] ) -> None: ... def has( self, *sprites: Union[_TSprite, AbstractGroup[_TSprite], Iterable[_TSprite]] ) -> bool: ... def update(self, *args: Any, **kwargs: Any) -> None: ... def draw( self, surface: Surface, bgsurf: Optional[Surface] = None, special_flags: int = 0 ) -> List[Rect]: ... def clear( self, surface: Surface, bgd: Union[Surface, Callable[[Surface, Rect], Any]] ) -> None: ... def empty(self) -> None: ... class Group(AbstractGroup[_TSprite]): def __init__( self, *sprites: Union[_TSprite, AbstractGroup[_TSprite], Iterable[_TSprite]] ) -> None: ... # these are aliased in the code too RenderPlain = Group RenderClear = Group class RenderUpdates(Group[_TSprite]): ... class OrderedUpdates(RenderUpdates[_TSprite]): ... class LayeredUpdates(AbstractGroup[_TSprite]): def __init__( self, *sprites: Union[ _TSprite, AbstractGroup[_TSprite], Iterable[Union[_TSprite, AbstractGroup[_TSprite]]], ], **kwargs: Any ) -> None: ... def add( self, *sprites: Union[ _TSprite, AbstractGroup[_TSprite], Iterable[Union[_TSprite, AbstractGroup[_TSprite]]], ], **kwargs: Any ) -> None: ... def get_sprites_at(self, pos: Coordinate) -> List[_TSprite]: ... def get_sprite(self, idx: int) -> _TSprite: ... def remove_sprites_of_layer(self, layer_nr: int) -> List[_TSprite]: ... def layers(self) -> List[int]: ... def change_layer(self, sprite: _TSprite, new_layer: int) -> None: ... def get_layer_of_sprite(self, sprite: _TSprite) -> int: ... def get_top_layer(self) -> int: ... def get_bottom_layer(self) -> int: ... def move_to_front(self, sprite: _TSprite) -> None: ... def move_to_back(self, sprite: _TSprite) -> None: ... def get_top_sprite(self) -> _TSprite: ... def get_sprites_from_layer(self, layer: int) -> List[_TSprite]: ... def switch_layer(self, layer1_nr: int, layer2_nr: int) -> None: ... class LayeredDirty(LayeredUpdates[_TDirtySprite]): def __init__(self, *sprites: _TDirtySprite, **kwargs: Any) -> None: ... def draw( self, surface: Surface, bgsurf: Optional[Surface] = None, special_flags: int = None ) -> List[Rect]: ... # clear breaks Liskov substitution principle in code def clear(self, surface: Surface, bgd: Surface) -> None: ... # type: ignore[override] def repaint_rect(self, screen_rect: RectValue) -> None: ... def set_clip(self, screen_rect: Optional[RectValue] = None) -> None: ... def get_clip(self) -> Rect: ... def set_timing_threshold( self, time_ms: SupportsFloat ) -> None: ... # This actually accept any value # deprecated alias set_timing_treshold = set_timing_threshold class GroupSingle(AbstractGroup[_TSprite]): sprite: _TSprite def __init__(self, sprite: Optional[_TSprite] = None) -> None: ... # argument to collide_rect must have rect attribute def collide_rect(left: _HasRect, right: _HasRect) -> bool: ... class collide_rect_ratio: ratio: float def __init__(self, ratio: float) -> None: ... def __call__(self, left: _HasRect, right: _HasRect) -> bool: ... # must have rect attribute, may optionally have radius attribute _SupportsCollideCircle = Union[_HasRect, _HasRadiusAndRect] def collide_circle( left: _SupportsCollideCircle, right: _SupportsCollideCircle ) -> bool: ... class collide_circle_ratio: ratio: float def __init__(self, ratio: float) -> None: ... def __call__( self, left: _SupportsCollideCircle, right: _SupportsCollideCircle ) -> bool: ... # argument to collide_mask must either have mask or have image attribute, in # addtion to mandatorily having a rect attribute _SupportsCollideMask = Union[_HasImageAndRect, _HasMaskAndRect] def collide_mask( left: _SupportsCollideMask, right: _SupportsCollideMask ) -> Optional[Tuple[int, int]]: ... def spritecollide( sprite: _HasRect, group: AbstractGroup[_TSprite], dokill: bool, collided: Optional[Callable[[_HasRect, _TSprite], bool]] = None, ) -> List[_TSprite]: ... def groupcollide( groupa: AbstractGroup[_TSprite], groupb: AbstractGroup[_TSprite2], dokilla: bool, dokillb: bool, collided: Optional[Callable[[_TSprite, _TSprite2], bool]] = None, ) -> Dict[_TSprite, List[_TSprite2]]: ... def spritecollideany( sprite: _HasRect, group: AbstractGroup[_TSprite], collided: Optional[Callable[[_HasRect, _TSprite], bool]] = None, ) -> Optional[_TSprite]: ...