dagster 1.12.11__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.
Files changed (43) hide show
  1. dagster/_cli/asset.py +15 -4
  2. dagster/_cli/job.py +8 -3
  3. dagster/_core/asset_graph_view/asset_graph_view.py +83 -19
  4. dagster/_core/asset_graph_view/entity_subset.py +14 -9
  5. dagster/_core/asset_graph_view/serializable_entity_subset.py +15 -0
  6. dagster/_core/code_pointer.py +8 -1
  7. dagster/_core/definitions/asset_checks/asset_check_evaluation.py +41 -68
  8. dagster/_core/definitions/asset_checks/asset_check_result.py +10 -0
  9. dagster/_core/definitions/asset_checks/asset_check_spec.py +11 -0
  10. dagster/_core/definitions/assets/graph/asset_graph.py +1 -0
  11. dagster/_core/definitions/assets/graph/base_asset_graph.py +29 -2
  12. dagster/_core/definitions/assets/graph/remote_asset_graph.py +9 -5
  13. dagster/_core/definitions/declarative_automation/legacy/valid_asset_subset.py +4 -4
  14. dagster/_core/definitions/declarative_automation/operands/operands.py +10 -4
  15. dagster/_core/definitions/declarative_automation/serialized_objects.py +36 -0
  16. dagster/_core/definitions/decorators/asset_check_decorator.py +6 -0
  17. dagster/_core/definitions/decorators/asset_decorator.py +13 -13
  18. dagster/_core/event_api.py +10 -0
  19. dagster/_core/execution/context/asset_check_execution_context.py +39 -0
  20. dagster/_core/execution/plan/execute_step.py +4 -3
  21. dagster/_core/execution/run_cancellation_thread.py +1 -0
  22. dagster/_core/instance/runs/run_domain.py +73 -90
  23. dagster/_core/remote_representation/external_data.py +6 -0
  24. dagster/_core/storage/asset_check_execution_record.py +49 -5
  25. dagster/_core/storage/asset_check_state.py +263 -0
  26. dagster/_core/storage/dagster_run.py +77 -0
  27. dagster/_core/storage/event_log/base.py +59 -1
  28. dagster/_core/storage/event_log/sql_event_log.py +174 -7
  29. dagster/_core/storage/event_log/sqlite/sqlite_event_log.py +6 -1
  30. dagster/_core/storage/legacy_storage.py +26 -5
  31. dagster/_core/telemetry.py +3 -0
  32. dagster/_core/workspace/load_target.py +1 -1
  33. dagster/_daemon/monitoring/run_monitoring.py +5 -1
  34. dagster/_generate/download.py +1 -0
  35. dagster/_utils/__init__.py +11 -0
  36. dagster/components/list/list.py +4 -1
  37. dagster/version.py +1 -1
  38. {dagster-1.12.11.dist-info → dagster-1.12.13.dist-info}/METADATA +4 -4
  39. {dagster-1.12.11.dist-info → dagster-1.12.13.dist-info}/RECORD +43 -42
  40. {dagster-1.12.11.dist-info → dagster-1.12.13.dist-info}/WHEEL +1 -1
  41. {dagster-1.12.11.dist-info → dagster-1.12.13.dist-info}/entry_points.txt +0 -0
  42. {dagster-1.12.11.dist-info → dagster-1.12.13.dist-info}/licenses/LICENSE +0 -0
  43. {dagster-1.12.11.dist-info → dagster-1.12.13.dist-info}/top_level.txt +0 -0
@@ -38,6 +38,7 @@ from dagster._core.errors import (
38
38
  )
39
39
  from dagster._core.event_api import (
40
40
  EventRecordsResult,
41
+ PartitionKeyFilter,
41
42
  RunShardedEventsCursor,
42
43
  RunStatusChangeRecordsFilter,
43
44
  )
@@ -58,6 +59,7 @@ from dagster._core.storage.asset_check_execution_record import (
58
59
  COMPLETED_ASSET_CHECK_EXECUTION_RECORD_STATUSES,
59
60
  AssetCheckExecutionRecord,
60
61
  AssetCheckExecutionRecordStatus,
62
+ AssetCheckPartitionInfo,
61
63
  )
