dagster 1.12.12__py3-none-any.whl → 1.12.13__py3-none-any.whl
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.
- dagster/_core/asset_graph_view/asset_graph_view.py +83 -19
- dagster/_core/asset_graph_view/entity_subset.py +14 -9
- dagster/_core/asset_graph_view/serializable_entity_subset.py +6 -0
- dagster/_core/definitions/asset_checks/asset_check_evaluation.py +41 -68
- dagster/_core/definitions/asset_checks/asset_check_result.py +10 -0
- dagster/_core/definitions/asset_checks/asset_check_spec.py +11 -0
- dagster/_core/definitions/assets/graph/asset_graph.py +1 -0
- dagster/_core/definitions/assets/graph/base_asset_graph.py +29 -2
- dagster/_core/definitions/assets/graph/remote_asset_graph.py +9 -5
- dagster/_core/definitions/declarative_automation/legacy/valid_asset_subset.py +4 -4
- dagster/_core/definitions/declarative_automation/operands/operands.py +10 -4
- dagster/_core/definitions/decorators/asset_check_decorator.py +6 -0
- dagster/_core/event_api.py +10 -0
- dagster/_core/execution/context/asset_check_execution_context.py +39 -0
- dagster/_core/execution/plan/execute_step.py +4 -3
- dagster/_core/instance/runs/run_domain.py +73 -90
- dagster/_core/remote_representation/external_data.py +6 -0
- dagster/_core/storage/asset_check_execution_record.py +49 -5
- dagster/_core/storage/asset_check_state.py +263 -0
- dagster/_core/storage/dagster_run.py +77 -0
- dagster/_core/storage/event_log/base.py +59 -1
- dagster/_core/storage/event_log/sql_event_log.py +174 -7
- dagster/_core/storage/event_log/sqlite/sqlite_event_log.py +6 -1
- dagster/_core/storage/legacy_storage.py +26 -5
- dagster/_core/workspace/load_target.py +1 -1
- dagster/_daemon/monitoring/run_monitoring.py +5 -1
- dagster/_utils/__init__.py +11 -0
- dagster/version.py +1 -1
- {dagster-1.12.12.dist-info → dagster-1.12.13.dist-info}/METADATA +3 -3
- {dagster-1.12.12.dist-info → dagster-1.12.13.dist-info}/RECORD +34 -33
- {dagster-1.12.12.dist-info → dagster-1.12.13.dist-info}/WHEEL +1 -1
- {dagster-1.12.12.dist-info → dagster-1.12.13.dist-info}/entry_points.txt +0 -0
- {dagster-1.12.12.dist-info → dagster-1.12.13.dist-info}/licenses/LICENSE +0 -0
- {dagster-1.12.12.dist-info → dagster-1.12.13.dist-info}/top_level.txt +0 -0
|
@@ -28,6 +28,9 @@ from dagster._core.definitions.decorators.decorator_assets_definition_builder im
|
|
|
28
28
|
from dagster._core.definitions.decorators.op_decorator import _Op
|
|
29
29
|
from dagster._core.definitions.events import AssetKey, CoercibleToAssetKey
|
|
30
30
|
from dagster._core.definitions.output import Out
|
|
31
|
+
from dagster._core.definitions.partitions.definition.partitions_definition import (
|
|
32
|
+
PartitionsDefinition,
|
|
33
|
+
)
|
|
31
34
|
from dagster._core.definitions.policy import RetryPolicy
|
|
32
35
|
from dagster._core.definitions.source_asset import SourceAsset
|
|
33
36
|
from dagster._core.definitions.utils import DEFAULT_OUTPUT
|
|
@@ -113,6 +116,7 @@ def asset_check(
|
|
|
113
116
|
metadata: Optional[Mapping[str, Any]] = None,
|
|
114
117
|
automation_condition: Optional[AutomationCondition[AssetCheckKey]] = None,
|
|
115
118
|
pool: Optional[str] = None,
|
|
119
|
+
partitions_def: Optional[PartitionsDefinition] = None,
|
|
116
120
|
) -> Callable[[AssetCheckFunction], AssetChecksDefinition]:
|
|
117
121
|
"""Create a definition for how to execute an asset check.
|
|
118
122
|
|
|
@@ -151,6 +155,7 @@ def asset_check(
|
|
|
151
155
|
automation_condition (Optional[AutomationCondition]): An AutomationCondition which determines
|
|
152
156
|
when this check should be executed.
|
|
153
157
|
pool (Optional[str]): A string that identifies the concurrency pool that governs this asset check's execution.
|
|
158
|
+
partitions_def (Optional[PartitionsDefinition]): The PartitionsDefinition for this asset check.
|
|
154
159
|
|
|
155
160
|
Produces an :py:class:`AssetChecksDefinition` object.
|
|
156
161
|
|
|
@@ -218,6 +223,7 @@ def asset_check(
|
|
|
218
223
|
blocking=blocking,
|
|
219
224
|
metadata=metadata,
|
|
220
225
|
automation_condition=automation_condition,
|
|
226
|
+
partitions_def=partitions_def,
|
|
221
227
|
)
|
|
222
228
|
|
|
223
229
|
resource_defs_for_execution = wrap_resources_for_execution(resource_defs)
|
dagster/_core/event_api.py
CHANGED
|
@@ -4,6 +4,7 @@ from datetime import datetime
|
|
|
4
4
|
from enum import Enum
|
|
5
5
|
from typing import Literal, NamedTuple, Optional, TypeAlias, Union
|
|
6
6
|
|
|
7
|
+
from dagster_shared.record import record
|
|
7
8
|
from dagster_shared.seven import json
|
|
8
9
|
|
|
9
10
|
import dagster._check as check
|
|
@@ -342,6 +343,15 @@ class AssetRecordsFilter(
|
|
|
342
343
|
return None
|
|
343
344
|
|
|
344
345
|
|
|
346
|
+
@record
|
|
347
|
+
class PartitionKeyFilter:
|
|
348
|
+
"""Filter for the partition keys that should be included in the result. Allows for filtering on
|
|
349
|
+
unpartitioned assets, specific partition keys, or a combination of both.
|
|
350
|
+
"""
|
|
351
|
+
|
|
352
|
+
key: Optional[str]
|
|
353
|
+
|
|
354
|
+
|
|
345
355
|
@whitelist_for_serdes
|
|
346
356
|
class RunStatusChangeRecordsFilter(
|
|
347
357
|
NamedTuple(
|
|
@@ -6,6 +6,8 @@ from dagster._annotations import public
|
|
|
6
6
|
from dagster._core.definitions.asset_checks.asset_check_spec import AssetCheckKey, AssetCheckSpec
|
|
7
7
|
from dagster._core.definitions.job_definition import JobDefinition
|
|
8
8
|
from dagster._core.definitions.op_definition import OpDefinition
|
|
9
|
+
from dagster._core.definitions.partitions.partition_key_range import PartitionKeyRange
|
|
10
|
+
from dagster._core.definitions.partitions.utils.time_window import TimeWindow
|
|
9
11
|
from dagster._core.definitions.repository_definition.repository_definition import (
|
|
10
12
|
RepositoryDefinition,
|
|
11
13
|
)
|
|
@@ -135,6 +137,43 @@ class AssetCheckExecutionContext:
|
|
|
135
137
|
def get_step_execution_context(self) -> StepExecutionContext:
|
|
136
138
|
return self.op_execution_context.get_step_execution_context()
|
|
137
139
|
|
|
140
|
+
#### partition related
|
|
141
|
+
@public
|
|
142
|
+
@property
|
|
143
|
+
@_copy_docs_from_op_execution_context
|
|
144
|
+
def has_partition_key(self) -> bool:
|
|
145
|
+
return self.op_execution_context.has_partition_key
|
|
146
|
+
|
|
147
|
+
@public
|
|
148
|
+
@property
|
|
149
|
+
@_copy_docs_from_op_execution_context
|
|
150
|
+
def partition_key(self) -> str:
|
|
151
|
+
return self.op_execution_context.partition_key
|
|
152
|
+
|
|
153
|
+
@public
|
|
154
|
+
@property
|
|
155
|
+
@_copy_docs_from_op_execution_context
|
|
156
|
+
def partition_keys(self) -> Sequence[str]:
|
|
157
|
+
return self.op_execution_context.partition_keys
|
|
158
|
+
|
|
159
|
+
@public
|
|
160
|
+
@property
|
|
161
|
+
@_copy_docs_from_op_execution_context
|
|
162
|
+
def has_partition_key_range(self) -> bool:
|
|
163
|
+
return self.op_execution_context.has_partition_key_range
|
|
164
|
+
|
|
165
|
+
@public
|
|
166
|
+
@property
|
|
167
|
+
@_copy_docs_from_op_execution_context
|
|
168
|
+
def partition_key_range(self) -> PartitionKeyRange:
|
|
169
|
+
return self.op_execution_context.partition_key_range
|
|
170
|
+
|
|
171
|
+
@public
|
|
172
|
+
@property
|
|
173
|
+
@_copy_docs_from_op_execution_context
|
|
174
|
+
def partition_time_window(self) -> TimeWindow:
|
|
175
|
+
return self.op_execution_context.partition_time_window
|
|
176
|
+
|
|
138
177
|
# misc
|
|
139
178
|
|
|
140
179
|
@public
|
|
@@ -97,9 +97,6 @@ def _process_user_event(
|
|
|
97
97
|
asset_key = _resolve_asset_result_asset_key(user_event, assets_def)
|
|
98
98
|
output_name = assets_def.get_output_name_for_asset_key(asset_key)
|
|
99
99
|
|
|
100
|
-
for check_result in user_event.check_results or []:
|
|
101
|
-
yield from _process_user_event(step_context, check_result)
|
|
102
|
-
|
|
103
100
|
with disable_dagster_warnings():
|
|
104
101
|
if isinstance(user_event, MaterializeResult):
|
|
105
102
|
value = user_event.value
|
|
@@ -112,6 +109,10 @@ def _process_user_event(
|
|
|
112
109
|
data_version=user_event.data_version,
|
|
113
110
|
tags=user_event.tags,
|
|
114
111
|
)
|
|
112
|
+
|
|
113
|
+
for check_result in user_event.check_results or []:
|
|
114
|
+
yield from _process_user_event(step_context, check_result)
|
|
115
|
+
|
|
115
116
|
elif isinstance(user_event, AssetCheckResult):
|
|
116
117
|
asset_check_evaluation = user_event.to_asset_check_evaluation(step_context)
|
|
117
118
|
assets_def = _get_assets_def_for_step(step_context, user_event)
|
|
@@ -2,7 +2,7 @@ import logging
|
|
|
2
2
|
import os
|
|
3
3
|
import warnings
|
|
4
4
|
from collections.abc import Mapping, Sequence, Set
|
|
5
|
-
from typing import TYPE_CHECKING, Any, Optional, cast
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Optional, cast, overload
|
|
6
6
|
|
|
7
7
|
import dagster._check as check
|
|
8
8
|
from dagster._core.definitions.asset_checks.asset_check_evaluation import (
|
|
@@ -50,10 +50,13 @@ from dagster._utils.warnings import disable_dagster_warnings
|
|
|
50
50
|
if TYPE_CHECKING:
|
|
51
51
|
from dagster._core.definitions.asset_checks.asset_check_spec import AssetCheckKey
|
|
52
52
|
from dagster._core.definitions.assets.graph.base_asset_graph import (
|
|
53
|
+
AssetCheckNode,
|
|
53
54
|
BaseAssetGraph,
|
|
54
55
|
BaseAssetNode,
|
|
56
|
+
BaseEntityNode,
|
|
55
57
|
)
|
|
56
58
|
from dagster._core.definitions.job_definition import JobDefinition
|
|
59
|
+
from dagster._core.definitions.partitions.definition import PartitionsDefinition
|
|
57
60
|
from dagster._core.definitions.repository_definition.repository_definition import (
|
|
58
61
|
RepositoryLoadData,
|
|
59
62
|
)
|
|
@@ -66,10 +69,7 @@ if TYPE_CHECKING:
|
|
|
66
69
|
from dagster._core.remote_representation.code_location import CodeLocation
|
|
67
70
|
from dagster._core.remote_representation.external import RemoteJob
|
|
68
71
|
from dagster._core.snap import ExecutionPlanSnapshot, JobSnap
|
|
69
|
-
from dagster._core.snap.execution_plan_snapshot import
|
|
70
|
-
ExecutionStepOutputSnap,
|
|
71
|
-
ExecutionStepSnap,
|
|
72
|
-
)
|
|
72
|
+
from dagster._core.snap.execution_plan_snapshot import ExecutionStepSnap
|
|
73
73
|
from dagster._core.workspace.context import BaseWorkspaceRequestContext
|
|
74
74
|
|
|
75
75
|
|
|
@@ -108,6 +108,7 @@ class RunDomain:
|
|
|
108
108
|
"""Create a run with the given parameters."""
|
|
109
109
|
from dagster._core.definitions.asset_key import AssetCheckKey
|
|
110
110
|
from dagster._core.definitions.assets.graph.remote_asset_graph import RemoteAssetGraph
|
|
111
|
+
from dagster._core.definitions.partitions.context import partition_loading_context
|
|
111
112
|
from dagster._core.remote_origin import RemoteJobOrigin
|
|
112
113
|
from dagster._core.snap import ExecutionPlanSnapshot, JobSnap
|
|
113
114
|
from dagster._utils.tags import normalize_tags
|
|
@@ -256,7 +257,8 @@ class RunDomain:
|
|
|
256
257
|
dagster_run = self._instance.run_storage.add_run(dagster_run)
|
|
257
258
|
|
|
258
259
|
if execution_plan_snapshot and not assets_are_externally_managed(dagster_run):
|
|
259
|
-
self.
|
|
260
|
+
with partition_loading_context(dynamic_partitions_store=self._instance):
|
|
261
|
+
self._log_asset_planned_events(dagster_run, execution_plan_snapshot, asset_graph)
|
|
260
262
|
|
|
261
263
|
return dagster_run
|
|
262
264
|
|
|
@@ -315,8 +317,8 @@ class RunDomain:
|
|
|
315
317
|
adjusted_output = output
|
|
316
318
|
|
|
317
319
|
if asset_key:
|
|
318
|
-
asset_node = self.
|
|
319
|
-
|
|
320
|
+
asset_node = self._get_repo_scoped_entity_node(
|
|
321
|
+
asset_key, asset_graph, remote_job_origin
|
|
320
322
|
)
|
|
321
323
|
if asset_node:
|
|
322
324
|
partitions_definition = asset_node.partitions_def
|
|
@@ -767,12 +769,28 @@ class RunDomain:
|
|
|
767
769
|
{key for key in to_reexecute if isinstance(key, AssetCheckKey)},
|
|
768
770
|
)
|
|
769
771
|
|
|
770
|
-
|
|
772
|
+
@overload
|
|
773
|
+
def _get_repo_scoped_entity_node(
|
|
771
774
|
self,
|
|
775
|
+
key: AssetKey,
|
|
776
|
+
asset_graph: "BaseAssetGraph",
|
|
777
|
+
remote_job_origin: Optional["RemoteJobOrigin"] = None,
|
|
778
|
+
) -> Optional["BaseAssetNode"]: ...
|
|
779
|
+
|
|
780
|
+
@overload
|
|
781
|
+
def _get_repo_scoped_entity_node(
|
|
782
|
+
self,
|
|
783
|
+
key: "AssetCheckKey",
|
|
772
784
|
asset_graph: "BaseAssetGraph",
|
|
773
|
-
asset_key: AssetKey,
|
|
774
785
|
remote_job_origin: Optional["RemoteJobOrigin"] = None,
|
|
775
|
-
) -> Optional["
|
|
786
|
+
) -> Optional["AssetCheckNode"]: ...
|
|
787
|
+
|
|
788
|
+
def _get_repo_scoped_entity_node(
|
|
789
|
+
self,
|
|
790
|
+
key: "EntityKey",
|
|
791
|
+
asset_graph: "BaseAssetGraph",
|
|
792
|
+
remote_job_origin: Optional["RemoteJobOrigin"] = None,
|
|
793
|
+
) -> Optional["BaseEntityNode"]:
|
|
776
794
|
from dagster._core.definitions.assets.graph.remote_asset_graph import (
|
|
777
795
|
RemoteWorkspaceAssetGraph,
|
|
778
796
|
)
|
|
@@ -783,16 +801,29 @@ class RunDomain:
|
|
|
783
801
|
# in all cases, return the BaseAssetNode for the supplied asset key if it exists.
|
|
784
802
|
if isinstance(asset_graph, RemoteWorkspaceAssetGraph):
|
|
785
803
|
return cast(
|
|
786
|
-
"Optional[
|
|
804
|
+
"Optional[BaseEntityNode]",
|
|
787
805
|
asset_graph.get_repo_scoped_node(
|
|
788
|
-
|
|
806
|
+
key, check.not_none(remote_job_origin).repository_origin.get_selector()
|
|
789
807
|
),
|
|
790
808
|
)
|
|
791
809
|
|
|
792
|
-
if not asset_graph.has(
|
|
810
|
+
if not asset_graph.has(key):
|
|
793
811
|
return None
|
|
794
812
|
|
|
795
|
-
return asset_graph.get(
|
|
813
|
+
return asset_graph.get(key)
|
|
814
|
+
|
|
815
|
+
def _get_partitions_def(
|
|
816
|
+
self,
|
|
817
|
+
key: "EntityKey",
|
|
818
|
+
asset_graph: "BaseAssetGraph",
|
|
819
|
+
remote_job_origin: Optional["RemoteJobOrigin"],
|
|
820
|
+
run: "DagsterRun",
|
|
821
|
+
) -> Optional["PartitionsDefinition"]:
|
|
822
|
+
# don't fetch the partitions def if the run is not partitioned
|
|
823
|
+
if not run.is_partitioned:
|
|
824
|
+
return None
|
|
825
|
+
entity_node = self._get_repo_scoped_entity_node(key, asset_graph, remote_job_origin)
|
|
826
|
+
return entity_node.partitions_def if entity_node else None
|
|
796
827
|
|
|
797
828
|
def _log_asset_planned_events(
|
|
798
829
|
self,
|
|
@@ -819,7 +850,7 @@ class RunDomain:
|
|
|
819
850
|
if asset_key:
|
|
820
851
|
events.extend(
|
|
821
852
|
self.get_materialization_planned_events_for_asset(
|
|
822
|
-
dagster_run, asset_key, job_name, step,
|
|
853
|
+
dagster_run, asset_key, job_name, step, asset_graph
|
|
823
854
|
)
|
|
824
855
|
)
|
|
825
856
|
|
|
@@ -830,6 +861,13 @@ class RunDomain:
|
|
|
830
861
|
target_asset_key = asset_check_key.asset_key
|
|
831
862
|
check_name = asset_check_key.name
|
|
832
863
|
|
|
864
|
+
partitions_def = self._get_partitions_def(
|
|
865
|
+
asset_check_key, asset_graph, dagster_run.remote_job_origin, dagster_run
|
|
866
|
+
)
|
|
867
|
+
partitions_subset = dagster_run.get_resolved_partitions_subset_for_events(
|
|
868
|
+
partitions_def
|
|
869
|
+
)
|
|
870
|
+
|
|
833
871
|
event = DagsterEvent(
|
|
834
872
|
event_type_value=DagsterEventType.ASSET_CHECK_EVALUATION_PLANNED.value,
|
|
835
873
|
job_name=job_name,
|
|
@@ -838,8 +876,9 @@ class RunDomain:
|
|
|
838
876
|
f" asset {target_asset_key.to_string()}"
|
|
839
877
|
),
|
|
840
878
|
event_specific_data=AssetCheckEvaluationPlanned(
|
|
841
|
-
target_asset_key,
|
|
879
|
+
asset_key=target_asset_key,
|
|
842
880
|
check_name=check_name,
|
|
881
|
+
partitions_subset=partitions_subset,
|
|
843
882
|
),
|
|
844
883
|
step_key=step.key,
|
|
845
884
|
)
|
|
@@ -878,94 +917,26 @@ class RunDomain:
|
|
|
878
917
|
asset_key: AssetKey,
|
|
879
918
|
job_name: str,
|
|
880
919
|
step: "ExecutionStepSnap",
|
|
881
|
-
output: "ExecutionStepOutputSnap",
|
|
882
920
|
asset_graph: "BaseAssetGraph[BaseAssetNode]",
|
|
883
921
|
) -> Sequence["DagsterEvent"]:
|
|
884
922
|
"""Moved from DagsterInstance._log_materialization_planned_event_for_asset."""
|
|
885
|
-
from dagster._core.definitions.partitions.context import partition_loading_context
|
|
886
|
-
from dagster._core.definitions.partitions.definition import DynamicPartitionsDefinition
|
|
887
923
|
from dagster._core.events import AssetMaterializationPlannedData, DagsterEvent
|
|
888
924
|
|
|
889
925
|
events = []
|
|
890
926
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
dagster_run.tags.get(ASSET_PARTITION_RANGE_START_TAG),
|
|
894
|
-
dagster_run.tags.get(ASSET_PARTITION_RANGE_END_TAG),
|
|
895
|
-
)
|
|
896
|
-
|
|
897
|
-
if partition_tag and (partition_range_start or partition_range_end):
|
|
898
|
-
raise DagsterInvariantViolationError(
|
|
899
|
-
f"Cannot have {ASSET_PARTITION_RANGE_START_TAG} or"
|
|
900
|
-
f" {ASSET_PARTITION_RANGE_END_TAG} set along with"
|
|
901
|
-
f" {PARTITION_NAME_TAG}"
|
|
902
|
-
)
|
|
903
|
-
|
|
904
|
-
partitions_subset = None
|
|
905
|
-
individual_partitions = None
|
|
906
|
-
if partition_range_start or partition_range_end:
|
|
907
|
-
if not partition_range_start or not partition_range_end:
|
|
908
|
-
raise DagsterInvariantViolationError(
|
|
909
|
-
f"Cannot have {ASSET_PARTITION_RANGE_START_TAG} or"
|
|
910
|
-
f" {ASSET_PARTITION_RANGE_END_TAG} set without the other"
|
|
911
|
-
)
|
|
912
|
-
|
|
913
|
-
asset_node = check.not_none(
|
|
914
|
-
self._get_repo_scoped_asset_node(
|
|
915
|
-
asset_graph, asset_key, dagster_run.remote_job_origin
|
|
916
|
-
)
|
|
917
|
-
)
|
|
918
|
-
|
|
919
|
-
partitions_def = asset_node.partitions_def
|
|
920
|
-
if (
|
|
921
|
-
isinstance(partitions_def, DynamicPartitionsDefinition)
|
|
922
|
-
and partitions_def.name is None
|
|
923
|
-
):
|
|
924
|
-
raise DagsterInvariantViolationError(
|
|
925
|
-
"Creating a run targeting a partition range is not supported for assets partitioned with function-based dynamic partitions"
|
|
926
|
-
)
|
|
927
|
-
|
|
928
|
-
if partitions_def is not None:
|
|
929
|
-
with partition_loading_context(dynamic_partitions_store=self._instance):
|
|
930
|
-
if self._instance.event_log_storage.supports_partition_subset_in_asset_materialization_planned_events:
|
|
931
|
-
partitions_subset = partitions_def.subset_with_partition_keys(
|
|
932
|
-
partitions_def.get_partition_keys_in_range(
|
|
933
|
-
PartitionKeyRange(partition_range_start, partition_range_end),
|
|
934
|
-
)
|
|
935
|
-
).to_serializable_subset()
|
|
936
|
-
individual_partitions = []
|
|
937
|
-
else:
|
|
938
|
-
individual_partitions = partitions_def.get_partition_keys_in_range(
|
|
939
|
-
PartitionKeyRange(partition_range_start, partition_range_end),
|
|
940
|
-
)
|
|
941
|
-
elif check.not_none(output.properties).is_asset_partitioned and partition_tag:
|
|
942
|
-
individual_partitions = [partition_tag]
|
|
943
|
-
|
|
944
|
-
assert not (individual_partitions and partitions_subset), (
|
|
945
|
-
"Should set either individual_partitions or partitions_subset, but not both"
|
|
927
|
+
partitions_def = self._get_partitions_def(
|
|
928
|
+
asset_key, asset_graph, dagster_run.remote_job_origin, dagster_run
|
|
946
929
|
)
|
|
947
930
|
|
|
948
|
-
|
|
931
|
+
partitions_subset = dagster_run.get_resolved_partitions_subset_for_events(partitions_def)
|
|
932
|
+
if partitions_subset is None:
|
|
949
933
|
materialization_planned = DagsterEvent.build_asset_materialization_planned_event(
|
|
950
934
|
job_name,
|
|
951
935
|
step.key,
|
|
952
936
|
AssetMaterializationPlannedData(asset_key, partition=None, partitions_subset=None),
|
|
953
937
|
)
|
|
954
938
|
events.append(materialization_planned)
|
|
955
|
-
elif
|
|
956
|
-
for individual_partition in individual_partitions:
|
|
957
|
-
materialization_planned = DagsterEvent.build_asset_materialization_planned_event(
|
|
958
|
-
job_name,
|
|
959
|
-
step.key,
|
|
960
|
-
AssetMaterializationPlannedData(
|
|
961
|
-
asset_key,
|
|
962
|
-
partition=individual_partition,
|
|
963
|
-
partitions_subset=partitions_subset,
|
|
964
|
-
),
|
|
965
|
-
)
|
|
966
|
-
events.append(materialization_planned)
|
|
967
|
-
|
|
968
|
-
else:
|
|
939
|
+
elif self._instance.event_log_storage.supports_partition_subset_in_asset_materialization_planned_events:
|
|
969
940
|
materialization_planned = DagsterEvent.build_asset_materialization_planned_event(
|
|
970
941
|
job_name,
|
|
971
942
|
step.key,
|
|
@@ -974,6 +945,18 @@ class RunDomain:
|
|
|
974
945
|
),
|
|
975
946
|
)
|
|
976
947
|
events.append(materialization_planned)
|
|
948
|
+
else:
|
|
949
|
+
for partition_key in partitions_subset.get_partition_keys():
|
|
950
|
+
materialization_planned = DagsterEvent.build_asset_materialization_planned_event(
|
|
951
|
+
job_name,
|
|
952
|
+
step.key,
|
|
953
|
+
AssetMaterializationPlannedData(
|
|
954
|
+
asset_key,
|
|
955
|
+
partition=partition_key,
|
|
956
|
+
partitions_subset=None,
|
|
957
|
+
),
|
|
958
|
+
)
|
|
959
|
+
events.append(materialization_planned)
|
|
977
960
|
|
|
978
961
|
return events
|
|
979
962
|
|
|
@@ -899,6 +899,7 @@ class AssetCheckNodeSnap(IHaveNew):
|
|
|
899
899
|
additional_asset_keys: Sequence[AssetKey]
|
|
900
900
|
automation_condition: Optional[AutomationCondition]
|
|
901
901
|
automation_condition_snapshot: Optional[AutomationConditionSnapshot]
|
|
902
|
+
partitions_def_snapshot: Optional[PartitionsSnap]
|
|
902
903
|
|
|
903
904
|
def __new__(
|
|
904
905
|
cls,
|
|
@@ -911,6 +912,7 @@ class AssetCheckNodeSnap(IHaveNew):
|
|
|
911
912
|
additional_asset_keys: Optional[Sequence[AssetKey]] = None,
|
|
912
913
|
automation_condition: Optional[AutomationCondition] = None,
|
|
913
914
|
automation_condition_snapshot: Optional[AutomationConditionSnapshot] = None,
|
|
915
|
+
partitions_def_snapshot: Optional[PartitionsSnap] = None,
|
|
914
916
|
):
|
|
915
917
|
return super().__new__(
|
|
916
918
|
cls,
|
|
@@ -923,6 +925,7 @@ class AssetCheckNodeSnap(IHaveNew):
|
|
|
923
925
|
additional_asset_keys=additional_asset_keys or [],
|
|
924
926
|
automation_condition=automation_condition,
|
|
925
927
|
automation_condition_snapshot=automation_condition_snapshot,
|
|
928
|
+
partitions_def_snapshot=partitions_def_snapshot,
|
|
926
929
|
)
|
|
927
930
|
|
|
928
931
|
@property
|
|
@@ -1211,6 +1214,9 @@ def asset_check_node_snaps_from_repo(repo: RepositoryDefinition) -> Sequence[Ass
|
|
|
1211
1214
|
additional_asset_keys=[dep.asset_key for dep in spec.additional_deps],
|
|
1212
1215
|
automation_condition=automation_condition,
|
|
1213
1216
|
automation_condition_snapshot=automation_condition_snapshot,
|
|
1217
|
+
partitions_def_snapshot=PartitionsSnap.from_def(spec.partitions_def)
|
|
1218
|
+
if spec.partitions_def
|
|
1219
|
+
else None,
|
|
1214
1220
|
)
|
|
1215
1221
|
)
|
|
1216
1222
|
|
|
@@ -2,6 +2,7 @@ import enum
|
|
|
2
2
|
from collections.abc import Iterable
|
|
3
3
|
from typing import NamedTuple, Optional, cast
|
|
4
4
|
|
|
5
|
+
from dagster_shared.record import record
|
|
5
6
|
from dagster_shared.serdes import deserialize_value
|
|
6
7
|
|
|
7
8
|
import dagster._check as check
|
|
@@ -60,6 +61,7 @@ class AssetCheckExecutionRecord(
|
|
|
60
61
|
# Old records won't have an event if the status is PLANNED.
|
|
61
62
|
("event", Optional[EventLogEntry]),
|
|
62
63
|
("create_timestamp", float),
|
|
64
|
+
("partition", Optional[str]),
|
|
63
65
|
],
|
|
64
66
|
),
|
|
65
67
|
LoadableBy[AssetCheckKey],
|
|
@@ -72,6 +74,7 @@ class AssetCheckExecutionRecord(
|
|
|
72
74
|
status: AssetCheckExecutionRecordStatus,
|
|
73
75
|
event: Optional[EventLogEntry],
|
|
74
76
|
create_timestamp: float,
|
|
77
|
+
partition: Optional[str],
|
|
75
78
|
):
|
|
76
79
|
check.inst_param(key, "key", AssetCheckKey)
|
|
77
80
|
check.int_param(id, "id")
|
|
@@ -79,6 +82,7 @@ class AssetCheckExecutionRecord(
|
|
|
79
82
|
check.inst_param(status, "status", AssetCheckExecutionRecordStatus)
|
|
80
83
|
check.opt_inst_param(event, "event", EventLogEntry)
|
|
81
84
|
check.float_param(create_timestamp, "create_timestamp")
|
|
85
|
+
check.opt_str_param(partition, "partition")
|
|
82
86
|
|
|
83
87
|
event_type = event.dagster_event_type if event else None
|
|
84
88
|
if status == AssetCheckExecutionRecordStatus.PLANNED:
|
|
@@ -105,6 +109,7 @@ class AssetCheckExecutionRecord(
|
|
|
105
109
|
status=status,
|
|
106
110
|
event=event,
|
|
107
111
|
create_timestamp=create_timestamp,
|
|
112
|
+
partition=partition,
|
|
108
113
|
)
|
|
109
114
|
|
|
110
115
|
@property
|
|
@@ -129,6 +134,7 @@ class AssetCheckExecutionRecord(
|
|
|
129
134
|
else None
|
|
130
135
|
),
|
|
131
136
|
create_timestamp=utc_datetime_from_naive(row["create_timestamp"]).timestamp(),
|
|
137
|
+
partition=row["partition"],
|
|
132
138
|
)
|
|
133
139
|
|
|
134
140
|
@classmethod
|
|
@@ -168,17 +174,28 @@ class AssetCheckExecutionRecord(
|
|
|
168
174
|
check.failed(f"Unexpected status {self.status}")
|
|
169
175
|
|
|
170
176
|
async def targets_latest_materialization(self, loading_context: LoadingContext) -> bool:
|
|
171
|
-
from dagster._core.storage.event_log.base import AssetRecord
|
|
177
|
+
from dagster._core.storage.event_log.base import AssetRecord, AssetRecordsFilter
|
|
172
178
|
|
|
173
179
|
resolved_status = await self.resolve_status(loading_context)
|
|
174
180
|
if resolved_status == AssetCheckExecutionResolvedStatus.IN_PROGRESS:
|
|
175
181
|
# all in-progress checks execute against the latest version
|
|
176
182
|
return True
|
|
177
183
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
184
|
+
if self.partition is None:
|
|
185
|
+
asset_record = await AssetRecord.gen(loading_context, self.key.asset_key)
|
|
186
|
+
latest_materialization = (
|
|
187
|
+
asset_record.asset_entry.last_materialization_record if asset_record else None
|
|
188
|
+
)
|
|
189
|
+
else:
|
|
190
|
+
records = loading_context.instance.fetch_materializations(
|
|
191
|
+
AssetRecordsFilter(
|
|
192
|
+
asset_key=self.key.asset_key,
|
|
193
|
+
asset_partitions=[self.partition],
|
|
194
|
+
),
|
|
195
|
+
limit=1,
|
|
196
|
+
)
|
|
197
|
+
latest_materialization = records.records[0] if records.records else None
|
|
198
|
+
|
|
182
199
|
if not latest_materialization:
|
|
183
200
|
# no previous materialization, so it's executing against the lastest version
|
|
184
201
|
return True
|
|
@@ -222,3 +239,30 @@ class AssetCheckExecutionRecord(
|
|
|
222
239
|
)
|
|
223
240
|
else:
|
|
224
241
|
check.failed(f"Unexpected check status {resolved_status}")
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@record
|
|
245
|
+
class AssetCheckPartitionInfo:
|
|
246
|
+
check_key: AssetCheckKey
|
|
247
|
+
partition_key: Optional[str]
|
|
248
|
+
# the status of the last execution of the check
|
|
249
|
+
latest_execution_status: AssetCheckExecutionRecordStatus
|
|
250
|
+
# the run id of the last planned event for the check
|
|
251
|
+
latest_planned_run_id: str
|
|
252
|
+
# the storage id of the last event (planned or evaluation) for the check
|
|
253
|
+
latest_check_event_storage_id: int
|
|
254
|
+
# the storage id of the last materialization for the asset / partition that this check targets
|
|
255
|
+
# this is the latest overall materialization, independent of if there has been a check event
|
|
256
|
+
# that targets it
|
|
257
|
+
latest_materialization_storage_id: Optional[int]
|
|
258
|
+
# the storage id of the materialization that the last execution of the check targeted
|
|
259
|
+
latest_target_materialization_storage_id: Optional[int]
|
|
260
|
+
|
|
261
|
+
@property
|
|
262
|
+
def is_current(self) -> bool:
|
|
263
|
+
"""Returns True if the latest check execution targets the latest materialization event."""
|
|
264
|
+
return (
|
|
265
|
+
self.latest_materialization_storage_id is None
|
|
266
|
+
or self.latest_materialization_storage_id
|
|
267
|
+
== self.latest_target_materialization_storage_id
|
|
268
|
+
)
|