tcod-ecs 5.4.0__tar.gz → 5.4.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.
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tcod-ecs
3
- Version: 5.4.0
3
+ Version: 5.4.2
4
4
  Summary: A type-hinted Entity Component System based on Python dictionaries and sets.
5
5
  Author-email: Kyle Benesch <4b796c65+github@gmail.com>
6
- Requires-Python: >=3.8
6
+ Requires-Python: >=3.10
7
7
  Description-Content-Type: text/markdown
8
8
  Classifier: Development Status :: 5 - Production/Stable
9
9
  Classifier: Intended Audience :: Developers
@@ -16,12 +16,12 @@ License-File: LICENSE
16
16
  Requires-Dist: attrs >=23.1.0
17
17
  Requires-Dist: cattrs >=23.1.2
18
18
  Requires-Dist: sentinel-value >=1.0.0
19
- Requires-Dist: typing-extensions >=4.9.0
19
+ Requires-Dist: typing-extensions >=4.13.1
20
20
  Requires-Dist: attrs >=22.2.0 ; extra == "test"
21
21
  Requires-Dist: pytest >=7.2.0 ; extra == "test"
22
22
  Requires-Dist: pytest-cov >=4.0.0 ; extra == "test"
23
23
  Requires-Dist: pytest-benchmark >=4.0.0 ; extra == "test"
24
- Requires-Dist: mypy >=1.1.1 ; extra == "test"
24
+ Requires-Dist: mypy >=1.17.0 ; extra == "test"
25
25
  Project-URL: Changelog, https://github.com/HexDecimal/python-tcod-ecs/blob/main/CHANGELOG.md
26
26
  Project-URL: Documentation, https://python-tcod-ecs.readthedocs.io
27
27
  Project-URL: Home, https://github.com/HexDecimal
@@ -17,12 +17,12 @@ classifiers = [
17
17
  "Typing :: Typed",
18
18
  ]
19
19
  dynamic = ["version", "description"]
20
- requires-python = ">=3.8"
20
+ requires-python = ">=3.10"
21
21
  dependencies = [
22
22
  "attrs >=23.1.0",
23
23
  "cattrs >=23.1.2",
24
24
  "sentinel-value >=1.0.0",
25
- "typing-extensions >=4.9.0",
25
+ "typing-extensions >=4.13.1",
26
26
  ]
27
27
 
28
28
  [tool.setuptools_scm]
@@ -34,7 +34,7 @@ test = [
34
34
  "pytest >=7.2.0",
35
35
  "pytest-cov >=4.0.0",
36
36
  "pytest-benchmark >=4.0.0",
37
- "mypy >=1.1.1",
37
+ "mypy >=1.17.0",
38
38
  ]
39
39
 
40
40
  [tool.flit.module]
@@ -50,7 +50,8 @@ Source = "https://github.com/HexDecimal/python-tcod-ecs"
50
50
  files = "."
51
51
  exclude = ['^build/', '^\.']
52
52
  explicit_package_bases = true
53
- python_version = "3.10" # Type check Python version with EllipsisType
53
+ python_version = "3.10"
54
+ enable_incomplete_feature = ["TypeForm"]
54
55
  warn_unused_configs = true
55
56
  disallow_any_generics = true
56
57
  disallow_subclassing_any = true
@@ -65,6 +66,8 @@ warn_unused_ignores = true
65
66
  warn_return_any = true
66
67
  no_implicit_reexport = true
67
68
  strict_equality = true
69
+ strict_bytes = true
70
+ extra_checks = true
68
71
 
69
72
  [tool.pyright]
70
73
  reportInconsistentOverload = false
@@ -4,12 +4,21 @@ from __future__ import annotations
4
4
 
5
5
  import importlib.metadata
6
6
  from collections import defaultdict
7
- from typing import TypeVar
7
+ from typing import TYPE_CHECKING, TypeVar
8
8
 
9
9
  from tcod.ecs.constants import IsA
10
10
  from tcod.ecs.entity import Entity
11
11
  from tcod.ecs.registry import Registry
12
- from tcod.ecs.registry import Registry as World
12
+
13
+ if TYPE_CHECKING:
14
+ from typing_extensions import deprecated
15
+
16
+ @deprecated("Renamed to Registry.")
17
+ class World(Registry): # noqa: D101
18
+ pass
19
+
20
+ else:
21
+ from tcod.ecs.registry import Registry as World
13
22
 
14
23
  __all__ = (
15
24
  "Entity",
@@ -2,10 +2,13 @@ from __future__ import annotations
2
2
 
3
3
  import functools
4
4
  from collections import defaultdict
5
- from typing import Any, Callable, get_args, get_origin
5
+ from typing import TYPE_CHECKING, Any, get_args, get_origin
6
6
 
7
7
  import cattrs
8
8
 
9
+ if TYPE_CHECKING:
10
+ from collections.abc import Callable
11
+
9
12
 
10
13
  def _is_defaultdict_type(type_hint: object) -> bool:
11
14
  """Return True if `type_hint` is a defaultdict type-hint."""
@@ -14,7 +17,7 @@ def _is_defaultdict_type(type_hint: object) -> bool:
14
17
 
15
18
  def _setup_defaultdict_factory(type_hint: type[defaultdict[Any, Any] | object]) -> Callable[[], Any]:
16
19
  """Return the factory value for a defaultdict given its value type-hint."""
17
- assert type_hint is not Any # type: ignore[comparison-overlap]
20
+ assert type_hint is not Any
18
21
  if get_origin(type_hint) is not defaultdict:
19
22
  return get_origin(type_hint) or type_hint
20
23
  return functools.partial(defaultdict, _setup_defaultdict_factory(get_args(type_hint)[1]))
@@ -1,7 +1,14 @@
1
1
  # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
3
 
4
- __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
5
12
 
6
13
  TYPE_CHECKING = False
7
14
  if TYPE_CHECKING:
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
9
16
  from typing import Union
10
17
 
11
18
  VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
12
20
  else:
13
21
  VERSION_TUPLE = object
22
+ COMMIT_ID = object
14
23
 
15
24
  version: str
16
25
  __version__: str
17
26
  __version_tuple__: VERSION_TUPLE
18
27
  version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
19
30
 
20
- __version__ = version = '5.4.0'
21
- __version_tuple__ = version_tuple = (5, 4, 0)
31
+ __version__ = version = '5.4.2'
32
+ __version_tuple__ = version_tuple = (5, 4, 2)
33
+
34
+ __commit_id__ = commit_id = 'g99e849adf'
@@ -2,9 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union
6
-
7
- from typing_extensions import TypeAlias
5
+ from collections.abc import Callable
6
+ from typing import TYPE_CHECKING, Any, TypeAlias, TypeVar
8
7
 
9
8
  if TYPE_CHECKING:
10
9
  from tcod.ecs.entity import Entity
@@ -12,7 +11,7 @@ if TYPE_CHECKING:
12
11
 
13
12
  _T = TypeVar("_T")
14
13
 
15
- _OnComponentChangedFunc: TypeAlias = Callable[["Entity", Union[_T, None], Union[_T, None]], None]
14
+ _OnComponentChangedFunc: TypeAlias = Callable[["Entity", _T | None, _T | None], None]
16
15
  _OnComponentChangedFuncT = TypeVar("_OnComponentChangedFuncT", bound=_OnComponentChangedFunc[Any])
17
16
 
18
17
  _on_component_changed_callbacks: dict[ComponentKey[Any], list[_OnComponentChangedFunc[Any]]] = {}
@@ -2,27 +2,20 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from collections.abc import Iterable, Iterator, Mapping, MutableMapping, MutableSet
5
6
  from typing import (
6
7
  TYPE_CHECKING,
7
8
  Any,
8
9
  Final,
9
10
  Generic,
10
- Iterable,
11
- Iterator,
12
- Mapping,
13
- MutableMapping,
14
- MutableSet,
15
- Tuple,
16
- Type,
17
11
  TypeVar,
18
- Union,
19
12
  overload,
20
13
  )
21
14
  from weakref import WeakKeyDictionary, WeakValueDictionary
22
15
 
23
16
  import attrs
24
17
  from sentinel_value import sentinel
25
- from typing_extensions import Self, deprecated
18
+ from typing_extensions import Self, TypeForm, deprecated
26
19
 
27
20
  import tcod.ecs.callbacks
28
21
  import tcod.ecs.query
@@ -389,7 +382,7 @@ def _traverse_entities(start: Entity, traverse_parents: tuple[object, ...]) -> I
389
382
 
390
383
 
391
384
  @attrs.define(eq=False, frozen=True, weakref_slot=False)
392
- class EntityComponents(MutableMapping[Union[Type[Any], Tuple[object, Type[Any]]], object]):
385
+ class EntityComponents(MutableMapping[TypeForm[Any] | tuple[object, TypeForm[Any]], object]):
393
386
  """A proxy attribute to access an entities components like a dictionary.
394
387
 
395
388
  See :any:`Entity.components`.
@@ -447,7 +440,7 @@ class EntityComponents(MutableMapping[Union[Type[Any], Tuple[object, Type[Any]]]
447
440
 
448
441
  tcod.ecs.callbacks._on_component_changed(key, self.entity, old_value, value)
449
442
 
450
- def __delitem__(self, key: type[object] | tuple[object, type[object]]) -> None:
443
+ def __delitem__(self, key: TypeForm[object] | tuple[object, TypeForm[object]]) -> None:
451
444
  """Delete a directly held component from an entity."""
452
445
  assert self.__assert_key(key)
453
446
 
@@ -474,8 +467,8 @@ class EntityComponents(MutableMapping[Union[Type[Any], Tuple[object, Type[Any]]]
474
467
  *(_components_by_entity.get(entity, ()) for entity in _traverse_entities(self.entity, self.traverse))
475
468
  )
476
469
 
477
- def __contains__(self, key: ComponentKey[object]) -> bool: # type: ignore[override]
478
- """Return True if this entity has the provided component."""
470
+ def __contains__(self, key: object) -> bool:
471
+ """Return True if this entity has the provided component key."""
479
472
  _components_by_entity = self.entity.registry._components_by_entity
480
473
  return any(
481
474
  key in _components_by_entity.get(entity, ()) for entity in _traverse_entities(self.entity, self.traverse)
@@ -500,7 +493,7 @@ class EntityComponents(MutableMapping[Union[Type[Any], Tuple[object, Type[Any]]]
500
493
  self.set(value)
501
494
 
502
495
  @deprecated("This method has been deprecated. Iterate over items instead.", category=FutureWarning)
503
- def by_name_type(self, name_type: type[_T1], component_type: type[_T2]) -> Iterator[tuple[_T1, type[_T2]]]:
496
+ def by_name_type(self, name_type: type[_T1], component_type: TypeForm[_T2]) -> Iterator[tuple[_T1, TypeForm[_T2]]]:
504
497
  """Iterate over all of an entities component keys with a specific (name_type, component_type) combination.
505
498
 
506
499
  .. versionadded:: 3.0
@@ -508,13 +501,12 @@ class EntityComponents(MutableMapping[Union[Type[Any], Tuple[object, Type[Any]]]
508
501
  .. deprecated:: 3.1
509
502
  This method has been deprecated. Iterate over items instead.
510
503
  """
511
- # Naive implementation until I feel like optimizing it
512
504
  for key in self:
513
505
  if not isinstance(key, tuple):
514
506
  continue
515
507
  key_name, key_component = key
516
508
  if key_component is component_type and isinstance(key_name, name_type):
517
- yield key_name, key_component
509
+ yield key_name, key_component # type: ignore[unused-ignore] # Too complex for PyLance, deprecated anyways
518
510
 
519
511
  @overload
520
512
  def __ior__(self, value: SupportsKeysAndGetItem[ComponentKey[Any], Any]) -> Self: ...
@@ -544,7 +536,7 @@ class EntityComponents(MutableMapping[Union[Type[Any], Tuple[object, Type[Any]]]
544
536
  except KeyError:
545
537
  return default # type: ignore[return-value] # https://github.com/python/mypy/issues/3737
546
538
 
547
- def setdefault(self, __key: ComponentKey[T], __default: T, /) -> T:
539
+ def setdefault(self, __key: ComponentKey[T], __default: T, /) -> T: # type: ignore[override] # Disallows default None
548
540
  """Assign a default value if a component is missing, then returns the current value."""
549
541
  try:
550
542
  return self[__key]
@@ -932,7 +924,7 @@ class EntityRelationsExclusive(MutableMapping[object, Entity]):
932
924
 
933
925
 
934
926
  @attrs.define(eq=False, frozen=True, weakref_slot=False)
935
- class EntityComponentRelationMapping(Generic[T], MutableMapping[Entity, T]):
927
+ class EntityComponentRelationMapping(MutableMapping[Entity, T], Generic[T]):
936
928
  """An entity-component mapping to access the relation target component objects.
937
929
 
938
930
  See :any:`Entity.relation_components`.
@@ -1051,14 +1043,14 @@ class EntityComponentRelations(MutableMapping[ComponentKey[Any], EntityComponent
1051
1043
  """Access relations for this component key as a `{target: component}` dict-like object."""
1052
1044
  return EntityComponentRelationMapping(self.entity, key, self.traverse)
1053
1045
 
1054
- def __setitem__(self, __key: ComponentKey[T], __values: Mapping[Entity, object], /) -> None:
1046
+ def __setitem__(self, __key: ComponentKey[T], __values: Mapping[Entity, T], /) -> None:
1055
1047
  """Redefine the component relations for this entity.
1056
1048
 
1057
1049
  ..versionadded:: 4.2.0
1058
1050
  """
1059
1051
  if isinstance(__values, EntityComponentRelationMapping) and __values.entity is self.entity:
1060
1052
  return
1061
- mapping: EntityComponentRelationMapping[object] = self[__key]
1053
+ mapping: EntityComponentRelationMapping[T] = self[__key]
1062
1054
  mapping.clear()
1063
1055
  for target, component in __values.items():
1064
1056
  mapping[target] = component
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import itertools
6
6
  import warnings
7
7
  from collections import defaultdict
8
- from typing import TYPE_CHECKING, Any, Iterable, Iterator, Protocol, TypeVar, overload
8
+ from typing import TYPE_CHECKING, Any, Protocol, TypeVar, overload
9
9
  from weakref import WeakKeyDictionary, WeakSet
10
10
 
11
11
  import attrs
@@ -15,6 +15,7 @@ import tcod.ecs.entity
15
15
  from tcod.ecs.constants import IsA
16
16
 
17
17
  if TYPE_CHECKING:
18
+ from collections.abc import Iterable, Iterator
18
19
  from collections.abc import Set as AbstractSet
19
20
 
20
21
  from tcod.ecs.entity import Entity
@@ -471,7 +472,7 @@ class BoundQuery:
471
472
  continue
472
473
  registry_components = self.registry._components_by_type[component_key]
473
474
  entity_components.append([registry_components[entity] for entity in entities])
474
- return zip(*entity_components)
475
+ return zip(*entity_components, strict=True)
475
476
 
476
477
 
477
478
  WorldQuery = BoundQuery
@@ -4,7 +4,8 @@ from __future__ import annotations
4
4
 
5
5
  import warnings
6
6
  from collections import defaultdict
7
- from typing import TYPE_CHECKING, Any, DefaultDict, Dict, Final, Iterable, Mapping, NoReturn, Set, TypeVar
7
+ from collections.abc import Iterable, Mapping
8
+ from typing import TYPE_CHECKING, Any, Final, NoReturn, TypeVar
8
9
 
9
10
  import attrs
10
11
  from typing_extensions import deprecated
@@ -32,10 +33,10 @@ def _defaultdict_of_dict() -> defaultdict[_T1, dict[_T2, _T3]]:
32
33
 
33
34
 
34
35
  def _components_by_entity_from(
35
- by_type: defaultdict[ComponentKey[object], dict[Entity, Any]],
36
- ) -> defaultdict[Entity, dict[ComponentKey[object], Any]]:
36
+ by_type: defaultdict[ComponentKey[_T1], dict[Entity, _T1]],
37
+ ) -> defaultdict[Entity, dict[ComponentKey[_T1], _T1]]:
37
38
  """Return the component lookup table from the components sparse-set."""
38
- by_entity: defaultdict[Entity, dict[ComponentKey[object], Any]] = defaultdict(dict)
39
+ by_entity: defaultdict[Entity, dict[ComponentKey[_T1], _T1]] = defaultdict(dict)
39
40
  for component_key, components in by_type.items():
40
41
  for entity, component in components.items():
41
42
  by_entity[entity][component_key] = component
@@ -195,23 +196,23 @@ class Registry:
195
196
  # Apply defaultdict types to unpickled dictionaries
196
197
  self._components_by_type = converter.structure(
197
198
  state.pop("_components_by_type"),
198
- DefaultDict[Any, Dict[Any, Any]],
199
+ defaultdict[Any, dict[Any, Any]],
199
200
  )
200
201
  self._components_by_entity = _components_by_entity_from(self._components_by_type)
201
202
 
202
203
  self._tags_by_entity = converter.structure(
203
204
  state.pop("_tags_by_entity"),
204
- DefaultDict[Any, Set[Any]],
205
+ defaultdict[Any, set[Any]],
205
206
  )
206
207
  self._tags_by_key = _tags_by_key_from_tags_by_entity(self._tags_by_entity)
207
208
 
208
209
  self._relation_tags_by_entity = converter.structure(
209
210
  state.pop("_relation_tags_by_entity"),
210
- DefaultDict[Any, DefaultDict[Any, Set[Any]]],
211
+ defaultdict[Any, defaultdict[Any, set[Any]]],
211
212
  )
212
213
  self._relation_components_by_entity = converter.structure(
213
214
  state.pop("_relation_components_by_entity"),
214
- DefaultDict[Any, DefaultDict[Any, Dict[Any, Any]]],
215
+ defaultdict[Any, defaultdict[Any, dict[Any, Any]]],
215
216
  )
216
217
  self._relations_lookup = _relations_lookup_from(
217
218
  self._relation_tags_by_entity, self._relation_components_by_entity
@@ -231,11 +232,11 @@ class Registry:
231
232
  converter = tcod.ecs._converter._get_converter()
232
233
  # Replace defaultdict types with plain dict when saving
233
234
  return {
234
- "_components_by_type": converter.structure(self._components_by_type, Dict[Any, Dict[Any, Any]]),
235
- "_tags_by_entity": converter.structure(self._tags_by_entity, Dict[Any, Any]),
236
- "_relation_tags_by_entity": converter.structure(self._relation_tags_by_entity, Dict[Any, Dict[Any, Any]]),
235
+ "_components_by_type": converter.structure(self._components_by_type, dict[Any, dict[Any, Any]]),
236
+ "_tags_by_entity": converter.structure(self._tags_by_entity, dict[Any, Any]),
237
+ "_relation_tags_by_entity": converter.structure(self._relation_tags_by_entity, dict[Any, dict[Any, Any]]),
237
238
  "_relation_components_by_entity": converter.structure(
238
- self._relation_components_by_entity, Dict[Any, Dict[Any, Any]]
239
+ self._relation_components_by_entity, dict[Any, dict[Any, Any]]
239
240
  ),
240
241
  "_names_by_name": self._names_by_name,
241
242
  }
@@ -0,0 +1,38 @@
1
+ """Common type-hints for tcod.ecs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from types import EllipsisType
6
+ from typing import TYPE_CHECKING, Any, TypeAlias, TypeVar
7
+
8
+ from typing_extensions import TypeForm
9
+
10
+ if TYPE_CHECKING:
11
+ from tcod.ecs.entity import Entity
12
+ from tcod.ecs.query import BoundQuery
13
+ else:
14
+ Entity = Any
15
+ BoundQuery = Any
16
+
17
+
18
+ _T = TypeVar("_T")
19
+
20
+ ComponentKey: TypeAlias = TypeForm[_T] | tuple[object, TypeForm[_T]]
21
+ """ComponentKey is plain `type` or tuple `(tag, type)`."""
22
+
23
+ _RelationTargetLookup: TypeAlias = Entity | EllipsisType
24
+ """Possible target for stored relations."""
25
+
26
+ _RelationQueryTarget: TypeAlias = _RelationTargetLookup | BoundQuery
27
+ """Possible target for relation queries."""
28
+
29
+ _RelationQuery: TypeAlias = tuple[object, _RelationQueryTarget] | tuple[_RelationQueryTarget, object, None] # noqa: PYI047
30
+ """Query format for relations.
31
+
32
+ One of 4 formats:
33
+
34
+ * (tag, target)
35
+ * (tag, ...)
36
+ * (origin, tag, None)
37
+ * (..., tag, None)
38
+ """
@@ -1,43 +0,0 @@
1
- """Common type-hints for tcod.ecs."""
2
-
3
- from __future__ import annotations
4
-
5
- import sys
6
- import types
7
- from typing import TYPE_CHECKING, Any, Tuple, Type, TypeVar, Union
8
-
9
- from typing_extensions import TypeAlias
10
-
11
- if TYPE_CHECKING:
12
- from tcod.ecs.entity import Entity
13
- from tcod.ecs.query import BoundQuery
14
- else:
15
- Entity = Any
16
- BoundQuery = Any
17
-
18
- if sys.version_info >= (3, 10): # pragma: no cover
19
- EllipsisType: TypeAlias = types.EllipsisType
20
- else: # pragma: no cover
21
- EllipsisType = Any
22
-
23
- _T = TypeVar("_T")
24
-
25
- ComponentKey: TypeAlias = Union[Type[_T], Tuple[object, Type[_T]]]
26
- """ComponentKey is plain `type` or tuple `(tag, type)`."""
27
-
28
- _RelationTargetLookup: TypeAlias = Union[Entity, EllipsisType]
29
- """Possible target for stored relations."""
30
-
31
- _RelationQueryTarget: TypeAlias = Union[_RelationTargetLookup, BoundQuery]
32
- """Possible target for relation queries."""
33
-
34
- _RelationQuery: TypeAlias = Union[Tuple[object, _RelationQueryTarget], Tuple[_RelationQueryTarget, object, None]] # noqa: PYI047
35
- """Query format for relations.
36
-
37
- One of 4 formats:
38
-
39
- * (tag, target)
40
- * (tag, ...)
41
- * (origin, tag, None)
42
- * (..., tag, None)
43
- """
File without changes
File without changes
File without changes
File without changes
File without changes