62
64
  from dagster._core.storage.dagster_run import DagsterRunStatsSnapshot
63
65
  from dagster._core.storage.event_log.base import (
@@ -2992,15 +2994,25 @@ class SqlEventLogStorage(EventLogStorage):
2992
2994
  planned = cast(
2993
2995
  "AssetCheckEvaluationPlanned", check.not_none(event.dagster_event).event_specific_data
2994
2996
  )
2997
+ partition_keys = (
2998
+ planned.partitions_subset.get_partition_keys() if planned.partitions_subset else [None]
2999
+ )
2995
3000
  with self.index_connection() as conn:
2996
3001
  conn.execute(
2997
3002
  AssetCheckExecutionsTable.insert().values(
2998
- asset_key=planned.asset_key.to_string(),
2999
- check_name=planned.check_name,
3000
- run_id=event.run_id,
3001
- execution_status=AssetCheckExecutionRecordStatus.PLANNED.value,
3002
- evaluation_event=serialize_value(event),
3003
- evaluation_event_timestamp=self._event_insert_timestamp(event),
3003
+ [
3004
+ dict(
3005
+ asset_key=planned.asset_key.to_string(),
3006
+ check_name=planned.check_name,
3007
+ partition=partition_key,
3008
+ run_id=event.run_id,
3009
+ execution_status=AssetCheckExecutionRecordStatus.PLANNED.value,
3010
+ evaluation_event=serialize_value(event),
3011
+ evaluation_event_timestamp=self._event_insert_timestamp(event),
3012
+ evaluation_event_storage_id=event_id,
3013
+ )
3014
+ for partition_key in partition_keys
3015
+ ]
3004
3016
  )
3005
3017
  )
3006
3018
 
@@ -3033,6 +3045,7 @@ class SqlEventLogStorage(EventLogStorage):
3033
3045
  if evaluation.target_materialization_data
3034
3046
  else None
3035
3047
  ),
3048
+ partition=evaluation.partition,
3036
3049
  )
3037
3050
  )
3038
3051
 
@@ -3049,6 +3062,9 @@ class SqlEventLogStorage(EventLogStorage):
3049
3062
  AssetCheckExecutionsTable.c.asset_key == evaluation.asset_key.to_string(),
3050
3063
  AssetCheckExecutionsTable.c.check_name == evaluation.check_name,
3051
3064
  AssetCheckExecutionsTable.c.run_id == event.run_id,
3065
+ self._get_asset_check_partition_filter_clause(
3066
+ PartitionKeyFilter(key=evaluation.partition)
3067
+ ),
3052
3068
  )
3053
3069
  )
3054
3070
  .values(
@@ -3065,6 +3081,7 @@ class SqlEventLogStorage(EventLogStorage):
3065
3081
  if evaluation.target_materialization_data
3066
3082
  else None
3067
3083
  ),
3084
+ partition=evaluation.partition,
3068
3085
  )
3069
3086
  ).rowcount
3070
3087
 
@@ -3089,6 +3106,7 @@ class SqlEventLogStorage(EventLogStorage):
3089
3106
  if evaluation.target_materialization_data
3090
3107
  else None
3091
3108
  ),
3109
+ partition=evaluation.partition,
3092
3110
  )
3093
3111
  ).rowcount
3094
3112
 
@@ -3100,12 +3118,23 @@ class SqlEventLogStorage(EventLogStorage):
3100
3118
  "as a result of duplicate AssetCheckPlanned events."
3101
3119
  )
3102
3120
 
3121
+ def _get_asset_check_partition_filter_clause(
3122
+ self, partition_filter: Optional[PartitionKeyFilter]
3123
+ ):
3124
+ if partition_filter is None:
3125
+ return True
3126
+ elif partition_filter.key is None:
3127
+ return AssetCheckExecutionsTable.c.partition.is_(None)
3128
+ else:
3129
+ return AssetCheckExecutionsTable.c.partition == partition_filter.key
3130
+
3103
3131
  def get_asset_check_execution_history(
3104
3132
  self,
3105
3133
  check_key: AssetCheckKey,
3106
3134
  limit: int,
3107
3135
  cursor: Optional[int] = None,
3108
3136
  status: Optional[AbstractSet[AssetCheckExecutionRecordStatus]] = None,
3137
+ partition_filter: Optional[PartitionKeyFilter] = None,
3109
3138
  ) -> Sequence[AssetCheckExecutionRecord]:
