Skip to content

Commit e6bc763

Browse files
committed
Changed Space.shapes/bodies/constraints to return a KeysView #275
1 parent e1a9c51 commit e6bc763

File tree

3 files changed

+58
-29
lines changed

3 files changed

+58
-29
lines changed

CHANGELOG.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
Changelog
33
=========
44
.. Pymunk 7.0.0
5+
Changed Space.shapes, Space.bodies and Space.constraints to return a KeysView of instead of a list of the items. Note that this means its no longer a copy. To get the old behavior, do list(space.shapes) etc.
6+
57
Breaking: At least one of the two bodies attached to constraint/joint must be dynamic.
68
New feature: Vec2d supports bool to test if zero. (bool(Vec2d(2,3) == True) Note this is a breaking change.
79
Added Vec2d.length_squared, and depreacted Vec2d.get_length_sqrd()
@@ -14,7 +16,7 @@ Changelog
1416
Changed body.shapes to return a KeysView instead of a set of the shapes.
1517
Changed Shape.segment_query to return None in case the query did not hit the shape.
1618
Changed ContactPointSet.points to be a tuple and not list to make it clear its length is fixed.
17-
19+
1820
Added default do_nothing and always_collide callback functions to the CollisionHandler, so that its clear how to reset and align with other callbacks.
1921
If in old code you did handler.begin = None, you should now instead to handler.begin = CollisionHandler.always_collide etc.
2022

pymunk/space.py

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import math
44
import platform
55
import weakref
6+
from collections.abc import KeysView
67
from typing import (
78
TYPE_CHECKING,
89
Any,
@@ -148,22 +149,47 @@ def spacefree(cp_space: ffi.CData) -> None:
148149
self._bodies_to_check: Set[Body] = set()
149150

150151
@property
151-
def shapes(self) -> List[Shape]:
152-
"""A list of all the shapes added to this space
152+
def shapes(self) -> KeysView[Shape]:
153+
"""The shapes added to this space returned as a KeysView.
153154
154-
(includes both static and non-static)
155+
Since its a view that is returned it will update as shapes are added::
156+
157+
>>> import pymunk
158+
>>> s = pymunk.Space()
159+
>>> s.add(pymunk.Circle(s.static_body, 1))
160+
>>> shapes_view = s.shapes
161+
>>> len(shapes_view)
162+
1
163+
>>> s.add(pymunk.Circle(s.static_body, 2))
164+
>>> len(shapes_view)
165+
2
155166
"""
156-
return list(self._shapes)
167+
return self._shapes.keys()
157168

158169
@property
159-
def bodies(self) -> List[Body]:
160-
"""A list of the bodies added to this space"""
161-
return list(self._bodies)
170+
def bodies(self) -> KeysView[Body]:
171+
"""The bodies added to this space returned as a KeysView.
172+
173+
This includes both static and non-static bodies added to the Space.
174+
175+
Since its a view that is returned it will update as bodies are added::
176+
177+
>>> import pymunk
178+
>>> s = pymunk.Space()
179+
>>> s.add(pymunk.Body())
180+
>>> bodies_view = s.bodies
181+
>>> len(bodies_view)
182+
1
183+
>>> s.add(pymunk.Body())
184+
>>> len(bodies_view)
185+
2
186+
"""
187+
return self._bodies.keys()
162188

163189
@property
164-
def constraints(self) -> List[Constraint]:
165-
"""A list of the constraints added to this space"""
166-
return list(self._constraints)
190+
def constraints(self) -> KeysView[Constraint]:
191+
"""The constraints added to this space as a KeysView."""
192+
return self._constraints.keys()
167193

168194
def _setup_static_body(self, static_body: Body) -> None:
169195
static_body._space = weakref.ref(self)
@@ -968,13 +994,13 @@ def __getstate__(self) -> _State:
968994

969995
d["special"].append(("pymunk_version", _version.version))
970996
# bodies needs to be added to the state before their shapes.
971-
d["special"].append(("bodies", self.bodies))
997+
d["special"].append(("bodies", list(self.bodies)))
972998
if self._static_body != None:
973999
# print("getstate", self._static_body)
9741000
d["special"].append(("_static_body", self._static_body))
9751001

976-
d["special"].append(("shapes", self.shapes))
977-
d["special"].append(("constraints", self.constraints))
1002+
d["special"].append(("shapes", list(self.shapes)))
1003+
d["special"].append(("constraints", list(self.constraints)))
9781004

9791005
handlers = []
9801006
for k, v in self._handlers.items():

pymunk/tests/test_space.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -102,18 +102,18 @@ def testSpatialHash(self) -> None:
102102
def testAddRemove(self) -> None:
103103
s = p.Space()
104104

105-
self.assertEqual(s.bodies, [])
106-
self.assertEqual(s.shapes, [])
105+
self.assertTrue(len(s.bodies) == 0)
106+
self.assertTrue(len(s.shapes) == 0)
107107

108108
b = p.Body(1, 2)
109109
s.add(b)
110-
self.assertEqual(s.bodies, [b])
111-
self.assertEqual(s.shapes, [])
110+
self.assertEqual(list(s.bodies), [b])
111+
self.assertTrue(len(s.shapes) == 0)
112112

113113
c1 = p.Circle(b, 10)
114114
s.add(c1)
115-
self.assertEqual(s.bodies, [b])
116-
self.assertEqual(s.shapes, [c1])
115+
self.assertEqual(list(s.bodies), [b])
116+
self.assertEqual(list(s.shapes), [c1])
117117

118118
c2 = p.Circle(b, 15)
119119
s.add(c2)
@@ -122,16 +122,16 @@ def testAddRemove(self) -> None:
122122
self.assertTrue(c2 in s.shapes)
123123

124124
s.remove(c1)
125-
self.assertEqual(s.shapes, [c2])
125+
self.assertEqual(list(s.shapes), [c2])
126126

127127
s.remove(c2, b)
128-
self.assertEqual(s.bodies, [])
129-
self.assertEqual(s.shapes, [])
128+
self.assertEqual(len(s.bodies), 0)
129+
self.assertEqual(len(s.shapes), 0)
130130

131131
# note that shape is before the body, which is something to test
132132
s.add(c2, b)
133-
self.assertEqual(s.bodies, [b])
134-
self.assertEqual(s.shapes, [c2])
133+
self.assertEqual(list(s.bodies), [b])
134+
self.assertEqual(list(s.shapes), [c2])
135135

136136
def testAddShapeAsserts(self) -> None:
137137
s1 = p.Space()
@@ -937,7 +937,7 @@ def pre_solve(arb: p.Arbiter, space: p.Space, data: Any) -> bool:
937937
ch = s.add_collision_handler(0, 0).pre_solve = pre_solve
938938

939939
s.step(0.1)
940-
self.assertEqual([], s.shapes)
940+
self.assertEqual(len(s.shapes), 0)
941941
self.assertEqual(self.calls, 1)
942942

943943
s.step(0.1)
@@ -1164,9 +1164,10 @@ def testPickleCachedArbiters(self) -> None:
11641164

11651165
# TODO: to assert that everything is working as it should all
11661166
# properties on the cached the arbiters should be asserted.
1167-
1168-
self.assertAlmostEqual(s.bodies[0].position.x, s_copy.bodies[0].position.x)
1169-
self.assertAlmostEqual(s.bodies[0].position.y, s_copy.bodies[0].position.y)
1167+
bodies = list(s.bodies)
1168+
copy_bodies = list(s_copy.bodies)
1169+
self.assertAlmostEqual(bodies[0].position.x, copy_bodies[0].position.x)
1170+
self.assertAlmostEqual(bodies[0].position.y, copy_bodies[0].position.y)
11701171

11711172
def testDeleteSpaceWithObjects(self) -> None:
11721173
s = p.Space()

0 commit comments

Comments
 (0)