prefect-client 3.1.9__py3-none-any.whl → 3.1.11__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 (113) hide show
  1. prefect/_experimental/lineage.py +7 -8
  2. prefect/_internal/_logging.py +15 -3
  3. prefect/_internal/compatibility/async_dispatch.py +22 -16
  4. prefect/_internal/compatibility/deprecated.py +42 -18
  5. prefect/_internal/compatibility/migration.py +2 -2
  6. prefect/_internal/concurrency/inspection.py +12 -14
  7. prefect/_internal/concurrency/primitives.py +2 -2
  8. prefect/_internal/concurrency/services.py +154 -80
  9. prefect/_internal/concurrency/waiters.py +13 -9
  10. prefect/_internal/pydantic/annotations/pendulum.py +7 -7
  11. prefect/_internal/pytz.py +4 -3
  12. prefect/_internal/retries.py +10 -5
  13. prefect/_internal/schemas/bases.py +19 -10
  14. prefect/_internal/schemas/validators.py +227 -388
  15. prefect/_version.py +3 -3
  16. prefect/artifacts.py +61 -74
  17. prefect/automations.py +27 -7
  18. prefect/blocks/core.py +3 -3
  19. prefect/client/{orchestration.py → orchestration/__init__.py} +38 -701
  20. prefect/client/orchestration/_artifacts/__init__.py +0 -0
  21. prefect/client/orchestration/_artifacts/client.py +239 -0
  22. prefect/client/orchestration/_concurrency_limits/__init__.py +0 -0
  23. prefect/client/orchestration/_concurrency_limits/client.py +762 -0
  24. prefect/client/orchestration/_logs/__init__.py +0 -0
  25. prefect/client/orchestration/_logs/client.py +95 -0
  26. prefect/client/orchestration/_variables/__init__.py +0 -0
  27. prefect/client/orchestration/_variables/client.py +157 -0
  28. prefect/client/orchestration/base.py +46 -0
  29. prefect/client/orchestration/routes.py +145 -0
  30. prefect/client/schemas/actions.py +2 -2
  31. prefect/client/schemas/filters.py +5 -0
  32. prefect/client/schemas/objects.py +3 -10
  33. prefect/client/schemas/schedules.py +22 -10
  34. prefect/concurrency/_asyncio.py +87 -0
  35. prefect/concurrency/{events.py → _events.py} +10 -10
  36. prefect/concurrency/asyncio.py +20 -104
  37. prefect/concurrency/context.py +6 -4
  38. prefect/concurrency/services.py +26 -74
  39. prefect/concurrency/sync.py +23 -44
  40. prefect/concurrency/v1/_asyncio.py +63 -0
  41. prefect/concurrency/v1/{events.py → _events.py} +13 -15
  42. prefect/concurrency/v1/asyncio.py +27 -80
  43. prefect/concurrency/v1/context.py +6 -4
  44. prefect/concurrency/v1/services.py +33 -79
  45. prefect/concurrency/v1/sync.py +18 -37
  46. prefect/context.py +66 -70
  47. prefect/deployments/base.py +4 -144
  48. prefect/deployments/flow_runs.py +12 -2
  49. prefect/deployments/runner.py +11 -3
  50. prefect/deployments/steps/pull.py +13 -0
  51. prefect/events/clients.py +7 -1
  52. prefect/events/schemas/events.py +3 -2
  53. prefect/flow_engine.py +54 -47
  54. prefect/flows.py +2 -1
  55. prefect/futures.py +42 -27
  56. prefect/input/run_input.py +2 -1
  57. prefect/locking/filesystem.py +8 -7
  58. prefect/locking/memory.py +5 -3
  59. prefect/locking/protocol.py +1 -1
  60. prefect/main.py +1 -3
  61. prefect/plugins.py +12 -10
  62. prefect/results.py +3 -308
  63. prefect/runner/storage.py +87 -21
  64. prefect/serializers.py +32 -25
  65. prefect/settings/legacy.py +4 -4
  66. prefect/settings/models/api.py +3 -3
  67. prefect/settings/models/cli.py +3 -3
  68. prefect/settings/models/client.py +5 -3
  69. prefect/settings/models/cloud.py +3 -3
  70. prefect/settings/models/deployments.py +3 -3
  71. prefect/settings/models/experiments.py +4 -2
  72. prefect/settings/models/flows.py +3 -3
  73. prefect/settings/models/internal.py +4 -2
  74. prefect/settings/models/logging.py +4 -3
  75. prefect/settings/models/results.py +3 -3
  76. prefect/settings/models/root.py +3 -2
  77. prefect/settings/models/runner.py +4 -4
  78. prefect/settings/models/server/api.py +3 -3
  79. prefect/settings/models/server/database.py +11 -4
  80. prefect/settings/models/server/deployments.py +6 -2
  81. prefect/settings/models/server/ephemeral.py +4 -2
  82. prefect/settings/models/server/events.py +3 -2
  83. prefect/settings/models/server/flow_run_graph.py +6 -2
  84. prefect/settings/models/server/root.py +3 -3
  85. prefect/settings/models/server/services.py +26 -11
  86. prefect/settings/models/server/tasks.py +6 -3
  87. prefect/settings/models/server/ui.py +3 -3
  88. prefect/settings/models/tasks.py +5 -5
  89. prefect/settings/models/testing.py +3 -3
  90. prefect/settings/models/worker.py +5 -3
  91. prefect/settings/profiles.py +15 -2
  92. prefect/states.py +4 -7
  93. prefect/task_engine.py +54 -75
  94. prefect/tasks.py +84 -32
  95. prefect/telemetry/processors.py +6 -6
  96. prefect/telemetry/run_telemetry.py +13 -8
  97. prefect/telemetry/services.py +32 -31
  98. prefect/transactions.py +4 -15
  99. prefect/utilities/_git.py +34 -0
  100. prefect/utilities/asyncutils.py +1 -1
  101. prefect/utilities/engine.py +3 -19
  102. prefect/utilities/generics.py +18 -0
  103. prefect/workers/__init__.py +2 -0
  104. {prefect_client-3.1.9.dist-info → prefect_client-3.1.11.dist-info}/METADATA +1 -1
  105. {prefect_client-3.1.9.dist-info → prefect_client-3.1.11.dist-info}/RECORD +108 -99
  106. prefect/records/__init__.py +0 -1
  107. prefect/records/base.py +0 -235
  108. prefect/records/filesystem.py +0 -213
  109. prefect/records/memory.py +0 -184
  110. prefect/records/result_store.py +0 -70
  111. {prefect_client-3.1.9.dist-info → prefect_client-3.1.11.dist-info}/LICENSE +0 -0
  112. {prefect_client-3.1.9.dist-info → prefect_client-3.1.11.dist-info}/WHEEL +0 -0
  113. {prefect_client-3.1.9.dist-info → prefect_client-3.1.11.dist-info}/top_level.txt +0 -0
