dagster-cloud 1.8.2__py3-none-any.whl → 1.12.6__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 (108) hide show
  1. dagster_cloud/__init__.py +3 -3
  2. dagster_cloud/agent/__init__.py +4 -4
  3. dagster_cloud/agent/cli/__init__.py +56 -17
  4. dagster_cloud/agent/dagster_cloud_agent.py +360 -172
  5. dagster_cloud/agent/instrumentation/__init__.py +0 -0
  6. dagster_cloud/agent/instrumentation/constants.py +2 -0
  7. dagster_cloud/agent/instrumentation/run_launch.py +23 -0
  8. dagster_cloud/agent/instrumentation/schedule.py +34 -0
  9. dagster_cloud/agent/instrumentation/sensor.py +34 -0
  10. dagster_cloud/anomaly_detection/__init__.py +2 -2
  11. dagster_cloud/anomaly_detection/defs.py +17 -12
  12. dagster_cloud/anomaly_detection/types.py +3 -3
  13. dagster_cloud/api/dagster_cloud_api.py +209 -293
  14. dagster_cloud/auth/constants.py +21 -5
  15. dagster_cloud/batching/__init__.py +1 -0
  16. dagster_cloud/batching/batcher.py +210 -0
  17. dagster_cloud/dagster_insights/__init__.py +12 -6
  18. dagster_cloud/dagster_insights/bigquery/bigquery_utils.py +3 -2
  19. dagster_cloud/dagster_insights/bigquery/dbt_wrapper.py +39 -12
  20. dagster_cloud/dagster_insights/bigquery/insights_bigquery_resource.py +8 -6
  21. dagster_cloud/dagster_insights/insights_utils.py +18 -8
  22. dagster_cloud/dagster_insights/metrics_utils.py +12 -12
  23. dagster_cloud/dagster_insights/snowflake/dagster_snowflake_insights.py +5 -12
  24. dagster_cloud/dagster_insights/snowflake/dbt_wrapper.py +34 -8
  25. dagster_cloud/dagster_insights/snowflake/definitions.py +38 -12
  26. dagster_cloud/dagster_insights/snowflake/insights_snowflake_resource.py +11 -23
  27. dagster_cloud/definitions/__init__.py +0 -0
  28. dagster_cloud/definitions/job_selection.py +36 -0
  29. dagster_cloud/execution/cloud_run_launcher/k8s.py +1 -1
  30. dagster_cloud/execution/cloud_run_launcher/process.py +3 -3
  31. dagster_cloud/execution/monitoring/__init__.py +27 -33
  32. dagster_cloud/execution/utils/process.py +3 -3
  33. dagster_cloud/instance/__init__.py +125 -38
  34. dagster_cloud/instrumentation/__init__.py +32 -0
  35. dagster_cloud/metadata/source_code.py +13 -8
  36. dagster_cloud/metrics/__init__.py +0 -0
  37. dagster_cloud/metrics/tracer.py +59 -0
  38. dagster_cloud/opentelemetry/__init__.py +0 -0
  39. dagster_cloud/opentelemetry/config/__init__.py +73 -0
  40. dagster_cloud/opentelemetry/config/exporter.py +81 -0
  41. dagster_cloud/opentelemetry/config/log_record_processor.py +40 -0
  42. dagster_cloud/opentelemetry/config/logging_handler.py +14 -0
  43. dagster_cloud/opentelemetry/config/meter_provider.py +9 -0
  44. dagster_cloud/opentelemetry/config/metric_reader.py +39 -0
  45. dagster_cloud/opentelemetry/controller.py +319 -0
  46. dagster_cloud/opentelemetry/enum.py +58 -0
  47. dagster_cloud/opentelemetry/factories/__init__.py +1 -0
  48. dagster_cloud/opentelemetry/factories/logs.py +113 -0
  49. dagster_cloud/opentelemetry/factories/metrics.py +121 -0
  50. dagster_cloud/opentelemetry/metrics/__init__.py +0 -0
  51. dagster_cloud/opentelemetry/metrics/meter.py +140 -0
  52. dagster_cloud/opentelemetry/observers/__init__.py +0 -0
  53. dagster_cloud/opentelemetry/observers/dagster_exception_handler.py +40 -0
  54. dagster_cloud/opentelemetry/observers/execution_observer.py +178 -0
  55. dagster_cloud/pex/grpc/__generated__/multi_pex_api_pb2.pyi +175 -0
  56. dagster_cloud/pex/grpc/__init__.py +2 -2
  57. dagster_cloud/pex/grpc/client.py +4 -4
  58. dagster_cloud/pex/grpc/compile.py +2 -2
  59. dagster_cloud/pex/grpc/server/__init__.py +2 -2
  60. dagster_cloud/pex/grpc/server/cli/__init__.py +31 -19
  61. dagster_cloud/pex/grpc/server/manager.py +60 -42
  62. dagster_cloud/pex/grpc/server/registry.py +28 -21
  63. dagster_cloud/pex/grpc/server/server.py +23 -14
  64. dagster_cloud/pex/grpc/types.py +5 -5
  65. dagster_cloud/py.typed +0 -0
  66. dagster_cloud/secrets/__init__.py +1 -1
  67. dagster_cloud/secrets/loader.py +3 -3
  68. dagster_cloud/serverless/__init__.py +1 -1
  69. dagster_cloud/serverless/io_manager.py +36 -53
  70. dagster_cloud/storage/client.py +54 -17
  71. dagster_cloud/storage/compute_logs/__init__.py +3 -1
  72. dagster_cloud/storage/compute_logs/compute_log_manager.py +22 -17
  73. dagster_cloud/storage/defs_state/__init__.py +3 -0
  74. dagster_cloud/storage/defs_state/queries.py +15 -0
  75. dagster_cloud/storage/defs_state/storage.py +113 -0
  76. dagster_cloud/storage/event_logs/__init__.py +3 -1
  77. dagster_cloud/storage/event_logs/queries.py +102 -4
  78. dagster_cloud/storage/event_logs/storage.py +266 -73
  79. dagster_cloud/storage/event_logs/utils.py +88 -7
  80. dagster_cloud/storage/runs/__init__.py +1 -1
  81. dagster_cloud/storage/runs/queries.py +17 -2
  82. dagster_cloud/storage/runs/storage.py +88 -42
  83. dagster_cloud/storage/schedules/__init__.py +1 -1
  84. dagster_cloud/storage/schedules/storage.py +6 -8
  85. dagster_cloud/storage/tags.py +66 -1
  86. dagster_cloud/util/__init__.py +10 -12
  87. dagster_cloud/util/errors.py +49 -64
  88. dagster_cloud/version.py +1 -1
  89. dagster_cloud/workspace/config_schema/__init__.py +55 -13
  90. dagster_cloud/workspace/docker/__init__.py +76 -25
  91. dagster_cloud/workspace/docker/utils.py +1 -1
  92. dagster_cloud/workspace/ecs/__init__.py +1 -1
  93. dagster_cloud/workspace/ecs/client.py +51 -33
  94. dagster_cloud/workspace/ecs/launcher.py +76 -22
  95. dagster_cloud/workspace/ecs/run_launcher.py +3 -3
  96. dagster_cloud/workspace/ecs/utils.py +14 -5
  97. dagster_cloud/workspace/kubernetes/__init__.py +1 -1
  98. dagster_cloud/workspace/kubernetes/launcher.py +61 -29
  99. dagster_cloud/workspace/kubernetes/utils.py +34 -22
  100. dagster_cloud/workspace/user_code_launcher/__init__.py +5 -3
  101. dagster_cloud/workspace/user_code_launcher/process.py +16 -14
  102. dagster_cloud/workspace/user_code_launcher/user_code_launcher.py +552 -172
  103. dagster_cloud/workspace/user_code_launcher/utils.py +105 -1
  104. {dagster_cloud-1.8.2.dist-info → dagster_cloud-1.12.6.dist-info}/METADATA +48 -42
  105. dagster_cloud-1.12.6.dist-info/RECORD +134 -0
  106. {dagster_cloud-1.8.2.dist-info → dagster_cloud-1.12.6.dist-info}/WHEEL +1 -1
  107. dagster_cloud-1.8.2.dist-info/RECORD +0 -100
  108. {dagster_cloud-1.8.2.dist-info → dagster_cloud-1.12.6.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,9 @@
1
- from typing import TYPE_CHECKING, Any, Iterable, Iterator, List, Optional, Tuple, Union
1
+ from collections.abc import Iterable, Iterator
2
+ from typing import TYPE_CHECKING, Any, Optional, Union
2
3
 
3
4
  import yaml
4
5
  from dagster import (
6
+ AssetCheckEvaluation,
5
7
  AssetCheckResult,
6
8
  AssetExecutionContext,
7
9
  AssetKey,
@@ -11,8 +13,15 @@ from dagster import (
11
13
  Output,
12
14
  )
13
15
 
14
- from ..insights_utils import extract_asset_info_from_event, handle_raise_on_error
15
- from .snowflake_utils import OPAQUE_ID_SQL_SIGIL, build_opaque_id_metadata, marker_asset_key_for_job
16
+ from dagster_cloud.dagster_insights.insights_utils import (
17
+ extract_asset_info_from_event,
18
+ handle_raise_on_error,
19
+ )
20
+ from dagster_cloud.dagster_insights.snowflake.snowflake_utils import (
21
+ OPAQUE_ID_SQL_SIGIL,
22
+ build_opaque_id_metadata,
23
+ marker_asset_key_for_job,
24
+ )
16
25
 
17
26
  if TYPE_CHECKING:
18
27
  from dagster_dbt import DbtCliInvocation
@@ -23,11 +32,21 @@ def dbt_with_snowflake_insights(
23
32
  context: Union[OpExecutionContext, AssetExecutionContext],
24
33
  dbt_cli_invocation: "DbtCliInvocation",
25
34
  dagster_events: Optional[
26
- Iterable[Union[Output, AssetMaterialization, AssetObservation, AssetCheckResult]]
35
+ Iterable[
36
+ Union[
37
+ Output,
38
+ AssetMaterialization,
39
+ AssetObservation,
40
+ AssetCheckResult,
41
+ AssetCheckEvaluation,
42
+ ]
43
+ ]
27
44
  ] = None,
28
45
  skip_config_check=False,
29
46
  record_observation_usage: bool = True,
30
- ) -> Iterator[Union[Output, AssetMaterialization, AssetObservation, AssetCheckResult]]:
47
+ ) -> Iterator[
48
+ Union[Output, AssetMaterialization, AssetObservation, AssetCheckResult, AssetCheckEvaluation]
49
+ ]:
31
50
  """Wraps a dagster-dbt invocation to associate each Snowflake query with the produced
32
51
  asset materializations. This allows the cost of each query to be associated with the asset
33
52
  materialization that it produced.
@@ -38,7 +57,7 @@ def dbt_with_snowflake_insights(
38
57
  Args:
39
58
  context (AssetExecutionContext): The context of the asset that is being materialized.
40
59
  dbt_cli_invocation (DbtCliInvocation): The invocation of the dbt CLI to wrap.
41
- dagster_events (Optional[Iterable[Union[Output, AssetObservation, AssetCheckResult]]]):
60
+ dagster_events (Optional[Iterable[Union[Output, AssetObservation, AssetCheckResult, AssetCheckEvaluation]]]):
42
61
  The events that were produced by the dbt CLI invocation. If not provided, it is assumed
43
62
  that the dbt CLI invocation has not yet been run, and it will be run and the events
44
63
  will be streamed.
@@ -89,10 +108,17 @@ def dbt_with_snowflake_insights(
89
108
  if dagster_events is None:
90
109
  dagster_events = dbt_cli_invocation.stream()
91
110
 
92
- asset_and_partition_key_to_unique_id: List[Tuple[AssetKey, Optional[str], Any]] = []
111
+ asset_and_partition_key_to_unique_id: list[tuple[AssetKey, Optional[str], Any]] = []
93
112
  for dagster_event in dagster_events:
94
113
  if isinstance(
95
- dagster_event, (AssetMaterialization, AssetObservation, Output, AssetCheckResult)
114
+ dagster_event,
115
+ (
116
+ AssetMaterialization,
117
+ AssetObservation,
118
+ Output,
119
+ AssetCheckResult,
120
+ AssetCheckEvaluation,
121
+ ),
96
122
  ):
97
123
  unique_id = dagster_event.metadata["unique_id"].value
98
124
  asset_key, partition = extract_asset_info_from_event(
@@ -1,14 +1,16 @@
1
1
  import warnings
2
+ from collections.abc import Sequence
2
3
  from dataclasses import dataclass
3
4
  from datetime import date, datetime, timedelta, timezone
4
- from pprint import pprint
5
- from typing import TYPE_CHECKING, Optional, Sequence, Union
5
+ from typing import TYPE_CHECKING, Optional, Union
6
6
 
7
7
  from dagster import (
8
8
  AssetExecutionContext,
9
+ AssetKey,
9
10
  AssetsDefinition,
10
11
  AssetSelection,
11
12
  HourlyPartitionsDefinition,
13
+ RunRequest,
12
14
  ScheduleDefinition,
13
15
  ScheduleEvaluationContext,
14
16
  TimeWindow,
@@ -18,9 +20,16 @@ from dagster import (
18
20
  fs_io_manager,
19
21
  schedule,
20
22
  )
23
+ from dagster._core.definitions.unresolved_asset_job_definition import UnresolvedAssetJobDefinition
24
+ from dagster._core.storage.tags import (
25
+ ASSET_PARTITION_RANGE_END_TAG,
26
+ ASSET_PARTITION_RANGE_START_TAG,
27
+ )
21
28
 
22
- from ..metrics_utils import put_cost_information
23
- from .dagster_snowflake_insights import get_cost_data_for_hour
29
+ from dagster_cloud.dagster_insights.metrics_utils import put_cost_information
30
+ from dagster_cloud.dagster_insights.snowflake.dagster_snowflake_insights import (
31
+ get_cost_data_for_hour,
32
+ )
24
33
 
25
34
  if TYPE_CHECKING:
26
35
  from dagster_snowflake import SnowflakeConnection
@@ -34,6 +43,22 @@ class SnowflakeInsightsDefinitions:
34
43
  schedule: ScheduleDefinition
35
44
 
36
45
 
46
+ def _build_run_request_for_partition_key_range(
47
+ job: UnresolvedAssetJobDefinition,
48
+ asset_keys: Sequence[AssetKey],
49
+ partition_range_start: str,
50
+ partition_range_end: str,
51
+ ) -> RunRequest:
52
+ tags = {
53
+ ASSET_PARTITION_RANGE_START_TAG: partition_range_start,
54
+ ASSET_PARTITION_RANGE_END_TAG: partition_range_end,
55
+ }
56
+ partition_key = partition_range_start if partition_range_start == partition_range_end else None
57
+ return RunRequest(
58
+ job_name=job.name, asset_selection=asset_keys, partition_key=partition_key, tags=tags
59
+ )
60
+
61
+
37
62
  def create_snowflake_insights_asset_and_schedule(
38
63
  start_date: Union[datetime, date, str],
39
64
  name: Optional[str] = None,
@@ -97,7 +122,7 @@ def create_snowflake_insights_asset_and_schedule(
97
122
  def poll_snowflake_query_history_hour(
98
123
  context: AssetExecutionContext,
99
124
  ) -> None:
100
- snowflake: "SnowflakeConnection" = getattr(context.resources, snowflake_resource_key)
125
+ snowflake: SnowflakeConnection = getattr(context.resources, snowflake_resource_key)
101
126
 
102
127
  start_hour = context.partition_time_window.start
103
128
  end_hour = context.partition_time_window.end
@@ -130,7 +155,7 @@ def create_snowflake_insights_asset_and_schedule(
130
155
  )
131
156
 
132
157
  if dry_run:
133
- pprint(costs)
158
+ pass
134
159
  else:
135
160
  context.log.info(
136
161
  f"Submitting cost information for {len(costs)} queries to Dagster Insights"
@@ -168,13 +193,14 @@ def create_snowflake_insights_asset_and_schedule(
168
193
  n_hours_ago = timestamp - timedelta(hours=schedule_batch_size_hrs)
169
194
  window = TimeWindow(start=n_hours_ago, end=timestamp)
170
195
 
171
- partition_keys = partitions_def.get_partition_keys_in_time_window(window)
196
+ partition_key_range = partitions_def.get_partition_key_range_for_time_window(window)
172
197
 
173
- for partition_key in partition_keys:
174
- yield insights_job.run_request_for_partition(
175
- partition_key=partition_key,
176
- run_key=partition_key,
177
- )
198
+ yield _build_run_request_for_partition_key_range(
199
+ insights_job,
200
+ [poll_snowflake_query_history_hour.key],
201
+ partition_key_range.start,
202
+ partition_key_range.end,
203
+ )
178
204
 
179
205
  insights_schedule = _insights_schedule
180
206
 
@@ -1,19 +1,8 @@
1
1
  import warnings
2
+ from collections.abc import Generator, Iterable, Iterator, Mapping, Sequence
2
3
  from contextlib import contextmanager
3
4
  from io import StringIO
4
- from typing import (
5
- Any,
6
- Generator,
7
- Iterable,
8
- Iterator,
9
- Mapping,
10
- Optional,
11
- Sequence,
12
- Tuple,
13
- Type,
14
- Union,
15
- cast,
16
- )
5
+ from typing import Any, Optional, Union, cast
17
6
 
18
7
  import snowflake.connector
19
8
  from dagster import (
@@ -23,7 +12,7 @@ from dagster import (
23
12
  OpExecutionContext,
24
13
  get_dagster_logger,
25
14
  )
26
- from dagster._annotations import experimental, public
15
+ from dagster._annotations import beta, public
27
16
  from dagster._check import CheckError
28
17
  from dagster._core.errors import DagsterInvalidPropertyError
29
18
  from dagster._core.storage.event_log.sql_event_log import SqlDbConnection
@@ -31,13 +20,12 @@ from dagster_snowflake import SnowflakeConnection, SnowflakeResource
31
20
  from snowflake.connector.cursor import SnowflakeCursor
32
21
 
33
22
  from dagster_cloud.dagster_insights.insights_utils import get_current_context_and_asset_key
34
-
35
- from .snowflake_utils import meter_snowflake_query
23
+ from dagster_cloud.dagster_insights.snowflake.snowflake_utils import meter_snowflake_query
36
24
 
37
25
 
38
- def get_current_context_and_asset_key_or_warn() -> (
39
- Tuple[Union[OpExecutionContext, AssetExecutionContext, None], Optional[AssetKey]]
40
- ):
26
+ def get_current_context_and_asset_key_or_warn() -> tuple[
27
+ Union[OpExecutionContext, AssetExecutionContext, None], Optional[AssetKey]
28
+ ]:
41
29
  try:
42
30
  return get_current_context_and_asset_key()
43
31
  except (DagsterInvalidPropertyError, DagsterInvariantViolationError, CheckError):
@@ -80,7 +68,7 @@ class WrappedSnowflakeConnection(snowflake.connector.SnowflakeConnection):
80
68
  sql_text: str,
81
69
  remove_comments: bool = False,
82
70
  return_cursors: bool = True,
83
- cursor_class: Type[SnowflakeCursor] = InsightsSnowflakeCursor,
71
+ cursor_class: type[SnowflakeCursor] = InsightsSnowflakeCursor,
84
72
  **kwargs,
85
73
  ) -> Iterable[SnowflakeCursor]:
86
74
  return super().execute_string(
@@ -95,14 +83,14 @@ class WrappedSnowflakeConnection(snowflake.connector.SnowflakeConnection):
95
83
  self,
96
84
  stream: StringIO,
97
85
  remove_comments: bool = False,
98
- cursor_class: Type[SnowflakeCursor] = InsightsSnowflakeCursor,
86
+ cursor_class: type[SnowflakeCursor] = InsightsSnowflakeCursor,
99
87
  **kwargs,
100
88
  ) -> Generator[SnowflakeCursor, None, None]:
101
89
  return super().execute_stream(stream, remove_comments, cursor_class, **kwargs) # type: ignore # (bad stubs)
102
90
 
103
91
  def cursor(self, cursor_class=None) -> SnowflakeCursor:
104
92
  if cursor_class is None:
105
- cursor = cast(InsightsSnowflakeCursor, super().cursor(InsightsSnowflakeCursor))
93
+ cursor = cast("InsightsSnowflakeCursor", super().cursor(InsightsSnowflakeCursor))
106
94
  cursor.set_asset_key(self._asset_key)
107
95
  else:
108
96
  cursor = super().cursor(cursor_class)
@@ -110,7 +98,7 @@ class WrappedSnowflakeConnection(snowflake.connector.SnowflakeConnection):
110
98
  return cursor
111
99
 
112
100
 
113
- @experimental
101
+ @beta
114
102
  class InsightsSnowflakeResource(SnowflakeResource):
115
103
  """A wrapper around :py:class:`SnowflakeResource` which automatically tags
116
104
  Snowflake queries with comments which can be used to attribute Snowflake
File without changes
@@ -0,0 +1,36 @@
1
+ from abc import ABC, abstractmethod
2
+ from collections.abc import Sequence
3
+ from typing import AbstractSet # noqa: UP035
4
+
5
+ import dagster._check as check
6
+ from dagster._record import record
7
+ from dagster_shared.serdes import whitelist_for_serdes
8
+
9
+
10
+ @whitelist_for_serdes
11
+ @record
12
+ class JobSelection(ABC):
13
+ code_location_name: str
14
+ repository_name: str
15
+
16
+ @abstractmethod
17
+ def resolve_job_names(self) -> AbstractSet[str]:
18
+ raise NotImplementedError()
19
+
20
+ @staticmethod
21
+ def names(
22
+ names: Sequence[str], code_location_name: str, repository_name: str
23
+ ) -> "NamesJobSelection":
24
+ check.invariant(len(names) == 1, "Only one job name is supported at this time")
25
+ return NamesJobSelection(
26
+ job_names=names, code_location_name=code_location_name, repository_name=repository_name
27
+ )
28
+
29
+
30
+ @whitelist_for_serdes
31
+ @record
32
+ class NamesJobSelection(JobSelection):
33
+ job_names: Sequence[str]
34
+
35
+ def resolve_job_names(self) -> AbstractSet[str]:
36
+ return set(self.job_names)
@@ -5,5 +5,5 @@ class CloudK8sRunLauncher(K8sRunLauncher):
5
5
  # Avoid call to `count_resume_run_attempts`, since resuming runs is not currently
6
6
  # supported in cloud and the method makes repeated event log calls.
7
7
  @property
8
- def supports_run_worker_crash_recovery(self):
8
+ def supports_run_worker_crash_recovery(self): # pyright: ignore[reportIncompatibleMethodOverride]
9
9
  return False
@@ -6,11 +6,11 @@ from dagster._core.launcher import RunLauncher
6
6
  from dagster._core.launcher.base import LaunchRunContext
7
7
  from dagster._core.utils import parse_env_var
8
8
  from dagster._grpc.types import ExecuteRunArgs
9
- from dagster._serdes.ipc import open_ipc_subprocess
10
- from dagster_cloud_cli.core.workspace import get_instance_ref_for_user_code
9
+ from dagster_shared.ipc import open_ipc_subprocess
11
10
 
12
11
  from dagster_cloud.execution.utils import TaskStatus
13
12
  from dagster_cloud.execution.utils.process import check_on_process, kill_process
13
+ from dagster_cloud.workspace.user_code_launcher.utils import get_instance_ref_for_user_code
14
14
 
15
15
  PID_TAG = "process/pid"
16
16
 
@@ -59,7 +59,7 @@ class CloudProcessRunLauncher(RunLauncher):
59
59
  for run_id in self._run_ids
60
60
  if (
61
61
  self._instance.get_run_by_id(run_id)
62
- and not self._instance.get_run_by_id(run_id).is_finished
62
+ and not self._instance.get_run_by_id(run_id).is_finished # pyright: ignore[reportOptionalMemberAccess]
63
63
  )
64
64
  ]
