11import operator
22from collections .abc import Callable , Sequence
33from dataclasses import dataclass , replace
4- from typing import Any , Generic , Optional , TypeAlias
4+ from typing import TYPE_CHECKING , Any , Generic , Optional , TypeAlias
5+
6+ if TYPE_CHECKING :
7+ from dagster ._core .asset_graph_view .entity_subset import AssetSubset
58
69from dagster_shared .serdes .serdes import DataclassSerializer , whitelist_for_serdes
710from typing_extensions import Self
811
912import dagster ._check as check
1013from dagster ._core .definitions .asset_key import T_EntityKey
11- from dagster ._core .definitions .events import AssetKeyPartitionKey
1214from dagster ._core .definitions .partitions .context import partition_loading_context
1315from dagster ._core .definitions .partitions .definition import PartitionsDefinition
1416from dagster ._core .definitions .partitions .snap .snap import PartitionsSnap
1921 TimeWindowPartitionsSubset ,
2022)
2123from dagster ._core .definitions .partitions .subset .key_ranges import KeyRangesPartitionsSubset
24+ from dagster ._core .errors import DagsterInvalidInvocationError
2225
2326EntitySubsetValue : TypeAlias = bool | PartitionsSubset
2427
@@ -43,6 +46,7 @@ def before_pack(self, value: "SerializableEntitySubset") -> "SerializableEntityS
4346 storage_field_names = {"key" : "asset_key" },
4447 old_storage_names = {"AssetSubset" },
4548)
49+
4650@dataclass (frozen = True )
4751class SerializableEntitySubset (Generic [T_EntityKey ]):
4852 """Represents a serializable subset of a given EntityKey."""
@@ -123,18 +127,29 @@ def subset_value(self) -> PartitionsSubset:
123127
124128 @property
125129 def size (self ) -> int :
130+
126131 if not self .is_partitioned :
127132 return int (self .bool_value )
128- else :
133+
134+ try :
129135 return len (self .subset_value )
130-
136+ except DagsterInvalidInvocationError :
137+ # If a dynamic partition key was deleted out from under this historical
138+ # subset evaluation, we gracefully fall back to a size of 0.
139+ return 0
140+
131141 @property
132142 def is_empty (self ) -> bool :
133- if self .is_partitioned :
134- return self .subset_value .is_empty
135- else :
143+
144+ if not self .is_partitioned :
136145 return not self .bool_value
137146
147+ try :
148+ return self .subset_value .is_empty
149+ except DagsterInvalidInvocationError :
150+ # If partition keys no longer exist, treat the historical subset as empty
151+ return True
152+
138153 def is_compatible_with_partitions_def (
139154 self , partitions_def : PartitionsDefinition | None
140155 ) -> bool :
@@ -188,11 +203,16 @@ def compute_union(self, other: Self) -> Self:
188203 def compute_intersection (self , other : Self ) -> Self :
189204 return self ._oper (other , operator .and_ )
190205
191- def __contains__ (self , item : AssetKeyPartitionKey ) -> bool :
206+ def __contains__ (self , item : "AssetSubset" ) -> bool :
192207 if not self .is_partitioned :
193- return item .asset_key == self .key and item .partition_key is None and self .bool_value
194- else :
195- return item .asset_key == self .key and item .partition_key in self .subset_value
196-
208+ return self .bool_value and item .bool_value
209+
210+ try :
211+ return item .partition_key in self .subset_value
212+ except DagsterInvalidInvocationError :
213+ # If a dynamic partition key was deleted out from under this historical
214+ # subset evaluation, gracefully treat it as not contained.
215+ return False
216+
197217 def __repr__ (self ) -> str :
198218 return f"{ self .__class__ .__name__ } <{ self .key } >({ self .value } )"
0 commit comments