Skip to content

Commit 098f9a1

Browse files
committed
Add and test clear methods for attributes
1 parent d48de23 commit 098f9a1

File tree

3 files changed

+74
-4
lines changed

3 files changed

+74
-4
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Allow setting the `traverse` state of an `entity.component_tags[type][entity](traverse=...)` attribute.
13+
1014
### Changed
1115

1216
- Updated deprecations to use [PEP 702](https://peps.python.org/pep-0702/).
1317

18+
### Fixed
19+
20+
- Fixed `.clear` methods for `entity.components` and `entity.component_relations`.
21+
1422
## [5.3.0] - 2025-03-08
1523

1624
### Added

tcod/ecs/entity.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,10 @@ def clear(self) -> None:
124124
125125
.. versionadded:: 4.2.0
126126
"""
127-
self.components(traverse=()).clear()
128-
self.tags(traverse=()).clear()
129-
self.relation_tags_many(traverse=()).clear()
130-
self.relation_components(traverse=()).clear()
127+
self.components.clear()
128+
self.tags.clear()
129+
self.relation_tags_many.clear()
130+
self.relation_components.clear()
131131

132132
def instantiate(self) -> Self:
133133
"""Return a new entity which inherits the components, tags, and relations of this entity.
@@ -594,6 +594,12 @@ def pop(
594594
del self[__key]
595595
return value
596596

597+
def clear(self) -> None:
598+
"""Remove any components stored directly in this entity."""
599+
if self.traverse:
600+
return self(traverse=()).clear()
601+
return super().clear()
602+
597603

598604
@attrs.define(eq=False, frozen=True, weakref_slot=False)
599605
class EntityTags(MutableSet[Any]):
@@ -942,6 +948,13 @@ def __attrs_post_init__(self) -> None:
942948
"""Validate attributes."""
943949
assert isinstance(self.entity, Entity), self.entity
944950

951+
def __call__(self, *, traverse: Iterable[object]) -> Self:
952+
"""Update this view with alternative parameters, such as a specific traversal relation.
953+
954+
.. versionadded:: Unreleased
955+
"""
956+
return self.__class__(self.entity, self.key, tuple(traverse))
957+
945958
def __getitem__(self, target: Entity) -> T:
946959
"""Return the component related to a target entity."""
947960
_relation_components_by_entity = self.entity.registry._relation_components_by_entity
@@ -1001,6 +1014,12 @@ def __len__(self) -> int:
10011014
"""Return the count of targets for this component relation."""
10021015
return len(self.keys())
10031016

1017+
def clear(self) -> None:
1018+
"""Remove any components stored directly in this entity relation."""
1019+
if self.traverse:
1020+
return self(traverse=()).clear()
1021+
return super().clear()
1022+
10041023

10051024
@attrs.define(eq=False, frozen=True, weakref_slot=False)
10061025
class EntityComponentRelations(MutableMapping[ComponentKey[Any], EntityComponentRelationMapping[Any]]):

tests/test_relations.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,46 @@ def test_relation_tag_tables() -> None:
168168
assert set(w.Q.all_of(relations=[(..., "tag", None)])) == {e2}
169169
assert set(w.Q.all_of(relations=[(e1, "tag", None)])) == {e2}
170170
assert not set(w.Q.all_of(relations=[(e3, "tag", None)]))
171+
172+
173+
def test_clear_with_relation() -> None:
174+
world = tcod.ecs.Registry()
175+
parent = world["parent"]
176+
child = parent.instantiate()
177+
entity_other = world[object()]
178+
for i in range(5):
179+
parent.components[(i, int)] = i
180+
parent.tags.add(i)
181+
parent.relation_components[(i, int)][entity_other] = i
182+
parent.relation_tag[i] = entity_other
183+
184+
for i in range(10):
185+
child.components[(i, int)] = i
186+
child.tags.add(i)
187+
child.relation_components[(i, int)][entity_other] = i
188+
child.relation_tag[i] = entity_other
189+
190+
components_all = {(i, int) for i in range(10)}
191+
components_after = {(i, int) for i in range(5)}
192+
tags_all = set(range(10))
193+
tags_after = set(range(5))
194+
195+
assert child.components.keys() == components_all
196+
assert child.relation_components.keys() == components_all
197+
assert set(child.tags) == tags_all
198+
assert set(child.relation_tag.keys()) == tags_all | {tcod.ecs.IsA}
199+
200+
# Clear direct values, keeping values from parent as long as the IsA relation exists
201+
child.components.clear()
202+
assert child.components.keys() == components_after
203+
child.relation_components.clear()
204+
assert child.relation_components.keys() == components_after
205+
child.tags.clear()
206+
assert set(child.tags) == tags_after
207+
208+
# Clearing relation_tags means breaking the IsA link to the parent
209+
child.relation_tag.clear()
210+
assert not child.relation_tag.keys()
211+
assert not child.components.keys()
212+
assert not child.tags
213+
assert not child.relation_components.keys()

0 commit comments

Comments
 (0)