prefect-client 2.16.9__py3-none-any.whl → 2.17.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 (50) hide show
  1. prefect/__init__.py +0 -18
  2. prefect/_internal/compatibility/deprecated.py +108 -5
  3. prefect/_internal/pydantic/__init__.py +4 -0
  4. prefect/_internal/pydantic/_base_model.py +36 -4
  5. prefect/_internal/pydantic/_compat.py +33 -2
  6. prefect/_internal/pydantic/_flags.py +3 -0
  7. prefect/_internal/pydantic/utilities/config_dict.py +72 -0
  8. prefect/_internal/pydantic/utilities/field_validator.py +135 -0
  9. prefect/_internal/pydantic/utilities/model_fields_set.py +29 -0
  10. prefect/_internal/pydantic/utilities/model_validator.py +79 -0
  11. prefect/agent.py +1 -1
  12. prefect/blocks/notifications.py +18 -18
  13. prefect/blocks/webhook.py +1 -1
  14. prefect/client/base.py +7 -0
  15. prefect/client/orchestration.py +44 -4
  16. prefect/client/schemas/actions.py +27 -20
  17. prefect/client/schemas/filters.py +28 -28
  18. prefect/client/schemas/objects.py +31 -21
  19. prefect/client/schemas/responses.py +17 -11
  20. prefect/client/schemas/schedules.py +6 -8
  21. prefect/context.py +2 -1
  22. prefect/deployments/base.py +2 -10
  23. prefect/deployments/deployments.py +34 -9
  24. prefect/deployments/runner.py +2 -2
  25. prefect/engine.py +32 -596
  26. prefect/events/clients.py +45 -13
  27. prefect/events/filters.py +19 -2
  28. prefect/events/utilities.py +12 -4
  29. prefect/events/worker.py +26 -8
  30. prefect/exceptions.py +3 -8
  31. prefect/filesystems.py +7 -7
  32. prefect/flows.py +4 -3
  33. prefect/manifests.py +1 -8
  34. prefect/profiles.toml +1 -1
  35. prefect/pydantic/__init__.py +27 -1
  36. prefect/pydantic/main.py +26 -2
  37. prefect/settings.py +33 -10
  38. prefect/task_server.py +2 -2
  39. prefect/utilities/dispatch.py +1 -0
  40. prefect/utilities/engine.py +629 -0
  41. prefect/utilities/pydantic.py +1 -1
  42. prefect/utilities/visualization.py +1 -1
  43. prefect/variables.py +88 -12
  44. prefect/workers/base.py +1 -1
  45. prefect/workers/block.py +1 -1
  46. {prefect_client-2.16.9.dist-info → prefect_client-2.17.1.dist-info}/METADATA +3 -3
  47. {prefect_client-2.16.9.dist-info → prefect_client-2.17.1.dist-info}/RECORD +50 -45
  48. {prefect_client-2.16.9.dist-info → prefect_client-2.17.1.dist-info}/LICENSE +0 -0
  49. {prefect_client-2.16.9.dist-info → prefect_client-2.17.1.dist-info}/WHEEL +0 -0
  50. {prefect_client-2.16.9.dist-info → prefect_client-2.17.1.dist-info}/top_level.txt +0 -0
@@ -77,7 +77,7 @@ class AppriseNotificationBlock(AbstractAppriseNotificationBlock, ABC):
77
77
  default=...,
78
78
  title="Webhook URL",
79
79
  description="Incoming webhook URL used to send notifications.",
80
- example="https://hooks.example.com/XXX",
80
+ examples=["https://hooks.example.com/XXX"],
81
81
  )
82
82
 
83
83
 
@@ -105,7 +105,7 @@ class SlackWebhook(AppriseNotificationBlock):
105
105
  default=...,
106
106
  title="Webhook URL",
107
107
  description="Slack incoming webhook URL used to send notifications.",
108
- example="https://hooks.slack.com/XXX",
108
+ examples=["https://hooks.slack.com/XXX"],
109
109
  )
110
110
 
111
111
 
@@ -131,9 +131,9 @@ class MicrosoftTeamsWebhook(AppriseNotificationBlock):
131
131
  ...,
