prefect-client 2.20.4__py3-none-any.whl → 3.0.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 (288) hide show
  1. prefect/__init__.py +74 -110
  2. prefect/_internal/compatibility/deprecated.py +6 -115
  3. prefect/_internal/compatibility/experimental.py +4 -79
  4. prefect/_internal/compatibility/migration.py +166 -0
  5. prefect/_internal/concurrency/__init__.py +2 -2
  6. prefect/_internal/concurrency/api.py +1 -35
  7. prefect/_internal/concurrency/calls.py +0 -6
  8. prefect/_internal/concurrency/cancellation.py +0 -3
  9. prefect/_internal/concurrency/event_loop.py +0 -20
  10. prefect/_internal/concurrency/inspection.py +3 -3
  11. prefect/_internal/concurrency/primitives.py +1 -0
  12. prefect/_internal/concurrency/services.py +23 -0
  13. prefect/_internal/concurrency/threads.py +35 -0
  14. prefect/_internal/concurrency/waiters.py +0 -28
  15. prefect/_internal/integrations.py +7 -0
  16. prefect/_internal/pydantic/__init__.py +0 -45
  17. prefect/_internal/pydantic/annotations/pendulum.py +2 -2
  18. prefect/_internal/pydantic/v1_schema.py +21 -22
  19. prefect/_internal/pydantic/v2_schema.py +0 -2
  20. prefect/_internal/pydantic/v2_validated_func.py +18 -23
  21. prefect/_internal/pytz.py +1 -1
  22. prefect/_internal/retries.py +61 -0
  23. prefect/_internal/schemas/bases.py +45 -177
  24. prefect/_internal/schemas/fields.py +1 -43
  25. prefect/_internal/schemas/validators.py +47 -233
  26. prefect/agent.py +3 -695
  27. prefect/artifacts.py +173 -14
  28. prefect/automations.py +39 -4
  29. prefect/blocks/abstract.py +1 -1
  30. prefect/blocks/core.py +405 -153
  31. prefect/blocks/fields.py +2 -57
  32. prefect/blocks/notifications.py +43 -28
  33. prefect/blocks/redis.py +168 -0
  34. prefect/blocks/system.py +67 -20
  35. prefect/blocks/webhook.py +2 -9
  36. prefect/cache_policies.py +239 -0
  37. prefect/client/__init__.py +4 -0
  38. prefect/client/base.py +33 -27
  39. prefect/client/cloud.py +65 -20
  40. prefect/client/collections.py +1 -1
  41. prefect/client/orchestration.py +650 -442
  42. prefect/client/schemas/actions.py +115 -100
  43. prefect/client/schemas/filters.py +46 -52
  44. prefect/client/schemas/objects.py +228 -178
  45. prefect/client/schemas/responses.py +18 -36
  46. prefect/client/schemas/schedules.py +55 -36
  47. prefect/client/schemas/sorting.py +2 -0
  48. prefect/client/subscriptions.py +8 -7
  49. prefect/client/types/flexible_schedule_list.py +11 -0
  50. prefect/client/utilities.py +9 -6
  51. prefect/concurrency/asyncio.py +60 -11
  52. prefect/concurrency/context.py +24 -0
  53. prefect/concurrency/events.py +2 -2
  54. prefect/concurrency/services.py +46 -16
  55. prefect/concurrency/sync.py +51 -7
  56. prefect/concurrency/v1/asyncio.py +143 -0
  57. prefect/concurrency/v1/context.py +27 -0
  58. prefect/concurrency/v1/events.py +61 -0
  59. prefect/concurrency/v1/services.py +116 -0
  60. prefect/concurrency/v1/sync.py +92 -0
  61. prefect/context.py +246 -149
  62. prefect/deployments/__init__.py +33 -18
  63. prefect/deployments/base.py +10 -15
  64. prefect/deployments/deployments.py +2 -1048
  65. prefect/deployments/flow_runs.py +178 -0
  66. prefect/deployments/runner.py +72 -173
  67. prefect/deployments/schedules.py +31 -25
  68. prefect/deployments/steps/__init__.py +0 -1
  69. prefect/deployments/steps/core.py +7 -0
  70. prefect/deployments/steps/pull.py +15 -21
  71. prefect/deployments/steps/utility.py +2 -1
  72. prefect/docker/__init__.py +20 -0
  73. prefect/docker/docker_image.py +82 -0
  74. prefect/engine.py +15 -2475
  75. prefect/events/actions.py +17 -23
  76. prefect/events/cli/automations.py +20 -7
  77. prefect/events/clients.py +142 -80
  78. prefect/events/filters.py +14 -18
  79. prefect/events/related.py +74 -75
  80. prefect/events/schemas/__init__.py +0 -5
  81. prefect/events/schemas/automations.py +55 -46
  82. prefect/events/schemas/deployment_triggers.py +7 -197
  83. prefect/events/schemas/events.py +46 -65
  84. prefect/events/schemas/labelling.py +10 -14
  85. prefect/events/utilities.py +4 -5
  86. prefect/events/worker.py +23 -8
  87. prefect/exceptions.py +15 -0
  88. prefect/filesystems.py +30 -529
  89. prefect/flow_engine.py +827 -0
  90. prefect/flow_runs.py +379 -7
  91. prefect/flows.py +470 -360
  92. prefect/futures.py +382 -331
  93. prefect/infrastructure/__init__.py +5 -26
  94. prefect/infrastructure/base.py +3 -320
  95. prefect/infrastructure/provisioners/__init__.py +5 -3
  96. prefect/infrastructure/provisioners/cloud_run.py +13 -8
  97. prefect/infrastructure/provisioners/container_instance.py +14 -9
  98. prefect/infrastructure/provisioners/ecs.py +10 -8
  99. prefect/infrastructure/provisioners/modal.py +8 -5
  100. prefect/input/__init__.py +4 -0
  101. prefect/input/actions.py +2 -4
  102. prefect/input/run_input.py +9 -9
  103. prefect/logging/formatters.py +2 -4
  104. prefect/logging/handlers.py +9 -14
  105. prefect/logging/loggers.py +5 -5
  106. prefect/main.py +72 -0
  107. prefect/plugins.py +2 -64
  108. prefect/profiles.toml +16 -2
  109. prefect/records/__init__.py +1 -0
  110. prefect/records/base.py +223 -0
  111. prefect/records/filesystem.py +207 -0
  112. prefect/records/memory.py +178 -0
  113. prefect/records/result_store.py +64 -0
  114. prefect/results.py +577 -504
  115. prefect/runner/runner.py +117 -47
  116. prefect/runner/server.py +32 -34
  117. prefect/runner/storage.py +3 -12
  118. prefect/runner/submit.py +2 -10
  119. prefect/runner/utils.py +2 -2
  120. prefect/runtime/__init__.py +1 -0
  121. prefect/runtime/deployment.py +1 -0
  122. prefect/runtime/flow_run.py +40 -5
  123. prefect/runtime/task_run.py +1 -0
  124. prefect/serializers.py +28 -39
  125. prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
  126. prefect/settings.py +209 -332
  127. prefect/states.py +160 -63
  128. prefect/task_engine.py +1478 -57
  129. prefect/task_runners.py +383 -287
  130. prefect/task_runs.py +240 -0
  131. prefect/task_worker.py +463 -0
  132. prefect/tasks.py +684 -374
  133. prefect/transactions.py +410 -0
  134. prefect/types/__init__.py +72 -86
  135. prefect/types/entrypoint.py +13 -0
  136. prefect/utilities/annotations.py +4 -3
  137. prefect/utilities/asyncutils.py +227 -148
  138. prefect/utilities/callables.py +137 -45
  139. prefect/utilities/collections.py +134 -86
  140. prefect/utilities/dispatch.py +27 -14
  141. prefect/utilities/dockerutils.py +11 -4
  142. prefect/utilities/engine.py +186 -32
  143. prefect/utilities/filesystem.py +4 -5
  144. prefect/utilities/importtools.py +26 -27
  145. prefect/utilities/pydantic.py +128 -38
  146. prefect/utilities/schema_tools/hydration.py +18 -1
  147. prefect/utilities/schema_tools/validation.py +30 -0
  148. prefect/utilities/services.py +35 -9
  149. prefect/utilities/templating.py +12 -2
  150. prefect/utilities/timeout.py +20 -5
  151. prefect/utilities/urls.py +195 -0
  152. prefect/utilities/visualization.py +1 -0
  153. prefect/variables.py +78 -59
  154. prefect/workers/__init__.py +0 -1
  155. prefect/workers/base.py +237 -244
  156. prefect/workers/block.py +5 -226
  157. prefect/workers/cloud.py +6 -0
  158. prefect/workers/process.py +265 -12
  159. prefect/workers/server.py +29 -11
  160. {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/METADATA +28 -24
  161. prefect_client-3.0.0.dist-info/RECORD +201 -0
  162. {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/WHEEL +1 -1
  163. prefect/_internal/pydantic/_base_model.py +0 -51
  164. prefect/_internal/pydantic/_compat.py +0 -82
  165. prefect/_internal/pydantic/_flags.py +0 -20
  166. prefect/_internal/pydantic/_types.py +0 -8
  167. prefect/_internal/pydantic/utilities/config_dict.py +0 -72
  168. prefect/_internal/pydantic/utilities/field_validator.py +0 -150
  169. prefect/_internal/pydantic/utilities/model_construct.py +0 -56
  170. prefect/_internal/pydantic/utilities/model_copy.py +0 -55
  171. prefect/_internal/pydantic/utilities/model_dump.py +0 -136
  172. prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
  173. prefect/_internal/pydantic/utilities/model_fields.py +0 -50
  174. prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
  175. prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
  176. prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
  177. prefect/_internal/pydantic/utilities/model_validate.py +0 -75
  178. prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
  179. prefect/_internal/pydantic/utilities/model_validator.py +0 -87
  180. prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
  181. prefect/_vendor/fastapi/__init__.py +0 -25
  182. prefect/_vendor/fastapi/applications.py +0 -946
  183. prefect/_vendor/fastapi/background.py +0 -3
  184. prefect/_vendor/fastapi/concurrency.py +0 -44
  185. prefect/_vendor/fastapi/datastructures.py +0 -58
  186. prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
  187. prefect/_vendor/fastapi/dependencies/models.py +0 -64
  188. prefect/_vendor/fastapi/dependencies/utils.py +0 -877
  189. prefect/_vendor/fastapi/encoders.py +0 -177
  190. prefect/_vendor/fastapi/exception_handlers.py +0 -40
  191. prefect/_vendor/fastapi/exceptions.py +0 -46
  192. prefect/_vendor/fastapi/logger.py +0 -3
  193. prefect/_vendor/fastapi/middleware/__init__.py +0 -1
  194. prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
  195. prefect/_vendor/fastapi/middleware/cors.py +0 -3
  196. prefect/_vendor/fastapi/middleware/gzip.py +0 -3
  197. prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
  198. prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
  199. prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
  200. prefect/_vendor/fastapi/openapi/__init__.py +0 -0
  201. prefect/_vendor/fastapi/openapi/constants.py +0 -2
  202. prefect/_vendor/fastapi/openapi/docs.py +0 -203
  203. prefect/_vendor/fastapi/openapi/models.py +0 -480
  204. prefect/_vendor/fastapi/openapi/utils.py +0 -485
  205. prefect/_vendor/fastapi/param_functions.py +0 -340
  206. prefect/_vendor/fastapi/params.py +0 -453
  207. prefect/_vendor/fastapi/py.typed +0 -0
  208. prefect/_vendor/fastapi/requests.py +0 -4
  209. prefect/_vendor/fastapi/responses.py +0 -40
  210. prefect/_vendor/fastapi/routing.py +0 -1331
  211. prefect/_vendor/fastapi/security/__init__.py +0 -15
  212. prefect/_vendor/fastapi/security/api_key.py +0 -98
  213. prefect/_vendor/fastapi/security/base.py +0 -6
  214. prefect/_vendor/fastapi/security/http.py +0 -172
  215. prefect/_vendor/fastapi/security/oauth2.py +0 -227
  216. prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
  217. prefect/_vendor/fastapi/security/utils.py +0 -10
  218. prefect/_vendor/fastapi/staticfiles.py +0 -1
  219. prefect/_vendor/fastapi/templating.py +0 -3
  220. prefect/_vendor/fastapi/testclient.py +0 -1
  221. prefect/_vendor/fastapi/types.py +0 -3
  222. prefect/_vendor/fastapi/utils.py +0 -235
  223. prefect/_vendor/fastapi/websockets.py +0 -7
  224. prefect/_vendor/starlette/__init__.py +0 -1
  225. prefect/_vendor/starlette/_compat.py +0 -28
  226. prefect/_vendor/starlette/_exception_handler.py +0 -80
  227. prefect/_vendor/starlette/_utils.py +0 -88
  228. prefect/_vendor/starlette/applications.py +0 -261
  229. prefect/_vendor/starlette/authentication.py +0 -159
  230. prefect/_vendor/starlette/background.py +0 -43
  231. prefect/_vendor/starlette/concurrency.py +0 -59
  232. prefect/_vendor/starlette/config.py +0 -151
  233. prefect/_vendor/starlette/convertors.py +0 -87
  234. prefect/_vendor/starlette/datastructures.py +0 -707
  235. prefect/_vendor/starlette/endpoints.py +0 -130
  236. prefect/_vendor/starlette/exceptions.py +0 -60
  237. prefect/_vendor/starlette/formparsers.py +0 -276
  238. prefect/_vendor/starlette/middleware/__init__.py +0 -17
  239. prefect/_vendor/starlette/middleware/authentication.py +0 -52
  240. prefect/_vendor/starlette/middleware/base.py +0 -220
  241. prefect/_vendor/starlette/middleware/cors.py +0 -176
  242. prefect/_vendor/starlette/middleware/errors.py +0 -265
  243. prefect/_vendor/starlette/middleware/exceptions.py +0 -74
  244. prefect/_vendor/starlette/middleware/gzip.py +0 -113
  245. prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
  246. prefect/_vendor/starlette/middleware/sessions.py +0 -82
  247. prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
  248. prefect/_vendor/starlette/middleware/wsgi.py +0 -147
  249. prefect/_vendor/starlette/py.typed +0 -0
  250. prefect/_vendor/starlette/requests.py +0 -328
  251. prefect/_vendor/starlette/responses.py +0 -347
  252. prefect/_vendor/starlette/routing.py +0 -933
  253. prefect/_vendor/starlette/schemas.py +0 -154
  254. prefect/_vendor/starlette/staticfiles.py +0 -248
  255. prefect/_vendor/starlette/status.py +0 -199
  256. prefect/_vendor/starlette/templating.py +0 -231
  257. prefect/_vendor/starlette/testclient.py +0 -804
  258. prefect/_vendor/starlette/types.py +0 -30
  259. prefect/_vendor/starlette/websockets.py +0 -193
  260. prefect/blocks/kubernetes.py +0 -119
  261. prefect/deprecated/__init__.py +0 -0
  262. prefect/deprecated/data_documents.py +0 -350
  263. prefect/deprecated/packaging/__init__.py +0 -12
  264. prefect/deprecated/packaging/base.py +0 -96
  265. prefect/deprecated/packaging/docker.py +0 -146
  266. prefect/deprecated/packaging/file.py +0 -92
  267. prefect/deprecated/packaging/orion.py +0 -80
  268. prefect/deprecated/packaging/serializers.py +0 -171
  269. prefect/events/instrument.py +0 -135
  270. prefect/infrastructure/container.py +0 -824
  271. prefect/infrastructure/kubernetes.py +0 -920
  272. prefect/infrastructure/process.py +0 -289
  273. prefect/manifests.py +0 -20
  274. prefect/new_flow_engine.py +0 -449
  275. prefect/new_task_engine.py +0 -423
  276. prefect/pydantic/__init__.py +0 -76
  277. prefect/pydantic/main.py +0 -39
  278. prefect/software/__init__.py +0 -2
  279. prefect/software/base.py +0 -50
  280. prefect/software/conda.py +0 -199
  281. prefect/software/pip.py +0 -122
  282. prefect/software/python.py +0 -52
  283. prefect/task_server.py +0 -322
  284. prefect_client-2.20.4.dist-info/RECORD +0 -294
  285. /prefect/{_internal/pydantic/utilities → client/types}/__init__.py +0 -0
  286. /prefect/{_vendor → concurrency/v1}/__init__.py +0 -0
  287. {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
  288. {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/top_level.txt +0 -0
prefect/input/__init__.py CHANGED
@@ -12,6 +12,8 @@ from .run_input import (
12
12
  RunInputMetadata,
13
13
  keyset_from_base_key,
14
14
  keyset_from_paused_state,
15
+ receive_input,
16
+ send_input,
15
17
  )
16
18
 
17
19
  __all__ = [
@@ -26,4 +28,6 @@ __all__ = [
26
28
  "keyset_from_base_key",
27
29
  "keyset_from_paused_state",
28
30
  "read_flow_run_input",
31
+ "receive_input",
32
+ "send_input",
29
33
  ]
prefect/input/actions.py CHANGED
@@ -4,7 +4,6 @@ from uuid import UUID
4
4
  import orjson
5
5
  import pydantic
6
6
 
7
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
8
7
  from prefect.client.utilities import client_injector
9
8
  from prefect.context import FlowRunContext
10
9
  from prefect.exceptions import PrefectHTTPStatusError
@@ -14,8 +13,7 @@ if TYPE_CHECKING:
14
13
  from prefect.client.orchestration import PrefectClient
15
14
 
16
15
 
17
- if HAS_PYDANTIC_V2:
18
- from prefect._internal.pydantic.v2_schema import is_v2_model
16
+ from prefect._internal.pydantic.v2_schema import is_v2_model
19
17
 
20
18
 
21
19
  def ensure_flow_run_id(flow_run_id: Optional[UUID] = None) -> UUID:
@@ -41,7 +39,7 @@ async def create_flow_run_input_from_model(
41
39
  if context is not None and context.flow_run is not None:
42
40
  sender = f"prefect.flow-run.{context.flow_run.id}"
43
41
 
44
- if HAS_PYDANTIC_V2 and is_v2_model(model_instance):
42
+ if is_v2_model(model_instance):
45
43
  json_safe = orjson.loads(model_instance.model_dump_json())
46
44
  else:
47
45
  json_safe = orjson.loads(model_instance.json())
@@ -18,7 +18,8 @@ Sender flow:
18
18
  ```python
19
19
  import random
20
20
  from uuid import UUID
21
- from prefect import flow, get_run_logger
21
+ from prefect import flow
22
+ from prefect.logging import get_run_logger
22
23
  from prefect.input import RunInput
23
24
 
24
25
  class NumberData(RunInput):
@@ -43,7 +44,8 @@ Receiver flow:
43
44
  ```python
44
45
  import random
45
46
  from uuid import UUID
46
- from prefect import flow, get_run_logger
47
+ from prefect import flow
48
+ from prefect.logging import get_run_logger
47
49
  from prefect.input import RunInput
48
50
 
49
51
  class NumberData(RunInput):
@@ -77,8 +79,8 @@ from uuid import UUID, uuid4
77
79
 
78
80
  import anyio
79
81
  import pydantic
82
+ from pydantic import ConfigDict
80
83
 
81
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
82
84
  from prefect.input.actions import (
83
85
  create_flow_run_input,
84
86
  create_flow_run_input_from_model,
@@ -92,8 +94,7 @@ if TYPE_CHECKING:
92
94
  from prefect.client.schemas.objects import FlowRunInput
93
95
  from prefect.states import State
94
96
 
95
- if HAS_PYDANTIC_V2:
96
- from prefect._internal.pydantic.v2_schema import create_v2_schema
97
+ from prefect._internal.pydantic.v2_schema import create_v2_schema, is_v2_model
97
98
 
98
99
  R = TypeVar("R", bound="RunInput")
99
100
  T = TypeVar("T", bound="object")
@@ -138,13 +139,12 @@ def keyset_from_base_key(base_key: str) -> Keyset:
138
139
 
139
140
  class RunInputMetadata(pydantic.BaseModel):
140
141
  key: str
141
- sender: Optional[str]
142
+ sender: Optional[str] = None
142
143
  receiver: UUID
143
144
 
144
145
 
145
146
  class RunInput(pydantic.BaseModel):
146
- class Config:
147
- extra = "forbid"
147
+ model_config = ConfigDict(extra="forbid")
148
148
 
149
149
  _description: Optional[str] = pydantic.PrivateAttr(default=None)
150
150
  _metadata: RunInputMetadata = pydantic.PrivateAttr()
@@ -168,7 +168,7 @@ class RunInput(pydantic.BaseModel):
168
168
  - flow_run_id (UUID, optional): the flow run ID to save the input for
169
169
  """
170
170
 
171
- if HAS_PYDANTIC_V2:
171
+ if is_v2_model(cls):
172
172
  schema = create_v2_schema(cls.__name__, model_base=cls)
173
173
  else:
174
174
  schema = cls.schema(by_alias=True)
@@ -48,7 +48,6 @@ class JsonFormatter(logging.Formatter):
48
48
 
49
49
  self.serializer = JSONSerializer(
50
50
  jsonlib="orjson",
51
- object_encoder="pydantic.json.pydantic_encoder",
52
51
  dumps_kwargs={"option": orjson.OPT_INDENT_2} if fmt == "pretty" else {},
53
52
  )
54
53
 
@@ -79,8 +78,8 @@ class PrefectFormatter(logging.Formatter):
79
78
  validate=True,
80
79
  *,
81
80
  defaults=None,
82
- task_run_fmt: str = None,
83
- flow_run_fmt: str = None,
81
+ task_run_fmt: Optional[str] = None,
82
+ flow_run_fmt: Optional[str] = None,
84
83
  ) -> None:
85
84
  """
86
85
  Implementation of the standard Python formatter with support for multiple
@@ -98,7 +97,6 @@ class PrefectFormatter(logging.Formatter):
98
97
  init_kwargs["defaults"] = defaults
99
98
  style_kwargs["defaults"] = defaults
100
99
 
101
- # validate added in 3.8
102
100
  init_kwargs["validate"] = validate
103
101
 
104
102
  super().__init__(format, datefmt, style, **init_kwargs)
@@ -108,8 +108,8 @@ class APILogHandler(logging.Handler):
108
108
  )
109
109
 
110
110
  # Not ideal, but this method is called by the stdlib and cannot return a
111
- # coroutine so we just schedule the drain in a new thread and continue
112
- from_sync.call_soon_in_new_thread(create_call(APILogWorker.drain_all))
111
+ # coroutine so we just schedule the drain in the global loop thread and continue
112
+ from_sync.call_soon_in_loop_thread(create_call(APILogWorker.drain_all))
113
113
  return None
114
114
  else:
115
115
  # We set a timeout of 5s because we don't want to block forever if the worker
@@ -119,21 +119,13 @@ class APILogHandler(logging.Handler):
119
119
  return APILogWorker.drain_all(timeout=5)
120
120
 
121
121
  @classmethod
122
- def aflush(cls):
122
+ async def aflush(cls):
123
123
  """
124
124
  Tell the `APILogWorker` to send any currently enqueued logs and block until
125
125
  completion.
126
-
127
- If called in a synchronous context, will only block up to 5s before returning.
128
126
  """
129
127
 
130
- if not get_running_loop():
131
- raise RuntimeError(
132
- "`aflush` cannot be used from a synchronous context; use `flush`"
133
- " instead."
134
- )
135
-
136
- return APILogWorker.drain_all()
128
+ return await APILogWorker.drain_all()
137
129
 
138
130
  def emit(self, record: logging.LogRecord):
139
131
  """
@@ -165,7 +157,10 @@ class APILogHandler(logging.Handler):
165
157
  if log_handling_when_missing_flow == "warn":
166
158
  # Warn when a logger is used outside of a run context, the stack level here
167
159
  # gets us to the user logging call
168
- warnings.warn(str(exc), stacklevel=8)
160
+ warnings.warn(
161
+ f"{exc} Set PREFECT_LOGGING_TO_API_WHEN_MISSING_FLOW=ignore to suppress this warning.",
162
+ stacklevel=8,
163
+ )
169
164
  return
170
165
  elif log_handling_when_missing_flow == "ignore":
171
166
  return
@@ -229,7 +224,7 @@ class APILogHandler(logging.Handler):
229
224
  getattr(record, "created", None) or time.time()
230
225
  ),
231
226
  message=self.format(record),
232
- ).dict(json_compatible=True)
227
+ ).model_dump(mode="json")
233
228
 
234
229
  log_size = log["__payload_size__"] = self._get_payload_size(log)
235
230
  if log_size > PREFECT_LOGGING_TO_API_MAX_LOG_SIZE.value():
@@ -5,7 +5,7 @@ import warnings
5
5
  from builtins import print
6
6
  from contextlib import contextmanager
7
7
  from functools import lru_cache
8
- from logging import LogRecord
8
+ from logging import LoggerAdapter, LogRecord
9
9
  from typing import TYPE_CHECKING, Dict, List, Optional, Union
10
10
 
11
11
  from typing_extensions import Self
@@ -69,7 +69,7 @@ class PrefectLogAdapter(logging.LoggerAdapter):
69
69
 
70
70
 
71
71
  @lru_cache()
72
- def get_logger(name: str = None) -> logging.Logger:
72
+ def get_logger(name: Optional[str] = None) -> logging.Logger:
73
73
  """
74
74
  Get a `prefect` logger. These loggers are intended for internal use within the
75
75
  `prefect` package.
@@ -97,7 +97,7 @@ def get_logger(name: str = None) -> logging.Logger:
97
97
 
98
98
 
99
99
  def get_run_logger(
100
- context: "RunContext" = None, **kwargs: str
100
+ context: Optional["RunContext"] = None, **kwargs: str
101
101
  ) -> Union[logging.Logger, logging.LoggerAdapter]:
102
102
  """
103
103
  Get a Prefect logger for the current task run or flow run.
@@ -115,7 +115,7 @@ def get_run_logger(
115
115
  addition to the run metadata
116
116
 
117
117
  Raises:
118
- RuntimeError: If no context can be found
118
+ MissingContextError: If no context can be found
119
119
  """
120
120
  # Check for existing contexts
121
121
  task_run_context = prefect.context.TaskRunContext.get()
@@ -161,7 +161,7 @@ def flow_run_logger(
161
161
  flow_run: Union["FlowRun", "ClientFlowRun"],
162
162
  flow: Optional["Flow"] = None,
163
163
  **kwargs: str,
164
- ):
164
+ ) -> LoggerAdapter:
165
165
  """
166
166
  Create a flow run logger with the run's metadata attached.
167
167
 
prefect/main.py ADDED
@@ -0,0 +1,72 @@
1
+ # Import user-facing API
2
+ from prefect.deployments import deploy
3
+ from prefect.states import State
4
+ from prefect.logging import get_run_logger
5
+ from prefect.flows import flow, Flow, serve
6
+ from prefect.transactions import Transaction
7
+ from prefect.tasks import task, Task
8
+ from prefect.context import tags
9
+ from prefect.utilities.annotations import unmapped, allow_failure
10
+ from prefect.results import BaseResult
11
+ from prefect.flow_runs import pause_flow_run, resume_flow_run, suspend_flow_run
12
+ from prefect.client.orchestration import get_client, PrefectClient
13
+ from prefect.client.cloud import get_cloud_client, CloudClient
14
+ import prefect.variables
15
+ import prefect.runtime
16
+
17
+ # Import modules that register types
18
+ import prefect.serializers
19
+ import prefect.blocks.notifications
20
+ import prefect.blocks.system
21
+
22
+ # Initialize the process-wide profile and registry at import time
23
+ import prefect.context
24
+
25
+ # Perform any forward-ref updates needed for Pydantic models
26
+ import prefect.client.schemas
27
+
28
+ prefect.context.FlowRunContext.model_rebuild(
29
+ _types_namespace={"Flow": Flow, "BaseResult": BaseResult}
30
+ )
31
+ prefect.context.TaskRunContext.model_rebuild(_types_namespace={"Task": Task})
32
+ prefect.client.schemas.State.model_rebuild(_types_namespace={"BaseResult": BaseResult})
33
+ prefect.client.schemas.StateCreate.model_rebuild(
34
+ _types_namespace={"BaseResult": BaseResult}
35
+ )
36
+ Transaction.model_rebuild()
37
+
38
+ # Configure logging
39
+ import prefect.logging.configuration
40
+
41
+ prefect.logging.configuration.setup_logging()
42
+ prefect.logging.get_logger("profiles").debug(
43
+ f"Using profile {prefect.context.get_settings_context().profile.name!r}"
44
+ )
45
+
46
+
47
+ from prefect._internal.compatibility.deprecated import (
48
+ inject_renamed_module_alias_finder,
49
+ )
50
+
51
+ inject_renamed_module_alias_finder()
52
+
53
+
54
+ # Declare API for type-checkers
55
+ __all__ = [
56
+ "allow_failure",
57
+ "flow",
58
+ "Flow",
59
+ "get_client",
60
+ "get_run_logger",
61
+ "State",
62
+ "tags",
63
+ "task",
64
+ "Task",
65
+ "Transaction",
66
+ "unmapped",
67
+ "serve",
68
+ "deploy",
69
+ "pause_flow_run",
70
+ "resume_flow_run",
71
+ "suspend_flow_run",
72
+ ]
prefect/plugins.py CHANGED
@@ -7,12 +7,12 @@ Currently supported entrypoints:
7
7
  - prefect.collections: Identifies this package as a Prefect collection that
8
8
  should be imported when Prefect is imported.
9
9
  """
10
- import sys
10
+
11
11
  from types import ModuleType
12
12
  from typing import Any, Dict, Union
13
13
 
14
14
  import prefect.settings
15
- from prefect.utilities.compat import EntryPoint, EntryPoints, entry_points
15
+ from prefect.utilities.compat import EntryPoints, entry_points
16
16
 
17
17
 
18
18
  def safe_load_entrypoints(entrypoints: EntryPoints) -> Dict[str, Union[Exception, Any]]:
@@ -38,68 +38,6 @@ def safe_load_entrypoints(entrypoints: EntryPoints) -> Dict[str, Union[Exception
38
38
  return results
39
39
 
40
40
 
41
- def load_extra_entrypoints() -> Dict[str, Union[Exception, Any]]:
42
- # Note: Return values are only exposed for testing.
43
- results = {}
44
-
45
- if not prefect.settings.PREFECT_EXTRA_ENTRYPOINTS.value():
46
- return results
47
-
48
- values = {
49
- value.strip()
50
- for value in prefect.settings.PREFECT_EXTRA_ENTRYPOINTS.value().split(",")
51
- }
52
-
53
- entrypoints = []
54
- for value in values:
55
- try:
56
- entrypoint = EntryPoint(name=None, value=value, group="prefect-extra")
57
- except Exception as exc:
58
- print(
59
- (
60
- f"Warning! Failed to parse extra entrypoint {value!r}:"
61
- f" {type(exc).__name__}: {exc}"
62
- ),
63
- file=sys.stderr,
64
- )
65
- results[value] = exc
66
- else:
67
- entrypoints.append(entrypoint)
68
-
69
- for value, result in zip(
70
- values, safe_load_entrypoints(EntryPoints(entrypoints)).values()
71
- ):
72
- results[value] = result
73
-
74
- if isinstance(result, Exception):
75
- print(
76
- (
77
- f"Warning! Failed to load extra entrypoint {value!r}:"
78
- f" {type(result).__name__}: {result}"
79
- ),
80
- file=sys.stderr,
81
- )
82
- elif callable(result):
83
- try:
84
- results[value] = result()
85
- except Exception as exc:
86
- print(
87
- (
88
- f"Warning! Failed to run callable entrypoint {value!r}:"
89
- f" {type(exc).__name__}: {exc}"
90
- ),
91
- file=sys.stderr,
92
- )
93
- results[value] = exc
94
- else:
95
- if prefect.settings.PREFECT_DEBUG_MODE:
96
- print(
97
- "Loaded extra entrypoint {value!r} successfully.", file=sys.stderr
98
- )
99
-
100
- return results
101
-
102
-
103
41
  def load_prefect_collections() -> Dict[str, ModuleType]:
104
42
  """
105
43
  Load all Prefect collections that define an entrypoint in the group
prefect/profiles.toml CHANGED
@@ -1,3 +1,17 @@
1
- active = "default"
1
+ # This is a template for profile configuration for Prefect.
2
+ # You can modify these profiles or create new ones to suit your needs.
2
3
 
3
- [profiles.default]
4
+ active = "ephemeral"
5
+
6
+ [profiles.ephemeral]
7
+ PREFECT_SERVER_ALLOW_EPHEMERAL_MODE = "true"
8
+
9
+ [profiles.local]
10
+ # You will need to set these values appropriately for your local development environment
11
+ PREFECT_API_URL = "http://127.0.0.1:4200/api"
12
+
13
+ [profiles.test]
14
+ PREFECT_SERVER_ALLOW_EPHEMERAL_MODE = "true"
15
+ PREFECT_API_DATABASE_CONNECTION_URL = "sqlite+aiosqlite:///:memory:"
16
+
17
+ [profiles.cloud]
@@ -0,0 +1 @@
1
+ from .base import RecordStore
@@ -0,0 +1,223 @@
1
+ import abc
2
+ import os
3
+ import socket
4
+ import threading
5
+ from contextlib import contextmanager
6
+ from dataclasses import dataclass
7
+ from typing import TYPE_CHECKING, Optional
8
+
9
+ if TYPE_CHECKING:
10
+ from prefect.results import BaseResult
11
+ from prefect.transactions import IsolationLevel
12
+
13
+
14
+ @dataclass
15
+ class TransactionRecord:
16
+ """
17
+ A dataclass representation of a transaction record.
18
+ """
19
+
20
+ key: str
21
+ result: "BaseResult"
22
+
23
+
24
+ class RecordStore(abc.ABC):
25
+ @abc.abstractmethod
26
+ def read(
27
+ self, key: str, holder: Optional[str] = None
28
+ ) -> Optional[TransactionRecord]:
29
+ """
30
+ Read the transaction record with the given key.
31
+
32
+ Args:
33
+ key: Unique identifier for the transaction record.
34
+ holder: Unique identifier for the holder of the lock. If a lock exists on
35
+ the record being written, the read will be blocked until the lock is
36
+ released if the provided holder does not match the holder of the lock.
37
+ If not provided, a default holder based on the current host, process,
38
+ and thread will be used.
39
+
40
+ Returns:
41
+ TransactionRecord: The transaction record with the given key.
42
+ """
43
+ ...
44
+
45
+ @abc.abstractmethod
46
+ def write(self, key: str, result: "BaseResult", holder: Optional[str] = None):
47
+ """
48
+ Write the transaction record with the given key.
49
+
50
+ Args:
51
+ key: Unique identifier for the transaction record.
52
+ record: The transaction record to write.
53
+ holder: Unique identifier for the holder of the lock. If a lock exists on
54
+ the record being written, the write will be rejected if the provided
55
+ holder does not match the holder of the lock. If not provided,
56
+ a default holder based on the current host, process, and thread will
57
+ be used.
58
+ """
59
+ ...
60
+
61
+ @abc.abstractmethod
62
+ def exists(self, key: str) -> bool:
63
+ """
64
+ Check if the transaction record with the given key exists.
65
+
66
+ Args:
67
+ key: Unique identifier for the transaction record.
68
+
69
+ Returns:
70
+ bool: True if the record exists; False otherwise.
71
+ """
72
+ ...
73
+
74
+ @abc.abstractmethod
75
+ def supports_isolation_level(self, isolation_level: "IsolationLevel") -> bool:
76
+ """
77
+ Check if the record store supports the given isolation level.
78
+
79
+ Args:
80
+ isolation_level: The isolation level to check.
81
+
82
+ Returns:
83
+ bool: True if the record store supports the isolation level; False otherwise.
84
+ """
85
+ ...
86
+
87
+ def acquire_lock(
88
+ self,
89
+ key: str,
90
+ holder: Optional[str] = None,
91
+ acquire_timeout: Optional[float] = None,
92
+ hold_timeout: Optional[float] = None,
93
+ ) -> bool:
94
+ """
95
+ Acquire a lock for a transaction record with the given key. Will block other
96
+ actors from updating this transaction record until the lock is
97
+ released.
98
+
99
+ Args:
100
+ key: Unique identifier for the transaction record.
101
+ holder: Unique identifier for the holder of the lock. If not provided,
102
+ a default holder based on the current host, process, and thread will
103
+ be used.
104
+ acquire_timeout: Max number of seconds to wait for the record to become
105
+ available if it is locked while attempting to acquire a lock. Pass 0
106
+ to attempt to acquire a lock without waiting. Blocks indefinitely by
107
+ default.
108
+ hold_timeout: Max number of seconds to hold the lock for. Holds the lock
109
+ indefinitely by default.
110
+
111
+ Returns:
112
+ bool: True if the lock was successfully acquired; False otherwise.
113
+ """
114
+ raise NotImplementedError
115
+
116
+ def release_lock(self, key: str, holder: Optional[str] = None):
117
+ """
118
+ Releases the lock on the corresponding transaction record.
119
+
120
+ Args:
121
+ key: Unique identifier for the transaction record.
122
+ holder: Unique identifier for the holder of the lock. Must match the
123
+ holder provided when acquiring the lock.
124
+ """
125
+ raise NotImplementedError
126
+
127
+ def is_locked(self, key: str) -> bool:
128
+ """
129
+ Simple check to see if the corresponding record is currently locked.
130
+
131
+ Args:
132
+ key: Unique identifier for the transaction record.
133
+
134
+ Returns:
135
+ True is the record is locked; False otherwise.
136
+ """
137
+ raise NotImplementedError
138
+
139
+ def is_lock_holder(self, key: str, holder: Optional[str] = None) -> bool:
140
+ """
141
+ Check if the current holder is the lock holder for the transaction record.
142
+
143
+ Args:
144
+ key: Unique identifier for the transaction record.
145
+ holder: Unique identifier for the holder of the lock. If not provided,
146
+ a default holder based on the current host, process, and thread will
147
+ be used.
148
+
149
+ Returns:
150
+ bool: True if the current holder is the lock holder; False otherwise.
151
+ """
152
+ raise NotImplementedError
153
+
154
+ def wait_for_lock(self, key: str, timeout: Optional[float] = None) -> bool:
155
+ """
156
+ Wait for the corresponding transaction record to become free.
157
+
158
+ Args:
159
+ key: Unique identifier for the transaction record.
160
+ timeout: Maximum time to wait. None means to wait indefinitely.
161
+
162
+ Returns:
163
+ bool: True if the lock becomes free within the timeout; False
164
+ otherwise.
165
+ """
166
+ ...
167
+
168
+ @staticmethod
169
+ def generate_default_holder() -> str:
170
+ """
171
+ Generate a default holder string using hostname, PID, and thread ID.
172
+
173
+ Returns:
174
+ str: A unique identifier string.
175
+ """
176
+ hostname = socket.gethostname()
177
+ pid = os.getpid()
178
+ thread_name = threading.current_thread().name
179
+ thread_id = threading.get_ident()
180
+ return f"{hostname}:{pid}:{thread_id}:{thread_name}"
181
+
182
+ @contextmanager
183
+ def lock(
184
+ self,
185
+ key: str,
186
+ holder: Optional[str] = None,
187
+ acquire_timeout: Optional[float] = None,
188
+ hold_timeout: Optional[float] = None,
189
+ ):
190
+ """
191
+ Context manager to lock the transaction record during the execution
192
+ of the nested code block.
193
+
194
+ Args:
195
+ key: Unique identifier for the transaction record.
196
+ holder: Unique identifier for the holder of the lock. If not provided,
197
+ a default holder based on the current host, process, and thread will
198
+ be used.
199
+ acquire_timeout: Max number of seconds to wait for the record to become
200
+ available if it is locked while attempting to acquire a lock. Pass 0
201
+ to attempt to acquire a lock without waiting. Blocks indefinitely by
202
+ default.
203
+ hold_timeout: Max number of seconds to hold the lock for. Holds the lock
204
+ indefinitely by default.
205
+
206
+ Example:
207
+ Hold a lock while during an operation:
208
+ ```python
209
+ with TransactionRecord(key="my-transaction-record-key").lock():
210
+ do_stuff()
211
+ ```
212
+ """
213
+ self.acquire_lock(
214
+ key=key,
215
+ holder=holder,
216
+ acquire_timeout=acquire_timeout,
217
+ hold_timeout=hold_timeout,
218
+ )
219
+
220
+ try:
221
+ yield
222
+ finally:
223
+ self.release_lock(key=key, holder=holder)