prefect-client 2.16.8__py3-none-any.whl → 2.17.0__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/__init__.py +0 -18
  2. prefect/_internal/compatibility/deprecated.py +108 -5
  3. prefect/_internal/compatibility/experimental.py +9 -8
  4. prefect/_internal/concurrency/api.py +23 -42
  5. prefect/_internal/concurrency/waiters.py +25 -22
  6. prefect/_internal/pydantic/__init__.py +16 -3
  7. prefect/_internal/pydantic/_base_model.py +39 -4
  8. prefect/_internal/pydantic/_compat.py +69 -452
  9. prefect/_internal/pydantic/_flags.py +5 -0
  10. prefect/_internal/pydantic/_types.py +8 -0
  11. prefect/_internal/pydantic/utilities/__init__.py +0 -0
  12. prefect/_internal/pydantic/utilities/config_dict.py +72 -0
  13. prefect/_internal/pydantic/utilities/field_validator.py +135 -0
  14. prefect/_internal/pydantic/utilities/model_construct.py +56 -0
  15. prefect/_internal/pydantic/utilities/model_copy.py +55 -0
  16. prefect/_internal/pydantic/utilities/model_dump.py +136 -0
  17. prefect/_internal/pydantic/utilities/model_dump_json.py +112 -0
  18. prefect/_internal/pydantic/utilities/model_fields.py +50 -0
  19. prefect/_internal/pydantic/utilities/model_fields_set.py +29 -0
  20. prefect/_internal/pydantic/utilities/model_json_schema.py +82 -0
  21. prefect/_internal/pydantic/utilities/model_rebuild.py +80 -0
  22. prefect/_internal/pydantic/utilities/model_validate.py +75 -0
  23. prefect/_internal/pydantic/utilities/model_validate_json.py +68 -0
  24. prefect/_internal/pydantic/utilities/model_validator.py +79 -0
  25. prefect/_internal/pydantic/utilities/type_adapter.py +71 -0
  26. prefect/_internal/schemas/bases.py +1 -17
  27. prefect/_internal/schemas/validators.py +425 -4
  28. prefect/agent.py +1 -1
  29. prefect/blocks/kubernetes.py +7 -3
  30. prefect/blocks/notifications.py +18 -18
  31. prefect/blocks/webhook.py +1 -1
  32. prefect/client/base.py +7 -0
  33. prefect/client/cloud.py +1 -1
  34. prefect/client/orchestration.py +51 -11
  35. prefect/client/schemas/actions.py +367 -297
  36. prefect/client/schemas/filters.py +28 -28
  37. prefect/client/schemas/objects.py +78 -147
  38. prefect/client/schemas/responses.py +240 -60
  39. prefect/client/schemas/schedules.py +6 -8
  40. prefect/concurrency/events.py +2 -2
  41. prefect/context.py +4 -2
  42. prefect/deployments/base.py +6 -13
  43. prefect/deployments/deployments.py +34 -9
  44. prefect/deployments/runner.py +9 -27
  45. prefect/deprecated/packaging/base.py +5 -6
  46. prefect/deprecated/packaging/docker.py +19 -25
  47. prefect/deprecated/packaging/file.py +10 -5
  48. prefect/deprecated/packaging/orion.py +9 -4
  49. prefect/deprecated/packaging/serializers.py +8 -58
  50. prefect/engine.py +55 -618
  51. prefect/events/actions.py +16 -1
  52. prefect/events/clients.py +45 -13
  53. prefect/events/filters.py +19 -2
  54. prefect/events/related.py +4 -4
  55. prefect/events/schemas/automations.py +13 -2
  56. prefect/events/schemas/deployment_triggers.py +73 -5
  57. prefect/events/schemas/events.py +1 -1
  58. prefect/events/utilities.py +12 -4
  59. prefect/events/worker.py +26 -8
  60. prefect/exceptions.py +3 -8
  61. prefect/filesystems.py +7 -7
  62. prefect/flows.py +7 -3
  63. prefect/infrastructure/provisioners/ecs.py +1 -0
  64. prefect/logging/configuration.py +2 -2
  65. prefect/manifests.py +1 -8
  66. prefect/profiles.toml +1 -1
  67. prefect/pydantic/__init__.py +74 -2
  68. prefect/pydantic/main.py +26 -2
  69. prefect/serializers.py +6 -31
  70. prefect/settings.py +72 -26
  71. prefect/software/python.py +3 -5
  72. prefect/task_server.py +2 -2
  73. prefect/utilities/callables.py +1 -1
  74. prefect/utilities/collections.py +2 -1
  75. prefect/utilities/dispatch.py +1 -0
  76. prefect/utilities/engine.py +629 -0
  77. prefect/utilities/pydantic.py +1 -1
  78. prefect/utilities/schema_tools/validation.py +2 -2
  79. prefect/utilities/visualization.py +1 -1
  80. prefect/variables.py +88 -12
  81. prefect/workers/base.py +20 -11
  82. prefect/workers/block.py +4 -8
  83. prefect/workers/process.py +2 -5
  84. {prefect_client-2.16.8.dist-info → prefect_client-2.17.0.dist-info}/METADATA +4 -3
  85. {prefect_client-2.16.8.dist-info → prefect_client-2.17.0.dist-info}/RECORD +88 -72
  86. prefect/_internal/schemas/transformations.py +0 -106
  87. {prefect_client-2.16.8.dist-info → prefect_client-2.17.0.dist-info}/LICENSE +0 -0
  88. {prefect_client-2.16.8.dist-info → prefect_client-2.17.0.dist-info}/WHEEL +0 -0
  89. {prefect_client-2.16.8.dist-info → prefect_client-2.17.0.dist-info}/top_level.txt +0 -0