65
65
 
@@ -2,22 +2,9 @@ import logging
2
2
  import os
3
3
  import sys
4
4
  import threading
5
+ from collections.abc import Iterable, Mapping, Sequence
5
6
  from enum import Enum
6
- from typing import (
7
- Any,
8
- Dict,
9
- Iterable,
10
- List,
11
- Mapping,
12
- NamedTuple,
13
- Optional,
14
- Sequence,
15
- Set,
16
- Tuple,
17
- TypedDict,
18
- Union,
19
- cast,
20
- )
7
+ from typing import Any, NamedTuple, Optional, TypedDict, Union, cast
21
8
 
22
9
  import dagster._check as check
23
10
  import grpc
@@ -96,13 +83,13 @@ class CloudCodeServerHeartbeat(
96
83
  error: Optional[SerializableErrorInfo] = None,
97
84
  metadata: Optional[Mapping[str, Any]] = None,
98
85
  ):
99
- return super(CloudCodeServerHeartbeat, cls).__new__(
86
+ return super().__new__(
100
87
  cls,
101
88
  location_name=check.str_param(location_name, "location_name"),
102
89
  server_status=check.inst_param(server_status, "server_status", CloudCodeServerStatus),
103
90
  error=check.opt_inst_param(error, "error", SerializableErrorInfo),
104
91
  metadata=cast(
105
- CloudCodeServerHeartbeatMetadata, check.opt_mapping_param(metadata, "metadata")
92
+ "CloudCodeServerHeartbeatMetadata", check.opt_mapping_param(metadata, "metadata")
106
93
  ),
107
94
  )
108
95
 
@@ -113,7 +100,7 @@ class CloudCodeServerHeartbeat(
113
100
  return self._replace(
114
101
  error=None,
115
102
  metadata=cast(
116
- CloudCodeServerHeartbeatMetadata,
103
+ "CloudCodeServerHeartbeatMetadata",
117
104
  {},
118
105
  ),
119
106
  )
@@ -148,7 +135,7 @@ class CloudRunWorkerStatus(
148
135
  transient: Optional[bool] = None,
149
136
  run_worker_id: Optional[str] = None,
150
137
  ):
151
- return super(CloudRunWorkerStatus, cls).__new__(
138
+ return super().__new__(
152
139
  cls,
153
140
  run_id=check.str_param(run_id, "run_id"),
154
141
  status_type=check.inst_param(status_type, "status_type", WorkerStatus),
@@ -180,7 +167,7 @@ class CloudRunWorkerStatuses(
180
167
  NamedTuple(
181
168
  "_CloudRunWorkerStatuses",
182
169
  [
183
- ("statuses", List[CloudRunWorkerStatus]),
170
+ ("statuses", list[CloudRunWorkerStatus]),
184
171
  ("run_worker_monitoring_supported", bool),
185
172
  ("run_worker_monitoring_thread_alive", Optional[bool]),
186
173
  ],
@@ -188,11 +175,11 @@ class CloudRunWorkerStatuses(
188
175
  ):
189
176
  def __new__(
190
177
  cls,
191
- statuses: Optional[List[CloudRunWorkerStatus]],
178
+ statuses: Optional[list[CloudRunWorkerStatus]],
192
179
  run_worker_monitoring_supported: bool,
193
180
  run_worker_monitoring_thread_alive: Optional[bool],
194
181
  ):
195
- return super(CloudRunWorkerStatuses, cls).__new__(
182
+ return super().__new__(
196
183
  cls,
197
184
  statuses=check.opt_list_param(statuses, "statuses", of_type=CloudRunWorkerStatus),
198
185
  run_worker_monitoring_supported=check.bool_param(
@@ -229,13 +216,13 @@ def _is_grpc_unknown_error(error: Exception) -> bool:
229
216
  def get_cloud_run_worker_statuses(
230
217
  instance: DagsterCloudAgentInstance, deployment_names: Iterable[str], logger: logging.Logger
231
218
  ) -> Mapping[str, Sequence[CloudRunWorkerStatus]]:
232
- statuses: Dict[str, Sequence[CloudRunWorkerStatus]] = {}
219
+ statuses: dict[str, Sequence[CloudRunWorkerStatus]] = {}
233
220
 
234
221
  # protected with a lock inside the method
235
222
  active_grpc_server_handles = instance.user_code_launcher.get_active_grpc_server_handles()
236
223
  active_grpc_server_handle_strings = [str(s) for s in active_grpc_server_handles]
237
224
 
238
- active_non_isolated_run_ids_by_server_handle: Dict[
225
+ active_non_isolated_run_ids_by_server_handle: dict[
239
226
  str, Union[Sequence[str], _GetCurrentRunsError]
240
227
  ] = {}
241
228
  if instance.user_code_launcher.supports_get_current_runs_for_server_handle:
@@ -276,12 +263,19 @@ def get_cloud_run_worker_statuses(
276
263
  instance.ref_for_deployment(deployment_name)
277
264
  ) as scoped_instance:
278
265
  runs = scoped_instance.get_runs(RunsFilter(statuses=IN_PROGRESS_RUN_STATUSES))
279
- statuses_for_deployment: List[CloudRunWorkerStatus] = []
266
+ statuses_for_deployment: list[CloudRunWorkerStatus] = []
280
267
  for run in runs:
281
268
  if is_isolated_run(run):
282
269
  launcher = scoped_instance.run_launcher
283
270
 
284
- run_worker_health = launcher.check_run_worker_health(run)
271
+ try:
272
+ run_worker_health = launcher.check_run_worker_health(run)
273
+ except Exception:
274
+ logger.exception(
275
+ f"An exception occurred while checking run worker health for run '{run.run_id}'"
276
+ )
277
+ continue
278
+
285
279
  run_worker_debug_info = None
286
280
 
287
281
  if run_worker_health.status == WorkerStatus.FAILED:
@@ -385,8 +379,8 @@ def get_cloud_run_worker_statuses(
385
379
 
386
380
  def run_worker_monitoring_thread(
387
381
  instance: DagsterCloudAgentInstance,
388
- deployments_to_check: Set[str],
389
- statuses_dict: Dict[str, Sequence[CloudRunWorkerStatus]],
382
+ deployments_to_check: set[str],
383
+ statuses_dict: dict[str, Sequence[CloudRunWorkerStatus]],
390
384
  run_worker_monitoring_lock: threading.Lock,
391
385
  shutdown_event: threading.Event,
392
386
  monitoring_interval: Optional[int] = None,
@@ -409,8 +403,8 @@ def run_worker_monitoring_thread(
409
403
 
410
404
  def run_worker_monitoring_thread_iteration(
411
405
  instance: DagsterCloudAgentInstance,
412
- deployments_to_check: Set[str],
413
- statuses_dict: Dict[str, Sequence[CloudRunWorkerStatus]],
406
+ deployments_to_check: set[str],
407
+ statuses_dict: dict[str, Sequence[CloudRunWorkerStatus]],
414
408
  run_worker_monitoring_lock: threading.Lock,
415
409
  logger: logging.Logger,
416
410
  ) -> None:
@@ -432,11 +426,11 @@ def run_worker_monitoring_thread_iteration(
432
426
 
433
427
  def start_run_worker_monitoring_thread(
434
428
  instance: DagsterCloudAgentInstance,
435
- deployments_to_check: Set[str],
436
- statuses_dict: Dict[str, List[CloudRunWorkerStatus]],
429
+ deployments_to_check: set[str],
430
+ statuses_dict: dict[str, list[CloudRunWorkerStatus]],
437
431
  run_worker_monitoring_lock: threading.Lock,
438
432
  monitoring_interval: Optional[int] = None,
439
- ) -> Tuple[threading.Thread, threading.Event]:
433
+ ) -> tuple[threading.Thread, threading.Event]:
440
434
  shutdown_event = threading.Event()
441
435
  thread = threading.Thread(
442
436
  target=run_worker_monitoring_thread,
@@ -1,10 +1,10 @@
1
1
  import os
2
2
  import sys
3
- from typing import Sequence
3
+ from collections.abc import Sequence
4
4
 
5
- from dagster._serdes.ipc import interrupt_ipc_subprocess_pid, open_ipc_subprocess
5
+ from dagster_shared.ipc import interrupt_ipc_subprocess_pid, open_ipc_subprocess
6
6
 
7
- from . import TaskStatus
7
+ from dagster_cloud.execution.utils import TaskStatus
8
8
 
9
9
 
10
10
  def launch_process(args: Sequence[str]) -> int: