2222from weakref import WeakKeyDictionary , WeakValueDictionary
2323
2424import attrs
25+ from sentinel_value import sentinel
2526from typing_extensions import Self
2627
2728import tcod .ecs .callbacks
4142_T1 = TypeVar ("_T1" )
4243_T2 = TypeVar ("_T2" )
4344
45+ _raise : Final = sentinel ("_raise" )
46+
4447_entity_table : WeakKeyDictionary [Registry , WeakValueDictionary [object , Entity ]] = WeakKeyDictionary ()
4548"""A weak table of registries and unique identifiers to entity objects.
4649
@@ -134,6 +137,10 @@ def instantiate(self) -> Self:
134137 This creates a new unique entity and assigns an :any:`IsA` relationship with `self` to the new entity.
135138 The :any:`IsA` relation is the only data this new entity directly holds.
136139
140+ Immutable components inherited from the parent are always copy-on-write for its child instances.
141+
142+ Keep in mind that components/tags/relations inherited from the parent are not removable from the child instance.
143+
137144 Example::
138145
139146 # 'child = entity.instantiate()' is equivalent to the following:
@@ -149,14 +156,18 @@ def instantiate(self) -> Self:
149156 >>> child.components[str] # Inherits components from parent
150157 'baz'
151158 >>> parent.components[str] = "foo"
152- >>> child.components[str] # Changes in parent and reflected in children
159+ >>> child.components[str] # Changes in parent are reflected in children
153160 'foo'
154161 >>> child.components[str] += "bar" # In-place assignment operators will copy-on-write immutable objects
155162 >>> child.components[str]
156163 'foobar'
157164 >>> parent.components[str]
158165 'foo'
159- >>> del child.components[str]
166+ >>> child.components.pop(str, None) # Revert the component to the inherited value
167+ 'foobar'
168+ >>> child.components[str]
169+ 'foo'
170+ >>> child.components.pop(str, None) # Safe to call .pop with default when the value isn't set on the child
160171 >>> child.components[str]
161172 'foo'
162173
@@ -530,16 +541,16 @@ def __ior__(
530541 return self
531542
532543 @overload
533- def get (self , __key : ComponentKey [T ]) -> T | None : ...
544+ def get (self , __key : ComponentKey [T ], / ) -> T | None : ...
534545 @overload
535- def get (self , __key : ComponentKey [T ], __default : _T1 ) -> T | _T1 : ...
546+ def get (self , __key : ComponentKey [T ], / , default : _T1 ) -> T | _T1 : ...
536547
537- def get (self , __key : ComponentKey [T ], __default : _T1 | None = None ) -> T | _T1 :
548+ def get (self , __key : ComponentKey [T ], / , default : _T1 | None = None ) -> T | _T1 :
538549 """Return a component, returns None or a default value when the component is missing."""
539550 try :
540551 return self [__key ]
541552 except KeyError :
542- return __default # type: ignore[return-value] # https://github.com/python/mypy/issues/3737
553+ return default # type: ignore[return-value] # https://github.com/python/mypy/issues/3737
543554
544555 def setdefault (self , __key : ComponentKey [T ], __default : T ) -> T : # type: ignore[override]
545556 """Assign a default value if a component is missing, then returns the current value."""
@@ -549,6 +560,48 @@ def setdefault(self, __key: ComponentKey[T], __default: T) -> T: # type: ignore
549560 self [__key ] = __default
550561 return __default
551562
563+ @overload
564+ def pop (self , __key : ComponentKey [T ], / ) -> T | None : ...
565+ @overload
566+ def pop (self , __key : ComponentKey [T ], / , default : _T1 ) -> T | _T1 : ...
567+
568+ def pop (
569+ self ,
570+ __key : ComponentKey [T ],
571+ / ,
572+ default : _T1 = _raise , # type: ignore[assignment] # https://github.com/python/mypy/issues/3737
573+ ) -> T | _T1 :
574+ """Remove a component directly from this entity.
575+
576+ Returns the removed value.
577+ If the value is missing returns `default`.
578+ If `default` is unset then raises :any:`KeyError` instead.
579+
580+ Operates directly on the entity without traversal.
581+
582+ >>> parent = registry[object()]
583+ >>> parent.components[str] = "foo"
584+ >>> child = parent.instantiate()
585+ >>> child.components[str] = "bar"
586+ >>> child.components.pop(str, None)
587+ 'bar'
588+ >>> child.components.pop(str, None)
589+ >>> child.components[str]
590+ 'foo'
591+ >>> child.components.pop(str)
592+ Traceback (most recent call last):
593+ ...
594+ KeyError: <class 'str'>
595+ """
596+ _components = self .entity .registry ._components_by_entity .get (self .entity , {})
597+ if __key not in _components :
598+ if default is _raise :
599+ raise KeyError (__key )
600+ return default
601+ value : T | _T1 = _components [__key ]
602+ del self [__key ]
603+ return value
604+
552605
553606@attrs .define (eq = False , frozen = True , weakref_slot = False )
554607class EntityTags (MutableSet [Any ]):
0 commit comments