tcod-ecs 5.5.0__tar.gz → 5.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tcod_ecs-5.5.0 → tcod_ecs-5.5.2}/PKG-INFO +1 -1
- {tcod_ecs-5.5.0 → tcod_ecs-5.5.2}/pyproject.toml +1 -0
- tcod_ecs-5.5.2/tcod/ecs/_version.py +24 -0
- {tcod_ecs-5.5.0 → tcod_ecs-5.5.2}/tcod/ecs/constants.py +5 -3
- {tcod_ecs-5.5.0 → tcod_ecs-5.5.2}/tcod/ecs/entity.py +56 -16
- {tcod_ecs-5.5.0 → tcod_ecs-5.5.2}/tcod/ecs/query.py +9 -17
- {tcod_ecs-5.5.0 → tcod_ecs-5.5.2}/tcod/ecs/registry.py +6 -1
- tcod_ecs-5.5.0/tcod/ecs/_version.py +0 -34
- {tcod_ecs-5.5.0 → tcod_ecs-5.5.2}/LICENSE +0 -0
- {tcod_ecs-5.5.0 → tcod_ecs-5.5.2}/README.md +0 -0
- {tcod_ecs-5.5.0 → tcod_ecs-5.5.2}/tcod/ecs/__init__.py +0 -0
- {tcod_ecs-5.5.0 → tcod_ecs-5.5.2}/tcod/ecs/_converter.py +0 -0
- {tcod_ecs-5.5.0 → tcod_ecs-5.5.2}/tcod/ecs/callbacks.py +0 -0
- {tcod_ecs-5.5.0 → tcod_ecs-5.5.2}/tcod/ecs/py.typed +0 -0
- {tcod_ecs-5.5.0 → tcod_ecs-5.5.2}/tcod/ecs/typing.py +0 -0
- {tcod_ecs-5.5.0 → tcod_ecs-5.5.2}/tcod/ecs/world.py +0 -0
|
@@ -92,6 +92,7 @@ line-length = 120
|
|
|
92
92
|
select = ["ALL"]
|
|
93
93
|
ignore = [
|
|
94
94
|
"A005", # stdlib-module-shadowing, workaround VSCode treating all modules as local
|
|
95
|
+
"CPY001", # missing-copyright-notice
|
|
95
96
|
"COM", # flake8-commas, handled by formatter
|
|
96
97
|
"E501", # line-too-long
|
|
97
98
|
"S101", # assert
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '5.5.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (5, 5, 2)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = 'g60f279cbb'
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from typing_extensions import Never
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
class _IgnoreSetState(type):
|
|
7
9
|
def __setstate__(cls, _state: object) -> None:
|
|
@@ -11,9 +13,9 @@ class _IgnoreSetState(type):
|
|
|
11
13
|
class IsA(metaclass=_IgnoreSetState):
|
|
12
14
|
"""The default is-a relationship tag used for entity inheritance."""
|
|
13
15
|
|
|
14
|
-
def __new__(cls: type[IsA], *
|
|
15
|
-
|
|
16
|
-
return cls
|
|
16
|
+
def __new__(cls: type[IsA], *_: object) -> Never: # noqa: D102
|
|
17
|
+
# Return own type instead of instance, for outdated sentinel-value pickle data.
|
|
18
|
+
return cls # type: ignore[misc]
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
_sentinel_IsA = IsA # Compatibility with sentinel-value, deprecated since 5.4 # noqa: N816
|
|
@@ -9,6 +9,7 @@ from typing import (
|
|
|
9
9
|
Final,
|
|
10
10
|
Generic,
|
|
11
11
|
TypeVar,
|
|
12
|
+
cast,
|
|
12
13
|
overload,
|
|
13
14
|
)
|
|
14
15
|
from weakref import WeakKeyDictionary, WeakValueDictionary
|
|
@@ -419,9 +420,12 @@ class EntityComponents(MutableMapping[TypeForm[Any] | tuple[object, TypeForm[Any
|
|
|
419
420
|
assert self.__assert_key(key)
|
|
420
421
|
_components_by_entity = self.entity.registry._components_by_entity
|
|
421
422
|
for entity in _traverse_entities(self.entity, self.traverse):
|
|
423
|
+
components = _components_by_entity.get(entity)
|
|
424
|
+
if components is None:
|
|
425
|
+
continue
|
|
422
426
|
try:
|
|
423
|
-
return
|
|
424
|
-
except KeyError:
|
|
427
|
+
return components[key] # type: ignore[no-any-return]
|
|
428
|
+
except KeyError:
|
|
425
429
|
pass
|
|
426
430
|
raise KeyError(key)
|
|
427
431
|
|
|
@@ -443,10 +447,13 @@ class EntityComponents(MutableMapping[TypeForm[Any] | tuple[object, TypeForm[Any
|
|
|
443
447
|
"""Delete a directly held component from an entity."""
|
|
444
448
|
assert self.__assert_key(key)
|
|
445
449
|
|
|
446
|
-
|
|
450
|
+
components = self.entity.registry._components_by_entity.get(self.entity)
|
|
451
|
+
if components is None:
|
|
452
|
+
raise KeyError(key)
|
|
453
|
+
old_value = components.get(key)
|
|
447
454
|
|
|
448
|
-
del
|
|
449
|
-
if not
|
|
455
|
+
del components[key]
|
|
456
|
+
if not components:
|
|
450
457
|
del self.entity.registry._components_by_entity[self.entity]
|
|
451
458
|
|
|
452
459
|
del self.entity.registry._components_by_type[key][self.entity]
|
|
@@ -588,8 +595,12 @@ class EntityComponents(MutableMapping[TypeForm[Any] | tuple[object, TypeForm[Any
|
|
|
588
595
|
def clear(self) -> None:
|
|
589
596
|
"""Remove any components stored directly in this entity."""
|
|
590
597
|
if self.traverse:
|
|
591
|
-
|
|
592
|
-
|
|
598
|
+
self(traverse=()).clear()
|
|
599
|
+
return
|
|
600
|
+
super().clear()
|
|
601
|
+
# MutableMapping.clear() only calls __delitem__ for existing components,
|
|
602
|
+
# so it leaves an already empty registry row behind.
|
|
603
|
+
self.entity.registry._components_by_entity.pop(self.entity, None)
|
|
593
604
|
|
|
594
605
|
|
|
595
606
|
@attrs.define(eq=False, frozen=True, weakref_slot=False)
|
|
@@ -602,6 +613,11 @@ class EntityTags(MutableSet[Any]):
|
|
|
602
613
|
entity: Entity
|
|
603
614
|
traverse: tuple[object, ...]
|
|
604
615
|
|
|
616
|
+
@classmethod
|
|
617
|
+
def _from_iterable(cls, it: Iterable[_T1]) -> set[_T1]:
|
|
618
|
+
"""Return an independent set for the inherited set operations."""
|
|
619
|
+
return set(it)
|
|
620
|
+
|
|
605
621
|
def __call__(self, *, traverse: Iterable[object]) -> Self:
|
|
606
622
|
"""Update this view with alternative parameters, such as a specific traversal relation.
|
|
607
623
|
|
|
@@ -620,7 +636,7 @@ class EntityTags(MutableSet[Any]):
|
|
|
620
636
|
|
|
621
637
|
def discard(self, tag: object) -> None:
|
|
622
638
|
"""Discard a tag directly held by an entity."""
|
|
623
|
-
if tag not in self.entity.registry._tags_by_entity
|
|
639
|
+
if tag not in self.entity.registry._tags_by_entity.get(self.entity, ()):
|
|
624
640
|
return # Already doesn't have tag
|
|
625
641
|
tcod.ecs.query._touch_tag(self.entity.registry, tag) # Tag removed
|
|
626
642
|
|
|
@@ -642,8 +658,9 @@ class EntityTags(MutableSet[Any]):
|
|
|
642
658
|
def clear(self) -> None:
|
|
643
659
|
"""Remove all tags directly in this entity."""
|
|
644
660
|
_tags_by_entity = self.entity.registry._tags_by_entity
|
|
645
|
-
while _tags_by_entity
|
|
646
|
-
self.discard(next(iter(
|
|
661
|
+
while tags := _tags_by_entity.get(self.entity):
|
|
662
|
+
self.discard(next(iter(tags)))
|
|
663
|
+
_tags_by_entity.pop(self.entity, None)
|
|
647
664
|
|
|
648
665
|
def __contains__(self, x: object) -> bool:
|
|
649
666
|
"""Return True if this entity has the given tag."""
|
|
@@ -695,6 +712,12 @@ def _relations_lookup_add(registry: Registry, origin: Entity, tag: object, targe
|
|
|
695
712
|
|
|
696
713
|
def _relations_lookup_discard(registry: Registry, origin: Entity, tag: object, target: Entity) -> None:
|
|
697
714
|
"""Discard a relation tag/component from the lookup table and handle side effects."""
|
|
715
|
+
tags = registry._relation_tags_by_entity.get(origin)
|
|
716
|
+
if tags is not None and target in tags.get(tag, ()):
|
|
717
|
+
return # A tag relation still owns this index entry.
|
|
718
|
+
components = registry._relation_components_by_entity.get(origin)
|
|
719
|
+
if components is not None and target in components.get(cast("ComponentKey[object]", tag), ()):
|
|
720
|
+
return # A component relation still owns this index entry.
|
|
698
721
|
registry._relations_lookup[(tag, target)].discard(origin)
|
|
699
722
|
if not registry._relations_lookup[(tag, target)]:
|
|
700
723
|
del registry._relations_lookup[(tag, target)]
|
|
@@ -725,6 +748,11 @@ class EntityRelationsMapping(MutableSet[Entity]):
|
|
|
725
748
|
key: object
|
|
726
749
|
traverse: tuple[object, ...]
|
|
727
750
|
|
|
751
|
+
@classmethod
|
|
752
|
+
def _from_iterable(cls, it: Iterable[_T1]) -> set[_T1]:
|
|
753
|
+
"""Return an independent set for the inherited set operations."""
|
|
754
|
+
return set(it)
|
|
755
|
+
|
|
728
756
|
if __debug__:
|
|
729
757
|
|
|
730
758
|
def __attrs_post_init__(self) -> None:
|
|
@@ -826,6 +854,7 @@ class EntityRelations(MutableMapping[object, EntityRelationsMapping]):
|
|
|
826
854
|
def __setitem__(self, key: object, values: Iterable[Entity]) -> None:
|
|
827
855
|
"""Overwrite the targets of a relation tag with the new values."""
|
|
828
856
|
assert not isinstance(values, Entity), "Did you mean `entity.relations[key] = (target,)`?"
|
|
857
|
+
values = tuple(values) # Values may be a live view of the mapping being replaced.
|
|
829
858
|
mapping = EntityRelationsMapping(self.entity, key, self.traverse)
|
|
830
859
|
mapping.clear()
|
|
831
860
|
for v in values:
|
|
@@ -978,10 +1007,16 @@ class EntityComponentRelationMapping(MutableMapping[Entity, T], Generic[T]):
|
|
|
978
1007
|
def __delitem__(self, target: Entity) -> None:
|
|
979
1008
|
"""Delete a component assigned to the target entity."""
|
|
980
1009
|
registry = self.entity.registry
|
|
981
|
-
|
|
982
|
-
if
|
|
983
|
-
|
|
984
|
-
|
|
1010
|
+
by_entity = registry._relation_components_by_entity.get(self.entity)
|
|
1011
|
+
if by_entity is None:
|
|
1012
|
+
raise KeyError(target)
|
|
1013
|
+
by_key = by_entity.get(self.key)
|
|
1014
|
+
if by_key is None:
|
|
1015
|
+
raise KeyError(target)
|
|
1016
|
+
del by_key[target]
|
|
1017
|
+
if not by_key:
|
|
1018
|
+
del by_entity[self.key]
|
|
1019
|
+
if not by_entity:
|
|
985
1020
|
del registry._relation_components_by_entity[self.entity]
|
|
986
1021
|
|
|
987
1022
|
_relations_lookup_discard(registry, self.entity, self.key, target)
|
|
@@ -1047,11 +1082,16 @@ class EntityComponentRelations(MutableMapping[ComponentKey[Any], EntityComponent
|
|
|
1047
1082
|
|
|
1048
1083
|
..versionadded:: 4.2.0
|
|
1049
1084
|
"""
|
|
1050
|
-
if
|
|
1085
|
+
if (
|
|
1086
|
+
isinstance(__values, EntityComponentRelationMapping)
|
|
1087
|
+
and __values.entity is self.entity
|
|
1088
|
+
and __values.key == __key
|
|
1089
|
+
):
|
|
1051
1090
|
return
|
|
1091
|
+
items = tuple(__values.items()) # The input may inherit values from the mapping being replaced.
|
|
1052
1092
|
mapping: EntityComponentRelationMapping[T] = self[__key]
|
|
1053
1093
|
mapping.clear()
|
|
1054
|
-
for target, component in
|
|
1094
|
+
for target, component in items:
|
|
1055
1095
|
mapping[target] = component
|
|
1056
1096
|
|
|
1057
1097
|
def __delitem__(self, key: ComponentKey[object]) -> None:
|
|
@@ -6,7 +6,7 @@ import itertools
|
|
|
6
6
|
import warnings
|
|
7
7
|
from collections import defaultdict
|
|
8
8
|
from typing import TYPE_CHECKING, Any, Protocol, TypeVar, overload
|
|
9
|
-
from weakref import
|
|
9
|
+
from weakref import WeakSet
|
|
10
10
|
|
|
11
11
|
import attrs
|
|
12
12
|
from typing_extensions import Self, deprecated
|
|
@@ -29,10 +29,6 @@ _T4 = TypeVar("_T4")
|
|
|
29
29
|
_T5 = TypeVar("_T5")
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
_query_caches: WeakKeyDictionary[Registry, _QueryCache] = WeakKeyDictionary()
|
|
33
|
-
"""The master table of cached queries."""
|
|
34
|
-
|
|
35
|
-
|
|
36
32
|
@attrs.define
|
|
37
33
|
class _QueryCache:
|
|
38
34
|
"""Main data structure for the query cache."""
|
|
@@ -59,7 +55,7 @@ def _drop_cached_query(cache: _QueryCache, query: _Query) -> None:
|
|
|
59
55
|
"""Drop a cached query and all of its dependant queries."""
|
|
60
56
|
cache.queries.pop(query, None)
|
|
61
57
|
for sub_registry, sub_query in cache.dependencies.pop(query, ()):
|
|
62
|
-
_drop_cached_query(
|
|
58
|
+
_drop_cached_query(_get_query_cache(sub_registry), sub_query)
|
|
63
59
|
|
|
64
60
|
|
|
65
61
|
def _touch_component(registry: Registry, component: ComponentKey[object]) -> None:
|
|
@@ -126,11 +122,8 @@ def _fetch_relation_table(registry: Registry, relation: _RelationQuery) -> Abstr
|
|
|
126
122
|
|
|
127
123
|
|
|
128
124
|
def _get_query_cache(registry: Registry) -> _QueryCache:
|
|
129
|
-
"""Return the
|
|
130
|
-
|
|
131
|
-
if cache is None:
|
|
132
|
-
cache = _query_caches[registry] = _QueryCache()
|
|
133
|
-
return cache
|
|
125
|
+
"""Return the cache owned by the given registry."""
|
|
126
|
+
return registry._query_cache
|
|
134
127
|
|
|
135
128
|
|
|
136
129
|
def _get_query(registry: Registry, query: _Query) -> AbstractSet[Entity]:
|
|
@@ -237,10 +230,6 @@ class _QueryLogicalAnd:
|
|
|
237
230
|
_all_of: frozenset[_Query] = frozenset()
|
|
238
231
|
_none_of: frozenset[_Query] = frozenset()
|
|
239
232
|
|
|
240
|
-
def __attrs_post_init__(self) -> None:
|
|
241
|
-
"""Verify the current state."""
|
|
242
|
-
assert self._all_of.isdisjoint(self._none_of)
|
|
243
|
-
|
|
244
233
|
def _add_to_cache(self, registry: Registry, cache: _QueryCache) -> None:
|
|
245
234
|
for dependency in itertools.chain(self._all_of, self._none_of):
|
|
246
235
|
cache.dependencies[dependency].add((registry, self))
|
|
@@ -465,13 +454,16 @@ class BoundQuery:
|
|
|
465
454
|
Entity = tcod.ecs.entity.Entity # noqa: N806
|
|
466
455
|
|
|
467
456
|
entities = list(self.all_of(components=set(key) - {Entity}).get_entities())
|
|
468
|
-
entity_components = []
|
|
457
|
+
entity_components: list[list[Any]] = []
|
|
469
458
|
for component_key in key:
|
|
470
459
|
if component_key is Entity:
|
|
471
460
|
entity_components.append(entities)
|
|
472
461
|
continue
|
|
473
462
|
registry_components = self.registry._components_by_type[component_key]
|
|
474
|
-
|
|
463
|
+
try:
|
|
464
|
+
entity_components.append([registry_components[entity] for entity in entities])
|
|
465
|
+
except KeyError: # Some matching entities inherit this component.
|
|
466
|
+
entity_components.append([entity.components[component_key] for entity in entities])
|
|
475
467
|
return zip(*entity_components, strict=True)
|
|
476
468
|
|
|
477
469
|
|
|
@@ -13,6 +13,7 @@ from typing_extensions import deprecated
|
|
|
13
13
|
import tcod.ecs._converter
|
|
14
14
|
import tcod.ecs.query
|
|
15
15
|
from tcod.ecs.entity import Entity
|
|
16
|
+
from tcod.ecs.query import _QueryCache
|
|
16
17
|
|
|
17
18
|
if TYPE_CHECKING:
|
|
18
19
|
from tcod.ecs.typing import ComponentKey, _RelationTargetLookup
|
|
@@ -71,7 +72,7 @@ def _relations_lookup_from(
|
|
|
71
72
|
for component_key, target_components in components.items():
|
|
72
73
|
for target in target_components:
|
|
73
74
|
relations_lookup[(component_key, ...)].add(origin)
|
|
74
|
-
relations_lookup[(component_key,
|
|
75
|
+
relations_lookup[(component_key, target)].add(origin)
|
|
75
76
|
relations_lookup[(origin, component_key, None)].add(target)
|
|
76
77
|
relations_lookup[(..., component_key, None)].add(target)
|
|
77
78
|
|
|
@@ -152,6 +153,9 @@ class Registry:
|
|
|
152
153
|
dict[Entity] = entities_name
|
|
153
154
|
"""
|
|
154
155
|
|
|
156
|
+
_query_cache: _QueryCache = attrs.field(init=False, factory=_QueryCache, repr=False)
|
|
157
|
+
"""Transient query cache, owned here so cached entities do not keep registries alive globally."""
|
|
158
|
+
|
|
155
159
|
@property
|
|
156
160
|
@deprecated(
|
|
157
161
|
"The 'registry.global_' attribute has been deprecated. Use 'registry[None]' to access this entity.",
|
|
@@ -178,6 +182,7 @@ class Registry:
|
|
|
178
182
|
|
|
179
183
|
def __setstate__(self, state: dict[str, Any]) -> None:
|
|
180
184
|
"""Unpickle this object and handle state migration."""
|
|
185
|
+
self._query_cache = _QueryCache()
|
|
181
186
|
global_: Entity | None = state.pop("global_", None) # Migrate from version <=1.2.0
|
|
182
187
|
|
|
183
188
|
# These attributes contain redundant data and will be removed
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# file generated by setuptools-scm
|
|
2
|
-
# don't change, don't track in version control
|
|
3
|
-
|
|
4
|
-
__all__ = [
|
|
5
|
-
"__version__",
|
|
6
|
-
"__version_tuple__",
|
|
7
|
-
"version",
|
|
8
|
-
"version_tuple",
|
|
9
|
-
"__commit_id__",
|
|
10
|
-
"commit_id",
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
TYPE_CHECKING = False
|
|
14
|
-
if TYPE_CHECKING:
|
|
15
|
-
from typing import Tuple
|
|
16
|
-
from typing import Union
|
|
17
|
-
|
|
18
|
-
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
-
COMMIT_ID = Union[str, None]
|
|
20
|
-
else:
|
|
21
|
-
VERSION_TUPLE = object
|
|
22
|
-
COMMIT_ID = object
|
|
23
|
-
|
|
24
|
-
version: str
|
|
25
|
-
__version__: str
|
|
26
|
-
__version_tuple__: VERSION_TUPLE
|
|
27
|
-
version_tuple: VERSION_TUPLE
|
|
28
|
-
commit_id: COMMIT_ID
|
|
29
|
-
__commit_id__: COMMIT_ID
|
|
30
|
-
|
|
31
|
-
__version__ = version = '5.5.0'
|
|
32
|
-
__version_tuple__ = version_tuple = (5, 5, 0)
|
|
33
|
-
|
|
34
|
-
__commit_id__ = commit_id = 'gf2ee28b92'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|