prefect-client 2.18.0__py3-none-any.whl → 2.18.1__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 (45) hide show
  1. prefect/_internal/schemas/fields.py +31 -12
  2. prefect/blocks/core.py +1 -1
  3. prefect/blocks/notifications.py +2 -2
  4. prefect/blocks/system.py +2 -3
  5. prefect/client/orchestration.py +283 -22
  6. prefect/client/schemas/sorting.py +9 -0
  7. prefect/client/utilities.py +25 -3
  8. prefect/concurrency/asyncio.py +11 -5
  9. prefect/concurrency/events.py +3 -3
  10. prefect/concurrency/services.py +1 -1
  11. prefect/concurrency/sync.py +9 -5
  12. prefect/deployments/deployments.py +27 -18
  13. prefect/deployments/runner.py +34 -26
  14. prefect/engine.py +3 -1
  15. prefect/events/actions.py +2 -1
  16. prefect/events/cli/automations.py +47 -9
  17. prefect/events/clients.py +50 -18
  18. prefect/events/filters.py +30 -3
  19. prefect/events/instrument.py +40 -40
  20. prefect/events/related.py +2 -1
  21. prefect/events/schemas/automations.py +50 -5
  22. prefect/events/schemas/deployment_triggers.py +15 -227
  23. prefect/events/schemas/events.py +7 -7
  24. prefect/events/utilities.py +1 -1
  25. prefect/events/worker.py +10 -7
  26. prefect/flows.py +33 -18
  27. prefect/input/actions.py +9 -9
  28. prefect/input/run_input.py +49 -37
  29. prefect/new_flow_engine.py +293 -0
  30. prefect/new_task_engine.py +374 -0
  31. prefect/results.py +3 -2
  32. prefect/runner/runner.py +3 -2
  33. prefect/server/api/collections_data/views/aggregate-worker-metadata.json +44 -3
  34. prefect/settings.py +26 -0
  35. prefect/states.py +25 -19
  36. prefect/tasks.py +17 -0
  37. prefect/utilities/asyncutils.py +37 -0
  38. prefect/utilities/engine.py +6 -4
  39. prefect/utilities/schema_tools/validation.py +1 -1
  40. {prefect_client-2.18.0.dist-info → prefect_client-2.18.1.dist-info}/METADATA +1 -1
  41. {prefect_client-2.18.0.dist-info → prefect_client-2.18.1.dist-info}/RECORD +44 -43
  42. prefect/concurrency/common.py +0 -0
  43. {prefect_client-2.18.0.dist-info → prefect_client-2.18.1.dist-info}/LICENSE +0 -0
  44. {prefect_client-2.18.0.dist-info → prefect_client-2.18.1.dist-info}/WHEEL +0 -0
  45. {prefect_client-2.18.0.dist-info → prefect_client-2.18.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,374 @@
1
+ import asyncio
2
+ import logging
3
+ from contextlib import asynccontextmanager
4
+ from dataclasses import dataclass, field
5
+ from typing import (
6
+ Any,
7
+ AsyncGenerator,
8
+ Callable,
9
+ Coroutine,
10
+ Dict,
11
+ Generic,
12
+ Iterable,
13
+ Literal,
14
+ Optional,
15
+ TypeVar,
16
+ Union,
17
+ cast,
18
+ )
19
+ from uuid import uuid4
20
+
21
+ import pendulum
22
+ from typing_extensions import ParamSpec
23
+
24
+ from prefect import Task, get_client
25
+ from prefect.client.orchestration import PrefectClient
26
+ from prefect.client.schemas import TaskRun
27
+ from prefect.client.schemas.objects import TaskRunResult
28
+ from prefect.context import FlowRunContext, TaskRunContext
29
+ from prefect.futures import PrefectFuture, resolve_futures_to_states
30
+ from prefect.logging.loggers import get_logger, task_run_logger
31
+ from prefect.results import ResultFactory
32
+ from prefect.server.schemas.states import State
33
+ from prefect.settings import PREFECT_TASKS_REFRESH_CACHE
34
+ from prefect.states import (
35
+ Pending,
36
+ Retrying,
37
+ Running,
38
+ StateDetails,
39
+ exception_to_crashed_state,
40
+ exception_to_failed_state,
41
+ return_value_to_state,
42
+ )
43
+ from prefect.utilities.asyncutils import A, Async, is_async_fn
44
+ from prefect.utilities.engine import (
45
+ _dynamic_key_for_task_run,
46
+ _get_hook_name,
47
+ _resolve_custom_task_run_name,
48
+ collect_task_run_inputs,
49
+ propose_state,
50
+ )
51
+
52
+
53
+ @asynccontextmanager
54
+ async def timeout(
55
+ delay: Optional[float], *, loop: Optional[asyncio.AbstractEventLoop] = None
56
+ ) -> AsyncGenerator[None, None]:
57
+ loop = loop or asyncio.get_running_loop()
58
+ task = asyncio.current_task(loop=loop)
59
+ timer_handle: Optional[asyncio.TimerHandle] = None
60
+
61
+ if delay is not None and task is not None:
62
+ timer_handle = loop.call_later(delay, task.cancel)
63
+
64
+ try:
65
+ yield
66
+ finally:
67
+ if timer_handle is not None:
68
+ timer_handle.cancel()
69
+
70
+
71
+ P = ParamSpec("P")
72
+ R = TypeVar("R")
73
+
74
+
75
+ @dataclass
76
+ class TaskRunEngine(Generic[P, R]):
77
+ task: Task[P, Coroutine[Any, Any, R]]
78
+ logger: logging.Logger = field(default_factory=lambda: get_logger("engine"))
79
+ parameters: Optional[Dict[str, Any]] = None
80
+ task_run: Optional[TaskRun] = None
81
+ retries: int = 0
82
+ _is_started: bool = False
83
+ _client: Optional[PrefectClient] = None
84
+
85
+ def __post_init__(self):
86
+ if self.parameters is None:
87
+ self.parameters = {}
88
+
89
+ @property
90
+ def client(self) -> PrefectClient:
91
+ if not self._is_started or self._client is None:
92
+ raise RuntimeError("Engine has not started.")
93
+ return self._client
94
+
95
+ @property
96
+ def state(self) -> State:
97
+ return self.task_run.state # type: ignore
98
+
99
+ @property
100
+ def can_retry(self) -> bool:
101
+ retry_condition: Optional[ # type: ignore
102
+ Callable[[Task[P, Coroutine[Any, Any, R]], TaskRun, State], bool]
103
+ ] = self.task.retry_condition_fn # type: ignore
104
+ return not retry_condition or retry_condition(
105
+ self.task, self.task_run, self.state
106
+ ) # type: ignore
107
+
108
+ async def _run_hooks(self, state: State) -> None:
109
+ """Run the on_failure and on_completion hooks for a task, making sure to
110
+ catch and log any errors that occur.
111
+ """
112
+ task = self.task
113
+ task_run = self.task_run
114
+
115
+ hooks = None
116
+ if state.is_failed() and task.on_failure:
117
+ hooks = task.on_failure
118
+ elif state.is_completed() and task.on_completion:
119
+ hooks = task.on_completion
120
+
121
+ if hooks:
122
+ for hook in hooks:
123
+ hook_name = _get_hook_name(hook)
124
+ try:
125
+ self.logger.info(
126
+ f"Running hook {hook_name!r} in response to entering state"
127
+ f" {state.name!r}"
128
+ )
129
+ if is_async_fn(hook):
130
+ await hook(task=task, task_run=task_run, state=state)
131
+ else:
132
+ hook(task=task, task_run=task_run, state=state)
133
+ except Exception:
134
+ self.logger.error(
135
+ f"An error was encountered while running hook {hook_name!r}",
136
+ exc_info=True,
137
+ )
138
+ else:
139
+ self.logger.info(
140
+ f"Hook {hook_name!r} finished running successfully"
141
+ )
142
+
143
+ def _compute_state_details(
144
+ self, include_cache_expiration: bool = False
145
+ ) -> StateDetails:
146
+ ## setup cache metadata
147
+ task_run_context = TaskRunContext.get()
148
+ cache_key = (
149
+ self.task.cache_key_fn(
150
+ task_run_context,
151
+ self.parameters,
152
+ )
153
+ if self.task.cache_key_fn
154
+ else None
155
+ )
156
+ # Ignore the cached results for a cache key, default = false
157
+ # Setting on task level overrules the Prefect setting (env var)
158
+ refresh_cache = (
159
+ self.task.refresh_cache
160
+ if self.task.refresh_cache is not None
161
+ else PREFECT_TASKS_REFRESH_CACHE.value()
162
+ )
163
+
164
+ if include_cache_expiration:
165
+ cache_expiration = (
166
+ (pendulum.now("utc") + self.task.cache_expiration)
167
+ if self.task.cache_expiration
168
+ else None
169
+ )
170
+ else:
171
+ cache_expiration = None
172
+ return StateDetails(
173
+ cache_key=cache_key,
174
+ refresh_cache=refresh_cache,
175
+ cache_expiration=cache_expiration,
176
+ )
177
+
178
+ async def begin_run(self) -> State:
179
+ state_details = self._compute_state_details()
180
+ new_state = Running(state_details=state_details)
181
+ state = await self.set_state(new_state)
182
+ while state.is_pending():
183
+ await asyncio.sleep(1)
184
+ state = await self.set_state(new_state)
185
+
186
+ async def set_state(self, state: State, force: bool = False) -> State:
187
+ new_state = await propose_state(
188
+ self.client, state, task_run_id=self.task_run.id, force=force
189
+ ) # type: ignore
190
+
191
+ # currently this is a hack to keep a reference to the state object
192
+ # that has an in-memory result attached to it; using the API state
193
+ # could result in losing that reference
194
+ self.task_run.state = new_state
195
+ if new_state.is_final():
196
+ await self._run_hooks(new_state)
197
+ return new_state
198
+
199
+ async def result(self, raise_on_failure: bool = True) -> "Union[R, State, None]":
200
+ return await self.state.result(raise_on_failure=raise_on_failure)
201
+
202
+ async def handle_success(self, result: R) -> R:
203
+ result_factory = getattr(TaskRunContext.get(), "result_factory", None)
204
+ terminal_state = await return_value_to_state(
205
+ await resolve_futures_to_states(result),
206
+ result_factory=result_factory,
207
+ )
208
+ terminal_state.state_details = self._compute_state_details(
209
+ include_cache_expiration=True
210
+ )
211
+ await self.set_state(terminal_state)
212
+ return result
213
+
214
+ async def handle_retry(self, exc: Exception) -> bool:
215
+ """
216
+ If the task has retries left, and the retry condition is met, set the task to retrying.
217
+ - If the task has no retries left, or the retry condition is not met, return False.
218
+ - If the task has retries left, and the retry condition is met, return True.
219
+ """
220
+ if self.retries < self.task.retries and self.can_retry:
221
+ await self.set_state(Retrying(), force=True)
222
+ self.retries = self.retries + 1
223
+ return True
224
+ return False
225
+
226
+ async def handle_exception(self, exc: Exception) -> None:
227
+ # If the task fails, and we have retries left, set the task to retrying.
228
+ if not await self.handle_retry(exc):
229
+ # If the task has no retries left, or the retry condition is not met, set the task to failed.
230
+ context = TaskRunContext.get()
231
+ state = await exception_to_failed_state(
232
+ exc,
233
+ message="Task run encountered an exception",
234
+ result_factory=getattr(context, "result_factory", None),
235
+ )
236
+ await self.set_state(state)
237
+
238
+ async def handle_crash(self, exc: BaseException) -> None:
239
+ state = await exception_to_crashed_state(exc)
240
+ self.logger.error(f"Crash detected! {state.message}")
241
+ self.logger.debug("Crash details:", exc_info=exc)
242
+ await self.set_state(state, force=True)
243
+
244
+ async def create_task_run(self, client: PrefectClient) -> TaskRun:
245
+ flow_run_ctx = FlowRunContext.get()
246
+ try:
247
+ task_run_name = _resolve_custom_task_run_name(self.task, self.parameters)
248
+ except TypeError:
249
+ task_run_name = None
250
+
251
+ # prep input tracking
252
+ task_inputs = {
253
+ k: await collect_task_run_inputs(v) for k, v in self.parameters.items()
254
+ }
255
+
256
+ # anticipate nested runs
257
+ task_run_ctx = TaskRunContext.get()
258
+ if task_run_ctx:
259
+ task_inputs["wait_for"] = [TaskRunResult(id=task_run_ctx.task_run.id)]
260
+
261
+ # TODO: implement wait_for
262
+ # if wait_for:
263
+ # task_inputs["wait_for"] = await collect_task_run_inputs(wait_for)
264
+
265
+ if flow_run_ctx:
266
+ dynamic_key = _dynamic_key_for_task_run(
267
+ context=flow_run_ctx, task=self.task
268
+ )
269
+ else:
270
+ dynamic_key = uuid4().hex
271
+ task_run = await client.create_task_run(
272
+ task=self.task,
273
+ name=task_run_name,
274
+ flow_run_id=(
275
+ getattr(flow_run_ctx.flow_run, "id", None)
276
+ if flow_run_ctx and flow_run_ctx.flow_run
277
+ else None
278
+ ),
279
+ dynamic_key=dynamic_key,
280
+ state=Pending(),
281
+ task_inputs=task_inputs,
282
+ )
283
+ return task_run
284
+
285
+ @asynccontextmanager
286
+ async def enter_run_context(self, client: Optional[PrefectClient] = None):
287
+ if client is None:
288
+ client = self.client
289
+
290
+ self.task_run = await client.read_task_run(self.task_run.id)
291
+
292
+ with TaskRunContext(
293
+ task=self.task,
294
+ log_prints=self.task.log_prints or False,
295
+ task_run=self.task_run,
296
+ parameters=self.parameters,
297
+ result_factory=await ResultFactory.from_autonomous_task(self.task),
298
+ client=client,
299
+ ):
300
+ self.logger = task_run_logger(task_run=self.task_run, task=self.task)
301
+ yield
302
+
303
+ @asynccontextmanager
304
+ async def start(self):
305
+ """
306
+ Enters a client context and creates a task run if needed.
307
+ """
308
+ async with get_client() as client:
309
+ self._client = client
310
+ self._is_started = True
311
+ try:
312
+ if not self.task_run:
313
+ self.task_run = await self.create_task_run(client)
314
+
315
+ yield self
316
+ except Exception:
317
+ # regular exceptions are caught and re-raised to the user
318
+ raise
319
+ except BaseException as exc:
320
+ # BaseExceptions are caught and handled as crashes
321
+ await self.handle_crash(exc)
322
+ raise
323
+ finally:
324
+ self._is_started = False
325
+ self._client = None
326
+
327
+ def is_running(self) -> bool:
328
+ if getattr(self, "task_run", None) is None:
329
+ return False
330
+ return getattr(self, "task_run").state.is_running()
331
+
332
+ def is_pending(self) -> bool:
333
+ if getattr(self, "task_run", None) is None:
334
+ return False # TODO: handle this differently?
335
+ return getattr(self, "task_run").state.is_pending()
336
+
337
+
338
+ async def run_task(
339
+ task: Task[P, Coroutine[Any, Any, R]],
340
+ task_run: Optional[TaskRun] = None,
341
+ parameters: Optional[Dict[str, Any]] = None,
342
+ wait_for: Optional[Iterable[PrefectFuture[A, Async]]] = None,
343
+ return_type: Literal["state", "result"] = "result",
344
+ ) -> "Union[R, State, None]":
345
+ """
346
+ Runs a task against the API.
347
+
348
+ We will most likely want to use this logic as a wrapper and return a coroutine for type inference.
349
+ """
350
+ engine = TaskRunEngine[P, R](task=task, parameters=parameters, task_run=task_run)
351
+ async with engine.start() as run:
352
+ # This is a context manager that keeps track of the run of the task run.
353
+ await run.begin_run()
354
+
355
+ while run.is_running():
356
+ async with run.enter_run_context():
357
+ try:
358
+ # This is where the task is actually run.
359
+ async with timeout(run.task.timeout_seconds):
360
+ if task.isasync:
361
+ result = cast(R, await task.fn(**(parameters or {}))) # type: ignore
362
+ else:
363
+ result = cast(R, task.fn(**(parameters or {}))) # type: ignore
364
+ # If the task run is successful, finalize it.
365
+ await run.handle_success(result)
366
+ if return_type == "result":
367
+ return result
368
+
369
+ except Exception as exc:
370
+ await run.handle_exception(exc)
371
+
372
+ if return_type == "state":
373
+ return run.state
374
+ return await run.result()
prefect/results.py CHANGED
@@ -16,7 +16,7 @@ from typing import (
16
16
  )
17
17
  from uuid import UUID
18
18
 
19
- from typing_extensions import Self
19
+ from typing_extensions import ParamSpec, Self
20
20
 
21
21
  import prefect
22
22
  from prefect._internal.pydantic import HAS_PYDANTIC_V2
@@ -63,6 +63,7 @@ def DEFAULT_STORAGE_KEY_FN():
63
63
 
64
64
 
65
65
  logger = get_logger("results")
66
+ P = ParamSpec("P")
66
67
  R = TypeVar("R")
67
68
 
68
69
 
@@ -286,7 +287,7 @@ class ResultFactory(pydantic.BaseModel):
286
287
  @classmethod
287
288
  @inject_client
288
289
  async def from_autonomous_task(
289
- cls: Type[Self], task: "Task", client: "PrefectClient" = None
290
+ cls: Type[Self], task: "Task[P, R]", client: "PrefectClient" = None
290
291
  ) -> Self:
291
292
  """
292
293
  Create a new result factory for an autonomous task.
prefect/runner/runner.py CHANGED
@@ -29,6 +29,7 @@ Example:
29
29
  ```
30
30
 
31
31
  """
32
+
32
33
  import asyncio
33
34
  import datetime
34
35
  import inspect
@@ -80,7 +81,7 @@ from prefect.deployments.runner import (
80
81
  )
81
82
  from prefect.deployments.schedules import FlexibleScheduleList
82
83
  from prefect.engine import propose_state
83
- from prefect.events import DeploymentTriggerTypes
84
+ from prefect.events import DeploymentTriggerTypes, TriggerTypes
84
85
  from prefect.exceptions import (
85
86
  Abort,
86
87
  )
@@ -232,7 +233,7 @@ class Runner:
232
233
  schedule: Optional[SCHEDULE_TYPES] = None,
233
234
  is_schedule_active: Optional[bool] = None,
234
235
  parameters: Optional[dict] = None,
235
- triggers: Optional[List[DeploymentTriggerTypes]] = None,
236
+ triggers: Optional[List[Union[DeploymentTriggerTypes, TriggerTypes]]] = None,
236
237
  description: Optional[str] = None,
237
238
  tags: Optional[List[str]] = None,
238
239
  version: Optional[str] = None,
@@ -111,10 +111,12 @@
111
111
  "taskRoleArn": "{{ task_role_arn }}"
112
112
  },
