prefect-client 3.1.6__py3-none-any.whl → 3.1.7__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.
- prefect/_experimental/__init__.py +0 -0
- prefect/_experimental/lineage.py +181 -0
- prefect/_internal/compatibility/async_dispatch.py +38 -9
- prefect/_internal/pydantic/v2_validated_func.py +15 -10
- prefect/_internal/retries.py +15 -6
- prefect/_internal/schemas/bases.py +2 -1
- prefect/_internal/schemas/validators.py +5 -4
- prefect/_version.py +3 -3
- prefect/blocks/core.py +144 -17
- prefect/blocks/system.py +2 -1
- prefect/client/orchestration.py +88 -0
- prefect/client/schemas/actions.py +5 -5
- prefect/client/schemas/filters.py +1 -1
- prefect/client/schemas/objects.py +5 -5
- prefect/client/schemas/responses.py +1 -2
- prefect/client/schemas/schedules.py +1 -1
- prefect/client/subscriptions.py +2 -1
- prefect/client/utilities.py +15 -1
- prefect/context.py +1 -1
- prefect/deployments/flow_runs.py +3 -3
- prefect/deployments/runner.py +14 -14
- prefect/deployments/steps/core.py +3 -1
- prefect/deployments/steps/pull.py +60 -12
- prefect/events/clients.py +55 -4
- prefect/events/filters.py +1 -1
- prefect/events/related.py +2 -1
- prefect/events/schemas/events.py +1 -1
- prefect/events/utilities.py +2 -0
- prefect/events/worker.py +8 -0
- prefect/flow_engine.py +41 -81
- prefect/flow_runs.py +4 -2
- prefect/flows.py +4 -6
- prefect/results.py +43 -22
- prefect/runner/storage.py +3 -3
- prefect/serializers.py +28 -24
- prefect/settings/models/experiments.py +5 -0
- prefect/task_engine.py +34 -26
- prefect/task_worker.py +43 -25
- prefect/tasks.py +118 -125
- prefect/telemetry/instrumentation.py +1 -1
- prefect/telemetry/processors.py +10 -7
- prefect/telemetry/run_telemetry.py +157 -33
- prefect/types/__init__.py +4 -1
- prefect/variables.py +127 -19
- {prefect_client-3.1.6.dist-info → prefect_client-3.1.7.dist-info}/METADATA +2 -1
- {prefect_client-3.1.6.dist-info → prefect_client-3.1.7.dist-info}/RECORD +49 -47
- {prefect_client-3.1.6.dist-info → prefect_client-3.1.7.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.6.dist-info → prefect_client-3.1.7.dist-info}/WHEEL +0 -0
- {prefect_client-3.1.6.dist-info → prefect_client-3.1.7.dist-info}/top_level.txt +0 -0
@@ -1,22 +1,33 @@
|
|
1
1
|
import time
|
2
2
|
from dataclasses import dataclass, field
|
3
|
-
from typing import TYPE_CHECKING, Any,
|
3
|
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
4
4
|
|
5
|
+
from opentelemetry import propagate, trace
|
6
|
+
from opentelemetry.context import Context
|
5
7
|
from opentelemetry.propagators.textmap import Setter
|
6
8
|
from opentelemetry.trace import (
|
9
|
+
Span,
|
7
10
|
Status,
|
8
11
|
StatusCode,
|
9
12
|
get_tracer,
|
10
13
|
)
|
14
|
+
from typing_extensions import TypeAlias
|
11
15
|
|
12
16
|
import prefect
|
13
|
-
from prefect.client.
|
17
|
+
from prefect.client.orchestration import PrefectClient, SyncPrefectClient
|
18
|
+
from prefect.client.schemas import FlowRun, TaskRun
|
14
19
|
from prefect.client.schemas.objects import State
|
20
|
+
from prefect.context import FlowRunContext, TaskRunContext
|
15
21
|
from prefect.types import KeyValueLabels
|
16
22
|
|
17
23
|
if TYPE_CHECKING:
|
18
24
|
from opentelemetry.trace import Tracer
|
19
25
|
|
26
|
+
LABELS_TRACEPARENT_KEY = "__OTEL_TRACEPARENT"
|
27
|
+
TRACEPARENT_KEY = "traceparent"
|
28
|
+
|
29
|
+
FlowOrTaskRun: TypeAlias = Union[FlowRun, TaskRun]
|
30
|
+
|
20
31
|
|
21
32
|
class OTELSetter(Setter[KeyValueLabels]):
|
22
33
|
"""
|
@@ -36,67 +47,148 @@ class RunTelemetry:
|
|
36
47
|
_tracer: "Tracer" = field(
|
37
48
|
default_factory=lambda: get_tracer("prefect", prefect.__version__)
|
38
49
|
)
|
39
|
-
|
50
|
+
span: Optional[Span] = None
|
51
|
+
|
52
|
+
async def async_start_span(
|
53
|
+
self,
|
54
|
+
run: FlowOrTaskRun,
|
55
|
+
client: PrefectClient,
|
56
|
+
name: Optional[str] = None,
|
57
|
+
parameters: Optional[dict[str, Any]] = None,
|
58
|
+
):
|
59
|
+
traceparent, span = self._start_span(run, name, parameters)
|
60
|
+
|
61
|
+
if self._run_type(run) == "flow" and traceparent:
|
62
|
+
# Only explicitly update labels if the run is a flow as task runs
|
63
|
+
# are updated via events.
|
64
|
+
await client.update_flow_run_labels(
|
65
|
+
run.id, {LABELS_TRACEPARENT_KEY: traceparent}
|
66
|
+
)
|
67
|
+
|
68
|
+
return span
|
40
69
|
|
41
70
|
def start_span(
|
42
71
|
self,
|
43
|
-
|
44
|
-
|
45
|
-
|
72
|
+
run: FlowOrTaskRun,
|
73
|
+
client: SyncPrefectClient,
|
74
|
+
name: Optional[str] = None,
|
75
|
+
parameters: Optional[dict[str, Any]] = None,
|
46
76
|
):
|
77
|
+
traceparent, span = self._start_span(run, name, parameters)
|
78
|
+
|
79
|
+
if self._run_type(run) == "flow" and traceparent:
|
80
|
+
# Only explicitly update labels if the run is a flow as task runs
|
81
|
+
# are updated via events.
|
82
|
+
client.update_flow_run_labels(run.id, {LABELS_TRACEPARENT_KEY: traceparent})
|
83
|
+
|
84
|
+
return span
|
85
|
+
|
86
|
+
def _start_span(
|
87
|
+
self,
|
88
|
+
run: FlowOrTaskRun,
|
89
|
+
name: Optional[str] = None,
|
90
|
+
parameters: Optional[dict[str, Any]] = None,
|
91
|
+
) -> tuple[Optional[str], Span]:
|
47
92
|
"""
|
48
|
-
Start a span for a
|
93
|
+
Start a span for a run.
|
49
94
|
"""
|
50
95
|
if parameters is None:
|
51
96
|
parameters = {}
|
52
|
-
|
53
|
-
labels = {}
|
97
|
+
|
54
98
|
parameter_attributes = {
|
55
99
|
f"prefect.run.parameter.{k}": type(v).__name__
|
56
100
|
for k, v in parameters.items()
|
57
101
|
}
|
58
|
-
|
59
|
-
|
102
|
+
|
103
|
+
# Use existing trace context if this run already has one (e.g., from
|
104
|
+
# server operations like Late), otherwise use parent's trace context if
|
105
|
+
# available (e.g., nested flow / task runs). If neither exists, this
|
106
|
+
# will be a root span (e.g., a top-level flow run).
|
107
|
+
if LABELS_TRACEPARENT_KEY in run.labels:
|
108
|
+
context = self._trace_context_from_labels(run.labels)
|
109
|
+
else:
|
110
|
+
parent_run = self._parent_run()
|
111
|
+
parent_labels = parent_run.labels if parent_run else {}
|
112
|
+
if LABELS_TRACEPARENT_KEY in parent_labels:
|
113
|
+
context = self._trace_context_from_labels(parent_labels)
|
114
|
+
else:
|
115
|
+
context = None
|
116
|
+
|
117
|
+
run_type = self._run_type(run)
|
118
|
+
|
119
|
+
self.span = self._tracer.start_span(
|
120
|
+
name=name or run.name,
|
121
|
+
context=context,
|
60
122
|
attributes={
|
61
|
-
"prefect.run.
|
62
|
-
"prefect.run.
|
63
|
-
"prefect.
|
123
|
+
"prefect.run.name": name or run.name,
|
124
|
+
"prefect.run.type": run_type,
|
125
|
+
"prefect.run.id": str(run.id),
|
126
|
+
"prefect.tags": run.tags,
|
64
127
|
**parameter_attributes,
|
65
|
-
**
|
128
|
+
**{
|
129
|
+
key: value
|
130
|
+
for key, value in run.labels.items()
|
131
|
+
if not key.startswith("__") # exclude internal labels
|
132
|
+
},
|
66
133
|
},
|
67
134
|
)
|
68
135
|
|
69
|
-
|
136
|
+
if traceparent := self._traceparent_from_span(self.span):
|
137
|
+
run.labels[LABELS_TRACEPARENT_KEY] = traceparent
|
138
|
+
|
139
|
+
return traceparent, self.span
|
140
|
+
|
141
|
+
def _run_type(self, run: FlowOrTaskRun) -> str:
|
142
|
+
return "task" if isinstance(run, TaskRun) else "flow"
|
143
|
+
|
144
|
+
def _trace_context_from_labels(
|
145
|
+
self, labels: Optional[KeyValueLabels]
|
146
|
+
) -> Optional[Context]:
|
147
|
+
"""Get trace context from run labels if it exists."""
|
148
|
+
if not labels or LABELS_TRACEPARENT_KEY not in labels:
|
149
|
+
return None
|
150
|
+
traceparent = labels[LABELS_TRACEPARENT_KEY]
|
151
|
+
carrier = {TRACEPARENT_KEY: traceparent}
|
152
|
+
return propagate.extract(carrier)
|
153
|
+
|
154
|
+
def _traceparent_from_span(self, span: Span) -> Optional[str]:
|
155
|
+
carrier = {}
|
156
|
+
propagate.inject(carrier, context=trace.set_span_in_context(span))
|
157
|
+
return carrier.get(TRACEPARENT_KEY)
|
158
|
+
|
159
|
+
def end_span_on_success(self) -> None:
|
70
160
|
"""
|
71
|
-
End a span for a
|
161
|
+
End a span for a run on success.
|
72
162
|
"""
|
73
|
-
if self.
|
74
|
-
self.
|
75
|
-
self.
|
76
|
-
self.
|
163
|
+
if self.span:
|
164
|
+
self.span.set_status(Status(StatusCode.OK))
|
165
|
+
self.span.end(time.time_ns())
|
166
|
+
self.span = None
|
77
167
|
|
78
|
-
def end_span_on_failure(self, terminal_message: str) -> None:
|
168
|
+
def end_span_on_failure(self, terminal_message: Optional[str] = None) -> None:
|
79
169
|
"""
|
80
|
-
End a span for a
|
170
|
+
End a span for a run on failure.
|
81
171
|
"""
|
82
|
-
if self.
|
83
|
-
self.
|
84
|
-
|
85
|
-
|
172
|
+
if self.span:
|
173
|
+
self.span.set_status(
|
174
|
+
Status(StatusCode.ERROR, terminal_message or "Run failed")
|
175
|
+
)
|
176
|
+
self.span.end(time.time_ns())
|
177
|
+
self.span = None
|
86
178
|
|
87
|
-
def record_exception(self, exc:
|
179
|
+
def record_exception(self, exc: BaseException) -> None:
|
88
180
|
"""
|
89
181
|
Record an exception on a span.
|
90
182
|
"""
|
91
|
-
if self.
|
92
|
-
self.
|
183
|
+
if self.span:
|
184
|
+
self.span.record_exception(exc)
|
93
185
|
|
94
186
|
def update_state(self, new_state: State) -> None:
|
95
187
|
"""
|
96
|
-
Update a span with the state of a
|
188
|
+
Update a span with the state of a run.
|
97
189
|
"""
|
98
|
-
if self.
|
99
|
-
self.
|
190
|
+
if self.span:
|
191
|
+
self.span.add_event(
|
100
192
|
new_state.name or new_state.type,
|
101
193
|
{
|
102
194
|
"prefect.state.message": new_state.message or "",
|
@@ -105,3 +197,35 @@ class RunTelemetry:
|
|
105
197
|
"prefect.state.id": str(new_state.id),
|
106
198
|
},
|
107
199
|
)
|
200
|
+
|
201
|
+
def _parent_run(self) -> Union[FlowOrTaskRun, None]:
|
202
|
+
"""
|
203
|
+
Identify the "parent run" for the current execution context.
|
204
|
+
|
205
|
+
Both flows and tasks can be nested "infinitely," and each creates a
|
206
|
+
corresponding context when executed. This method determines the most
|
207
|
+
appropriate parent context (either a task run or a flow run) based on
|
208
|
+
their relationship in the current hierarchy.
|
209
|
+
|
210
|
+
Returns:
|
211
|
+
FlowOrTaskRun: The parent run object (task or flow) if applicable.
|
212
|
+
None: If there is no parent context, implying the current run is the top-level parent.
|
213
|
+
"""
|
214
|
+
parent_flow_run_context = FlowRunContext.get()
|
215
|
+
parent_task_run_context = TaskRunContext.get()
|
216
|
+
|
217
|
+
if parent_task_run_context and parent_flow_run_context:
|
218
|
+
# If both contexts exist, which is common for nested flows or tasks,
|
219
|
+
# check if the task's flow_run_id matches the current flow_run.
|
220
|
+
# If they match, the task is a child of the flow and is the parent of the current run.
|
221
|
+
flow_run_id = getattr(parent_flow_run_context.flow_run, "id", None)
|
222
|
+
if parent_task_run_context.task_run.flow_run_id == flow_run_id:
|
223
|
+
return parent_task_run_context.task_run
|
224
|
+
# Otherwise, assume the flow run is the entry point and is the parent.
|
225
|
+
return parent_flow_run_context.flow_run
|
226
|
+
elif parent_flow_run_context:
|
227
|
+
return parent_flow_run_context.flow_run
|
228
|
+
elif parent_task_run_context:
|
229
|
+
return parent_task_run_context.task_run
|
230
|
+
|
231
|
+
return None
|
prefect/types/__init__.py
CHANGED
@@ -3,7 +3,8 @@ from typing import Annotated, Any, Dict, List, Optional, Set, TypeVar, Union
|
|
3
3
|
from typing_extensions import Literal, TypeAlias
|
4
4
|
import orjson
|
5
5
|
import pydantic
|
6
|
-
|
6
|
+
from pydantic_extra_types.pendulum_dt import DateTime as PydanticDateTime
|
7
|
+
from pydantic_extra_types.pendulum_dt import Date as PydanticDate
|
7
8
|
from pydantic import (
|
8
9
|
BeforeValidator,
|
9
10
|
Field,
|
@@ -34,6 +35,8 @@ TimeZone = Annotated[
|
|
34
35
|
),
|
35
36
|
]
|
36
37
|
|
38
|
+
DateTime: TypeAlias = PydanticDateTime
|
39
|
+
Date: TypeAlias = PydanticDate
|
37
40
|
|
38
41
|
BANNED_CHARACTERS = ["/", "%", "&", ">", "<"]
|
39
42
|
|
prefect/variables.py
CHANGED
@@ -1,13 +1,14 @@
|
|
1
|
-
from typing import
|
1
|
+
from typing import Optional
|
2
2
|
|
3
3
|
from pydantic import BaseModel, Field
|
4
4
|
|
5
|
+
from prefect._internal.compatibility.async_dispatch import async_dispatch
|
5
6
|
from prefect._internal.compatibility.migration import getattr_migration
|
7
|
+
from prefect.client.orchestration import get_client
|
6
8
|
from prefect.client.schemas.actions import VariableCreate, VariableUpdate
|
7
9
|
from prefect.client.utilities import get_or_create_client
|
8
10
|
from prefect.exceptions import ObjectNotFound
|
9
11
|
from prefect.types import MAX_VARIABLE_NAME_LENGTH, StrictVariableValue
|
10
|
-
from prefect.utilities.asyncutils import sync_compatible
|
11
12
|
|
12
13
|
|
13
14
|
class Variable(BaseModel):
|
@@ -31,19 +32,18 @@ class Variable(BaseModel):
|
|
31
32
|
description="The value of the variable",
|
32
33
|
examples=["my-value"],
|
33
34
|
)
|
34
|
-
tags: Optional[
|
35
|
+
tags: Optional[list[str]] = Field(default=None)
|
35
36
|
|
36
37
|
@classmethod
|
37
|
-
|
38
|
-
async def set(
|
38
|
+
async def aset(
|
39
39
|
cls,
|
40
40
|
name: str,
|
41
41
|
value: StrictVariableValue,
|
42
|
-
tags: Optional[
|
42
|
+
tags: Optional[list[str]] = None,
|
43
43
|
overwrite: bool = False,
|
44
44
|
) -> "Variable":
|
45
45
|
"""
|
46
|
-
|
46
|
+
Asynchronously sets a new variable. If one exists with the same name, must pass `overwrite=True`
|
47
47
|
|
48
48
|
Returns the newly set variable object.
|
49
49
|
|
@@ -60,8 +60,8 @@ class Variable(BaseModel):
|
|
60
60
|
from prefect.variables import Variable
|
61
61
|
|
62
62
|
@flow
|
63
|
-
def my_flow():
|
64
|
-
Variable.
|
63
|
+
async def my_flow():
|
64
|
+
await Variable.aset(name="my_var",value="test_value", tags=["hi", "there"], overwrite=True)
|
65
65
|
```
|
66
66
|
"""
|
67
67
|
client, _ = get_or_create_client()
|
@@ -87,14 +87,62 @@ class Variable(BaseModel):
|
|
87
87
|
return cls.model_validate(var_dict)
|
88
88
|
|
89
89
|
@classmethod
|
90
|
-
@
|
91
|
-
|
90
|
+
@async_dispatch(aset)
|
91
|
+
def set(
|
92
|
+
cls,
|
93
|
+
name: str,
|
94
|
+
value: StrictVariableValue,
|
95
|
+
tags: Optional[list[str]] = None,
|
96
|
+
overwrite: bool = False,
|
97
|
+
) -> "Variable":
|
98
|
+
"""
|
99
|
+
Sets a new variable. If one exists with the same name, must pass `overwrite=True`
|
100
|
+
|
101
|
+
Returns the newly set variable object.
|
102
|
+
|
103
|
+
Args:
|
104
|
+
- name: The name of the variable to set.
|
105
|
+
- value: The value of the variable to set.
|
106
|
+
- tags: An optional list of strings to associate with the variable.
|
107
|
+
- overwrite: Whether to overwrite the variable if it already exists.
|
108
|
+
|
109
|
+
Example:
|
110
|
+
Set a new variable and overwrite it if it already exists.
|
111
|
+
|
112
|
+
```
|
113
|
+
from prefect.variables import Variable
|
114
|
+
|
115
|
+
@flow
|
116
|
+
def my_flow():
|
117
|
+
Variable.set(name="my_var",value="test_value", tags=["hi", "there"], overwrite=True)
|
118
|
+
```
|
119
|
+
"""
|
120
|
+
with get_client(sync_client=True) as client:
|
121
|
+
variable_exists = client.read_variable_by_name(name)
|
122
|
+
var_dict = {"name": name, "value": value, "tags": tags or []}
|
123
|
+
|
124
|
+
if variable_exists:
|
125
|
+
if not overwrite:
|
126
|
+
raise ValueError(
|
127
|
+
f"Variable {name!r} already exists. Use `overwrite=True` to update it."
|
128
|
+
)
|
129
|
+
client.update_variable(variable=VariableUpdate.model_validate(var_dict))
|
130
|
+
variable = client.read_variable_by_name(name)
|
131
|
+
for key in var_dict.keys():
|
132
|
+
var_dict.update({key: getattr(variable, key)})
|
133
|
+
else:
|
134
|
+
client.create_variable(variable=VariableCreate.model_validate(var_dict))
|
135
|
+
|
136
|
+
return cls.model_validate(var_dict)
|
137
|
+
|
138
|
+
@classmethod
|
139
|
+
async def aget(
|
92
140
|
cls,
|
93
141
|
name: str,
|
94
142
|
default: StrictVariableValue = None,
|
95
143
|
) -> StrictVariableValue:
|
96
144
|
"""
|
97
|
-
|
145
|
+
Asynchronously get a variable's value by name.
|
98
146
|
|
99
147
|
If the variable does not exist, return the default value.
|
100
148
|
|
@@ -109,8 +157,8 @@ class Variable(BaseModel):
|
|
109
157
|
from prefect.variables import Variable
|
110
158
|
|
111
159
|
@flow
|
112
|
-
def my_flow():
|
113
|
-
var = Variable.
|
160
|
+
async def my_flow():
|
161
|
+
var = await Variable.aget("my_var")
|
114
162
|
```
|
115
163
|
"""
|
116
164
|
client, _ = get_or_create_client()
|
@@ -119,10 +167,41 @@ class Variable(BaseModel):
|
|
119
167
|
return variable.value if variable else default
|
120
168
|
|
121
169
|
@classmethod
|
122
|
-
@
|
123
|
-
|
170
|
+
@async_dispatch(aget)
|
171
|
+
def get(
|
172
|
+
cls,
|
173
|
+
name: str,
|
174
|
+
default: StrictVariableValue = None,
|
175
|
+
) -> StrictVariableValue:
|
124
176
|
"""
|
125
|
-
|
177
|
+
Get a variable's value by name.
|
178
|
+
|
179
|
+
If the variable does not exist, return the default value.
|
180
|
+
|
181
|
+
Args:
|
182
|
+
- name: The name of the variable value to get.
|
183
|
+
- default: The default value to return if the variable does not exist.
|
184
|
+
|
185
|
+
Example:
|
186
|
+
Get a variable's value by name.
|
187
|
+
```python
|
188
|
+
from prefect import flow
|
189
|
+
from prefect.variables import Variable
|
190
|
+
|
191
|
+
@flow
|
192
|
+
def my_flow():
|
193
|
+
var = Variable.get("my_var")
|
194
|
+
```
|
195
|
+
"""
|
196
|
+
with get_client(sync_client=True) as client:
|
197
|
+
variable = client.read_variable_by_name(name)
|
198
|
+
|
199
|
+
return variable.value if variable else default
|
200
|
+
|
201
|
+
@classmethod
|
202
|
+
async def aunset(cls, name: str) -> bool:
|
203
|
+
"""
|
204
|
+
Asynchronously unset a variable by name.
|
126
205
|
|
127
206
|
Args:
|
128
207
|
- name: The name of the variable to unset.
|
@@ -136,8 +215,8 @@ class Variable(BaseModel):
|
|
136
215
|
from prefect.variables import Variable
|
137
216
|
|
138
217
|
@flow
|
139
|
-
def my_flow():
|
140
|
-
Variable.
|
218
|
+
async def my_flow():
|
219
|
+
await Variable.aunset("my_var")
|
141
220
|
```
|
142
221
|
"""
|
143
222
|
client, _ = get_or_create_client()
|
@@ -147,5 +226,34 @@ class Variable(BaseModel):
|
|
147
226
|
except ObjectNotFound:
|
148
227
|
return False
|
149
228
|
|
229
|
+
@classmethod
|
230
|
+
@async_dispatch(aunset)
|
231
|
+
def unset(cls, name: str) -> bool:
|
232
|
+
"""
|
233
|
+
Unset a variable by name.
|
234
|
+
|
235
|
+
Args:
|
236
|
+
- name: The name of the variable to unset.
|
237
|
+
|
238
|
+
Returns `True` if the variable was deleted, `False` if the variable did not exist.
|
239
|
+
|
240
|
+
Example:
|
241
|
+
Unset a variable by name.
|
242
|
+
```python
|
243
|
+
from prefect import flow
|
244
|
+
from prefect.variables import Variable
|
245
|
+
|
246
|
+
@flow
|
247
|
+
def my_flow():
|
248
|
+
Variable.unset("my_var")
|
249
|
+
```
|
250
|
+
"""
|
251
|
+
with get_client(sync_client=True) as client:
|
252
|
+
try:
|
253
|
+
client.delete_variable_by_name(name=name)
|
254
|
+
return True
|
255
|
+
except ObjectNotFound:
|
256
|
+
return False
|
257
|
+
|
150
258
|
|
151
259
|
__getattr__ = getattr_migration(__name__)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: prefect-client
|
3
|
-
Version: 3.1.
|
3
|
+
Version: 3.1.7
|
4
4
|
Summary: Workflow orchestration and management.
|
5
5
|
Home-page: https://www.prefect.io
|
6
6
|
Author: Prefect Technologies, Inc.
|
@@ -51,6 +51,7 @@ Requires-Dist: pydantic-extra-types<3.0.0,>=2.8.2
|
|
51
51
|
Requires-Dist: pydantic-settings>2.2.1
|
52
52
|
Requires-Dist: python-dateutil<3.0.0,>=2.8.2
|
53
53
|
Requires-Dist: python-slugify<9.0,>=5.0
|
54
|
+
Requires-Dist: python-socks[asyncio]<3.0,>=2.5.3
|
54
55
|
Requires-Dist: pyyaml<7.0.0,>=5.4.1
|
55
56
|
Requires-Dist: rfc3339-validator<0.2.0,>=0.1.4
|
56
57
|
Requires-Dist: rich<14.0,>=11.0
|