132
132
  title="Webhook URL",
133
133
  description="The Teams incoming webhook URL used to send notifications.",
134
- example=(
134
+ examples=[
135
135
  "https://your-org.webhook.office.com/webhookb2/XXX/IncomingWebhook/YYY/ZZZ"
136
- ),
136
+ ],
137
137
  )
138
138
 
139
139
 
@@ -222,7 +222,7 @@ class PagerDutyWebHook(AbstractAppriseNotificationBlock):
222
222
  custom_details: Optional[Dict[str, str]] = Field(
223
223
  default=None,
224
224
  description="Additional details to include as part of the payload.",
225
- example='{"disk_space_left": "145GB"}',
225
+ examples=['{"disk_space_left": "145GB"}'],
226
226
  )
227
227
 
228
228
  def block_initialization(self) -> None:
@@ -283,14 +283,14 @@ class TwilioSMS(AbstractAppriseNotificationBlock):
283
283
  from_phone_number: str = Field(
284
284
  default=...,
285
285
  description="The valid Twilio phone number to send the message from.",
286
- example="18001234567",
286
+ examples=["18001234567"],
287
287
  )
288
288
 
289
289
  to_phone_numbers: List[str] = Field(
290
290
  default=...,
291
291
  description="A list of valid Twilio phone number(s) to send the message to.",
292
292
  # not wrapped in brackets because of the way UI displays examples; in code should be ["18004242424"]
293
- example="18004242424",
293
+ examples=["18004242424"],
294
294
  )
295
295
 
296
296
  def block_initialization(self) -> None:
@@ -366,7 +366,7 @@ class OpsgenieWebhook(AbstractAppriseNotificationBlock):
366
366
  "A comma-separated list of tags you can associate with your Opsgenie"
367
367
  " message."
368
368
  ),
369
- example='["tag1", "tag2"]',
369
+ examples=['["tag1", "tag2"]'],
370
370
  )
371
371
 
372
372
  priority: Optional[str] = Field(
@@ -388,7 +388,7 @@ class OpsgenieWebhook(AbstractAppriseNotificationBlock):
388
388
  details: Optional[Dict[str, str]] = Field(
389
389
  default=None,
390
390
  description="Additional details composed of key/values pairs.",
391
- example='{"key1": "value1", "key2": "value2"}',
391
+ examples=['{"key1": "value1", "key2": "value2"}'],
392
392
  )
393
393
 
394
394
  def block_initialization(self) -> None:
@@ -445,7 +445,7 @@ class MattermostWebhook(AbstractAppriseNotificationBlock):
445
445
  hostname: str = Field(
446
446
  default=...,
447
447
  description="The hostname of your Mattermost server.",
448
- example="Mattermost.example.com",
448
+ examples=["Mattermost.example.com"],
449
449
  )
450
450
 
451
451
  token: SecretStr = Field(
@@ -617,7 +617,7 @@ class CustomWebhookNotificationBlock(NotificationBlock):
617
617
  url: str = Field(
618
618
  title="Webhook URL",
619
619
  description="The webhook URL.",
620
- example="https://hooks.slack.com/XXX",
620
+ examples=["https://hooks.slack.com/XXX"],
621
621
  )
622
622
 
623
623
  method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"] = Field(
@@ -631,10 +631,10 @@ class CustomWebhookNotificationBlock(NotificationBlock):
631
631
  default=None,
632
632
  title="JSON Data",
633
633
  description="Send json data as payload.",
634
- example=(
634
+ examples=[
635
635
  '{"text": "{{subject}}\\n{{body}}", "title": "{{name}}", "token":'
636
636
  ' "{{tokenFromSecrets}}"}'
637
- ),
637
+ ],
638
638
  )
639
639
  form_data: Optional[Dict[str, str]] = Field(
640
640
  default=None,
@@ -642,10 +642,10 @@ class CustomWebhookNotificationBlock(NotificationBlock):
642
642
  description=(
643
643
  "Send form data as payload. Should not be used together with _JSON Data_."
644
644
  ),
645
- example=(
645
+ examples=[
646
646
  '{"text": "{{subject}}\\n{{body}}", "title": "{{name}}", "token":'
647
647
  ' "{{tokenFromSecrets}}"}'
648
- ),
648
+ ],
649
649
  )
650
650
 
651
651
  headers: Optional[Dict[str, str]] = Field(None, description="Custom headers.")
@@ -659,7 +659,7 @@ class CustomWebhookNotificationBlock(NotificationBlock):
659
659
  default_factory=lambda: SecretDict(dict()),
660
660
  title="Custom Secret Values",
661
661
  description="A dictionary of secret values to be substituted in other configs.",
662
- example='{"tokenFromSecrets":"SomeSecretToken"}',
662
+ examples=['{"tokenFromSecrets":"SomeSecretToken"}'],
663
663
  )
