Skip to content

Commit 799358f

Browse files
committed
Updated support prompt to indicate free-threaded builds, and GIL status on free-threaded builds
1 parent 94370b6 commit 799358f

File tree

2 files changed

+281
-1
lines changed

2 files changed

+281
-1
lines changed

src_py/__init__.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,9 +399,20 @@ def __color_reduce(c):
399399
copyreg.pickle(Color, __color_reduce, __color_constructor)
400400

401401
if "PYGAME_HIDE_SUPPORT_PROMPT" not in os.environ:
402+
python_implementation = sys.version
403+
python_version = platform.python_version()
404+
from packaging import version
405+
406+
if (
407+
sys.platform not in ("wasi", "wasm")
408+
and (version.parse(python_version) >= version.parse("3.13.0"))
409+
and "free-threading" in python_implementation
410+
):
411+
python_version += f"t, {'' if sys._is_gil_enabled() else 'No '}GIL"
412+
402413
print(
403414
f"pygame-ce {ver} (SDL {'.'.join(map(str, get_sdl_version()))}, "
404-
f"Python {platform.python_version()})"
415+
f"Python {python_version})"
405416
)
406417

407418
# cleanup namespace

src_py/gpusprite.py

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
import types
2+
from typing import Optional
3+
4+
import pygame
5+
from pygame._sdl2.video import Renderer, Texture
6+
7+
8+
class Sprite:
9+
__slots__ = {"__image", "__rect", "__g"}
10+
11+
def __init__(self, *groups):
12+
self.__g = {}
13+
self.__rect: Optional[pygame.Rect] = None
14+
self.__image: Optional[Texture] = None
15+
16+
for g in groups:
17+
self.add(g)
18+
19+
@property
20+
def image(self):
21+
return self.__image
22+
23+
@image.setter
24+
def image(self, value: Optional[Texture]):
25+
self.__image = value
26+
27+
@property
28+
def rect(self):
29+
return self.__rect
30+
31+
@rect.setter
32+
def rect(self, value: Optional[pygame.Rect]):
33+
self.__rect = value
34+
35+
def add(self, *groups):
36+
has = self.__g.__contains__
37+
for group in groups:
38+
if hasattr(group, "_spritegroup"):
39+
if not has(group):
40+
group.add_internal(self)
41+
self.add_internal(group)
42+
else:
43+
self.add(*group)
44+
45+
def remove(self, *groups):
46+
has = self.__g.__contains__
47+
for group in groups:
48+
if hasattr(group, "_spritegroup"):
49+
if has(group):
50+
group.remove_internal(self)
51+
self.remove_internal(group)
52+
else:
53+
self.remove(*group)
54+
55+
def add_internal(self, group):
56+
self.__g[group] = 0
57+
58+
def remove_internal(self, group):
59+
del self.__g[group]
60+
61+
def update(self, *args, **kwargs):
62+
pass
63+
64+
def kill(self):
65+
for group in self.__g:
66+
group.remove_internal(self)
67+
self.__g.clear()
68+
69+
def groups(self):
70+
return list(self.__g)
71+
72+
def alive(self):
73+
return bool(self.__g)
74+
75+
def __repr__(self):
76+
return f"<{self.__class__.__name__} Sprite(in {len(self.__g)} groups)>"
77+
78+
@property
79+
def layer(self):
80+
return getattr(self, "_layer")
81+
82+
@layer.setter
83+
def layer(self, value):
84+
if not self.alive():
85+
setattr(self, "_layer", value)
86+
else:
87+
raise AttributeError(
88+
"Can't set layer directly after "
89+
"adding to group. Use "
90+
"group.change_layer(sprite, new_layer) "
91+
"instead."
92+
)
93+
94+
95+
class AbstractGroup:
96+
__slots__ = ["spritedict", "lostsprites"]
97+
__class_getitem__ = classmethod(types.GenericAlias)
98+
99+
# protected identifier value to identify sprite groups, and avoid infinite recursion
100+
_spritegroup = True
101+
102+
def __init__(self):
103+
self.spritedict = {}
104+
self.lostsprites = []
105+
106+
def sprites(self):
107+
return list(self.spritedict)
108+
109+
def add_internal(
110+
self,
111+
sprite,
112+
):
113+
self.spritedict[sprite] = None
114+
115+
def remove_internal(self, sprite):
116+
if lost_rect := self.spritedict[sprite]:
117+
self.lostsprites.append(lost_rect)
118+
del self.spritedict[sprite]
119+
120+
def has_internal(self, sprite):
121+
return sprite in self.spritedict
122+
123+
def copy(self):
124+
return self.__class__( # noqa pylint: disable=too-many-function-args
125+
self.sprites() # Needed because copy() won't work on AbstractGroup
126+
)
127+
128+
def __iter__(self):
129+
return iter(self.sprites())
130+
131+
def __contains__(self, sprite):
132+
return self.has(sprite)
133+
134+
def add(self, *sprites):
135+
for sprite in sprites:
136+
# It's possible that some sprite is also an iterator.
137+
# If this is the case, we should add the sprite itself,
138+
# and not the iterator object.
139+
if isinstance(sprite, Sprite):
140+
if not self.has_internal(sprite):
141+
self.add_internal(sprite)
142+
sprite.add_internal(self)
143+
else:
144+
try:
145+
# See if sprite is an iterator, like a list or sprite
146+
# group.
147+
self.add(*sprite)
148+
except (TypeError, AttributeError):
149+
# Not iterable. This is probably a sprite that is not an
150+
# instance of the Sprite class or is not an instance of a
151+
# subclass of the Sprite class. Alternately, it could be an
152+
# old-style sprite group.
153+
if hasattr(sprite, "_spritegroup"):
154+
for spr in sprite.sprites():
155+
if not self.has_internal(spr):
156+
self.add_internal(spr)
157+
spr.add_internal(self)
158+
elif not self.has_internal(sprite):
159+
self.add_internal(sprite)
160+
sprite.add_internal(self)
161+
162+
def remove(self, *sprites):
163+
# This function behaves essentially the same as Group.add. It first
164+
# tries to handle each argument as an instance of the Sprite class. If
165+
# that fails, then it tries to handle the argument as an iterable
166+
# object. If that fails, then it tries to handle the argument as an
167+
# old-style sprite group. Lastly, if that fails, it assumes that the
168+
# normal Sprite methods should be used.
169+
for sprite in sprites:
170+
if isinstance(sprite, Sprite):
171+
if self.has_internal(sprite):
172+
self.remove_internal(sprite)
173+
sprite.remove_internal(self)
174+
else:
175+
try:
176+
self.remove(*sprite)
177+
except (TypeError, AttributeError):
178+
if hasattr(sprite, "_spritegroup"):
179+
for spr in sprite.sprites():
180+
if self.has_internal(spr):
181+
self.remove_internal(spr)
182+
spr.remove_internal(self)
183+
elif self.has_internal(sprite):
184+
self.remove_internal(sprite)
185+
sprite.remove_internal(self)
186+
187+
def has(self, *sprites):
188+
if not sprites:
189+
return False # return False if no sprites passed in
190+
191+
for sprite in sprites:
192+
if isinstance(sprite, Sprite):
193+
# Check for Sprite instance's membership in this group
194+
if not self.has_internal(sprite):
195+
return False
196+
else:
197+
try:
198+
if not self.has(*sprite):
199+
return False
200+
except (TypeError, AttributeError):
201+
if hasattr(sprite, "_spritegroup"):
202+
for spr in sprite.sprites():
203+
if not self.has_internal(spr):
204+
return False
205+
else:
206+
if not self.has_internal(sprite):
207+
return False
208+
209+
return True
210+
211+
def update(self, *args, **kwargs):
212+
for sprite in self.sprites():
213+
sprite.update(*args, **kwargs)
214+
215+
def draw(self, surface, bgd=None, special_flags=0): # noqa pylint: disable=unused-argument; bgd arg used in LayeredDirty
216+
sprites = self.sprites()
217+
if hasattr(surface, "blits"):
218+
self.spritedict.update(
219+
zip(
220+
sprites,
221+
surface.blits(
222+
(spr.image, spr.rect, None, special_flags) for spr in sprites
223+
),
224+
)
225+
)
226+
else:
227+
for spr in sprites:
228+
self.spritedict[spr] = surface.blit(
229+
spr.image, spr.rect, None, special_flags
230+
)
231+
self.lostsprites = []
232+
dirty = self.lostsprites
233+
234+
return dirty
235+
236+
def clear(self, surface, bgd):
237+
if callable(bgd):
238+
for lost_clear_rect in self.lostsprites:
239+
bgd(surface, lost_clear_rect)
240+
for clear_rect in self.spritedict.values():
241+
if clear_rect:
242+
bgd(surface, clear_rect)
243+
else:
244+
surface_blit = surface.blit
245+
for lost_clear_rect in self.lostsprites:
246+
surface_blit(bgd, lost_clear_rect, lost_clear_rect)
247+
for clear_rect in self.spritedict.values():
248+
if clear_rect:
249+
surface_blit(bgd, clear_rect, clear_rect)
250+
251+
def empty(self):
252+
for sprite in self.sprites():
253+
self.remove_internal(sprite)
254+
sprite.remove_internal(self)
255+
256+
def __bool__(self):
257+
return bool(self.sprites())
258+
259+
def __len__(self):
260+
return len(self.sprites())
261+
262+
def __repr__(self):
263+
return f"<{self.__class__.__name__}({len(self)} sprites)>"
264+
265+
266+
class Group(AbstractGroup):
267+
def __init__(self, *sprites):
268+
AbstractGroup.__init__(self)
269+
self.add(*sprites)

0 commit comments

Comments
 (0)