3110
3139
  check.inst_param(check_key, "key", AssetCheckKey)
3111
3140
  check.int_param(limit, "limit")
@@ -3119,12 +3148,14 @@ class SqlEventLogStorage(EventLogStorage):
3119
3148
  AssetCheckExecutionsTable.c.execution_status,
3120
3149
  AssetCheckExecutionsTable.c.evaluation_event,
3121
3150
  AssetCheckExecutionsTable.c.create_timestamp,
3151
+ AssetCheckExecutionsTable.c.partition,
3122
3152
  ]
3123
3153
  )
3124
3154
  .where(
3125
3155
  db.and_(
3126
3156
  AssetCheckExecutionsTable.c.asset_key == check_key.asset_key.to_string(),
3127
3157
  AssetCheckExecutionsTable.c.check_name == check_key.name,
3158
+ self._get_asset_check_partition_filter_clause(partition_filter),
3128
3159
  )
3129
3160
  )
3130
3161
  .order_by(AssetCheckExecutionsTable.c.id.desc())
@@ -3144,8 +3175,13 @@ class SqlEventLogStorage(EventLogStorage):
3144
3175
  return [AssetCheckExecutionRecord.from_db_row(row, key=check_key) for row in rows]
3145
3176
 
3146
3177
  def get_latest_asset_check_execution_by_key(
3147
- self, check_keys: Sequence[AssetCheckKey]
3178
+ self,
3179
+ check_keys: Sequence[AssetCheckKey],
3180
+ partition_filter: Optional[PartitionKeyFilter] = None,
3148
3181
  ) -> Mapping[AssetCheckKey, AssetCheckExecutionRecord]:
3182
+ """Returns the latest AssetCheckExecutionRecord for each check key. By default, returns the latest
3183
+ record regardless of partitioning.
3184
+ """
3149
3185
  if not check_keys:
3150
3186
  return {}
3151
3187
 
@@ -3161,6 +3197,7 @@ class SqlEventLogStorage(EventLogStorage):
3161
3197
  [key.asset_key.to_string() for key in check_keys]
3162
3198
  ),
3163
3199
  AssetCheckExecutionsTable.c.check_name.in_([key.name for key in check_keys]),
3200
+ self._get_asset_check_partition_filter_clause(partition_filter),
3164
3201
  )
3165
3202
  )
3166
3203
  .group_by(
@@ -3178,6 +3215,7 @@ class SqlEventLogStorage(EventLogStorage):
3178
3215
  AssetCheckExecutionsTable.c.execution_status,
3179
3216
  AssetCheckExecutionsTable.c.evaluation_event,
3180
3217
  AssetCheckExecutionsTable.c.create_timestamp,
3218
+ AssetCheckExecutionsTable.c.partition,
3181
3219
  ]