@@ -44,7 +44,11 @@ from rich.table import Table
44
44
 
45
45
  from prefect._internal.concurrency.api import create_call, from_async
46
46
  from prefect._internal.pydantic import HAS_PYDANTIC_V2
47
- from prefect._internal.schemas.validators import validate_automation_names
47
+ from prefect._internal.schemas.validators import (
48
+ reconcile_paused_deployment,
49
+ reconcile_schedules_runner,
50
+ validate_automation_names,
51
+ )
48
52
  from prefect.runner.storage import RunnerStorage
49
53
  from prefect.settings import (
50
54
  PREFECT_DEFAULT_DOCKER_BUILD_NAMESPACE,
@@ -67,7 +71,6 @@ from prefect.client.schemas.schedules import (
67
71
  from prefect.deployments.schedules import (
68
72
  FlexibleScheduleList,
69
73
  create_minimal_deployment_schedule,
70
- normalize_to_minimal_deployment_schedules,
71
74
  )
72
75
  from prefect.events import DeploymentTriggerTypes
73
76
  from prefect.exceptions import (
@@ -236,32 +239,11 @@ class RunnerDeployment(BaseModel):
236
239
 
237
240
  @root_validator(pre=True)
238
241
  def reconcile_paused(cls, values):
239
- paused = values.get("paused")
240
- is_schedule_active = values.get("is_schedule_active")
241
-
242
- if paused is not None:
243
- values["paused"] = paused
244
- values["is_schedule_active"] = not paused
245
- elif is_schedule_active is not None:
246
- values["paused"] = not is_schedule_active
247
- values["is_schedule_active"] = is_schedule_active
248
- else:
249
- values["paused"] = False
250
- values["is_schedule_active"] = True
251
-
252
- return values
242
+ return reconcile_paused_deployment(values)
253
243
 
254
244
  @root_validator(pre=True)
255
245
  def reconcile_schedules(cls, values):
256
- schedule = values.get("schedule")
257
- schedules = values.get("schedules")
258
-
259
- if schedules is None and schedule is not None:
260
- values["schedules"] = [create_minimal_deployment_schedule(schedule)]
261
- elif schedules is not None and len(schedules) > 0:
262
- values["schedules"] = normalize_to_minimal_deployment_schedules(schedules)
263
-
264
- return values
246
+ return reconcile_schedules_runner(values)
265
247
 
266
248
  @sync_compatible
267
249
  async def apply(
@@ -324,9 +306,9 @@ class RunnerDeployment(BaseModel):
324
306
  )
325
307
 
326
308
  if work_pool_name:
327
- create_payload["infra_overrides"] = self.job_variables
309
+ create_payload["job_variables"] = self.job_variables
328
310
  if image:
329
- create_payload["infra_overrides"]["image"] = image
311
+ create_payload["job_variables"]["image"] = image
330
312
  create_payload["path"] = None if self.storage else self._path
331
313
  create_payload["pull_steps"] = (
332
314
  [self.storage.to_pull_step()] if self.storage else []
@@ -4,7 +4,7 @@ This module is deprecated as of March 2024 and will not be available after Septe
4
4
  """
5
5
 
6
6
  import abc
7
- from typing import Generic, TypeVar
7
+ from typing import Generic, Type, TypeVar
8
8
 
9
9
  from prefect._internal.compatibility.deprecated import deprecated_class
10
10
  from prefect._internal.pydantic import HAS_PYDANTIC_V2
@@ -17,7 +17,7 @@ else:
17
17
  from prefect.flows import Flow
18
18
  from prefect.utilities.callables import ParameterSchema, parameter_schema
19
19
  from prefect.utilities.dispatch import lookup_type
20
- from prefect.utilities.pydantic import PartialModel, add_type_dispatch
20
+ from prefect.utilities.pydantic import add_type_dispatch
21
21
 
22
22
  D = TypeVar("D")
23
23
 
@@ -81,10 +81,9 @@ class Packager(BaseModel, abc.ABC):
81
81
 
82
82
  type: str
83
83
 
84
- def base_manifest(self, flow: Flow) -> PartialModel[PackageManifest]:
85
- manifest_cls = lookup_type(PackageManifest, self.type)
86
- return PartialModel(
87
- manifest_cls,
84
+ def base_manifest(self, flow: Flow) -> PackageManifest:
85
+ manifest_cls: Type[BaseModel] = lookup_type(PackageManifest, self.type)
86
+ return manifest_cls.construct(
88
87
  type=self.type,
89
88
  flow_name=flow.name,
90
89
  flow_parameter_schema=parameter_schema(flow.fn),
@@ -10,6 +10,12 @@ from typing import Any, Mapping, Optional, Union
10
10
 
11
11
  from prefect._internal.compatibility.deprecated import deprecated_class
12
12
  from prefect._internal.pydantic import HAS_PYDANTIC_V2
13
+ from prefect._internal.schemas.validators import (
14
+ assign_default_base_image,
15
+ base_image_xor_dockerfile,
16
+ set_default_python_environment,
17
+ validate_registry_url,
18
+ )
13
19
 
14
20
  if HAS_PYDANTIC_V2:
15
21
  from pydantic.v1 import AnyHttpUrl, root_validator, validator
@@ -25,7 +31,6 @@ from prefect.utilities.asyncutils import run_sync_in_worker_thread
25
31
  from prefect.utilities.dockerutils import (
26
32
  ImageBuilder,
27
33
  build_image,
28
- get_prefect_image_name,
29
34
  push_image,
30
35
  to_run_command,
31
36
  )
@@ -63,7 +68,7 @@ class DockerPackager(Packager):
63
68
  registry, given by `registry_url`.
64
69
  """
65
70
 
66
- type: Literal["docker"] = "docker"
71
+ type: str = "docker"
67
72
 
68
73
  base_image: Optional[str] = None
69
74
  python_environment: Optional[Union[PythonEnvironment, CondaEnvironment]] = None
@@ -74,36 +79,19 @@ class DockerPackager(Packager):
74
79
 
75
80
  @root_validator
76
81
  def set_default_base_image(cls, values):
77
- if not values.get("base_image") and not values.get("dockerfile"):
78
- values["base_image"] = get_prefect_image_name(
79
- flavor=(
80
- "conda"
81
- if isinstance(values.get("python_environment"), CondaEnvironment)
82
- else None
83
- )
84
- )
85
- return values
82
+ return assign_default_base_image(values)
86
83
 
87
84
  @root_validator
88
85
  def base_image_and_dockerfile_exclusive(cls, values: Mapping[str, Any]):
89
- if values.get("base_image") and values.get("dockerfile"):
90
- raise ValueError(
91
- "Either `base_image` or `dockerfile` should be provided, but not both"
92
- )
93
- return values
86
+ return base_image_xor_dockerfile(values)
94
87
 
95
88
  @root_validator
96
89
  def default_python_environment(cls, values: Mapping[str, Any]):
97
- if values.get("base_image") and not values.get("python_environment"):
98
- values["python_environment"] = PythonEnvironment.from_environment()
99
- return values
90
+ return set_default_python_environment(values)
100
91
 
101
92
  @validator("registry_url", pre=True)
102
93
  def ensure_registry_url_is_prefixed(cls, value):
103
- if isinstance(value, str):
104
- if "://" not in value:
105
- return "https://" + value
106
- return value
94
+ validate_registry_url(value)
107
95
 
108
96
  async def package(self, flow: Flow) -> DockerPackageManifest:
109
97
  """
@@ -117,8 +105,14 @@ class DockerPackager(Packager):
117
105
  push_image, image_reference, self.registry_url, image_name
118
106
  )
119
107
 
120
- return self.base_manifest(flow).finalize(
121
- image=image_reference, image_flow_location=self.image_flow_location
108
+ return DockerPackageManifest(
109
+ **{
110
+ **self.base_manifest(flow).dict(),
111
+ **{
112
+ "image": image_reference,
113
+ "image_flow_location": self.image_flow_location,
114
+ },
115
+ }
122
116
  )
123
117
 
124
118
  async def _build_image(self, flow: Flow) -> str:
@@ -34,7 +34,7 @@ class FilePackageManifest(PackageManifest):
34
34
  This class is deprecated as of version March 2024 and will not be available after September 2024.
35
35
  """
36
36
 
37
- type: Literal["file"] = "file"
37
+ type: str = "file"
38
38
  serializer: Serializer
39
39
  key: str
40
40
  filesystem_id: UUID
@@ -80,8 +80,13 @@ class FilePackager(Packager):
80
80
  or await self.filesystem._save(is_anonymous=True)
81
81
  )
82
82
 
83
- return self.base_manifest(flow).finalize(
84
- serializer=self.serializer,
85
- filesystem_id=filesystem_id,
86
- key=key,
83
+ return FilePackageManifest(
84
+ **{
85
+ **self.base_manifest(flow).dict(),
86
+ **{
87
+ "serializer": self.serializer,
88
+ "filesystem_id": filesystem_id,
89
+ "key": key,
90
+ },
91
+ }
87
92
  )
@@ -31,7 +31,7 @@ class OrionPackageManifest(PackageManifest):
31
31
  This class is deprecated as of version March 2024 and will not be available after September 2024.
32
32
  """
33
33
 
34
- type: Literal["orion"] = "orion"
34
+ type: str = "orion"
35
35
  serializer: Serializer
36
36
  block_document_id: UUID
37
37
 
@@ -69,7 +69,12 @@ class OrionPackager(Packager):
69
69
  value={"flow": self.serializer.dumps(flow)}
70
70
  )._save(is_anonymous=True)
71
71
 
72
- return self.base_manifest(flow).finalize(
73
- serializer=self.serializer,
74
- block_document_id=block_document_id,
72
+ return OrionPackageManifest(
73
+ **{
74
+ **self.base_manifest(flow).dict(),
75
+ **{
76
+ "serializer": self.serializer,
77
+ "block_document_id": block_document_id,
78
+ },
79
+ }
75
80
  )
@@ -7,13 +7,17 @@ import base64
7
7
  import inspect
8
8
  import json
9
9
  import os.path
10
- import warnings
11
10
  from pathlib import Path
12
11
  from tempfile import TemporaryDirectory
13
12
  from typing import Any, List
14
13
 
15
14
  from prefect._internal.compatibility.deprecated import deprecated_class
16
15
  from prefect._internal.pydantic import HAS_PYDANTIC_V2
16
+ from prefect._internal.schemas.validators import (
17
+ validate_picklelib,
18
+ validate_picklelib_and_modules,
19
+ validate_picklelib_version,
20
+ )
17
21
 
18
22
  if HAS_PYDANTIC_V2:
19
23
  import pydantic.v1 as pydantic
@@ -55,69 +59,15 @@ class PickleSerializer(Serializer):
55
59
 
56
60
  @pydantic.validator("picklelib")
57
61
  def check_picklelib(cls, value):
58
- """
59
- Check that the given pickle library is importable and has dumps/loads methods.
60
- """
61
- try:
62
- pickler = from_qualified_name(value)
63
- except (ImportError, AttributeError) as exc:
64
- raise ValueError(
65
- f"Failed to import requested pickle library: {value!r}."
66
- ) from exc
67
-
68
- if not callable(getattr(pickler, "dumps", None)):
69
- raise ValueError(
70
- f"Pickle library at {value!r} does not have a 'dumps' method."
71
- )
72
-
73
- if not callable(getattr(pickler, "loads", None)):
74
- raise ValueError(
75
- f"Pickle library at {value!r} does not have a 'loads' method."
76
- )
77
-
78
- return value
62
+ return validate_picklelib(value)
79
63
 
80
64
  @pydantic.root_validator
81
65
  def check_picklelib_version(cls, values):
82
- """
83
- Infers a default value for `picklelib_version` if null or ensures it matches
84
- the version retrieved from the `pickelib`.
85
- """
86
- picklelib = values.get("picklelib")
87
- picklelib_version = values.get("picklelib_version")
88
-
89
- if not picklelib:
90
- raise ValueError("Unable to check version of unrecognized picklelib module")
91
-
92
- pickler = from_qualified_name(picklelib)
93
- pickler_version = getattr(pickler, "__version__", None)
94
-
95
- if not picklelib_version:
96
- values["picklelib_version"] = pickler_version
97
- elif picklelib_version != pickler_version:
98
- warnings.warn(
99
- (
100
- f"Mismatched {picklelib!r} versions. Found {pickler_version} in the"
101
- f" environment but {picklelib_version} was requested. This may"
102
- " cause the serializer to fail."
103
- ),
104
- RuntimeWarning,
105
- stacklevel=3,
106
- )
107
-
108
- return values
66
+ return validate_picklelib_version(values)
109
67
 
110
68
  @pydantic.root_validator
111
69
  def check_picklelib_and_modules(cls, values):
112
- """
113
- Prevents modules from being specified if picklelib is not cloudpickle
114
- """
115
- if values.get("picklelib") != "cloudpickle" and values.get("pickle_modules"):
116
- raise ValueError(
117
- "`pickle_modules` cannot be used without 'cloudpickle'. Got"
118
- f" {values.get('picklelib')!r}."
119
- )
120
- return values
70
+ return validate_picklelib_and_modules(values)
121
71
 
122
72
  def dumps(self, obj: Any) -> bytes:
123
73
  pickler = from_qualified_name(self.picklelib)