664
664
 
665
665
  def _build_request_args(self, body: str, subject: Optional[str]):
@@ -753,14 +753,14 @@ class SendgridEmail(AbstractAppriseNotificationBlock):
753
753
  sender_email: str = Field(
754
754
  title="Sender email id",
755
755
  description="The sender email id.",
756
- example="test-support@gmail.com",
756
+ examples=["test-support@gmail.com"],
757
757
  )
758
758
 
759
759
  to_emails: List[str] = Field(
760
760
  default=...,
761
761
  title="Recipient emails",
762
762
  description="Email ids of all recipients.",
763
- example='"recipient1@gmail.com"',
763
+ examples=['"recipient1@gmail.com"'],
764
764
  )
765
765
 
766
766
  def block_initialization(self) -> None:
prefect/blocks/webhook.py CHANGED
@@ -36,7 +36,7 @@ class Webhook(Block):
36
36
  default=...,
37
37
  title="Webhook URL",
38
38
  description="The webhook URL.",
39
- example="https://hooks.slack.com/XXX",
39
+ examples=["https://hooks.slack.com/XXX"],
40
40
  )
41
41
 
42
42
  headers: SecretDict = Field(
prefect/client/base.py CHANGED
@@ -27,6 +27,8 @@ from httpx import HTTPStatusError, Request, Response
27
27
  from prefect._vendor.starlette import status
28
28
  from typing_extensions import Self
29
29
 
30
+ import prefect
31
+ from prefect.client import constants
30
32
  from prefect.client.schemas.objects import CsrfToken
31
33
  from prefect.exceptions import PrefectHTTPStatusError
32
34
  from prefect.logging import get_logger
@@ -199,6 +201,11 @@ class PrefectHttpxClient(httpx.AsyncClient):
199
201
 
200
202
  super().__init__(*args, **kwargs)
201
203
 
204
+ user_agent = (
205
+ f"prefect/{prefect.__version__} (API {constants.SERVER_API_VERSION})"
206
+ )
207
+ self.headers["User-Agent"] = user_agent
208
+
202
209
  async def _send_with_retry(
203
210
  self,
204
211
  request: Request,
@@ -20,6 +20,9 @@ import httpcore
20
20
  import httpx
21
21
  import pendulum
22
22
 
23
+ from prefect._internal.compatibility.deprecated import (
24
+ handle_deprecated_infra_overrides_parameter,
25
+ )
23
26
  from prefect._internal.compatibility.experimental import (
24
27
  EXPERIMENTAL_WARNING,
25
28
  ExperimentalFeature,
@@ -68,6 +71,8 @@ from prefect.client.schemas.actions import (
68
71
  LogCreate,
69
72
  TaskRunCreate,
70
73
  TaskRunUpdate,
74
+ VariableCreate,
75
+ VariableUpdate,
71
76
  WorkPoolCreate,
72
77
  WorkPoolUpdate,
73
78
  WorkQueueCreate,
@@ -1588,12 +1593,13 @@ class PrefectClient:
1588
1593
  path: str = None,
1589
1594
  entrypoint: str = None,
1590
1595
  infrastructure_document_id: UUID = None,
1591
- infra_overrides: Optional[Dict[str, Any]] = None,
1596
+ infra_overrides: Optional[Dict[str, Any]] = None, # for backwards compat
1592
1597
  parameter_openapi_schema: Optional[Dict[str, Any]] = None,
1593
1598
  is_schedule_active: Optional[bool] = None,
1594
1599
  paused: Optional[bool] = None,
1595
1600
  pull_steps: Optional[List[dict]] = None,
1596
1601
  enforce_parameter_schema: Optional[bool] = None,
1602
+ job_variables: Optional[Dict[str, Any]] = None,
1597
1603
  ) -> UUID:
1598
1604
  """
1599
1605
  Create a deployment.
@@ -1608,6 +1614,10 @@ class PrefectClient:
1608
1614
  used for the deployed flow
1609
1615
  infrastructure_document_id: an reference to the infrastructure block document
1610
1616
  to use for this deployment
1617
+ job_variables: A dictionary of dot delimited infrastructure overrides that
1618
+ will be applied at runtime; for example `env.CONFIG_KEY=config_value` or
1619
+ `namespace='prefect'`. This argument was previously named `infra_overrides`.
1620
+ Both arguments are supported for backwards compatibility.
1611
1621
 
1612
1622
  Raises:
1613
1623
  httpx.RequestError: if the deployment was not created for any reason
@@ -1615,6 +1625,7 @@ class PrefectClient:
1615
1625
  Returns:
1616
1626
  the ID of the deployment in the backend
1617
1627
  """
1628
+ jv = handle_deprecated_infra_overrides_parameter(job_variables, infra_overrides)
1618
1629
 
1619
1630
  deployment_create = DeploymentCreate(
1620
1631
  flow_id=flow_id,
@@ -1629,7 +1640,7 @@ class PrefectClient:
1629
1640
  entrypoint=entrypoint,
1630
1641
  manifest_path=manifest_path, # for backwards compat
1631
1642
  infrastructure_document_id=infrastructure_document_id,
1632
- infra_overrides=infra_overrides or {},
1643
+ job_variables=jv,
1633
1644
  parameter_openapi_schema=parameter_openapi_schema,
1634
1645
  is_schedule_active=is_schedule_active,
1635
1646
  paused=paused,
@@ -1706,7 +1717,7 @@ class PrefectClient:
1706
1717
  parameters=deployment.parameters,
1707
1718
  storage_document_id=deployment.storage_document_id,
1708
1719
  infrastructure_document_id=deployment.infrastructure_document_id,
1709
- infra_overrides=deployment.infra_overrides,
1720
+ job_variables=deployment.job_variables,
1710
1721
  enforce_parameter_schema=deployment.enforce_parameter_schema,
1711
1722
  )
1712
1723
 
@@ -2926,11 +2937,40 @@ class PrefectClient:
2926
2937
  else:
2927
2938
  raise
2928
2939
 
2940
+ async def create_variable(self, variable: VariableCreate) -> Variable:
2941
+ """
2942
+ Creates an variable with the provided configuration.
2943
+
2944
+ Args:
2945
+ variable: Desired configuration for the new variable.
2946
+ Returns:
2947
+ Information about the newly created variable.
2948
+ """
2949
+ response = await self._client.post(
2950
+ "/variables/",
2951
+ json=variable.dict(json_compatible=True, exclude_unset=True),
2952
+ )
2953
+ return Variable(**response.json())
2954
+
2955
+ async def update_variable(self, variable: VariableUpdate) -> None:
2956
+ """
2957
+ Updates a variable with the provided configuration.
2958
+
2959
+ Args:
2960
+ variable: Desired configuration for the updated variable.
2961
+ Returns:
2962
+ Information about the updated variable.
2963
+ """
2964
+ await self._client.patch(
2965
+ f"/variables/name/{variable.name}",
2966
+ json=variable.dict(json_compatible=True, exclude_unset=True),
2967
+ )
2968
+
2929
2969
  async def read_variable_by_name(self, name: str) -> Optional[Variable]:
2930
2970
  """Reads a variable by name. Returns None if no variable is found."""
2931
2971
  try:
2932
2972
  response = await self._client.get(f"/variables/name/{name}")
2933
- return pydantic.parse_obj_as(Variable, response.json())
2973
+ return Variable(**response.json())
2934
2974
  except httpx.HTTPStatusError as e:
2935
2975
  if e.response.status_code == status.HTTP_404_NOT_FOUND:
2936
2976
  return None
@@ -4,6 +4,7 @@ from uuid import UUID, uuid4
4
4
 
5
5
  import jsonschema
6
6
 
7
+ from prefect._internal.compatibility.deprecated import DeprecatedInfraOverridesField
7
8
  from prefect._internal.pydantic import HAS_PYDANTIC_V2
8
9
 
9
10
  if HAS_PYDANTIC_V2:
@@ -62,7 +63,7 @@ class StateCreate(ActionBaseModel):
62
63
 
63
64
  type: StateType
64
65
  name: Optional[str] = Field(default=None)
65
- message: Optional[str] = Field(default=None, example="Run started")
66
+ message: Optional[str] = Field(default=None, examples=["Run started"])
66
67
  state_details: StateDetails = Field(default_factory=StateDetails)
67
68
  data: Union["BaseResult[R]", "DataDocument[R]", Any] = Field(
68
69
  default=None,
@@ -73,12 +74,12 @@ class FlowCreate(ActionBaseModel):
73
74
  """Data used by the Prefect REST API to create a flow."""
74
75
 
75
76
  name: str = Field(
76
- default=..., description="The name of the flow", example="my-flow"
77
+ default=..., description="The name of the flow", examples=["my-flow"]
77
78
  )
78
79
  tags: List[str] = Field(
79
80
  default_factory=list,
80
81
  description="A list of flow tags",
81
- example=["tag-1", "tag-2"],
82
+ examples=[["tag-1", "tag-2"]],
82
83
  )
83
84
 
84
85
 
@@ -88,7 +89,7 @@ class FlowUpdate(ActionBaseModel):
88
89
  tags: List[str] = Field(
89
90
  default_factory=list,
90
91
  description="A list of flow tags",
91
- example=["tag-1", "tag-2"],
92
+ examples=[["tag-1", "tag-2"]],
92
93
  )
93
94
 
94
95
 
@@ -110,7 +111,7 @@ class DeploymentScheduleUpdate(ActionBaseModel):
110
111
  )
111
112
 
112
113
 
113
- class DeploymentCreate(ActionBaseModel):
114
+ class DeploymentCreate(DeprecatedInfraOverridesField, ActionBaseModel):
114
115
  """Data used by the Prefect REST API to create a deployment."""
115
116
 
116
117
  @root_validator(pre=True)
@@ -144,7 +145,7 @@ class DeploymentCreate(ActionBaseModel):
144
145
  work_pool_name: Optional[str] = Field(
145
146
  default=None,
146
147
  description="The name of the deployment's work pool.",
147
- example="my-work-pool",
148
+ examples=["my-work-pool"],
148
149
  )
149
150
  storage_document_id: Optional[UUID] = Field(None)
150
151
  infrastructure_document_id: Optional[UUID] = Field(None)
@@ -153,11 +154,14 @@ class DeploymentCreate(ActionBaseModel):
153
154
  path: Optional[str] = Field(None)
154
155
  version: Optional[str] = Field(None)
155
156
  entrypoint: Optional[str] = Field(None)
156
- infra_overrides: Optional[Dict[str, Any]] = Field(None)
157
+ job_variables: Optional[Dict[str, Any]] = Field(
158
+ default_factory=dict,
159
+ description="Overrides to apply to flow run infrastructure at runtime.",
160
+ )
157
161
 
158
162
  def check_valid_configuration(self, base_job_template: dict):
159
163
  """Check that the combination of base_job_template defaults
160
- and infra_overrides conforms to the specified schema.
164
+ and job_variables conforms to the specified schema.
161
165
  """
162
166
  variables_schema = deepcopy(base_job_template.get("variables"))
163
167
 
@@ -172,10 +176,10 @@ class DeploymentCreate(ActionBaseModel):
172
176
  if "default" in v and k in required:
173
177
  required.remove(k)
174
178
 
175
- jsonschema.validate(self.infra_overrides, variables_schema)
179
+ jsonschema.validate(self.job_variables, variables_schema)
176
180
 
177
181
 
178
- class DeploymentUpdate(ActionBaseModel):
182
+ class DeploymentUpdate(DeprecatedInfraOverridesField, ActionBaseModel):
179
183
  """Data used by the Prefect REST API to update a deployment."""
180
184
 
181
185
  @root_validator(pre=True)
@@ -199,10 +203,13 @@ class DeploymentUpdate(ActionBaseModel):
199
203
  work_pool_name: Optional[str] = Field(
200
204
  default=None,
201
205
  description="The name of the deployment's work pool.",
202
- example="my-work-pool",
206
+ examples=["my-work-pool"],
203
207
  )
204
208
  path: Optional[str] = Field(None)
205
- infra_overrides: Optional[Dict[str, Any]] = Field(None)
209
+ job_variables: Optional[Dict[str, Any]] = Field(
210
+ default_factory=dict,
211
+ description="Overrides to apply to flow run infrastructure at runtime.",
212
+ )
206
213
  entrypoint: Optional[str] = Field(None)
207
214
  manifest_path: Optional[str] = Field(None)
208
215
  storage_document_id: Optional[UUID] = Field(None)
@@ -216,7 +223,7 @@ class DeploymentUpdate(ActionBaseModel):
216
223
 
217
224
  def check_valid_configuration(self, base_job_template: dict):
218
225
  """Check that the combination of base_job_template defaults
219
- and infra_overrides conforms to the specified schema.
226
+ and job_variables conforms to the specified schema.
220
227
  """
221
228
  variables_schema = deepcopy(base_job_template.get("variables"))
222
229
 
@@ -232,7 +239,7 @@ class DeploymentUpdate(ActionBaseModel):
232
239
  required.remove(k)
233
240
 
234
241
  if variables_schema is not None:
235
- jsonschema.validate(self.infra_overrides, variables_schema)
242
+ jsonschema.validate(self.job_variables, variables_schema)
236
243
 
237
244
 
238
245
  class FlowRunUpdate(ActionBaseModel):
@@ -605,10 +612,10 @@ class FlowRunNotificationPolicyCreate(ActionBaseModel):
605
612
  " Valid variables include:"
606
613
  f" {listrepr(sorted(objects.FLOW_RUN_NOTIFICATION_TEMPLATE_KWARGS), sep=', ')}"
607
614
  ),
608
- example=(
615
+ examples=[
609
616
  "Flow run {flow_run_name} with id {flow_run_id} entered state"
610
617
  " {flow_run_state_name}."
611
- ),
618
+ ],
612
619
  )
613
620
 
614
621
  @validator("message_template")
@@ -656,13 +663,13 @@ class VariableCreate(ActionBaseModel):
656
663
  name: str = Field(
657
664
  default=...,
658
665
  description="The name of the variable",
659
- example="my_variable",
666
+ examples=["my_variable"],
660
667
  max_length=objects.MAX_VARIABLE_NAME_LENGTH,
661
668
  )
662
669
  value: str = Field(
663
670
  default=...,
664
671
  description="The value of the variable",
665
- example="my-value",
672
+ examples=["my-value"],
666
673
  max_length=objects.MAX_VARIABLE_VALUE_LENGTH,
667
674
  )
668
675
  tags: Optional[List[str]] = Field(default=None)
@@ -677,13 +684,13 @@ class VariableUpdate(ActionBaseModel):
677
684
  name: Optional[str] = Field(
678
685
  default=None,
679
686
  description="The name of the variable",
680
- example="my_variable",
687
+ examples=["my_variable"],
681
688
  max_length=objects.MAX_VARIABLE_NAME_LENGTH,
682
689
  )
683
690
  value: Optional[str] = Field(
684
691
  default=None,
685
692
  description="The value of the variable",
686
- example="my-value",
693
+ examples=["my-value"],
687
694
  max_length=objects.MAX_VARIABLE_NAME_LENGTH,
688
695
  )
689
696
  tags: Optional[List[str]] = Field(default=None)