3182
3220
  ).select_from(
3183
3221
  AssetCheckExecutionsTable.join(
@@ -3200,6 +3238,135 @@ class SqlEventLogStorage(EventLogStorage):
3200
3238
  results[check_key] = AssetCheckExecutionRecord.from_db_row(row, key=check_key)
3201
3239
  return results
3202
3240
 
3241
+ def _get_asset_check_partition_info_for_key(
3242
+ self,
3243
+ check_key: AssetCheckKey,
3244
+ after_storage_id: Optional[int],
3245
+ partition_keys: Optional[Sequence[str]],
3246
+ latest_unpartitioned_materialization_storage_ids: Mapping[AssetKey, int],
3247
+ ) -> Sequence[AssetCheckPartitionInfo]:
3248
+ # Build the base filter conditions
3249
+ filter_conditions = [
3250
+ AssetCheckExecutionsTable.c.asset_key == check_key.asset_key.to_string(),
3251
+ AssetCheckExecutionsTable.c.check_name == check_key.name,
3252
+ # Historical records may have NULL in the evaluation_event_storage_id column for
3253
+ # PLANNED events
3254
+ AssetCheckExecutionsTable.c.evaluation_event_storage_id.isnot(None),
3255
+ ]
3256
+ if partition_keys is not None:
3257
+ filter_conditions.append(AssetCheckExecutionsTable.c.partition.in_(partition_keys))
3258
+
3259
+ # Subquery to find the max id for each partition
3260
+ latest_check_ids_subquery = db_subquery(
3261
+ db_select(
3262
+ [
3263
+ db.func.max(AssetCheckExecutionsTable.c.id).label("id"),
3264
+ AssetCheckExecutionsTable.c.partition.label("partition"),
3265
+ ]
3266
+ )
3267
+ .where(db.and_(*filter_conditions))
3268
+ .group_by(AssetCheckExecutionsTable.c.partition),
3269
+ "latest_check_ids_subquery",
3270
+ )
3271
+
3272
+ # Subquery to find the latest materialization storage id for each partition of the
3273
+ # target asset. Note: we don't filter by after_storage_id here because we always want
3274
+ # to return the latest materialization storage id, even if it's older than after_storage_id.
3275
+ latest_materialization_ids_subquery = self._latest_event_ids_by_partition_subquery(
3276
+ check_key.asset_key,
3277
+ [DagsterEventType.ASSET_MATERIALIZATION],
3278
+ asset_partitions=partition_keys,
3279
+ )
3280
+
3281
+ # Main query to get all columns for the latest records, joined with latest
3282
+ # materialization storage ids
3283
+ query = db_select(
3284
+ [
3285
+ AssetCheckExecutionsTable.c.id,
3286
+ AssetCheckExecutionsTable.c.partition,
3287
+ AssetCheckExecutionsTable.c.execution_status,
3288
+ AssetCheckExecutionsTable.c.evaluation_event_storage_id,
3289
+ AssetCheckExecutionsTable.c.materialization_event_storage_id,
3290
+ AssetCheckExecutionsTable.c.run_id,
3291
+ latest_materialization_ids_subquery.c.id.label("latest_materialization_storage_id"),
3292
+ ]
3293
+ ).select_from(
3294
+ AssetCheckExecutionsTable.join(
3295
+ latest_check_ids_subquery,
3296
+ AssetCheckExecutionsTable.c.id == latest_check_ids_subquery.c.id,
3297
+ ).join(
3298
+ latest_materialization_ids_subquery,
3299
+ AssetCheckExecutionsTable.c.partition
3300
+ == latest_materialization_ids_subquery.c.partition,
3301
+ isouter=True,
3302
+ )
3303
+ )
3304
+
3305
+ # these filters are applied to the main query rather than the individual subqueries to ensure
3306
+ # we don't miss records that only have a new materialization or a new check execution but not both
3307
+ if after_storage_id is not None:
3308
+ query = query.where(
3309
+ db.or_(
3310
+ AssetCheckExecutionsTable.c.evaluation_event_storage_id > after_storage_id,
3311
+ latest_materialization_ids_subquery.c.id > after_storage_id,
3312
+ )
3313
+ )
3314
+
3315
+ with self.index_connection() as conn:
3316
+ rows = db_fetch_mappings(conn, query)
3317
+
3318
+ return [
3319
+ AssetCheckPartitionInfo(
3320
+ check_key=check_key,
3321
+ partition_key=row["partition"],
3322
+ latest_execution_status=AssetCheckExecutionRecordStatus(row["execution_status"]),
3323
+ latest_target_materialization_storage_id=row["materialization_event_storage_id"],
3324
+ latest_planned_run_id=row["run_id"],
3325
+ latest_check_event_storage_id=row["evaluation_event_storage_id"],
3326
+ latest_materialization_storage_id=max(
3327
+ filter(
3328
+ None,
3329
+ [
3330
+ row["latest_materialization_storage_id"],
3331
+ latest_unpartitioned_materialization_storage_ids.get(
3332
+ check_key.asset_key
3333
+ ),
3334
+ ],
3335
+ ),
3336
+ default=None,
3337
+ ),
3338
+ )
3339
+ for row in rows
3340
+ ]
3341
+
3342
+ def get_asset_check_partition_info(
3343
+ self,
3344
+ keys: Sequence[AssetCheckKey],
3345
+ after_storage_id: Optional[int] = None,
3346
+ partition_keys: Optional[Sequence[str]] = None,
3347
+ ) -> Sequence[AssetCheckPartitionInfo]:
3348
+ check.list_param(keys, "keys", of_type=AssetCheckKey)
3349
+ check.opt_int_param(after_storage_id, "after_storage_id")
3350
+
3351
+ infos = []
3352
+ latest_unpartitioned_materialization_storage_ids = (
3353
+ self._get_latest_unpartitioned_materialization_storage_ids(
3354
+ list(set(key.asset_key for key in keys))
3355
+ )
3356
+ )
3357
+ # the inner query is not feasible to join in a single query because the latest materialization ids subquery,
3358
+ # so for now we fetch the info for each key separately
3359
+ for key in keys:
3360
+ infos.extend(
3361
+ self._get_asset_check_partition_info_for_key(
3362
+ key,
3363
+ after_storage_id,
3364
+ partition_keys,
3365
+ latest_unpartitioned_materialization_storage_ids,
3366
+ )
3367
+ )
3368
+ return infos
3369
+
3203
3370
  @property
3204
3371
  def supports_asset_checks(self): # pyright: ignore[reportIncompatibleMethodOverride]
3205
3372
  return self.has_table(AssetCheckExecutionsTable.name)
@@ -276,7 +276,12 @@ class SqliteEventLogStorage(SqlEventLogStorage, ConfigurableClass):
276
276
  self.store_asset_event_tags([event], [event_id])
277
277
 
278
278
  if event.is_dagster_event and event.dagster_event_type in ASSET_CHECK_EVENTS:
279
- self.store_asset_check_event(event, None)
279
+ # mirror the event in the cross-run index database
280
+ with self.index_connection() as conn:
281
+ result = conn.execute(insert_event_statement)
282
+ event_id = result.inserted_primary_key[0]
283
+
284
+ self.store_asset_check_event(event, event_id)
280
285
 
281
286
  if event.is_dagster_event and event.dagster_event_type in EVENT_TYPE_TO_PIPELINE_RUN_STATUS:
282
287
  # should mirror run status change events in the index shard
@@ -4,17 +4,17 @@ from typing import TYPE_CHECKING, AbstractSet, Optional, Union # noqa: UP035
4
4
 
5
5
  from dagster import _check as check
6
6
  from dagster._config.config_schema import UserConfigSchema
7
- from dagster._core.definitions.asset_checks.asset_check_spec import AssetCheckKey
8
7
  from dagster._core.definitions.asset_key import EntityKey
9
8
  from dagster._core.definitions.declarative_automation.serialized_objects import (
10
9
  AutomationConditionEvaluationWithRunIds,
11
10
  )
12
11
  from dagster._core.definitions.events import AssetKey
13
12
  from dagster._core.definitions.freshness import FreshnessStateRecord
14
- from dagster._core.event_api import EventHandlerFn
13
+ from dagster._core.event_api import EventHandlerFn, PartitionKeyFilter
15
14
  from dagster._core.storage.asset_check_execution_record import (
16
15
  AssetCheckExecutionRecord,
17
16
  AssetCheckExecutionRecordStatus,
17
+ AssetCheckPartitionInfo,
18
18
  )
19
19
  from dagster._core.storage.base_storage import DagsterStorage
20
20
  from dagster._core.storage.event_log.base import (
@@ -60,6 +60,7 @@ if TYPE_CHECKING:
60
60
  )
61
61
  from dagster._core.snap.execution_plan_snapshot import ExecutionPlanSnapshot
62
62
  from dagster._core.snap.job_snapshot import JobSnap
63
+ from dagster._core.storage.asset_check_state import AssetCheckState
63
64
  from dagster._core.storage.dagster_run import (
64
65
  DagsterRun,
65
66
  DagsterRunStatsSnapshot,
@@ -745,19 +746,39 @@ class LegacyEventLogStorage(EventLogStorage, ConfigurableClass):
745
746
  limit: int,
746
747
  cursor: Optional[int] = None,
747
748
  status: Optional[AbstractSet[AssetCheckExecutionRecordStatus]] = None,
749
+ partition_filter: Optional[PartitionKeyFilter] = None,
748
750
  ) -> Sequence[AssetCheckExecutionRecord]:
749
751
  return self._storage.event_log_storage.get_asset_check_execution_history(
750
752
  check_key=check_key,
751
753
  limit=limit,
752
754
  cursor=cursor,
753
755
  status=status,
756
+ partition_filter=partition_filter,
754
757
  )
755
758
 
756
- def get_latest_asset_check_execution_by_key( # pyright: ignore[reportIncompatibleMethodOverride]
759
+ def get_latest_asset_check_execution_by_key(
757
760
  self,
758
761
  check_keys: Sequence["AssetCheckKey"],
759
- ) -> Mapping["AssetCheckKey", Optional[AssetCheckExecutionRecord]]:
760
- return self._storage.event_log_storage.get_latest_asset_check_execution_by_key(check_keys)
762
+ partition_filter: Optional[PartitionKeyFilter] = None,
763
+ ) -> Mapping["AssetCheckKey", AssetCheckExecutionRecord]:
764
+ return self._storage.event_log_storage.get_latest_asset_check_execution_by_key(
765
+ check_keys, partition_filter=partition_filter
766
+ )
767
+
768
+ def get_asset_check_partition_info(
769
+ self,
770
+ keys: Sequence["AssetCheckKey"],
771
+ after_storage_id: Optional[int] = None,
772
+ partition_keys: Optional[Sequence[str]] = None,
773
+ ) -> Sequence[AssetCheckPartitionInfo]:
774
+ return self._storage.event_log_storage.get_asset_check_partition_info(
775
+ keys=keys, after_storage_id=after_storage_id, partition_keys=partition_keys
776
+ )
777
+
778
+ def get_checkpointed_asset_check_state(
779
+ self, keys: Sequence["AssetCheckKey"]
780
+ ) -> Mapping["AssetCheckKey", "AssetCheckState"]:
781
+ return self._storage.event_log_storage.get_checkpointed_asset_check_state(keys)
761
782
 
762
783
 
763
784
  class LegacyScheduleStorage(ScheduleStorage, ConfigurableClass):
@@ -25,6 +25,7 @@ from dagster_shared.telemetry import (
25
25
  TelemetrySettings,
26
26
  dagster_home_if_set,
27
27
  get_or_set_instance_id,
28
+ get_or_set_user_id,
28
29
  log_telemetry_action,
29
30
  write_telemetry_log_line,
30
31
  )
@@ -379,6 +380,7 @@ def log_remote_repo_stats(
379
380
  client_time=str(datetime.datetime.now()),
380
381
  event_id=str(uuid.uuid4()),
381
382
  instance_id=instance_id,
383
+ user_id=get_or_set_user_id(),
382
384
  metadata={
383
385
  **get_stats_from_remote_repo(remote_repo),
384
386
  "source": source,
@@ -450,6 +452,7 @@ def log_repo_stats(
450
452
  client_time=str(datetime.datetime.now()),
451
453
  event_id=str(uuid.uuid4()),
452
454
  instance_id=instance_id,
455
+ user_id=get_or_set_user_id(),
453
456
  metadata={
454
457
  "source": source,
455
458
  "pipeline_name_hash": job_name_hash,
@@ -67,7 +67,7 @@ def validate_dagster_block_for_module_name_or_modules(dagster_block):
67
67
  modules_present = "modules" in dagster_block and isinstance(dagster_block.get("modules"), list)
68
68
 
69
69
  if module_name_present and modules_present:
70
- # Here we have the check only for list; to be a bit more forgiving in comparison to 'is_valid_modules_list' in case it's an empty list next to 'module_name' existance
70
+ # Here we have the check only for list; to be a bit more forgiving in comparison to 'is_valid_modules_list' in case it's an empty list next to 'module_name' existence
71
71
  if len(dagster_block["modules"]) > 0:
72
72
  raise ValueError(
73
73
  "Only one of 'module_name' or 'modules' should be specified, not both."
@@ -235,7 +235,11 @@ def check_run_timeout(
235
235
  MAX_RUNTIME_SECONDS_TAG, run_record.dagster_run.tags.get("dagster/max_runtime_seconds")
236
236
  )
237
237
  if max_time_str:
238
- max_time = float(max_time_str)
238
+ try:
239
+ max_time = float(max_time_str)
240
+ except ValueError:
241
+ logger.warning(f"Invalid max runtime value: {max_time_str}")
242
+ max_time = None
239
243
  else:
240
244
  max_time = default_timeout_seconds
241
245
 
@@ -43,6 +43,7 @@ AVAILABLE_EXAMPLES = [
43
43
  "project_analytics",
44
44
  "project_fully_featured",
45
45
  "quickstart_etl",
46
+ "snowflake_cortex",
46
47
  "tutorial_notebook_assets",
47
48
  "with_great_expectations",
48
49
  "with_openai",
@@ -465,6 +465,17 @@ def git_repository_root() -> str:
465
465
  return subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip()
466
466
 
467
467
 
468
+ _DAGSTER_OSS_SUBDIRECTORY = "dagster-oss"
469
+
470
+
471
+ def discover_oss_root(path: Path) -> Path:
472
+ while path != path.parent:
473
+ if (path / ".git").exists() or path.name == _DAGSTER_OSS_SUBDIRECTORY:
474
+ return path
475
+ path = path.parent
476
+ raise ValueError("Could not find OSS root")
477
+
478
+
468
479
  def segfault() -> None:
469
480
  """Reliable cross-Python version segfault.
470
481
 
@@ -28,6 +28,7 @@ from dagster._cli.workspace.cli_target import get_repository_python_origin_from_
28
28
  from dagster._config.pythonic_config.resource import get_resource_type_name
29
29
  from dagster._core.definitions.asset_selection import AssetSelection
30
30
  from dagster._core.definitions.assets.job.asset_job import is_reserved_asset_job_name
31
+ from dagster._core.definitions.declarative_automation.serialized_objects import get_expanded_label
31
32
  from dagster._core.definitions.definitions_load_context import (
32
33
  DefinitionsLoadContext,
33
34
  DefinitionsLoadType,
@@ -177,7 +178,9 @@ def list_definitions(
177
178
  group=node.group_name,
178
179
  kinds=sorted(list(node.kinds)),
179
180
  description=node.description,
180
- automation_condition=node.automation_condition.get_label()
181
+ automation_condition=" ".join(
182
+ get_expanded_label(node.automation_condition.get_snapshot())
183
+ )
181
184
  if node.automation_condition
182
185
  else None,
183
186
  tags=sorted(f'"{k}"="{v}"' for k, v in node.tags.items() if _tag_filter(k)),
dagster/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "1.12.11"
1
+ __version__ = "1.12.13"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dagster
3
- Version: 1.12.11
3
+ Version: 1.12.13
4
4
  Summary: Dagster is an orchestration platform for the development, production, and observation of data assets.
5
5
  Author: Dagster Labs
6
6
  Author-email: hello@dagsterlabs.com
@@ -60,8 +60,8 @@ Requires-Dist: universal_pathlib; python_version < "3.12"
60
60
  Requires-Dist: universal_pathlib>=0.2.0; python_version >= "3.12"
61
61
  Requires-Dist: rich
62
62
  Requires-Dist: filelock
63
- Requires-Dist: dagster-pipes==1.12.11
64
- Requires-Dist: dagster-shared==1.12.11
63
+ Requires-Dist: dagster-pipes==1.12.13
64
+ Requires-Dist: dagster-shared==1.12.13
65
65
  Requires-Dist: antlr4-python3-runtime
66
66
  Provides-Extra: docker
67
67
  Requires-Dist: docker; extra == "docker"
@@ -88,7 +88,7 @@ Requires-Dist: ruff==0.11.5; extra == "test"
88
88
  Provides-Extra: test-components
89
89
  Requires-Dist: tomlkit; extra == "test-components"
90
90
  Requires-Dist: jsonschema; extra == "test-components"
91
- Requires-Dist: pandas; extra == "test-components"
91
+ Requires-Dist: pandas<3.0.0; extra == "test-components"
92
92
  Requires-Dist: duckdb; extra == "test-components"
93
93
  Provides-Extra: mypy
94
94
  Requires-Dist: mypy==1.8.0; extra == "mypy"