113
113
  "tags": "{{ labels }}",
114
- "taskDefinition": "{{ task_definition_arn }}"
114
+ "taskDefinition": "{{ task_definition_arn }}",
115
+ "capacityProviderStrategy": "{{ capacity_provider_strategy }}"
115
116
  },
116
117
  "configure_cloudwatch_logs": "{{ configure_cloudwatch_logs }}",
117
118
  "cloudwatch_logs_options": "{{ cloudwatch_logs_options }}",
119
+ "cloudwatch_logs_prefix": "{{ cloudwatch_logs_prefix }}",
118
120
  "network_configuration": "{{ network_configuration }}",
119
121
  "stream_output": "{{ stream_output }}",
120
122
  "task_start_timeout_seconds": "{{ task_start_timeout_seconds }}",
@@ -191,6 +193,14 @@
191
193
  ],
192
194
  "type": "string"
193
195
  },
196
+ "capacity_provider_strategy": {
197
+ "title": "Capacity Provider Strategy",
198
+ "description": "The capacity provider strategy to use when running the task. If a capacity provider strategy is specified, the selected launch type will be ignored.",
199
+ "type": "array",
200
+ "items": {
201
+ "$ref": "#/definitions/CapacityProvider"
202
+ }
203
+ },
194
204
  "image": {
195
205
  "title": "Image",
196
206
  "description": "The image to use for the Prefect container in the task. If this value is not null, it will override the value in the task definition. This value defaults to a Prefect base image matching your local versions.",
@@ -239,6 +249,11 @@
239
249
  "type": "string"
240
250
  }
241
251
  },
252
+ "cloudwatch_logs_prefix": {
253
+ "title": "Cloudwatch Logs Prefix",
254
+ "description": "When `configure_cloudwatch_logs` is enabled, this setting may be used to set a prefix for the log group. If not provided, the default prefix will be `prefect-logs_<work_pool_name>_<deployment_id>`. If `awslogs-stream-prefix` is present in `Cloudwatch logs options` this setting will be ignored.",
255
+ "type": "string"
256
+ },
242
257
  "network_configuration": {
243
258
  "title": "Network Configuration",
244
259
  "description": "When `network_configuration` is supplied it will override ECS Worker'sawsvpcConfiguration that defined in the ECS task executing your workload. See the [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-service-awsvpcconfiguration.html) for available options.",
@@ -370,6 +385,30 @@
370
385
  "aws_secret_access_key"
371
386
  ],
372
387
  "block_schema_references": {}
388
+ },
389
+ "CapacityProvider": {
390
+ "title": "CapacityProvider",
391
+ "description": "The capacity provider strategy to use when running the task.",
392
+ "type": "object",
393
+ "properties": {
394
+ "capacityProvider": {
395
+ "title": "Capacityprovider",
396
+ "type": "string"
397
+ },
398
+ "weight": {
399
+ "title": "Weight",
400
+ "type": "integer"
401
+ },
402
+ "base": {
403
+ "title": "Base",
404
+ "type": "integer"
405
+ }
406
+ },
407
+ "required": [
408
+ "capacityProvider",
409
+ "weight",
410
+ "base"
411
+ ]
373
412
  }
