prefect-client 2.19.2__py3-none-any.whl → 3.0.0rc1__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 (239) hide show
  1. prefect/__init__.py +8 -56
  2. prefect/_internal/compatibility/deprecated.py +6 -115
  3. prefect/_internal/compatibility/experimental.py +4 -79
  4. prefect/_internal/concurrency/api.py +0 -34
  5. prefect/_internal/concurrency/calls.py +0 -6
  6. prefect/_internal/concurrency/cancellation.py +0 -3
  7. prefect/_internal/concurrency/event_loop.py +0 -20
  8. prefect/_internal/concurrency/inspection.py +3 -3
  9. prefect/_internal/concurrency/threads.py +35 -0
  10. prefect/_internal/concurrency/waiters.py +0 -28
  11. prefect/_internal/pydantic/__init__.py +0 -45
  12. prefect/_internal/pydantic/v1_schema.py +21 -22
  13. prefect/_internal/pydantic/v2_schema.py +0 -2
  14. prefect/_internal/pydantic/v2_validated_func.py +18 -23
  15. prefect/_internal/schemas/bases.py +44 -177
  16. prefect/_internal/schemas/fields.py +1 -43
  17. prefect/_internal/schemas/validators.py +60 -158
  18. prefect/artifacts.py +161 -14
  19. prefect/automations.py +39 -4
  20. prefect/blocks/abstract.py +1 -1
  21. prefect/blocks/core.py +268 -148
  22. prefect/blocks/fields.py +2 -57
  23. prefect/blocks/kubernetes.py +8 -12
  24. prefect/blocks/notifications.py +40 -20
  25. prefect/blocks/system.py +22 -11
  26. prefect/blocks/webhook.py +2 -9
  27. prefect/client/base.py +4 -4
  28. prefect/client/cloud.py +8 -13
  29. prefect/client/orchestration.py +347 -341
  30. prefect/client/schemas/actions.py +92 -86
  31. prefect/client/schemas/filters.py +20 -40
  32. prefect/client/schemas/objects.py +151 -145
  33. prefect/client/schemas/responses.py +16 -24
  34. prefect/client/schemas/schedules.py +47 -35
  35. prefect/client/subscriptions.py +2 -2
  36. prefect/client/utilities.py +5 -2
  37. prefect/concurrency/asyncio.py +3 -1
  38. prefect/concurrency/events.py +1 -1
  39. prefect/concurrency/services.py +6 -3
  40. prefect/context.py +195 -27
  41. prefect/deployments/__init__.py +5 -6
  42. prefect/deployments/base.py +7 -5
  43. prefect/deployments/flow_runs.py +185 -0
  44. prefect/deployments/runner.py +50 -45
  45. prefect/deployments/schedules.py +28 -23
  46. prefect/deployments/steps/__init__.py +0 -1
  47. prefect/deployments/steps/core.py +1 -0
  48. prefect/deployments/steps/pull.py +7 -21
  49. prefect/engine.py +12 -2422
  50. prefect/events/actions.py +17 -23
  51. prefect/events/cli/automations.py +19 -6
  52. prefect/events/clients.py +14 -37
  53. prefect/events/filters.py +14 -18
  54. prefect/events/related.py +2 -2
  55. prefect/events/schemas/__init__.py +0 -5
  56. prefect/events/schemas/automations.py +55 -46
  57. prefect/events/schemas/deployment_triggers.py +7 -197
  58. prefect/events/schemas/events.py +34 -65
  59. prefect/events/schemas/labelling.py +10 -14
  60. prefect/events/utilities.py +2 -3
  61. prefect/events/worker.py +2 -3
  62. prefect/filesystems.py +6 -517
  63. prefect/{new_flow_engine.py → flow_engine.py} +313 -72
  64. prefect/flow_runs.py +377 -5
  65. prefect/flows.py +307 -166
  66. prefect/futures.py +186 -345
  67. prefect/infrastructure/__init__.py +0 -27
  68. prefect/infrastructure/provisioners/__init__.py +5 -3
  69. prefect/infrastructure/provisioners/cloud_run.py +11 -6
  70. prefect/infrastructure/provisioners/container_instance.py +11 -7
  71. prefect/infrastructure/provisioners/ecs.py +6 -4
  72. prefect/infrastructure/provisioners/modal.py +8 -5
  73. prefect/input/actions.py +2 -4
  74. prefect/input/run_input.py +5 -7
  75. prefect/logging/formatters.py +0 -2
  76. prefect/logging/handlers.py +3 -11
  77. prefect/logging/loggers.py +2 -2
  78. prefect/manifests.py +2 -1
  79. prefect/records/__init__.py +1 -0
  80. prefect/records/result_store.py +42 -0
  81. prefect/records/store.py +9 -0
  82. prefect/results.py +43 -39
  83. prefect/runner/runner.py +19 -15
  84. prefect/runner/server.py +6 -10
  85. prefect/runner/storage.py +3 -8
  86. prefect/runner/submit.py +2 -2
  87. prefect/runner/utils.py +2 -2
  88. prefect/serializers.py +24 -35
  89. prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
  90. prefect/settings.py +70 -133
  91. prefect/states.py +17 -47
  92. prefect/task_engine.py +697 -58
  93. prefect/task_runners.py +269 -301
  94. prefect/task_server.py +53 -34
  95. prefect/tasks.py +327 -337
  96. prefect/transactions.py +220 -0
  97. prefect/types/__init__.py +61 -82
  98. prefect/utilities/asyncutils.py +195 -136
  99. prefect/utilities/callables.py +311 -43
  100. prefect/utilities/collections.py +23 -38
  101. prefect/utilities/dispatch.py +11 -3
  102. prefect/utilities/dockerutils.py +4 -0
  103. prefect/utilities/engine.py +140 -20
  104. prefect/utilities/importtools.py +97 -27
  105. prefect/utilities/pydantic.py +128 -38
  106. prefect/utilities/schema_tools/hydration.py +5 -1
  107. prefect/utilities/templating.py +12 -2
  108. prefect/variables.py +78 -61
  109. prefect/workers/__init__.py +0 -1
  110. prefect/workers/base.py +15 -17
  111. prefect/workers/process.py +3 -8
  112. prefect/workers/server.py +2 -2
  113. {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/METADATA +22 -21
  114. prefect_client-3.0.0rc1.dist-info/RECORD +176 -0
  115. prefect/_internal/pydantic/_base_model.py +0 -51
  116. prefect/_internal/pydantic/_compat.py +0 -82
  117. prefect/_internal/pydantic/_flags.py +0 -20
  118. prefect/_internal/pydantic/_types.py +0 -8
  119. prefect/_internal/pydantic/utilities/__init__.py +0 -0
  120. prefect/_internal/pydantic/utilities/config_dict.py +0 -72
  121. prefect/_internal/pydantic/utilities/field_validator.py +0 -150
  122. prefect/_internal/pydantic/utilities/model_construct.py +0 -56
  123. prefect/_internal/pydantic/utilities/model_copy.py +0 -55
  124. prefect/_internal/pydantic/utilities/model_dump.py +0 -136
  125. prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
  126. prefect/_internal/pydantic/utilities/model_fields.py +0 -50
  127. prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
  128. prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
  129. prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
  130. prefect/_internal/pydantic/utilities/model_validate.py +0 -75
  131. prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
  132. prefect/_internal/pydantic/utilities/model_validator.py +0 -87
  133. prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
  134. prefect/_vendor/__init__.py +0 -0
  135. prefect/_vendor/fastapi/__init__.py +0 -25
  136. prefect/_vendor/fastapi/applications.py +0 -946
  137. prefect/_vendor/fastapi/background.py +0 -3
  138. prefect/_vendor/fastapi/concurrency.py +0 -44
  139. prefect/_vendor/fastapi/datastructures.py +0 -58
  140. prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
  141. prefect/_vendor/fastapi/dependencies/models.py +0 -64
  142. prefect/_vendor/fastapi/dependencies/utils.py +0 -877
  143. prefect/_vendor/fastapi/encoders.py +0 -177
  144. prefect/_vendor/fastapi/exception_handlers.py +0 -40
  145. prefect/_vendor/fastapi/exceptions.py +0 -46
  146. prefect/_vendor/fastapi/logger.py +0 -3
  147. prefect/_vendor/fastapi/middleware/__init__.py +0 -1
  148. prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
  149. prefect/_vendor/fastapi/middleware/cors.py +0 -3
  150. prefect/_vendor/fastapi/middleware/gzip.py +0 -3
  151. prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
  152. prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
  153. prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
  154. prefect/_vendor/fastapi/openapi/__init__.py +0 -0
  155. prefect/_vendor/fastapi/openapi/constants.py +0 -2
  156. prefect/_vendor/fastapi/openapi/docs.py +0 -203
  157. prefect/_vendor/fastapi/openapi/models.py +0 -480
  158. prefect/_vendor/fastapi/openapi/utils.py +0 -485
  159. prefect/_vendor/fastapi/param_functions.py +0 -340
  160. prefect/_vendor/fastapi/params.py +0 -453
  161. prefect/_vendor/fastapi/requests.py +0 -4
  162. prefect/_vendor/fastapi/responses.py +0 -40
  163. prefect/_vendor/fastapi/routing.py +0 -1331
  164. prefect/_vendor/fastapi/security/__init__.py +0 -15
  165. prefect/_vendor/fastapi/security/api_key.py +0 -98
  166. prefect/_vendor/fastapi/security/base.py +0 -6
  167. prefect/_vendor/fastapi/security/http.py +0 -172
  168. prefect/_vendor/fastapi/security/oauth2.py +0 -227
  169. prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
  170. prefect/_vendor/fastapi/security/utils.py +0 -10
  171. prefect/_vendor/fastapi/staticfiles.py +0 -1
  172. prefect/_vendor/fastapi/templating.py +0 -3
  173. prefect/_vendor/fastapi/testclient.py +0 -1
  174. prefect/_vendor/fastapi/types.py +0 -3
  175. prefect/_vendor/fastapi/utils.py +0 -235
  176. prefect/_vendor/fastapi/websockets.py +0 -7
  177. prefect/_vendor/starlette/__init__.py +0 -1
  178. prefect/_vendor/starlette/_compat.py +0 -28
  179. prefect/_vendor/starlette/_exception_handler.py +0 -80
  180. prefect/_vendor/starlette/_utils.py +0 -88
  181. prefect/_vendor/starlette/applications.py +0 -261
  182. prefect/_vendor/starlette/authentication.py +0 -159
  183. prefect/_vendor/starlette/background.py +0 -43
  184. prefect/_vendor/starlette/concurrency.py +0 -59
  185. prefect/_vendor/starlette/config.py +0 -151
  186. prefect/_vendor/starlette/convertors.py +0 -87
  187. prefect/_vendor/starlette/datastructures.py +0 -707
  188. prefect/_vendor/starlette/endpoints.py +0 -130
  189. prefect/_vendor/starlette/exceptions.py +0 -60
  190. prefect/_vendor/starlette/formparsers.py +0 -276
  191. prefect/_vendor/starlette/middleware/__init__.py +0 -17
  192. prefect/_vendor/starlette/middleware/authentication.py +0 -52
  193. prefect/_vendor/starlette/middleware/base.py +0 -220
  194. prefect/_vendor/starlette/middleware/cors.py +0 -176
  195. prefect/_vendor/starlette/middleware/errors.py +0 -265
  196. prefect/_vendor/starlette/middleware/exceptions.py +0 -74
  197. prefect/_vendor/starlette/middleware/gzip.py +0 -113
  198. prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
  199. prefect/_vendor/starlette/middleware/sessions.py +0 -82
  200. prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
  201. prefect/_vendor/starlette/middleware/wsgi.py +0 -147
  202. prefect/_vendor/starlette/requests.py +0 -328
  203. prefect/_vendor/starlette/responses.py +0 -347
  204. prefect/_vendor/starlette/routing.py +0 -933
  205. prefect/_vendor/starlette/schemas.py +0 -154
  206. prefect/_vendor/starlette/staticfiles.py +0 -248
  207. prefect/_vendor/starlette/status.py +0 -199
  208. prefect/_vendor/starlette/templating.py +0 -231
  209. prefect/_vendor/starlette/testclient.py +0 -804
  210. prefect/_vendor/starlette/types.py +0 -30
  211. prefect/_vendor/starlette/websockets.py +0 -193
  212. prefect/agent.py +0 -698
  213. prefect/deployments/deployments.py +0 -1042
  214. prefect/deprecated/__init__.py +0 -0
  215. prefect/deprecated/data_documents.py +0 -350
  216. prefect/deprecated/packaging/__init__.py +0 -12
  217. prefect/deprecated/packaging/base.py +0 -96
  218. prefect/deprecated/packaging/docker.py +0 -146
  219. prefect/deprecated/packaging/file.py +0 -92
  220. prefect/deprecated/packaging/orion.py +0 -80
  221. prefect/deprecated/packaging/serializers.py +0 -171
  222. prefect/events/instrument.py +0 -135
  223. prefect/infrastructure/base.py +0 -323
  224. prefect/infrastructure/container.py +0 -818
  225. prefect/infrastructure/kubernetes.py +0 -920
  226. prefect/infrastructure/process.py +0 -289
  227. prefect/new_task_engine.py +0 -423
  228. prefect/pydantic/__init__.py +0 -76
  229. prefect/pydantic/main.py +0 -39
  230. prefect/software/__init__.py +0 -2
  231. prefect/software/base.py +0 -50
  232. prefect/software/conda.py +0 -199
  233. prefect/software/pip.py +0 -122
  234. prefect/software/python.py +0 -52
  235. prefect/workers/block.py +0 -218
  236. prefect_client-2.19.2.dist-info/RECORD +0 -292
  237. {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/LICENSE +0 -0
  238. {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/WHEEL +0 -0
  239. {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/top_level.txt +0 -0
@@ -1,37 +1,36 @@
1
1
  import inspect
2
2
  import typing
3
+ import warnings
3
4
 
4
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
5
-
6
- if HAS_PYDANTIC_V2:
7
- from pydantic.v1 import BaseModel as V1BaseModel
8
- else:
9
- from pydantic import BaseModel as V1BaseModel
5
+ import pydantic
6
+ from pydantic.v1 import BaseModel as V1BaseModel
10
7
 
11
8
 
12
9
  def is_v1_model(v) -> bool:
13
- if isinstance(v, V1BaseModel):
14
- return True
15
- try:
16
- if inspect.isclass(v) and issubclass(v, V1BaseModel):
10
+ with warnings.catch_warnings():
11
+ warnings.filterwarnings(
12
+ "ignore", category=pydantic.warnings.PydanticDeprecatedSince20
13
+ )
14
+
15
+ if isinstance(v, V1BaseModel):
17
16
  return True
18
- except TypeError:
19
- pass
17
+ try:
18
+ if inspect.isclass(v) and issubclass(v, V1BaseModel):
19
+ return True
20
+ except TypeError:
21
+ pass
20
22
 
21
- return False
23
+ return False
22
24
 
23
25
 
24
26
  def is_v1_type(v) -> bool:
25
- if HAS_PYDANTIC_V2:
26
- if is_v1_model(v):
27
- return True
28
-
29
- try:
30
- return v.__module__.startswith("pydantic.v1.types")
31
- except AttributeError:
32
- return False
27
+ if is_v1_model(v):
28
+ return True
33
29
 
34
- return True
30
+ try:
31
+ return v.__module__.startswith("pydantic.v1.types")
32
+ except AttributeError:
33
+ return False
35
34
 
36
35
 
37
36
  def has_v1_type_as_param(signature: inspect.Signature) -> bool:
@@ -1,5 +1,3 @@
1
- # Note: This file should only be imported under a HAS_PYDANTIC_V2 flag
2
-
3
1
  import inspect
4
2
  import typing
5
3
  import typing as t
@@ -1,16 +1,15 @@
1
1
  """
2
- This module contains an implementation of pydantic v1's ValidateFunction
2
+ This module contains an implementation of pydantic v1's ValidateFunction
3
3
  modified to validate function arguments and return a pydantic v2 model.
4
4
 
5
- Specifically it allows for us to validate v2 models used as flow/task
5
+ Specifically it allows for us to validate v2 models used as flow/task
6
6
  arguments.
7
7
  """
8
8
 
9
9
  from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, Union
10
10
 
11
11
  # importing directly from v2 to be able to create a v2 model
12
- from pydantic import BaseModel, create_model
13
- from pydantic.v1 import validator
12
+ from pydantic import BaseModel, ConfigDict, create_model, field_validator
14
13
  from pydantic.v1.decorator import ValidatedFunction
15
14
  from pydantic.v1.errors import ConfigError
16
15
  from pydantic.v1.utils import to_camel
@@ -28,28 +27,25 @@ class V2ValidatedFunction(ValidatedFunction):
28
27
  fields: Dict[str, Any],
29
28
  takes_args: bool,
30
29
  takes_kwargs: bool,
31
- config: "ConfigType",
30
+ config: ConfigDict,
32
31
  ) -> None:
33
32
  pos_args = len(self.arg_mapping)
34
33
 
35
- class CustomConfig:
36
- pass
37
-
38
- if not TYPE_CHECKING: # pragma: no branch
39
- if isinstance(config, dict):
40
- CustomConfig = type("Config", (), config) # noqa: F811
41
- elif config is not None:
42
- CustomConfig = config # noqa: F811
43
-
44
- if hasattr(CustomConfig, "fields") or hasattr(CustomConfig, "alias_generator"):
34
+ if config.get("fields") or config.get("alias_generator"):
45
35
  raise ConfigError(
46
36
  'Setting the "fields" and "alias_generator" property on custom Config'
47
37
  " for @validate_arguments is not yet supported, please remove."
48
38
  )
49
39
 
40
+ if "extra" not in config:
41
+ config["extra"] = "forbid"
42
+
50
43
  # This is the key change -- inheriting the BaseModel class from v2
51
44
  class DecoratorBaseModel(BaseModel):
52
- @validator(self.v_args_name, check_fields=False, allow_reuse=True)
45
+ model_config = config
46
+
47
+ @field_validator(self.v_args_name, check_fields=False)
48
+ @classmethod
53
49
  def check_args(cls, v: Optional[List[Any]]) -> Optional[List[Any]]:
54
50
  if takes_args or v is None:
55
51
  return v
@@ -59,7 +55,8 @@ class V2ValidatedFunction(ValidatedFunction):
59
55
  f" {pos_args + len(v)} given"
60
56
  )
61
57
 
62
- @validator(self.v_kwargs_name, check_fields=False, allow_reuse=True)
58
+ @field_validator(self.v_kwargs_name, check_fields=False)
59
+ @classmethod
63
60
  def check_kwargs(
64
61
  cls, v: Optional[Dict[str, Any]]
65
62
  ) -> Optional[Dict[str, Any]]:
@@ -70,7 +67,8 @@ class V2ValidatedFunction(ValidatedFunction):
70
67
  keys = ", ".join(map(repr, v.keys()))
71
68
  raise TypeError(f"unexpected keyword argument{plural}: {keys}")
72
69
 
73
- @validator(V_POSITIONAL_ONLY_NAME, check_fields=False, allow_reuse=True)
70
+ @field_validator(V_POSITIONAL_ONLY_NAME, check_fields=False)
71
+ @classmethod
74
72
  def check_positional_only(cls, v: Optional[List[str]]) -> None:
75
73
  if v is None:
76
74
  return
@@ -82,7 +80,8 @@ class V2ValidatedFunction(ValidatedFunction):
82
80
  f" argument{plural}: {keys}"
83
81
  )
84
82
 
85
- @validator(V_DUPLICATE_KWARGS, check_fields=False, allow_reuse=True)
83
+ @field_validator(V_DUPLICATE_KWARGS, check_fields=False)
84
+ @classmethod
86
85
  def check_duplicate_kwargs(cls, v: Optional[List[str]]) -> None:
87
86
  if v is None:
88
87
  return
@@ -91,10 +90,6 @@ class V2ValidatedFunction(ValidatedFunction):
91
90
  keys = ", ".join(map(repr, v))
92
91
  raise TypeError(f"multiple values for argument{plural}: {keys}")
93
92
 
94
- class Config(CustomConfig):
95
- # extra = getattr(CustomConfig, "extra", Extra.forbid)
96
- extra = getattr(CustomConfig, "extra", "forbid")
97
-
98
93
  self.model = create_model(
99
94
  to_camel(self.raw_function.__name__),
100
95
  __base__=DecoratorBaseModel,
@@ -3,29 +3,18 @@ Utilities for creating and working with Prefect REST API schemas.
3
3
  """
4
4
 
5
5
  import datetime
6
- import json
7
6
  import os
8
- from functools import partial
9
- from typing import Any, Dict, Generator, Optional, Set, TypeVar
7
+ from typing import Any, ClassVar, Optional, Set, TypeVar
10
8
  from uuid import UUID, uuid4
11
9
 
12
- import orjson
13
10
  import pendulum
14
- from packaging.version import Version
15
-
16
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
17
-
18
- if HAS_PYDANTIC_V2:
19
- import pydantic.v1 as pydantic
20
- from pydantic.v1 import BaseModel, Field, SecretField
21
- from pydantic.v1.json import custom_pydantic_encoder
22
- else:
23
- import pydantic
24
- from pydantic import BaseModel, Field, SecretField
25
- from pydantic.json import custom_pydantic_encoder
26
-
27
- from prefect._internal.schemas.fields import DateTimeTZ
28
- from prefect._internal.schemas.serializers import orjson_dumps_extra_compatible
11
+ from pydantic import (
12
+ BaseModel,
13
+ ConfigDict,
14
+ Field,
15
+ )
16
+ from pydantic_extra_types.pendulum_dt import DateTime
17
+ from typing_extensions import Self
29
18
 
30
19
  T = TypeVar("T")
31
20
 
@@ -42,39 +31,16 @@ class PrefectBaseModel(BaseModel):
42
31
  subtle unintentional testing errors.
43
32
  """
44
33
 
45
- class Config:
46
- # extra attributes are forbidden in order to raise meaningful errors for
47
- # bad API payloads
48
- # We cannot load this setting through the normal pattern due to circular
49
- # imports; instead just check if its a truthy setting directly
50
- if os.getenv("PREFECT_TEST_MODE", "0").lower() in ["1", "true"]:
51
- extra = "forbid"
52
- else:
53
- extra = "ignore"
54
-
55
- json_encoders = {
56
- # Uses secret fields and strange logic to avoid a circular import error
57
- # for Secret dict in prefect.blocks.fields
58
- SecretField: lambda v: v.dict() if getattr(v, "dict", None) else str(v)
59
- }
60
-
61
- pydantic_version = getattr(pydantic, "__version__", None)
62
- if pydantic_version is not None and Version(pydantic_version) >= Version(
63
- "1.9.2"
64
- ):
65
- copy_on_model_validation = "none"
66
- else:
67
- copy_on_model_validation = False
34
+ _reset_fields: ClassVar[Set[str]] = set()
68
35
 
69
- # Use orjson for serialization
70
- json_loads = orjson.loads
71
- json_dumps = orjson_dumps_extra_compatible
72
-
73
- def _reset_fields(self) -> Set[str]:
74
- """A set of field names that are reset when the PrefectBaseModel is copied.
75
- These fields are also disregarded for equality comparisons.
76
- """
77
- return set()
36
+ model_config = ConfigDict(
37
+ ser_json_timedelta="float",
38
+ extra=(
39
+ "ignore"
40
+ if os.getenv("PREFECT_TEST_MODE", "0").lower() not in ["true", "1"]
41
+ else "forbid"
42
+ ),
43
+ )
78
44
 
79
45
  def __eq__(self, other: Any) -> bool:
80
46
  """Equaltiy operator that ignores the resettable fields of the PrefectBaseModel.
@@ -82,124 +48,47 @@ class PrefectBaseModel(BaseModel):
82
48
  NOTE: this equality operator will only be applied if the PrefectBaseModel is
83
49
  the left-hand operand. This is a limitation of Python.
84
50
  """
85
- copy_dict = self.dict(exclude=self._reset_fields())
51
+ copy_dict = self.model_dump(exclude=self._reset_fields)
86
52
  if isinstance(other, PrefectBaseModel):
87
- return copy_dict == other.dict(exclude=other._reset_fields())
53
+ return copy_dict == other.model_dump(exclude=other._reset_fields)
88
54
  if isinstance(other, BaseModel):
89
- return copy_dict == other.dict()
55
+ return copy_dict == other.model_dump()
90
56
  else:
91
57
  return copy_dict == other
92
58
 
93
- def json(self, *args, include_secrets: bool = False, **kwargs) -> str:
94
- """
95
- Returns a representation of the model as JSON.
96
-
97
- If `include_secrets=True`, then `SecretStr` and `SecretBytes` objects are
98
- fully revealed. Otherwise they are obfuscated.
99
-
100
- """
101
- if include_secrets:
102
- if "encoder" in kwargs:
103
- raise ValueError(
104
- "Alternative encoder provided; can not set encoder for"
105
- " SecretFields."
106
- )
107
- kwargs["encoder"] = partial(
108
- custom_pydantic_encoder,
109
- {SecretField: lambda v: v.get_secret_value() if v else None},
110
- )
111
- return super().json(*args, **kwargs)
112
-
113
- def dict(
114
- self, *args, shallow: bool = False, json_compatible: bool = False, **kwargs
115
- ) -> dict:
116
- """Returns a representation of the model as a Python dictionary.
117
-
118
- For more information on this distinction please see
119
- https://pydantic-docs.helpmanual.io/usage/exporting_models/#dictmodel-and-iteration
120
-
121
-
122
- Args:
123
- shallow (bool, optional): If True (default), nested Pydantic fields
124
- are also coerced to dicts. If false, they are left as Pydantic
125
- models.
126
- json_compatible (bool, optional): if True, objects are converted
127
- into json-compatible representations, similar to calling
128
- `json.loads(self.json())`. Not compatible with shallow=True.
129
-
130
- Returns:
131
- dict
132
- """
133
-
134
- if json_compatible and shallow:
135
- raise ValueError(
136
- "`json_compatible` can only be applied to the entire object."
137
- )
138
-
139
- # return a json-compatible representation of the object
140
- elif json_compatible:
141
- return json.loads(self.json(*args, **kwargs))
142
-
143
- # if shallow wasn't requested, return the standard pydantic behavior
144
- elif not shallow:
145
- return super().dict(*args, **kwargs)
146
-
147
- # if no options were requested, return simple dict transformation
148
- # to apply shallow conversion
149
- elif not args and not kwargs:
150
- return dict(self)
151
-
152
- # if options like include/exclude were provided, perform
153
- # a full dict conversion then overwrite with any shallow
154
- # differences
155
- else:
156
- deep_dict = super().dict(*args, **kwargs)
157
- shallow_dict = dict(self)
158
- for k, v in list(deep_dict.items()):
159
- if isinstance(v, dict) and isinstance(shallow_dict[k], BaseModel):
160
- deep_dict[k] = shallow_dict[k]
161
- return deep_dict
162
-
163
- def copy(
164
- self: T, *, update: Dict = None, reset_fields: bool = False, **kwargs: Any
165
- ) -> T:
166
- """
167
- Duplicate a model.
168
-
169
- Args:
170
- update: values to change/add to the model copy
171
- reset_fields: if True, reset the fields specified in `self._reset_fields`
172
- to their default value on the new model
173
- kwargs: kwargs to pass to `pydantic.BaseModel.copy`
174
-
175
- Returns:
176
- A new copy of the model
177
- """
178
- if reset_fields:
179
- update = update or dict()
180
- for field in self._reset_fields():
181
- update.setdefault(field, self.__fields__[field].get_default())
182
- return super().copy(update=update, **kwargs)
183
-
184
59
  def __rich_repr__(self):
185
60
  # Display all of the fields in the model if they differ from the default value
186
- for name, field in self.__fields__.items():
61
+ for name, field in self.model_fields.items():
187
62
  value = getattr(self, name)
188
63
 
189
64
  # Simplify the display of some common fields
190
- if field.type_ == UUID and value:
65
+ if field.annotation == UUID and value:
191
66
  value = str(value)
192
67
  elif (
193
- isinstance(field.type_, datetime.datetime)
68
+ isinstance(field.annotation, datetime.datetime)
194
69
  and name == "timestamp"
195
70
  and value
196
71
  ):
197
72
  value = pendulum.instance(value).isoformat()
198
- elif isinstance(field.type_, datetime.datetime) and value:
73
+ elif isinstance(field.annotation, datetime.datetime) and value:
199
74
  value = pendulum.instance(value).diff_for_humans()
200
75
 
201
76
  yield name, value, field.get_default()
202
77
 
78
+ def reset_fields(self: Self) -> Self:
79
+ """
80
+ Reset the fields of the model that are in the `_reset_fields` set.
81
+
82
+ Returns:
83
+ PrefectBaseModel: A new instance of the model with the reset fields.
84
+ """
85
+ return self.model_copy(
86
+ update={
87
+ field: self.model_fields[field].get_default(call_default_factory=True)
88
+ for field in self._reset_fields
89
+ }
90
+ )
91
+
203
92
 
204
93
  class IDBaseModel(PrefectBaseModel):
205
94
  """
@@ -208,11 +97,9 @@ class IDBaseModel(PrefectBaseModel):
208
97
  The ID is reset on copy() and not included in equality comparisons.
209
98
  """
210
99
 
100
+ _reset_fields: ClassVar[Set[str]] = {"id"}
211
101
  id: UUID = Field(default_factory=uuid4)
212
102
 
213
- def _reset_fields(self) -> Set[str]:
214
- return super()._reset_fields().union({"id"})
215
-
216
103
 
217
104
  class ObjectBaseModel(IDBaseModel):
218
105
  """
@@ -223,32 +110,12 @@ class ObjectBaseModel(IDBaseModel):
223
110
  equality comparisons.
224
111
  """
225
112
 
226
- class Config:
227
- orm_mode = True
228
-
229
- created: Optional[DateTimeTZ] = Field(default=None, repr=False)
230
- updated: Optional[DateTimeTZ] = Field(default=None, repr=False)
113
+ _reset_fields: ClassVar[Set[str]] = {"id", "created", "updated"}
114
+ model_config = ConfigDict(from_attributes=True)
231
115
 
232
- def _reset_fields(self) -> Set[str]:
233
- return super()._reset_fields().union({"created", "updated"})
116
+ created: Optional[DateTime] = Field(default=None, repr=False)
117
+ updated: Optional[DateTime] = Field(default=None, repr=False)
234
118
 
235
119
 
236
120
  class ActionBaseModel(PrefectBaseModel):
237
- class Config:
238
- extra = "forbid"
239
-
240
- def __iter__(self):
241
- # By default, `pydantic.BaseModel.__iter__` yields from `self.__dict__` directly
242
- # instead of going through `_iter`. We want tor retain our custom logic in
243
- # `_iter` during `dict(model)` calls which is what Pydantic uses for
244
- # `parse_obj(model)`
245
- yield from self._iter(to_dict=True)
246
-
247
- def _iter(self, *args, **kwargs) -> Generator[tuple, None, None]:
248
- # Drop fields that are marked as `ignored` from json and dictionary outputs
249
- exclude = kwargs.pop("exclude", None) or set()
250
- for name, field in self.__fields__.items():
251
- if field.field_info.extra.get("ignored"):
252
- exclude.add(name)
253
-
254
- return super()._iter(*args, **kwargs, exclude=exclude)
121
+ model_config: ConfigDict = ConfigDict(extra="forbid")
@@ -1,49 +1,7 @@
1
- import datetime
2
1
  from typing import Optional
3
2
  from uuid import UUID
4
3
 
5
- import pendulum
6
- from typing_extensions import TypeAlias
7
-
8
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
9
-
10
- if HAS_PYDANTIC_V2:
11
- from pydantic.v1 import BaseModel, Field
12
- else:
13
- from pydantic import BaseModel, Field
14
-
15
-
16
- # Rather than subclassing pendulum.DateTime to add our pydantic-specific validation,
17
- # which will lead to a lot of funky typing issues, we'll just monkeypatch the pydantic
18
- # validators onto the class. Retaining this type alias means that we can still use it
19
- # as we have been in class definitions, also guaranteeing that we'll be applying these
20
- # validators by importing this module.
21
-
22
- DateTimeTZ: TypeAlias = pendulum.DateTime
23
-
24
-
25
- def _datetime_patched_classmethod(function):
26
- if hasattr(DateTimeTZ, function.__name__):
27
- return function
28
- setattr(DateTimeTZ, function.__name__, classmethod(function))
29
- return function
30
-
31
-
32
- @_datetime_patched_classmethod
33
- def __get_validators__(cls):
34
- yield getattr(cls, "validate")
35
-
36
-
37
- @_datetime_patched_classmethod
38
- def validate(cls, v) -> pendulum.DateTime:
39
- if isinstance(v, str):
40
- parsed = pendulum.parse(v)
41
- assert isinstance(parsed, pendulum.DateTime)
42
- return parsed
43
- elif isinstance(v, datetime.datetime):
44
- return pendulum.instance(v)
45
- else:
46
- raise ValueError("Unrecognized datetime.")
4
+ from pydantic import BaseModel, Field
47
5
 
48
6
 
49
7
  class CreatedBy(BaseModel):