@@ -19,6 +19,27 @@ from packaging import version
19
19
  from starlette import status
20
20
  from typing_extensions import ParamSpec, Self, TypeVar
21
21
 
22
+ from prefect.client.orchestration._artifacts.client import (
23
+ ArtifactClient,
24
+ ArtifactAsyncClient,
25
+ ArtifactCollectionClient,
26
+ ArtifactCollectionAsyncClient,
27
+ )
28
+
29
+ from prefect.client.orchestration._concurrency_limits.client import (
30
+ ConcurrencyLimitAsyncClient,
31
+ ConcurrencyLimitClient,
32
+ )
33
+
34
+ from prefect.client.orchestration._logs.client import (
35
+ LogClient,
36
+ LogAsyncClient,
37
+ )
38
+ from prefect.client.orchestration._variables.client import (
39
+ VariableClient,
40
+ VariableAsyncClient,
41
+ )
42
+
22
43
  import prefect
23
44
  import prefect.exceptions
24
45
  import prefect.settings
@@ -26,14 +47,11 @@ import prefect.states
26
47
  from prefect.client.constants import SERVER_API_VERSION
27
48
  from prefect.client.schemas import FlowRun, OrchestrationResult, TaskRun, sorting
28
49
  from prefect.client.schemas.actions import (
29
- ArtifactCreate,
30
- ArtifactUpdate,
31
50
  BlockDocumentCreate,
32
51
  BlockDocumentUpdate,
33
52
  BlockSchemaCreate,
34
53
  BlockTypeCreate,
35
54
  BlockTypeUpdate,
36
- ConcurrencyLimitCreate,
37
55
  DeploymentCreate,
38
56
  DeploymentFlowRunCreate,
39
57
  DeploymentScheduleCreate,
@@ -44,26 +62,21 @@ from prefect.client.schemas.actions import (
44
62
  FlowRunNotificationPolicyCreate,
45
63
  FlowRunNotificationPolicyUpdate,
46
64
  FlowRunUpdate,
65
+ LogCreate,
47
66
  GlobalConcurrencyLimitCreate,
48
67
  GlobalConcurrencyLimitUpdate,
49
- LogCreate,
50
68
  TaskRunCreate,
51
69
  TaskRunUpdate,
52
- VariableCreate,
53
- VariableUpdate,
54
70
  WorkPoolCreate,
55
71
  WorkPoolUpdate,
56
72
  WorkQueueCreate,
57
73
  WorkQueueUpdate,
58
74
  )
59
75
  from prefect.client.schemas.filters import (
60
- ArtifactCollectionFilter,
61
- ArtifactFilter,
62
76
  DeploymentFilter,
63
77
  FlowFilter,
64
78
  FlowRunFilter,
65
79
  FlowRunNotificationPolicyFilter,
66
- LogFilter,
67
80
  TaskRunFilter,
68
81
  WorkerFilter,
69
82
  WorkPoolFilter,
@@ -71,12 +84,9 @@ from prefect.client.schemas.filters import (
71
84
  WorkQueueFilterName,
72
85
  )
73
86
  from prefect.client.schemas.objects import (
74
- Artifact,
75
- ArtifactCollection,
76
87
  BlockDocument,
77
88
  BlockSchema,
78
89
  BlockType,
79
- ConcurrencyLimit,
80
90
  ConcurrencyOptions,
81
91
  Constant,
82
92
  DeploymentSchedule,
@@ -84,11 +94,9 @@ from prefect.client.schemas.objects import (
84
94
  FlowRunInput,
85
95
  FlowRunNotificationPolicy,
86
96
  FlowRunPolicy,
87
- Log,
88
97
  Parameter,
89
98
  TaskRunPolicy,
90
99
  TaskRunResult,
91
- Variable,
92
100
  Worker,
93
101
  WorkerMetadata,
94
102
  WorkPool,
@@ -98,17 +106,13 @@ from prefect.client.schemas.objects import (
98
106
  from prefect.client.schemas.responses import (
99
107
  DeploymentResponse,
100
108
  FlowRunResponse,
101
- GlobalConcurrencyLimitResponse,
102
109
  WorkerFlowRunResponse,
103
110
  )
104
111
  from prefect.client.schemas.schedules import SCHEDULE_TYPES
105
112
  from prefect.client.schemas.sorting import (
106
- ArtifactCollectionSort,
107
- ArtifactSort,
108
113
  DeploymentSort,
109
114
  FlowRunSort,
110
115
  FlowSort,
111
- LogSort,
112
116
  TaskRunSort,
113
117
  )
114
118
  from prefect.events import filters
@@ -245,7 +249,13 @@ def get_client(
245
249
  )
246
250
 
247
251
 
248
- class PrefectClient:
252
+ class PrefectClient(
253
+ ArtifactAsyncClient,
254
+ ArtifactCollectionAsyncClient,
255
+ LogAsyncClient,
256
+ VariableAsyncClient,
257
+ ConcurrencyLimitAsyncClient,
258
+ ):
249
259
  """
250
260
  An asynchronous client for interacting with the [Prefect REST API](/api-ref/rest-api/).
251
261
 
@@ -615,6 +625,7 @@ class PrefectClient:
615
625
  parent_task_run_id: Optional[UUID] = None,
616
626
  work_queue_name: Optional[str] = None,
617
627
  job_variables: Optional[dict[str, Any]] = None,
628
+ labels: Optional[KeyValueLabelsField] = None,
618
629
  ) -> FlowRun:
619
630
  """
620
631
  Create a flow run for a deployment.
@@ -660,6 +671,7 @@ class PrefectClient:
660
671
  idempotency_key=idempotency_key,
661
672
  parent_task_run_id=parent_task_run_id,
662
673
  job_variables=job_variables,
674
+ labels=labels,
663
675
  )
664
676
 
665
677
  # done separately to avoid including this field in payloads sent to older API versions
@@ -810,213 +822,6 @@ class PrefectClient:
810
822
  else:
811
823
  raise
812
824
 
813
- async def create_concurrency_limit(
814
- self,
815
- tag: str,
816
- concurrency_limit: int,
817
- ) -> UUID:
818
- """
819
- Create a tag concurrency limit in the Prefect API. These limits govern concurrently
820
- running tasks.
821
-
822
- Args:
823
- tag: a tag the concurrency limit is applied to
824
- concurrency_limit: the maximum number of concurrent task runs for a given tag
825
-
826
- Raises:
827
- httpx.RequestError: if the concurrency limit was not created for any reason
828
-
829
- Returns:
830
- the ID of the concurrency limit in the backend
831
- """
832
-
833
- concurrency_limit_create = ConcurrencyLimitCreate(
834
- tag=tag,
835
- concurrency_limit=concurrency_limit,
836
- )
837
- response = await self._client.post(
838
- "/concurrency_limits/",
839
- json=concurrency_limit_create.model_dump(mode="json"),
840
- )
841
-
842
- concurrency_limit_id = response.json().get("id")
843
-
844
- if not concurrency_limit_id:
845
- raise httpx.RequestError(f"Malformed response: {response}")
846
-
847
- return UUID(concurrency_limit_id)
848
-
849
- async def read_concurrency_limit_by_tag(
850
- self,
851
- tag: str,
852
- ) -> ConcurrencyLimit:
853
- """
854
- Read the concurrency limit set on a specific tag.
855
-
856
- Args:
857
- tag: a tag the concurrency limit is applied to
858
-
859
- Raises:
860
- prefect.exceptions.ObjectNotFound: If request returns 404
861
- httpx.RequestError: if the concurrency limit was not created for any reason
862
-
863
- Returns:
864
- the concurrency limit set on a specific tag
865
- """
866
- try:
867
- response = await self._client.get(
868
- f"/concurrency_limits/tag/{tag}",
869
- )
870
- except httpx.HTTPStatusError as e:
871
- if e.response.status_code == status.HTTP_404_NOT_FOUND:
872
- raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
873
- else:
874
- raise
875
-
876
- concurrency_limit_id = response.json().get("id")
877
-
878
- if not concurrency_limit_id:
879
- raise httpx.RequestError(f"Malformed response: {response}")
880
-
881
- concurrency_limit = ConcurrencyLimit.model_validate(response.json())
882
- return concurrency_limit
883
-
884
- async def read_concurrency_limits(
885
- self,
886
- limit: int,
887
- offset: int,
888
- ) -> list[ConcurrencyLimit]:
889
- """
890
- Lists concurrency limits set on task run tags.
891
-
892
- Args:
893
- limit: the maximum number of concurrency limits returned
894
- offset: the concurrency limit query offset
895
-
896
- Returns:
897
- a list of concurrency limits
898
- """
899
-
900
- body = {
901
- "limit": limit,
902
- "offset": offset,
903
- }
904
-
905
- response = await self._client.post("/concurrency_limits/filter", json=body)
906
- return pydantic.TypeAdapter(list[ConcurrencyLimit]).validate_python(
907
- response.json()
908
- )
909
-
910
- async def reset_concurrency_limit_by_tag(
911
- self,
912
- tag: str,
913
- slot_override: Optional[list[Union[UUID, str]]] = None,
914
- ) -> None:
915
- """
916
- Resets the concurrency limit slots set on a specific tag.
917
-
918
- Args:
919
- tag: a tag the concurrency limit is applied to
920
- slot_override: a list of task run IDs that are currently using a
921
- concurrency slot, please check that any task run IDs included in
922
- `slot_override` are currently running, otherwise those concurrency
923
- slots will never be released.
924
-
925
- Raises:
926
- prefect.exceptions.ObjectNotFound: If request returns 404
927
- httpx.RequestError: If request fails
928
-
929
- """
930
- if slot_override is not None:
931
- slot_override = [str(slot) for slot in slot_override]
932
-
933
- try:
934
- await self._client.post(
935
- f"/concurrency_limits/tag/{tag}/reset",
936
- json=dict(slot_override=slot_override),
937
- )
938
- except httpx.HTTPStatusError as e:
939
- if e.response.status_code == status.HTTP_404_NOT_FOUND:
940
- raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
941
- else:
942
- raise
943
-
944
- async def delete_concurrency_limit_by_tag(
945
- self,
946
- tag: str,
947
- ) -> None:
948
- """
949
- Delete the concurrency limit set on a specific tag.
950
-
951
- Args:
952
- tag: a tag the concurrency limit is applied to
953
-
954
- Raises:
955
- prefect.exceptions.ObjectNotFound: If request returns 404
956
- httpx.RequestError: If request fails
957
-
958
- """
959
- try:
960
- await self._client.delete(
961
- f"/concurrency_limits/tag/{tag}",
962
- )
963
- except httpx.HTTPStatusError as e:
964
- if e.response.status_code == status.HTTP_404_NOT_FOUND:
965
- raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
966
- else:
967
- raise
968
-
969
- async def increment_v1_concurrency_slots(
970
- self,
971
- names: list[str],
972
- task_run_id: UUID,
973
- ) -> httpx.Response:
974
- """
975
- Increment concurrency limit slots for the specified limits.
976
-
977
- Args:
978
- names (List[str]): A list of limit names for which to increment limits.
979
- task_run_id (UUID): The task run ID incrementing the limits.
980
- """
981
- data: dict[str, Any] = {
982
- "names": names,
983
- "task_run_id": str(task_run_id),
984
- }
985
-
986
- return await self._client.post(
987
- "/concurrency_limits/increment",
988
- json=data,
989
- )
990
-
991
- async def decrement_v1_concurrency_slots(
992
- self,
993
- names: list[str],
994
- task_run_id: UUID,
995
- occupancy_seconds: float,
996
- ) -> httpx.Response:
997
- """
998
- Decrement concurrency limit slots for the specified limits.
999
-
1000
- Args:
1001
- names (List[str]): A list of limit names to decrement.
1002
- task_run_id (UUID): The task run ID that incremented the limits.
1003
- occupancy_seconds (float): The duration in seconds that the limits
1004
- were held.
1005
-
1006
- Returns:
1007
- httpx.Response: The HTTP response from the server.
1008
- """
1009
- data: dict[str, Any] = {
1010
- "names": names,
1011
- "task_run_id": str(task_run_id),
1012
- "occupancy_seconds": occupancy_seconds,
1013
- }
1014
-
1015
- return await self._client.post(
1016
- "/concurrency_limits/decrement",
1017
- json=data,
1018
- )
1019
-
1020
825
  async def create_work_queue(
1021
826
  self,
1022
827
  name: str,
@@ -2463,21 +2268,6 @@ class PrefectClient:
2463
2268
  response.json()
2464
2269
  )
2465
2270
 
2466
- async def create_logs(
2467
- self, logs: Iterable[Union[LogCreate, dict[str, Any]]]
2468
- ) -> None:
2469
- """
2470
- Create logs for a flow or task run
2471
-
2472
- Args:
2473
- logs: An iterable of `LogCreate` objects or already json-compatible dicts
2474
- """
2475
- serialized_logs = [
2476
- log.model_dump(mode="json") if isinstance(log, LogCreate) else log
2477
- for log in logs
2478
- ]
2479
- await self._client.post("/logs/", json=serialized_logs)
2480
-
2481
2271
  async def create_flow_run_notification_policy(
2482
2272
  self,
2483
2273
  block_document_id: UUID,
@@ -2623,26 +2413,6 @@ class PrefectClient:
2623
2413
  response.json()
2624
2414
  )
2625
2415
 
2626
- async def read_logs(
2627
- self,
2628
- log_filter: Optional[LogFilter] = None,
2629
- limit: Optional[int] = None,
2630
- offset: Optional[int] = None,
2631
- sort: LogSort = LogSort.TIMESTAMP_ASC,
2632
- ) -> list[Log]:
2633
- """
2634
- Read flow and task run logs.
2635
- """
2636
- body: dict[str, Any] = {
2637
- "logs": log_filter.model_dump(mode="json") if log_filter else None,
2638
- "limit": limit,
2639
- "offset": offset,
2640
- "sort": sort,
2641
- }
2642
-
2643
- response = await self._client.post("/logs/filter", json=body)
2644
- return pydantic.TypeAdapter(list[Log]).validate_python(response.json())
2645
-
2646
2416
  async def send_worker_heartbeat(
2647
2417
  self,
2648
2418
  work_pool_name: str,
@@ -2952,333 +2722,12 @@ class PrefectClient:
2952
2722
  response.json()
2953
2723
  )
2954
2724
 
2955
- async def create_artifact(
2956
- self,
2957
- artifact: ArtifactCreate,
2958
- ) -> Artifact:
2959
- """
2960
- Creates an artifact with the provided configuration.
2961
-
2962
- Args:
2963
- artifact: Desired configuration for the new artifact.
2964
- Returns:
2965
- Information about the newly created artifact.
2966
- """
2967
-
2968
- response = await self._client.post(
2969
- "/artifacts/",
2970
- json=artifact.model_dump(mode="json", exclude_unset=True),
2971
- )
2972
-
2973
- return Artifact.model_validate(response.json())
2974
-
2975
- async def update_artifact(
2976
- self,
2977
- artifact_id: UUID,
2978
- artifact: ArtifactUpdate,
2979
- ) -> None:
2980
- """
2981
- Updates an artifact
2982
-
2983
- Args:
2984
- artifact: Desired values for the updated artifact.
2985
- Returns:
2986
- Information about the updated artifact.
2987
- """
2988
-
2989
- await self._client.patch(
2990
- f"/artifacts/{artifact_id}",
2991
- json=artifact.model_dump(mode="json", exclude_unset=True),
2992
- )
2993
-
2994
- async def read_artifacts(
2995
- self,
2996
- *,
2997
- artifact_filter: Optional[ArtifactFilter] = None,
2998
- flow_run_filter: Optional[FlowRunFilter] = None,
2999
- task_run_filter: Optional[TaskRunFilter] = None,
3000
- sort: Optional[ArtifactSort] = None,
3001
- limit: Optional[int] = None,
3002
- offset: int = 0,
3003
- ) -> list[Artifact]:
3004
- """
3005
- Query the Prefect API for artifacts. Only artifacts matching all criteria will
3006
- be returned.
3007
- Args:
3008
- artifact_filter: filter criteria for artifacts
3009
- flow_run_filter: filter criteria for flow runs
3010
- task_run_filter: filter criteria for task runs
3011
- sort: sort criteria for the artifacts
3012
- limit: limit for the artifact query
3013
- offset: offset for the artifact query
3014
- Returns:
3015
- a list of Artifact model representations of the artifacts
3016
- """
3017
- body: dict[str, Any] = {
3018
- "artifacts": (
3019
- artifact_filter.model_dump(mode="json") if artifact_filter else None
3020
- ),
3021
- "flow_runs": (
3022
- flow_run_filter.model_dump(mode="json") if flow_run_filter else None
3023
- ),
3024
- "task_runs": (
3025
- task_run_filter.model_dump(mode="json") if task_run_filter else None
3026
- ),
3027
- "sort": sort,
3028
- "limit": limit,
3029
- "offset": offset,
3030
- }
3031
- response = await self._client.post("/artifacts/filter", json=body)
3032
- return pydantic.TypeAdapter(list[Artifact]).validate_python(response.json())
3033
-
3034
- async def read_latest_artifacts(
3035
- self,
3036
- *,
3037
- artifact_filter: Optional[ArtifactCollectionFilter] = None,
3038
- flow_run_filter: Optional[FlowRunFilter] = None,
3039
- task_run_filter: Optional[TaskRunFilter] = None,
3040
- sort: Optional[ArtifactCollectionSort] = None,
3041
- limit: Optional[int] = None,
3042
- offset: int = 0,
3043
- ) -> list[ArtifactCollection]:
3044
- """
3045
- Query the Prefect API for artifacts. Only artifacts matching all criteria will
3046
- be returned.
3047
- Args:
3048
- artifact_filter: filter criteria for artifacts
3049
- flow_run_filter: filter criteria for flow runs
3050
- task_run_filter: filter criteria for task runs
3051
- sort: sort criteria for the artifacts
3052
- limit: limit for the artifact query
3053
- offset: offset for the artifact query
3054
- Returns:
3055
- a list of Artifact model representations of the artifacts
3056
- """
3057
- body: dict[str, Any] = {
3058
- "artifacts": (
3059
- artifact_filter.model_dump(mode="json") if artifact_filter else None
3060
- ),
3061
- "flow_runs": (
3062
- flow_run_filter.model_dump(mode="json") if flow_run_filter else None
3063
- ),
3064
- "task_runs": (
3065
- task_run_filter.model_dump(mode="json") if task_run_filter else None
3066
- ),
3067
- "sort": sort,
3068
- "limit": limit,
3069
- "offset": offset,
3070
- }
3071
- response = await self._client.post("/artifacts/latest/filter", json=body)
3072
- return pydantic.TypeAdapter(list[ArtifactCollection]).validate_python(
3073
- response.json()
3074
- )
3075
-
3076
- async def delete_artifact(self, artifact_id: UUID) -> None:
3077
- """
3078
- Deletes an artifact with the provided id.
3079
-
3080
- Args:
3081
- artifact_id: The id of the artifact to delete.
3082
- """
3083
- try:
3084
- await self._client.delete(f"/artifacts/{artifact_id}")
3085
- except httpx.HTTPStatusError as e:
3086
- if e.response.status_code == 404:
3087
- raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
3088
- else:
3089
- raise
3090
-
3091
- async def create_variable(self, variable: VariableCreate) -> Variable:
3092
- """
3093
- Creates an variable with the provided configuration.
3094
-
3095
- Args:
3096
- variable: Desired configuration for the new variable.
3097
- Returns:
3098
- Information about the newly created variable.
3099
- """
3100
- response = await self._client.post(
3101
- "/variables/",
3102
- json=variable.model_dump(mode="json", exclude_unset=True),
3103
- )
3104
- return Variable(**response.json())
3105
-
3106
- async def update_variable(self, variable: VariableUpdate) -> None:
3107
- """
3108
- Updates a variable with the provided configuration.
3109
-
3110
- Args:
3111
- variable: Desired configuration for the updated variable.
3112
- Returns:
3113
- Information about the updated variable.
3114
- """
3115
- await self._client.patch(
3116
- f"/variables/name/{variable.name}",
3117
- json=variable.model_dump(mode="json", exclude_unset=True),
3118
- )
3119
-
3120
- async def read_variable_by_name(self, name: str) -> Optional[Variable]:
3121
- """Reads a variable by name. Returns None if no variable is found."""
3122
- try:
3123
- response = await self._client.get(f"/variables/name/{name}")
3124
- return Variable(**response.json())
3125
- except httpx.HTTPStatusError as e:
3126
- if e.response.status_code == status.HTTP_404_NOT_FOUND:
3127
- return None
3128
- else:
3129
- raise
3130
-
3131
- async def delete_variable_by_name(self, name: str) -> None:
3132
- """Deletes a variable by name."""
3133
- try:
3134
- await self._client.delete(f"/variables/name/{name}")
3135
- except httpx.HTTPStatusError as e:
3136
- if e.response.status_code == 404:
3137
- raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
3138
- else:
3139
- raise
3140
-
3141
- async def read_variables(self, limit: Optional[int] = None) -> list[Variable]:
3142
- """Reads all variables."""
3143
- response = await self._client.post("/variables/filter", json={"limit": limit})
3144
- return pydantic.TypeAdapter(list[Variable]).validate_python(response.json())
3145
-
3146
2725
  async def read_worker_metadata(self) -> dict[str, Any]:
3147
2726
  """Reads worker metadata stored in Prefect collection registry."""
3148
2727
  response = await self._client.get("collections/views/aggregate-worker-metadata")
3149
2728
  response.raise_for_status()
3150
2729
  return response.json()
3151
2730
 
3152
- async def increment_concurrency_slots(
3153
- self,
3154
- names: list[str],
3155
- slots: int,
3156
- mode: str,
3157
- create_if_missing: Optional[bool] = None,
3158
- ) -> httpx.Response:
3159
- return await self._client.post(
3160
- "/v2/concurrency_limits/increment",
3161
- json={
3162
- "names": names,
3163
- "slots": slots,
3164
- "mode": mode,
3165
- "create_if_missing": create_if_missing if create_if_missing else False,
3166
- },
3167
- )
3168
-
3169
- async def release_concurrency_slots(
3170
- self, names: list[str], slots: int, occupancy_seconds: float
3171
- ) -> httpx.Response:
3172
- """
3173
- Release concurrency slots for the specified limits.
3174
-
3175
- Args:
3176
- names (List[str]): A list of limit names for which to release slots.
3177
- slots (int): The number of concurrency slots to release.
3178
- occupancy_seconds (float): The duration in seconds that the slots
3179
- were occupied.
3180
-
3181
- Returns:
3182
- httpx.Response: The HTTP response from the server.
3183
- """
3184
-
3185
- return await self._client.post(
3186
- "/v2/concurrency_limits/decrement",
3187
- json={
3188
- "names": names,
3189
- "slots": slots,
3190
- "occupancy_seconds": occupancy_seconds,
3191
- },
3192
- )
3193
-
3194
- async def create_global_concurrency_limit(
3195
- self, concurrency_limit: GlobalConcurrencyLimitCreate
3196
- ) -> UUID:
3197
- response = await self._client.post(
3198
- "/v2/concurrency_limits/",
3199
- json=concurrency_limit.model_dump(mode="json", exclude_unset=True),
3200
- )
3201
- return UUID(response.json()["id"])
3202
-
3203
- async def update_global_concurrency_limit(
3204
- self, name: str, concurrency_limit: GlobalConcurrencyLimitUpdate
3205
- ) -> httpx.Response:
3206
- try:
3207
- response = await self._client.patch(
3208
- f"/v2/concurrency_limits/{name}",
3209
- json=concurrency_limit.model_dump(mode="json", exclude_unset=True),
3210
- )
3211
- return response
3212
- except httpx.HTTPStatusError as e:
3213
- if e.response.status_code == status.HTTP_404_NOT_FOUND:
3214
- raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
3215
- else:
3216
- raise
3217
-
3218
- async def delete_global_concurrency_limit_by_name(
3219
- self, name: str
3220
- ) -> httpx.Response:
3221
- try:
3222
- response = await self._client.delete(f"/v2/concurrency_limits/{name}")
3223
- return response
3224
- except httpx.HTTPStatusError as e:
3225
- if e.response.status_code == status.HTTP_404_NOT_FOUND:
3226
- raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
3227
- else:
3228
- raise
3229
-
3230
- async def read_global_concurrency_limit_by_name(
3231
- self, name: str
3232
- ) -> GlobalConcurrencyLimitResponse:
3233
- try:
3234
- response = await self._client.get(f"/v2/concurrency_limits/{name}")
3235
- return GlobalConcurrencyLimitResponse.model_validate(response.json())
3236
- except httpx.HTTPStatusError as e:
3237
- if e.response.status_code == status.HTTP_404_NOT_FOUND:
3238
- raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
3239
- else:
3240
- raise
3241
-
3242
- async def upsert_global_concurrency_limit_by_name(
3243
- self, name: str, limit: int
3244
- ) -> None:
3245
- """Creates a global concurrency limit with the given name and limit if one does not already exist.
3246
-
3247
- If one does already exist matching the name then update it's limit if it is different.
3248
-
3249
- Note: This is not done atomically.
3250
- """
3251
- try:
3252
- existing_limit = await self.read_global_concurrency_limit_by_name(name)
3253
- except prefect.exceptions.ObjectNotFound:
3254
- existing_limit = None
3255
-
3256
- if not existing_limit:
3257
- await self.create_global_concurrency_limit(
3258
- GlobalConcurrencyLimitCreate(
3259
- name=name,
3260
- limit=limit,
3261
- )
3262
- )
3263
- elif existing_limit.limit != limit:
3264
- await self.update_global_concurrency_limit(
3265
- name, GlobalConcurrencyLimitUpdate(limit=limit)
3266
- )
3267
-
3268
- async def read_global_concurrency_limits(
3269
- self, limit: int = 10, offset: int = 0
3270
- ) -> list[GlobalConcurrencyLimitResponse]:
3271
- response = await self._client.post(
3272
- "/v2/concurrency_limits/filter",
3273
- json={
3274
- "limit": limit,
3275
- "offset": offset,
3276
- },
3277
- )
3278
- return pydantic.TypeAdapter(
3279
- list[GlobalConcurrencyLimitResponse]
3280
- ).validate_python(response.json())
3281
-
3282
2731
  async def create_flow_run_input(
3283
2732
  self, flow_run_id: UUID, key: str, value: str, sender: Optional[str] = None
3284
2733
  ) -> None:
@@ -3571,7 +3020,13 @@ class PrefectClient:
3571
3020
  assert False, "This should never be called but must be defined for __enter__"
3572
3021
 
3573
3022
 
3574
- class SyncPrefectClient:
3023
+ class SyncPrefectClient(
3024
+ ArtifactClient,
3025
+ ArtifactCollectionClient,
3026
+ LogClient,
3027
+ VariableClient,
3028
+ ConcurrencyLimitClient,
3029
+ ):
3575
3030
  """
3576
3031
  A synchronous client for interacting with the [Prefect REST API](/api-ref/rest-api/).
3577
3032
 
@@ -4354,74 +3809,6 @@ class SyncPrefectClient:
4354
3809
 
4355
3810
  return DeploymentResponse.model_validate(response.json())
4356
3811
 
4357
- def create_artifact(
4358
- self,
4359
- artifact: ArtifactCreate,
4360
- ) -> Artifact:
4361
- """
4362
- Creates an artifact with the provided configuration.
4363
-
4364
- Args:
4365
- artifact: Desired configuration for the new artifact.
4366
- Returns:
4367
- Information about the newly created artifact.
4368
- """
4369
-
4370
- response = self._client.post(
4371
- "/artifacts/",
4372
- json=artifact.model_dump(mode="json", exclude_unset=True),
4373
- )
4374
-
4375
- return Artifact.model_validate(response.json())
4376
-
4377
- def release_concurrency_slots(
4378
- self, names: list[str], slots: int, occupancy_seconds: float
4379
- ) -> httpx.Response:
4380
- """
4381
- Release concurrency slots for the specified limits.
4382
-
4383
- Args:
4384
- names (List[str]): A list of limit names for which to release slots.
4385
- slots (int): The number of concurrency slots to release.
4386
- occupancy_seconds (float): The duration in seconds that the slots
4387
- were occupied.
4388
-
4389
- Returns:
4390
- httpx.Response: The HTTP response from the server.
4391
- """
4392
- return self._client.post(
4393
- "/v2/concurrency_limits/decrement",
4394
- json={
4395
- "names": names,
4396
- "slots": slots,
4397
- "occupancy_seconds": occupancy_seconds,
4398
- },
4399
- )
4400
-
4401
- def decrement_v1_concurrency_slots(
4402
- self, names: list[str], occupancy_seconds: float, task_run_id: UUID
4403
- ) -> httpx.Response:
4404
- """
4405
- Release the specified concurrency limits.
4406
-
4407
- Args:
4408
- names (List[str]): A list of limit names to decrement.
4409
- occupancy_seconds (float): The duration in seconds that the slots
4410
- were held.
4411
- task_run_id (UUID): The task run ID that incremented the limits.
4412
-
4413
- Returns:
4414
- httpx.Response: The HTTP response from the server.
4415
- """
4416
- return self._client.post(
4417
- "/concurrency_limits/decrement",
4418
- json={
4419
- "names": names,
4420
- "occupancy_seconds": occupancy_seconds,
4421
- "task_run_id": str(task_run_id),
4422
- },
4423
- )
4424
-
4425
3812
  def update_flow_run_labels(
4426
3813
  self, flow_run_id: UUID, labels: KeyValueLabelsField
4427
3814
  ) -> None:
@@ -4471,53 +3858,3 @@ class SyncPrefectClient:
4471
3858
  else:
4472
3859
  raise
4473
3860
  return BlockDocument.model_validate(response.json())
4474
-
4475
- def create_variable(self, variable: VariableCreate) -> Variable:
4476
- """
4477
- Creates an variable with the provided configuration.
4478
-
4479
- Args:
4480
- variable: Desired configuration for the new variable.
4481
- Returns:
4482
- Information about the newly created variable.
4483
- """
4484
- response = self._client.post(
4485
- "/variables/",
4486
- json=variable.model_dump(mode="json", exclude_unset=True),
4487
- )
4488
- return Variable(**response.json())
4489
-
4490
- def update_variable(self, variable: VariableUpdate) -> None:
4491
- """
4492
- Updates a variable with the provided configuration.
4493
-
4494
- Args:
4495
- variable: Desired configuration for the updated variable.
4496
- Returns:
4497
- Information about the updated variable.
4498
- """
4499
- self._client.patch(
4500
- f"/variables/name/{variable.name}",
4501
- json=variable.model_dump(mode="json", exclude_unset=True),
4502
- )
4503
-
4504
- def read_variable_by_name(self, name: str) -> Optional[Variable]:
4505
- """Reads a variable by name. Returns None if no variable is found."""
4506
- try:
4507
- response = self._client.get(f"/variables/name/{name}")
4508
- return Variable(**response.json())
4509
- except httpx.HTTPStatusError as e:
4510
- if e.response.status_code == status.HTTP_404_NOT_FOUND:
4511
- return None
4512
- else:
4513
- raise
4514
-
4515
- def delete_variable_by_name(self, name: str) -> None:
4516
- """Deletes a variable by name."""
4517
- try:
4518
- self._client.delete(f"/variables/name/{name}")
4519
- except httpx.HTTPStatusError as e:
4520
- if e.response.status_code == 404:
4521
- raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
4522
- else:
4523
- raise