374
413
  }
375
414
  }
@@ -1100,7 +1139,9 @@
1100
1139
  "serviceAccount": "{{ service_account_name }}",
1101
1140
  "maxRetries": "{{ max_retries }}",
1102
1141
  "timeout": "{{ timeout }}",
1103
- "vpcAccess": "{{ vpc_connector_name }}",
1142
+ "vpcAccess": {
1143
+ "connector": "{{ vpc_connector_name }}"
1144
+ },
1104
1145
  "containers": [
1105
1146
  {
1106
1147
  "env": [],
@@ -1647,4 +1688,4 @@
1647
1688
  "type": "kubernetes"
1648
1689
  }
1649
1690
  }
1650
- }
1691
+ }
prefect/settings.py CHANGED
@@ -1214,6 +1214,20 @@ PREFECT_API_SERVICES_CANCELLATION_CLEANUP_LOOP_SECONDS = Setting(
1214
1214
  this often. Defaults to `20`.
1215
1215
  """
1216
1216
 
1217
+ PREFECT_API_SERVICES_FOREMAN_ENABLED = Setting(bool, default=True)
1218
+ """Whether or not to start the Foreman service in the server application."""
1219
+
1220
+ PREFECT_API_SERVICES_FOREMAN_LOOP_SECONDS = Setting(float, default=15)
1221
+ """The number of seconds to wait between each iteration of the Foreman loop which checks
1222
+ for offline workers and updates work pool status."""
1223
+
1224
+ PREFECT_API_SERVICES_FOREMAN_DEPLOYMENT_LAST_POLLED_TIMEOUT_SECONDS = Setting(
1225
+ int, default=60
1226
+ )
1227
+ """The number of seconds before a deployment is marked as not ready if it has not been
1228
+ polled."""
1229
+
1230
+
1217
1231
  PREFECT_API_DEFAULT_LIMIT = Setting(
1218
1232
  int,
1219
1233
  default=200,
@@ -1579,6 +1593,11 @@ PREFECT_EXPERIMENTAL_ENABLE_WORK_QUEUE_STATUS = Setting(bool, default=True)
1579
1593
  Whether or not to enable experimental work queue status in-place of work queue health.
1580
1594
  """
1581
1595
 
1596
+ PREFECT_EXPERIMENTAL_ENABLE_NEW_ENGINE = Setting(bool, default=False)
1597
+ """
1598
+ Whether or not to enable experimental new engine.
1599
+ """
1600
+
1582
1601
 
1583
1602
  # Defaults -----------------------------------------------------------------------------
1584
1603
 
@@ -1698,6 +1717,13 @@ PREFECT_API_EVENTS_STREAM_OUT_ENABLED = Setting(bool, default=True)
1698
1717
  Whether or not to allow streaming events out of via websockets.
1699
1718
  """
1700
1719
 
1720
+ PREFECT_API_EVENTS_RELATED_RESOURCE_CACHE_TTL = Setting(
1721
+ timedelta, default=timedelta(minutes=5)
1722
+ )
1723
+ """
1724
+ How long to cache related resource data for emitting server-side vents
1725
+ """
1726
+
1701
1727
  # Deprecated settings ------------------------------------------------------------------
1702
1728
 
1703
1729
 
prefect/states.py CHANGED
@@ -486,8 +486,10 @@ class StateGroup:
486
486
 
487
487
 
488
488
  def Scheduled(
489
- cls: Type[State] = State, scheduled_time: datetime.datetime = None, **kwargs
490
- ) -> State:
489
+ cls: Type[State[R]] = State,
490
+ scheduled_time: Optional[datetime.datetime] = None,
491
+ **kwargs: Any,
492
+ ) -> State[R]:
491
493
  """Convenience function for creating `Scheduled` states.
492
494
 
493
495
  Returns:
@@ -503,7 +505,7 @@ def Scheduled(
503
505
  return cls(type=StateType.SCHEDULED, state_details=state_details, **kwargs)
504
506
 
505
507
 
506
- def Completed(cls: Type[State] = State, **kwargs) -> State:
508
+ def Completed(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
507
509
  """Convenience function for creating `Completed` states.
508
510
 
509
511
  Returns:
@@ -512,7 +514,7 @@ def Completed(cls: Type[State] = State, **kwargs) -> State:
512
514
  return cls(type=StateType.COMPLETED, **kwargs)
513
515
 
514
516
 
515
- def Running(cls: Type[State] = State, **kwargs) -> State:
517
+ def Running(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
516
518
  """Convenience function for creating `Running` states.
517
519
 
518
520
  Returns:
@@ -521,7 +523,7 @@ def Running(cls: Type[State] = State, **kwargs) -> State:
521
523
  return cls(type=StateType.RUNNING, **kwargs)
522
524
 
523
525
 
524
- def Failed(cls: Type[State] = State, **kwargs) -> State:
526
+ def Failed(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
525
527
  """Convenience function for creating `Failed` states.
526
528
 
527
529
  Returns:
@@ -530,7 +532,7 @@ def Failed(cls: Type[State] = State, **kwargs) -> State:
530
532
  return cls(type=StateType.FAILED, **kwargs)
531
533
 
532
534
 
533
- def Crashed(cls: Type[State] = State, **kwargs) -> State:
535
+ def Crashed(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
534
536
  """Convenience function for creating `Crashed` states.
535
537
 
536
538
  Returns:
@@ -539,7 +541,7 @@ def Crashed(cls: Type[State] = State, **kwargs) -> State:
539
541
  return cls(type=StateType.CRASHED, **kwargs)
540
542
 
541
543
 
542
- def Cancelling(cls: Type[State] = State, **kwargs) -> State:
544
+ def Cancelling(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
543
545
  """Convenience function for creating `Cancelling` states.
544
546
 
545
547
  Returns:
@@ -548,7 +550,7 @@ def Cancelling(cls: Type[State] = State, **kwargs) -> State:
548
550
  return cls(type=StateType.CANCELLING, **kwargs)
549
551
 
550
552
 
551
- def Cancelled(cls: Type[State] = State, **kwargs) -> State:
553
+ def Cancelled(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
552
554
  """Convenience function for creating `Cancelled` states.
553
555
 
554
556
  Returns:
@@ -557,7 +559,7 @@ def Cancelled(cls: Type[State] = State, **kwargs) -> State:
557
559
  return cls(type=StateType.CANCELLED, **kwargs)
558
560
 
559
561
 
560
- def Pending(cls: Type[State] = State, **kwargs) -> State:
562
+ def Pending(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
561
563
  """Convenience function for creating `Pending` states.
562
564
 
563
565
  Returns:
@@ -567,13 +569,13 @@ def Pending(cls: Type[State] = State, **kwargs) -> State:
567
569
 
568
570
 
569
571
  def Paused(
570
- cls: Type[State] = State,
572
+ cls: Type[State[R]] = State,
571
573
  timeout_seconds: Optional[int] = None,
572
574
  pause_expiration_time: Optional[datetime.datetime] = None,
573
575
  reschedule: bool = False,
574
576
  pause_key: Optional[str] = None,
575
- **kwargs,
576
- ) -> State:
577
+ **kwargs: Any,
578
+ ) -> State[R]:
577
579
  """Convenience function for creating `Paused` states.
578
580
 
579
581
  Returns:
@@ -603,11 +605,11 @@ def Paused(
603
605
 
604
606
 
605
607
  def Suspended(
606
- cls: Type[State] = State,
608
+ cls: Type[State[R]] = State,
607
609
  timeout_seconds: Optional[int] = None,
608
610
  pause_expiration_time: Optional[datetime.datetime] = None,
609
611
  pause_key: Optional[str] = None,
610
- **kwargs,
612
+ **kwargs: Any,
611
613
  ):
612
614
  """Convenience function for creating `Suspended` states.
613
615
 
@@ -626,8 +628,10 @@ def Suspended(
626
628
 
627
629
 
628
630
  def AwaitingRetry(
629
- cls: Type[State] = State, scheduled_time: datetime.datetime = None, **kwargs
630
- ) -> State:
631
+ cls: Type[State[R]] = State,
632
+ scheduled_time: Optional[datetime.datetime] = None,
633
+ **kwargs: Any,
634
+ ) -> State[R]:
631
635
  """Convenience function for creating `AwaitingRetry` states.
632
636
 
633
637
  Returns:
@@ -638,7 +642,7 @@ def AwaitingRetry(
638
642
  )
639
643
 
640
644
 
641
- def Retrying(cls: Type[State] = State, **kwargs) -> State:
645
+ def Retrying(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
642
646
  """Convenience function for creating `Retrying` states.
643
647
 
644
648
  Returns:
@@ -648,8 +652,10 @@ def Retrying(cls: Type[State] = State, **kwargs) -> State:
648
652
 
649
653
 
650
654
  def Late(
651
- cls: Type[State] = State, scheduled_time: datetime.datetime = None, **kwargs
652
- ) -> State:
655
+ cls: Type[State[R]] = State,
656
+ scheduled_time: Optional[datetime.datetime] = None,
657
+ **kwargs: Any,
658
+ ) -> State[R]:
653
659
  """Convenience function for creating `Late` states.
654
660
 
655
661
  Returns: