prefect-client 3.1.15__py3-none-any.whl → 3.2.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 (89) hide show
  1. prefect/_experimental/sla/objects.py +29 -1
  2. prefect/_internal/compatibility/deprecated.py +4 -4
  3. prefect/_internal/compatibility/migration.py +1 -1
  4. prefect/_internal/concurrency/calls.py +1 -2
  5. prefect/_internal/concurrency/cancellation.py +2 -4
  6. prefect/_internal/concurrency/threads.py +3 -3
  7. prefect/_internal/schemas/bases.py +3 -11
  8. prefect/_internal/schemas/validators.py +36 -60
  9. prefect/_result_records.py +235 -0
  10. prefect/_version.py +3 -3
  11. prefect/agent.py +1 -0
  12. prefect/automations.py +4 -8
  13. prefect/blocks/notifications.py +8 -8
  14. prefect/cache_policies.py +2 -0
  15. prefect/client/base.py +7 -8
  16. prefect/client/collections.py +3 -6
  17. prefect/client/orchestration/__init__.py +15 -263
  18. prefect/client/orchestration/_deployments/client.py +14 -6
  19. prefect/client/orchestration/_flow_runs/client.py +10 -6
  20. prefect/client/orchestration/_work_pools/__init__.py +0 -0
  21. prefect/client/orchestration/_work_pools/client.py +598 -0
  22. prefect/client/orchestration/base.py +9 -2
  23. prefect/client/schemas/actions.py +66 -2
  24. prefect/client/schemas/objects.py +22 -50
  25. prefect/client/schemas/schedules.py +7 -18
  26. prefect/client/types/flexible_schedule_list.py +2 -1
  27. prefect/context.py +2 -3
  28. prefect/deployments/flow_runs.py +1 -1
  29. prefect/deployments/runner.py +119 -43
  30. prefect/deployments/schedules.py +7 -1
  31. prefect/engine.py +4 -9
  32. prefect/events/schemas/automations.py +4 -2
  33. prefect/events/utilities.py +15 -13
  34. prefect/exceptions.py +1 -1
  35. prefect/flow_engine.py +19 -10
  36. prefect/flow_runs.py +4 -8
  37. prefect/flows.py +53 -22
  38. prefect/infrastructure/__init__.py +1 -0
  39. prefect/infrastructure/base.py +1 -0
  40. prefect/infrastructure/provisioners/__init__.py +3 -6
  41. prefect/infrastructure/provisioners/coiled.py +3 -3
  42. prefect/infrastructure/provisioners/container_instance.py +1 -0
  43. prefect/infrastructure/provisioners/ecs.py +6 -6
  44. prefect/infrastructure/provisioners/modal.py +3 -3
  45. prefect/input/run_input.py +5 -7
  46. prefect/locking/filesystem.py +4 -3
  47. prefect/main.py +1 -1
  48. prefect/results.py +42 -249
  49. prefect/runner/runner.py +9 -4
  50. prefect/runner/server.py +5 -5
  51. prefect/runner/storage.py +12 -10
  52. prefect/runner/submit.py +2 -4
  53. prefect/schedules.py +231 -0
  54. prefect/serializers.py +5 -5
  55. prefect/settings/__init__.py +2 -1
  56. prefect/settings/base.py +3 -3
  57. prefect/settings/models/root.py +4 -0
  58. prefect/settings/models/server/services.py +50 -9
  59. prefect/settings/sources.py +8 -4
  60. prefect/states.py +42 -11
  61. prefect/task_engine.py +10 -10
  62. prefect/task_runners.py +11 -22
  63. prefect/task_worker.py +9 -9
  64. prefect/tasks.py +22 -41
  65. prefect/telemetry/bootstrap.py +4 -6
  66. prefect/telemetry/services.py +2 -4
  67. prefect/types/__init__.py +2 -1
  68. prefect/types/_datetime.py +28 -1
  69. prefect/utilities/_engine.py +0 -1
  70. prefect/utilities/asyncutils.py +4 -8
  71. prefect/utilities/collections.py +13 -22
  72. prefect/utilities/dispatch.py +2 -4
  73. prefect/utilities/dockerutils.py +6 -6
  74. prefect/utilities/importtools.py +1 -68
  75. prefect/utilities/names.py +1 -1
  76. prefect/utilities/processutils.py +3 -6
  77. prefect/utilities/pydantic.py +4 -6
  78. prefect/utilities/schema_tools/hydration.py +6 -5
  79. prefect/utilities/templating.py +16 -10
  80. prefect/utilities/visualization.py +2 -4
  81. prefect/workers/base.py +3 -3
  82. prefect/workers/block.py +1 -0
  83. prefect/workers/cloud.py +1 -0
  84. prefect/workers/process.py +1 -0
  85. {prefect_client-3.1.15.dist-info → prefect_client-3.2.1.dist-info}/METADATA +1 -1
  86. {prefect_client-3.1.15.dist-info → prefect_client-3.2.1.dist-info}/RECORD +89 -85
  87. {prefect_client-3.1.15.dist-info → prefect_client-3.2.1.dist-info}/LICENSE +0 -0
  88. {prefect_client-3.1.15.dist-info → prefect_client-3.2.1.dist-info}/WHEEL +0 -0
  89. {prefect_client-3.1.15.dist-info → prefect_client-3.2.1.dist-info}/top_level.txt +0 -0
@@ -22,7 +22,14 @@ from prefect.client.schemas.objects import (
22
22
  StateDetails,
23
23
  StateType,
24
24
  )
25
- from prefect.client.schemas.schedules import SCHEDULE_TYPES
25
+ from prefect.client.schemas.schedules import (
26
+ SCHEDULE_TYPES,
27
+ CronSchedule,
28
+ IntervalSchedule,
29
+ NoSchedule,
30
+ RRuleSchedule,
31
+ )
32
+ from prefect.schedules import Schedule
26
33
  from prefect.settings import PREFECT_DEPLOYMENT_SCHEDULE_MAX_SCHEDULED_RUNS
27
34
  from prefect.types import (
28
35
  MAX_VARIABLE_NAME_LENGTH,
@@ -39,7 +46,7 @@ from prefect.utilities.collections import listrepr
39
46
  from prefect.utilities.pydantic import get_class_fields_only
40
47
 
41
48
  if TYPE_CHECKING:
42
- from prefect.results import ResultRecordMetadata
49
+ from prefect._result_records import ResultRecordMetadata
43
50
 
44
51
  R = TypeVar("R")
45
52
 
@@ -92,6 +99,14 @@ class DeploymentScheduleCreate(ActionBaseModel):
92
99
  default=None,
93
100
  description="The maximum number of scheduled runs for the schedule.",
94
101
  )
102
+ parameters: dict[str, Any] = Field(
103
+ default_factory=dict,
104
+ description="Parameter overrides for the schedule.",
105
+ )
106
+ slug: Optional[str] = Field(
107
+ default=None,
108
+ description="A unique identifier for the schedule.",
109
+ )
95
110
 
96
111
  @field_validator("active", mode="wrap")
97
112
  @classmethod
@@ -110,6 +125,45 @@ class DeploymentScheduleCreate(ActionBaseModel):
110
125
  v, PREFECT_DEPLOYMENT_SCHEDULE_MAX_SCHEDULED_RUNS.value()
111
126
  )
112
127
 
128
+ @classmethod
129
+ def from_schedule(cls, schedule: Schedule) -> "DeploymentScheduleCreate":
130
+ if schedule.interval is not None:
131
+ return cls(
132
+ schedule=IntervalSchedule(
133
+ interval=schedule.interval,
134
+ timezone=schedule.timezone,
135
+ anchor_date=schedule.anchor_date,
136
+ ),
137
+ parameters=schedule.parameters,
138
+ active=schedule.active,
139
+ slug=schedule.slug,
140
+ )
141
+ elif schedule.cron is not None:
142
+ return cls(
143
+ schedule=CronSchedule(
144
+ cron=schedule.cron,
145
+ timezone=schedule.timezone,
146
+ day_or=schedule.day_or,
147
+ ),
148
+ parameters=schedule.parameters,
149
+ active=schedule.active,
150
+ slug=schedule.slug,
151
+ )
152
+ elif schedule.rrule is not None:
153
+ return cls(
154
+ schedule=RRuleSchedule(
155
+ rrule=schedule.rrule,
156
+ timezone=schedule.timezone,
157
+ ),
158
+ parameters=schedule.parameters,
159
+ active=schedule.active,
160
+ slug=schedule.slug,
161
+ )
162
+ else:
163
+ return cls(
164
+ schedule=NoSchedule(),
165
+ )
166
+
113
167
 
114
168
  class DeploymentScheduleUpdate(ActionBaseModel):
115
169
  schedule: Optional[SCHEDULE_TYPES] = Field(
@@ -123,6 +177,14 @@ class DeploymentScheduleUpdate(ActionBaseModel):
123
177
  default=None,
124
178
  description="The maximum number of scheduled runs for the schedule.",
125
179
  )
180
+ parameters: Optional[dict[str, Any]] = Field(
181
+ default=None,
182
+ description="Parameter overrides for the schedule.",
183
+ )
184
+ slug: Optional[str] = Field(
185
+ default=None,
186
+ description="A unique identifier for the schedule.",
187
+ )
126
188
 
127
189
  @field_validator("max_scheduled_runs")
128
190
  @classmethod
@@ -264,6 +326,8 @@ class DeploymentUpdate(ActionBaseModel):
264
326
  "Whether or not the deployment should enforce the parameter schema."
265
327
  ),
266
328
  )
329
+ parameter_openapi_schema: Optional[dict[str, Any]] = Field(default_factory=dict)
330
+ pull_steps: Optional[list[dict[str, Any]]] = Field(default=None)
267
331
 
268
332
  def check_valid_configuration(self, base_job_template: dict[str, Any]) -> None:
269
333
  """Check that the combination of base_job_template defaults
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import datetime
2
4
  import warnings
3
5
  from collections.abc import Callable, Mapping
@@ -15,7 +17,6 @@ from typing import (
15
17
  from uuid import UUID, uuid4
16
18
 
17
19
  import orjson
18
- import pendulum
19
20
  from pydantic import (
20
21
  ConfigDict,
21
22
  Discriminator,
@@ -48,6 +49,7 @@ from prefect._internal.schemas.validators import (
48
49
  validate_not_negative,
49
50
  validate_parent_and_ref_diff,
50
51
  )
52
+ from prefect._result_records import ResultRecordMetadata
51
53
  from prefect.client.schemas.schedules import SCHEDULE_TYPES
52
54
  from prefect.settings import PREFECT_CLOUD_API_URL, PREFECT_CLOUD_UI_URL
53
55
  from prefect.types import (
@@ -58,25 +60,17 @@ from prefect.types import (
58
60
  PositiveInteger,
59
61
  StrictVariableValue,
60
62
  )
63
+ from prefect.types._datetime import DateTime
61
64
  from prefect.utilities.collections import AutoEnum, listrepr, visit_collection
62
65
  from prefect.utilities.names import generate_slug
63
66
  from prefect.utilities.pydantic import handle_secret_render
64
67
 
65
- if TYPE_CHECKING:
66
- from prefect.client.schemas.actions import StateCreate
67
- from prefect.results import ResultRecordMetadata
68
-
69
- DateTime = pendulum.DateTime
70
- else:
71
- from prefect.types import DateTime
72
-
73
-
74
68
  R = TypeVar("R", default=Any)
75
69
 
76
70
 
77
- DEFAULT_BLOCK_SCHEMA_VERSION = "non-versioned"
78
- DEFAULT_AGENT_WORK_POOL_NAME = "default-agent-pool"
79
- FLOW_RUN_NOTIFICATION_TEMPLATE_KWARGS = [
71
+ DEFAULT_BLOCK_SCHEMA_VERSION: Literal["non-versioned"] = "non-versioned"
72
+ DEFAULT_AGENT_WORK_POOL_NAME: Literal["default-agent-pool"] = "default-agent-pool"
73
+ FLOW_RUN_NOTIFICATION_TEMPLATE_KWARGS: list[str] = [
80
74
  "flow_run_notification_policy_id",
81
75
  "flow_id",
82
76
  "flow_name",
@@ -105,7 +99,7 @@ class StateType(AutoEnum):
105
99
  CANCELLING = AutoEnum.auto()
106
100
 
107
101
 
108
- TERMINAL_STATES = {
102
+ TERMINAL_STATES: set[StateType] = {
109
103
  StateType.COMPLETED,
110
104
  StateType.CANCELLED,
111
105
  StateType.FAILED,
@@ -223,8 +217,7 @@ class State(ObjectBaseModel, Generic[R]):
223
217
  raise_on_failure: Literal[True] = ...,
224
218
  fetch: bool = ...,
225
219
  retry_result_failure: bool = ...,
226
- ) -> R:
227
- ...
220
+ ) -> R: ...
228
221
 
229
222
  @overload
230
223
  def result(
@@ -232,8 +225,7 @@ class State(ObjectBaseModel, Generic[R]):
232
225
  raise_on_failure: Literal[False] = False,
233
226
  fetch: bool = ...,
234
227
  retry_result_failure: bool = ...,
235
- ) -> Union[R, Exception]:
236
- ...
228
+ ) -> Union[R, Exception]: ...
237
229
 
238
230
  @overload
239
231
  def result(
@@ -241,8 +233,7 @@ class State(ObjectBaseModel, Generic[R]):
241
233
  raise_on_failure: bool = ...,
242
234
  fetch: bool = ...,
243
235
  retry_result_failure: bool = ...,
244
- ) -> Union[R, Exception]:
245
- ...
236
+ ) -> Union[R, Exception]: ...
246
237
 
247
238
  @deprecated.deprecated_parameter(
248
239
  "fetch",
@@ -333,33 +324,6 @@ class State(ObjectBaseModel, Generic[R]):
333
324
  retry_result_failure=retry_result_failure,
334
325
  )
335
326
 
336
- def to_state_create(self) -> "StateCreate":
337
- """
338
- Convert this state to a `StateCreate` type which can be used to set the state of
339
- a run in the API.
340
-
341
- This method will drop this state's `data` if it is not a result type. Only
342
- results should be sent to the API. Other data is only available locally.
343
- """
344
- from prefect.client.schemas.actions import StateCreate
345
- from prefect.results import (
346
- ResultRecord,
347
- should_persist_result,
348
- )
349
-
350
- if isinstance(self.data, ResultRecord) and should_persist_result():
351
- data = self.data.metadata # pyright: ignore[reportUnknownMemberType] unable to narrow ResultRecord type
352
- else:
353
- data = None
354
-
355
- return StateCreate(
356
- type=self.type,
357
- name=self.name,
358
- message=self.message,
359
- data=data,
360
- state_details=self.state_details,
361
- )
362
-
363
327
  @model_validator(mode="after")
364
328
  def default_name_from_type(self) -> Self:
365
329
  """If a name is not provided, use the type"""
@@ -374,7 +338,7 @@ class State(ObjectBaseModel, Generic[R]):
374
338
  def default_scheduled_start_time(self) -> Self:
375
339
  if self.type == StateType.SCHEDULED:
376
340
  if not self.state_details.scheduled_time:
377
- self.state_details.scheduled_time = pendulum.DateTime.now("utc")
341
+ self.state_details.scheduled_time = DateTime.now("utc")
378
342
  return self
379
343
 
380
344
  @model_validator(mode="after")
@@ -1099,6 +1063,14 @@ class DeploymentSchedule(ObjectBaseModel):
1099
1063
  default=None,
1100
1064
  description="The maximum number of scheduled runs for the schedule.",
1101
1065
  )
1066
+ parameters: dict[str, Any] = Field(
1067
+ default_factory=dict,
1068
+ description="Parameter overrides for the schedule.",
1069
+ )
1070
+ slug: Optional[str] = Field(
1071
+ default=None,
1072
+ description="A unique identifier for the schedule.",
1073
+ )
1102
1074
 
1103
1075
 
1104
1076
  class Deployment(ObjectBaseModel):
@@ -1374,7 +1346,7 @@ class WorkQueueHealthPolicy(PrefectBaseModel):
1374
1346
  )
1375
1347
 
1376
1348
  def evaluate_health_status(
1377
- self, late_runs_count: int, last_polled: Optional[pendulum.DateTime] = None
1349
+ self, late_runs_count: int, last_polled: DateTime | None = None
1378
1350
  ) -> bool:
1379
1351
  """
1380
1352
  Given empirical information about the state of the work queue, evaluate its health status.
@@ -1396,7 +1368,7 @@ class WorkQueueHealthPolicy(PrefectBaseModel):
1396
1368
  if self.maximum_seconds_since_last_polled is not None:
1397
1369
  if (
1398
1370
  last_polled is None
1399
- or pendulum.now("UTC").diff(last_polled).in_seconds()
1371
+ or DateTime.now("UTC").diff(last_polled).in_seconds()
1400
1372
  > self.maximum_seconds_since_last_polled
1401
1373
  ):
1402
1374
  healthy = False
@@ -8,7 +8,6 @@ from typing import TYPE_CHECKING, Annotated, Any, ClassVar, Optional, Union
8
8
  import dateutil
9
9
  import dateutil.rrule
10
10
  import dateutil.tz
11
- import pendulum
12
11
  from pydantic import AfterValidator, ConfigDict, Field, field_validator, model_validator
13
12
  from typing_extensions import TypeAlias, TypeGuard
14
13
 
@@ -19,14 +18,7 @@ from prefect._internal.schemas.validators import (
19
18
  validate_cron_string,
20
19
  validate_rrule_string,
21
20
  )
22
-
23
- if TYPE_CHECKING:
24
- # type checkers have difficulty accepting that
25
- # pydantic_extra_types.pendulum_dt and pendulum.DateTime can be used
26
- # together.
27
- DateTime = pendulum.DateTime
28
- else:
29
- from prefect.types import DateTime
21
+ from prefect.types._datetime import Date, DateTime
30
22
 
31
23
  MAX_ITERATIONS = 1000
32
24
  # approx. 1 years worth of RDATEs + buffer
@@ -83,7 +75,7 @@ class IntervalSchedule(PrefectBaseModel):
83
75
 
84
76
  interval: datetime.timedelta = Field(gt=datetime.timedelta(0))
85
77
  anchor_date: Annotated[DateTime, AfterValidator(default_anchor_date)] = Field(
86
- default_factory=lambda: pendulum.now("UTC"),
78
+ default_factory=lambda: DateTime.now("UTC"),
87
79
  examples=["2020-01-01T00:00:00Z"],
88
80
  )
89
81
  timezone: Optional[str] = Field(default=None, examples=["America/New_York"])
@@ -99,12 +91,9 @@ class IntervalSchedule(PrefectBaseModel):
99
91
  self,
100
92
  /,
101
93
  interval: datetime.timedelta,
102
- anchor_date: Optional[
103
- Union[pendulum.DateTime, datetime.datetime, str]
104
- ] = None,
94
+ anchor_date: Optional[Union[DateTime, datetime.datetime, str]] = None,
105
95
  timezone: Optional[str] = None,
106
- ) -> None:
107
- ...
96
+ ) -> None: ...
108
97
 
109
98
 
110
99
  class CronSchedule(PrefectBaseModel):
@@ -154,7 +143,7 @@ class CronSchedule(PrefectBaseModel):
154
143
  return validate_cron_string(v)
155
144
 
156
145
 
157
- DEFAULT_ANCHOR_DATE = pendulum.date(2020, 1, 1)
146
+ DEFAULT_ANCHOR_DATE = Date(2020, 1, 1)
158
147
 
159
148
 
160
149
  def _rrule_dt(
@@ -220,7 +209,7 @@ class RRuleSchedule(PrefectBaseModel):
220
209
  return RRuleSchedule(rrule=str(rrule), timezone=timezone)
221
210
  rrules = _rrule(rrule)
222
211
  dtstarts = [dts for rr in rrules if (dts := _rrule_dt(rr)) is not None]
223
- unique_dstarts = set(pendulum.instance(d).in_tz("UTC") for d in dtstarts)
212
+ unique_dstarts = set(DateTime.instance(d).in_tz("UTC") for d in dtstarts)
224
213
  unique_timezones = set(d.tzinfo for d in dtstarts if d.tzinfo is not None)
225
214
 
226
215
  if len(unique_timezones) > 1:
@@ -381,7 +370,7 @@ def construct_schedule(
381
370
  if isinstance(interval, (int, float)):
382
371
  interval = datetime.timedelta(seconds=interval)
383
372
  if not anchor_date:
384
- anchor_date = pendulum.DateTime.now()
373
+ anchor_date = DateTime.now()
385
374
  schedule = IntervalSchedule(
386
375
  interval=interval, anchor_date=anchor_date, timezone=timezone
387
376
  )
@@ -5,7 +5,8 @@ from typing_extensions import TypeAlias
5
5
  if TYPE_CHECKING:
6
6
  from prefect.client.schemas.actions import DeploymentScheduleCreate
7
7
  from prefect.client.schemas.schedules import SCHEDULE_TYPES
8
+ from prefect.schedules import Schedule
8
9
 
9
10
  FlexibleScheduleList: TypeAlias = Sequence[
10
- Union["DeploymentScheduleCreate", dict[str, Any], "SCHEDULE_TYPES"]
11
+ Union["DeploymentScheduleCreate", dict[str, Any], "SCHEDULE_TYPES", "Schedule"]
11
12
  ]
prefect/context.py CHANGED
@@ -73,8 +73,8 @@ def hydrated_context(
73
73
  # We need to rebuild the models because we might be hydrating in a remote
74
74
  # environment where the models are not available.
75
75
  # TODO: Remove this once we have fixed our circular imports and we don't need to rebuild models any more.
76
+ from prefect._result_records import ResultRecordMetadata
76
77
  from prefect.flows import Flow
77
- from prefect.results import ResultRecordMetadata
78
78
  from prefect.tasks import Task
79
79
 
80
80
  _types: dict[str, Any] = dict(
@@ -123,8 +123,7 @@ class ContextModel(BaseModel):
123
123
 
124
124
  if TYPE_CHECKING:
125
125
  # subclasses can pass through keyword arguments to the pydantic base model
126
- def __init__(self, **kwargs: Any) -> None:
127
- ...
126
+ def __init__(self, **kwargs: Any) -> None: ...
128
127
 
129
128
  # The context variable for storing data must be defined by the child class
130
129
  __var__: ClassVar[ContextVar[Self]]
@@ -6,11 +6,11 @@ import anyio
6
6
  import pendulum
7
7
 
8
8
  import prefect
9
+ from prefect._result_records import ResultRecordMetadata
9
10
  from prefect.client.schemas import FlowRun
10
11
  from prefect.client.utilities import inject_client
11
12
  from prefect.context import FlowRunContext, TaskRunContext
12
13
  from prefect.logging import get_logger
13
- from prefect.results import ResultRecordMetadata
14
14
  from prefect.states import Pending, Scheduled
15
15
  from prefect.tasks import Task
16
16
  from prefect.telemetry.run_telemetry import (