-
-
Notifications
You must be signed in to change notification settings - Fork 201
Improve sprite stubs #3525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Improve sprite stubs #3525
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM (though I havent rigorously tested this).
Left a few minor observations but nothing that should block a merge
@coderabbitai full review please |
@aatle I'll conduct a full review of the sprite module improvements in this PR. ✅ Actions performedFull review triggered. |
WalkthroughRefactors pygame.sprite type stubs to a single generic Sprite parameterization (_SpriteT), replaces legacy protocols/typevars, introduces _GroupOrGroups/_SpriteOrSprites, updates class bases and method signatures across Sprite, groups, layered groups, and collision helpers, and makes _HasMask mask a method. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
buildconfig/stubs/pygame/sprite.pyi (1)
171-173
: Simplifying _SupportsCollideCircle to _HasRect is fineThe union was redundant; code only requires .rect, with .radius optional per docs. The simplification avoids confusion.
🧹 Nitpick comments (2)
buildconfig/stubs/pygame/sprite.pyi (2)
66-66
: Loosen Sprite.groups() return type to avoid generic invariance frictionAbstractGroup is invariant in its type parameter. Returning list[AbstractGroup[Sprite]] will reject groups parameterized with a Sprite subclass (e.g., AbstractGroup[DirtySprite]). list[AbstractGroup[Any]] avoids this.
Apply this diff:
- def groups(self) -> list[AbstractGroup[Sprite]]: ... + def groups(self) -> list[AbstractGroup[Any]]: ...
195-212
: Tighten collided callback return type to boolThe documentation and implementations treat collided callbacks as boolean predicates. Using Any obscures misuse.
Apply this diff:
def spritecollide( - sprite: _HasRectT, - group: AbstractGroup[_SpriteT], - dokill: bool, - collided: Optional[Callable[[_HasRectT, _SpriteT], Any]] = None, -) -> list[_SpriteT]: ... + sprite: _HasRectT, + group: AbstractGroup[_SpriteT], + dokill: bool, + collided: Optional[Callable[[_HasRectT, _SpriteT], bool]] = None, +) -> list[_SpriteT]: ... def groupcollide( - groupa: AbstractGroup[_SpriteT], - groupb: AbstractGroup[_SpriteT2], + groupa: AbstractGroup[_SpriteT], + groupb: AbstractGroup[_SpriteT2], dokilla: bool, dokillb: bool, - collided: Optional[Callable[[_SpriteT, _SpriteT2], Any]] = None, + collided: Optional[Callable[[_SpriteT, _SpriteT2], bool]] = None, ) -> dict[_SpriteT, list[_SpriteT2]]: ... def spritecollideany( - sprite: _HasRectT, - group: AbstractGroup[_SpriteT], - collided: Optional[Callable[[_HasRectT, _SpriteT], Any]] = None, + sprite: _HasRectT, + group: AbstractGroup[_SpriteT], + collided: Optional[Callable[[_HasRectT, _SpriteT], bool]] = None, ) -> Optional[_SpriteT]: ...
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
buildconfig/stubs/pygame/sprite.pyi
(6 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
buildconfig/stubs/pygame/sprite.pyi (4)
buildconfig/stubs/pygame/mask.pyi (2)
Mask
(18-60)copy
(21-21)src_py/sprite.py (45)
Sprite
(97-264)groups
(216-224)add_internal
(171-177)add_internal
(396-407)add_internal
(759-794)add_internal
(1115-1135)add_internal
(1424-1428)AbstractGroup
(362-637)remove_internal
(179-185)remove_internal
(409-417)remove_internal
(839-854)remove_internal
(1454-1458)update
(187-199)update
(546-556)add
(135-151)add
(446-479)add
(796-837)remove
(153-169)remove
(481-511)kill
(201-214)alive
(226-233)visible
(322-327)visible
(330-331)sprite
(1442-1448)sprite
(1451-1452)layer
(239-252)layer
(255-264)layer
(334-342)layer
(345-354)has_internal
(419-425)has_internal
(1460-1461)copy
(427-438)copy
(1416-1417)sprites
(383-394)sprites
(856-862)sprites
(1419-1422)has
(513-544)Group
(640-662)get_sprite
(903-911)change_layer
(932-966)change_layer
(1345-1356)GroupSingle
(1398-1465)spritecollide
(1690-1724)groupcollide
(1727-1756)spritecollideany
(1759-1789)buildconfig/stubs/pygame/rect.pyi (8)
update
(179-181)update
(183-183)update
(185-185)FRect
(304-304)Rect
(303-303)copy
(152-152)left
(51-51)left
(53-53)buildconfig/stubs/pygame/surface.pyi (1)
copy
(335-346)
🔇 Additional comments (7)
buildconfig/stubs/pygame/sprite.pyi (7)
41-44
: Mask as a property remains attribute-compatible for collide_maskUsing a read-only property for mask keeps structural compatibility with code that uses obj.mask as an attribute. No behavioral or typing regressions here.
68-74
: DirtySprite deriving directly from Sprite simplifies the surfaceRemoving the auxiliary _SupportsDirtySprite protocol reduces indirection without losing expressiveness. Looks good.
75-77
: TypeVar renames align with PEP 8/typevar naming conventionsSuffix-T naming (_SpriteT, _SpriteT2, _DirtySpriteT) is the convention used by typeshed and linters. Good change.
110-124
: Generic propagation across Group/Render classes is consistent*The shift to Group[_SpriteT] and related aliases unifies the API and keeps variance consistent throughout. No issues spotted.
185-188
: collide_mask support via either image or mask remains accurateProtocol union covers both access paths; comment tweak improves clarity. No further changes needed.
142-148
: Confirm Optional[int] for special_flags matches runtime semanticsThe stub in buildconfig/stubs/pygame/sprite.pyi widens
special_flags
fromint
toOptional[int] = None
(contravariance is type-safe), but you need to verify thatLayeredDirty.draw
actually acceptsNone
at runtime (e.g., treats it as 0). If it doesn’t, revert the stub tospecial_flags: int = 0
to match the real behavior.
- File: buildconfig/stubs/pygame/sprite.pyi, lines 142–148
- Stub definition:
def draw( self, surface: Surface, bgd: Optional[Surface] = None, special_flags: Optional[int] = None, ) -> list[Union[FRect, Rect]]: ...- Action: inspect
LayeredDirty.draw
in the installed pygame/sprite.py (or its source) to confirmNone
is handled; otherwise update the stub.
79-81
: Apply TypeAlias + string forward refs for recursive aliasesSome type checkers (e.g. mypy in strict modes) reject self-referential type aliases without a
TypeAlias
annotation and quoted forward references. Please update:• In
buildconfig/stubs/pygame/sprite.pyi
(around lines 79–81):-_GroupOrGroups = Union[AbstractGroup[_SpriteT], Iterable[_GroupOrGroups[_SpriteT]]] -_SpriteOrSprites = Union[_SpriteT, Iterable[_SpriteOrSprites[_SpriteT]]] +_GroupOrGroups: TypeAlias = "Union[AbstractGroup[_SpriteT], Iterable[_GroupOrGroups[_SpriteT]]]" +_SpriteOrSprites: TypeAlias = "Union[_SpriteT, Iterable[_SpriteOrSprites[_SpriteT]]]"• Add the import (near the existing
Self
/deprecated imports):if sys.version_info >= (3, 11): from typing import Self, TypeAlias else: from typing_extensions import Self, TypeAliasFinally, verify that mypy (v1.10.0) accepts the new recursive aliases (e.g. with
--python-version 3.9
and appropriateMYPYPATH
or--explicit-package-bases
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🔭 Outside diff range comments (1)
buildconfig/stubs/pygame/sprite.pyi (1)
45-67
: Fix return type of Sprite.groups() to avoid generic invariance breakAbstractGroup is invariant in its type parameter, so returning list[AbstractGroup[Sprite]] is too narrow. E.g., a DirtySprite can belong to an AbstractGroup[DirtySprite], which is not a subtype of AbstractGroup[Sprite]. Use Any (or a covariant param if you ever change variance) to reflect reality and prevent false-positive type errors.
Proposed diff:
- def groups(self) -> list[AbstractGroup[Sprite]]: ... + def groups(self) -> list[AbstractGroup[Any]]: ...
♻️ Duplicate comments (2)
buildconfig/stubs/pygame/sprite.pyi (2)
82-99
: Layer argument in add_internal correctly widened to Optional[int]This resolves the earlier concern about forbidding layer integers. Thanks for addressing it.
171-173
: Comment mentions optional radius but the type alias no longer models it — update the comment for clarityThe simplification to _SupportsCollideCircle = _HasRect is fine, but the comment still suggests a radius attribute might be present. Consider clarifying that radius is a runtime convention not modeled in types, to avoid confusing users.
Proposed diff:
-# Must have rect attribute, may optionally have radius attribute +# Must have a rect attribute. A 'radius' attribute, if present at runtime, +# may be used by collide_circle, but it is intentionally not modeled here. _SupportsCollideCircle = _HasRect
🧹 Nitpick comments (2)
buildconfig/stubs/pygame/sprite.pyi (2)
75-81
: Recursive type aliases look good; consider TypeAlias for clarity (optional)Most type checkers understand self-referential aliases in .pyi files. If you want to be extra explicit, you could mark them as TypeAlias, but it’s not required.
Example (optional, not strictly necessary in .pyi):
-from typing import ( +from typing import ( Any, Generic, Optional, Protocol, SupportsFloat, TypeVar, Union, + TypeAlias, ) @@ -_GroupOrGroups = Union[AbstractGroup[_SpriteT], Iterable[_GroupOrGroups[_SpriteT]]] -_SpriteOrSprites = Union[_SpriteT, Iterable[_SpriteOrSprites[_SpriteT]]] +_GroupOrGroups: TypeAlias = Union[AbstractGroup[_SpriteT], Iterable[_GroupOrGroups[_SpriteT]]] +_SpriteOrSprites: TypeAlias = Union[_SpriteT, Iterable[_SpriteOrSprites[_SpriteT]]]
185-187
: Polish the collide_mask comment grammarMinor wording clean-up to read naturally.
Proposed diff:
-# Arguments to collide_mask must either have mask or have image attribute, in -# addition to mandatorily having a rect attribute +# Arguments to collide_mask must have either a mask or an image attribute, +# in addition to a mandatory rect attribute. _SupportsCollideMask = Union[_HasImageAndRect, _HasMaskAndRect]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
buildconfig/stubs/pygame/sprite.pyi
(6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
- GitHub Check: build (ubuntu-22.04)
- GitHub Check: Debian (Bookworm - 12) [ppc64le]
- GitHub Check: AMD64
- GitHub Check: x86
- GitHub Check: i686
- GitHub Check: x86_64
- GitHub Check: aarch64
- GitHub Check: msys2 (clang64, clang-x86_64)
- GitHub Check: msys2 (mingw64, x86_64)
- GitHub Check: msys2 (ucrt64, ucrt-x86_64)
- GitHub Check: Debian (Bookworm - 12) [armv6]
- GitHub Check: Debian (Bookworm - 12) [s390x]
- GitHub Check: Debian (Bookworm - 12) [armv7]
- GitHub Check: debug_coverage (ubuntu-24.04, 3.13.5)
- GitHub Check: debug_coverage (ubuntu-24.04, 3.14.0rc1)
- GitHub Check: dev-check
- GitHub Check: debug_coverage (ubuntu-24.04, 3.9.23)
- GitHub Check: build (windows-latest)
- GitHub Check: build (macos-14)
- GitHub Check: build (ubuntu-24.04)
🔇 Additional comments (10)
buildconfig/stubs/pygame/sprite.pyi (10)
43-44
: Good change: express mask as a property in the protocolDefining mask via a @Property keeps compatibility with objects that expose a simple attribute while remaining precise for structural typing.
58-63
: Constructor and add/remove now accept nested groups via recursive alias — nice usability improvementThe _GroupOrGroups[Any] recursion captures nested iterables of groups well. This aligns with how users actually pass groups to Sprite.
68-74
: Dropping the extra dirty-sprite protocol from DirtySprite is a welcome simplificationKeeping DirtySprite as a plain Sprite subtype reduces protocol clutter without losing typing precision.
110-124
: Deprecation wrappers and aliases are consistent with the runtime APIThe deprecation annotations and aliases for RenderPlain, RenderClear, RenderUpdates, and OrderedUpdates match user expectations and keep the surface area tidy.
125-140
: LayeredUpdates generics and return types are coherentSignatures now consistently use _SpriteT across add/get/change/move accessors. This should improve type inference in user code.
142-148
: LayeredDirty.draw: special_flags made Optional — good alignment with runtime behaviorThis matches the semantics where None means “use default blit flags,” and reads better than sentinel integers.
160-162
: GroupSingle typing tightenedUsing Optional[_SpriteT] for the sprite attribute and constructor matches real usage and avoids unnecessary Any.
193-201
: Collision helpers adopt the new generics cleanlySwitching to _HasRectT/_SpriteT keeps type relationships precise without over-constraining the APIs.
202-207
: groupcollide generic parameters and return map are correctly parameterizedThe dict[_SpriteT, list[_SpriteT2]] return type aligns with the function semantics.
209-212
: spritecollideany return type Optional[_SpriteT] is correctReflects None when no collision is found.
Typing fixes, simplifications, improvements, and renames for the sprite module.
Also see #3488, which had other fixes.
In the future, the
Optional
might get removed fromimage
andrect
, possibly with runtime changes. This would fix the last sprite typing issue and make it as comfortable to use static typing with as other pygame modules.Summary by CodeRabbit
New Features
Refactor
Bug Fixes
Documentation