prefect-client 3.1.11__py3-none-any.whl → 3.1.13__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/sla/__init__.py +0 -0
- prefect/_experimental/sla/client.py +92 -0
- prefect/_experimental/sla/objects.py +61 -0
- prefect/_internal/concurrency/services.py +2 -2
- prefect/_internal/concurrency/threads.py +6 -0
- prefect/_internal/retries.py +6 -3
- prefect/_internal/schemas/validators.py +6 -4
- prefect/_version.py +3 -3
- prefect/artifacts.py +4 -1
- prefect/automations.py +236 -30
- prefect/blocks/__init__.py +3 -3
- prefect/blocks/abstract.py +57 -31
- prefect/blocks/core.py +181 -82
- prefect/blocks/notifications.py +134 -73
- prefect/blocks/redis.py +13 -9
- prefect/blocks/system.py +24 -11
- prefect/blocks/webhook.py +7 -5
- prefect/cache_policies.py +23 -22
- prefect/client/orchestration/__init__.py +103 -2006
- prefect/client/orchestration/_automations/__init__.py +0 -0
- prefect/client/orchestration/_automations/client.py +329 -0
- prefect/client/orchestration/_blocks_documents/__init__.py +0 -0
- prefect/client/orchestration/_blocks_documents/client.py +334 -0
- prefect/client/orchestration/_blocks_schemas/__init__.py +0 -0
- prefect/client/orchestration/_blocks_schemas/client.py +200 -0
- prefect/client/orchestration/_blocks_types/__init__.py +0 -0
- prefect/client/orchestration/_blocks_types/client.py +380 -0
- prefect/client/orchestration/_deployments/__init__.py +0 -0
- prefect/client/orchestration/_deployments/client.py +1128 -0
- prefect/client/orchestration/_flow_runs/__init__.py +0 -0
- prefect/client/orchestration/_flow_runs/client.py +903 -0
- prefect/client/orchestration/_flows/__init__.py +0 -0
- prefect/client/orchestration/_flows/client.py +343 -0
- prefect/client/orchestration/_logs/client.py +16 -14
- prefect/client/schemas/__init__.py +68 -28
- prefect/client/schemas/objects.py +5 -5
- prefect/client/utilities.py +3 -3
- prefect/context.py +15 -1
- prefect/deployments/base.py +13 -4
- prefect/deployments/flow_runs.py +5 -1
- prefect/deployments/runner.py +37 -1
- prefect/deployments/steps/core.py +1 -1
- prefect/deployments/steps/pull.py +8 -3
- prefect/deployments/steps/utility.py +2 -2
- prefect/docker/docker_image.py +13 -9
- prefect/engine.py +33 -11
- prefect/events/cli/automations.py +4 -4
- prefect/events/clients.py +17 -14
- prefect/events/schemas/automations.py +12 -8
- prefect/events/schemas/events.py +5 -1
- prefect/events/worker.py +1 -1
- prefect/filesystems.py +7 -3
- prefect/flow_engine.py +64 -47
- prefect/flows.py +128 -74
- prefect/futures.py +14 -7
- prefect/infrastructure/provisioners/__init__.py +2 -0
- prefect/infrastructure/provisioners/cloud_run.py +4 -4
- prefect/infrastructure/provisioners/coiled.py +249 -0
- prefect/infrastructure/provisioners/container_instance.py +4 -3
- prefect/infrastructure/provisioners/ecs.py +55 -43
- prefect/infrastructure/provisioners/modal.py +5 -4
- prefect/input/actions.py +5 -1
- prefect/input/run_input.py +157 -43
- prefect/logging/configuration.py +3 -3
- prefect/logging/filters.py +2 -2
- prefect/logging/formatters.py +15 -11
- prefect/logging/handlers.py +24 -14
- prefect/logging/highlighters.py +5 -5
- prefect/logging/loggers.py +28 -18
- prefect/logging/logging.yml +1 -1
- prefect/main.py +3 -1
- prefect/results.py +166 -86
- prefect/runner/runner.py +38 -29
- prefect/runner/server.py +3 -1
- prefect/runner/storage.py +18 -18
- prefect/runner/submit.py +19 -12
- prefect/runtime/deployment.py +15 -8
- prefect/runtime/flow_run.py +19 -6
- prefect/runtime/task_run.py +7 -3
- prefect/settings/base.py +17 -7
- prefect/settings/legacy.py +4 -4
- prefect/settings/models/api.py +4 -3
- prefect/settings/models/cli.py +4 -3
- prefect/settings/models/client.py +7 -4
- prefect/settings/models/cloud.py +9 -3
- prefect/settings/models/deployments.py +4 -3
- prefect/settings/models/experiments.py +4 -8
- prefect/settings/models/flows.py +4 -3
- prefect/settings/models/internal.py +4 -3
- prefect/settings/models/logging.py +8 -6
- prefect/settings/models/results.py +4 -3
- prefect/settings/models/root.py +11 -16
- prefect/settings/models/runner.py +8 -5
- prefect/settings/models/server/api.py +6 -3
- prefect/settings/models/server/database.py +120 -25
- prefect/settings/models/server/deployments.py +4 -3
- prefect/settings/models/server/ephemeral.py +7 -4
- prefect/settings/models/server/events.py +6 -3
- prefect/settings/models/server/flow_run_graph.py +4 -3
- prefect/settings/models/server/root.py +4 -3
- prefect/settings/models/server/services.py +15 -12
- prefect/settings/models/server/tasks.py +7 -4
- prefect/settings/models/server/ui.py +4 -3
- prefect/settings/models/tasks.py +10 -5
- prefect/settings/models/testing.py +4 -3
- prefect/settings/models/worker.py +7 -4
- prefect/settings/profiles.py +13 -12
- prefect/settings/sources.py +20 -19
- prefect/states.py +74 -51
- prefect/task_engine.py +43 -33
- prefect/task_runners.py +85 -72
- prefect/task_runs.py +20 -11
- prefect/task_worker.py +14 -9
- prefect/tasks.py +36 -28
- prefect/telemetry/bootstrap.py +13 -9
- prefect/telemetry/run_telemetry.py +15 -13
- prefect/telemetry/services.py +4 -0
- prefect/transactions.py +3 -3
- prefect/types/__init__.py +3 -1
- prefect/utilities/_deprecated.py +38 -0
- prefect/utilities/engine.py +11 -4
- prefect/utilities/filesystem.py +2 -2
- prefect/utilities/generics.py +1 -1
- prefect/utilities/pydantic.py +21 -36
- prefect/utilities/templating.py +25 -1
- prefect/workers/base.py +58 -33
- prefect/workers/process.py +20 -15
- prefect/workers/server.py +4 -5
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/METADATA +3 -3
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/RECORD +133 -114
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/WHEEL +0 -0
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/top_level.txt +0 -0
prefect/tasks.py
CHANGED
@@ -30,7 +30,7 @@ from uuid import UUID, uuid4
|
|
30
30
|
from typing_extensions import Literal, ParamSpec, Self, TypeAlias, TypeIs
|
31
31
|
|
32
32
|
import prefect.states
|
33
|
-
from prefect.cache_policies import DEFAULT,
|
33
|
+
from prefect.cache_policies import DEFAULT, NO_CACHE, CachePolicy
|
34
34
|
from prefect.client.orchestration import get_client
|
35
35
|
from prefect.client.schemas import TaskRun
|
36
36
|
from prefect.client.schemas.objects import (
|
@@ -53,10 +53,7 @@ from prefect.results import (
|
|
53
53
|
ResultStore,
|
54
54
|
get_or_create_default_task_scheduling_storage,
|
55
55
|
)
|
56
|
-
from prefect.settings import
|
57
|
-
PREFECT_TASK_DEFAULT_RETRIES,
|
58
|
-
PREFECT_TASK_DEFAULT_RETRY_DELAY_SECONDS,
|
59
|
-
)
|
56
|
+
from prefect.settings.context import get_current_settings
|
60
57
|
from prefect.states import Pending, Scheduled, State
|
61
58
|
from prefect.utilities.annotations import NotSet
|
62
59
|
from prefect.utilities.asyncutils import run_coro_as_sync, sync_compatible
|
@@ -70,6 +67,8 @@ from prefect.utilities.importtools import to_qualified_name
|
|
70
67
|
from prefect.utilities.urls import url_for
|
71
68
|
|
72
69
|
if TYPE_CHECKING:
|
70
|
+
import logging
|
71
|
+
|
73
72
|
from prefect.client.orchestration import PrefectClient
|
74
73
|
from prefect.context import TaskRunContext
|
75
74
|
from prefect.transactions import Transaction
|
@@ -80,7 +79,7 @@ P = ParamSpec("P") # The parameters of the task
|
|
80
79
|
|
81
80
|
NUM_CHARS_DYNAMIC_KEY = 8
|
82
81
|
|
83
|
-
logger = get_logger("tasks")
|
82
|
+
logger: "logging.Logger" = get_logger("tasks")
|
84
83
|
|
85
84
|
FutureOrResult: TypeAlias = Union[PrefectFuture[T], T]
|
86
85
|
OneOrManyFutureOrResult: TypeAlias = Union[
|
@@ -383,19 +382,19 @@ class Task(Generic[P, R]):
|
|
383
382
|
if not callable(fn):
|
384
383
|
raise TypeError("'fn' must be callable")
|
385
384
|
|
386
|
-
self.description = description or inspect.getdoc(fn)
|
385
|
+
self.description: str | None = description or inspect.getdoc(fn)
|
387
386
|
update_wrapper(self, fn)
|
388
387
|
self.fn = fn
|
389
388
|
|
390
389
|
# the task is considered async if its function is async or an async
|
391
390
|
# generator
|
392
|
-
self.isasync = asyncio.iscoroutinefunction(
|
391
|
+
self.isasync: bool = asyncio.iscoroutinefunction(
|
393
392
|
self.fn
|
394
393
|
) or inspect.isasyncgenfunction(self.fn)
|
395
394
|
|
396
395
|
# the task is considered a generator if its function is a generator or
|
397
396
|
# an async generator
|
398
|
-
self.isgenerator = inspect.isgeneratorfunction(
|
397
|
+
self.isgenerator: bool = inspect.isgeneratorfunction(
|
399
398
|
self.fn
|
400
399
|
) or inspect.isasyncgenfunction(self.fn)
|
401
400
|
|
@@ -405,7 +404,7 @@ class Task(Generic[P, R]):
|
|
405
404
|
else:
|
406
405
|
self.name = self.fn.__name__
|
407
406
|
else:
|
408
|
-
self.name = name
|
407
|
+
self.name: str = name
|
409
408
|
|
410
409
|
if task_run_name is not None:
|
411
410
|
if not isinstance(task_run_name, str) and not callable(task_run_name):
|
@@ -420,9 +419,9 @@ class Task(Generic[P, R]):
|
|
420
419
|
|
421
420
|
raise_for_reserved_arguments(self.fn, ["return_state", "wait_for"])
|
422
421
|
|
423
|
-
self.tags = set(tags if tags else [])
|
422
|
+
self.tags: set[str] = set(tags if tags else [])
|
424
423
|
|
425
|
-
self.task_key = _generate_task_key(self.fn)
|
424
|
+
self.task_key: str = _generate_task_key(self.fn)
|
426
425
|
|
427
426
|
if cache_policy is not NotSet and cache_key_fn is not None:
|
428
427
|
logger.warning(
|
@@ -441,7 +440,9 @@ class Task(Generic[P, R]):
|
|
441
440
|
if persist_result is None:
|
442
441
|
if any(
|
443
442
|
[
|
444
|
-
cache_policy
|
443
|
+
cache_policy
|
444
|
+
and cache_policy != NO_CACHE
|
445
|
+
and cache_policy != NotSet,
|
445
446
|
cache_key_fn is not None,
|
446
447
|
result_storage_key is not None,
|
447
448
|
result_storage is not None,
|
@@ -451,8 +452,8 @@ class Task(Generic[P, R]):
|
|
451
452
|
persist_result = True
|
452
453
|
|
453
454
|
if persist_result is False:
|
454
|
-
self.cache_policy = None if cache_policy is None else
|
455
|
-
if cache_policy and cache_policy is not NotSet and cache_policy !=
|
455
|
+
self.cache_policy = None if cache_policy is None else NO_CACHE
|
456
|
+
if cache_policy and cache_policy is not NotSet and cache_policy != NO_CACHE:
|
456
457
|
logger.warning(
|
457
458
|
"Ignoring `cache_policy` because `persist_result` is False"
|
458
459
|
)
|
@@ -462,26 +463,29 @@ class Task(Generic[P, R]):
|
|
462
463
|
# TODO: handle this situation with double storage
|
463
464
|
self.cache_policy = None
|
464
465
|
else:
|
465
|
-
self.cache_policy = cache_policy
|
466
|
+
self.cache_policy: Union[CachePolicy, type[NotSet], None] = cache_policy
|
466
467
|
|
467
468
|
# TaskRunPolicy settings
|
468
469
|
# TODO: We can instantiate a `TaskRunPolicy` and add Pydantic bound checks to
|
469
470
|
# validate that the user passes positive numbers here
|
470
471
|
|
471
|
-
|
472
|
-
|
472
|
+
settings = get_current_settings()
|
473
|
+
self.retries: int = (
|
474
|
+
retries if retries is not None else settings.tasks.default_retries
|
473
475
|
)
|
474
476
|
if retry_delay_seconds is None:
|
475
|
-
retry_delay_seconds =
|
477
|
+
retry_delay_seconds = settings.tasks.default_retry_delay_seconds
|
476
478
|
|
477
479
|
if callable(retry_delay_seconds):
|
478
|
-
self.retry_delay_seconds = retry_delay_seconds(retries)
|
480
|
+
self.retry_delay_seconds = retry_delay_seconds(self.retries)
|
479
481
|
elif not isinstance(retry_delay_seconds, (list, int, float, type(None))):
|
480
482
|
raise TypeError(
|
481
483
|
f"Invalid `retry_delay_seconds` provided; must be an int, float, list or callable. Received type {type(retry_delay_seconds)}"
|
482
484
|
)
|
483
485
|
else:
|
484
|
-
self.retry_delay_seconds
|
486
|
+
self.retry_delay_seconds: Union[
|
487
|
+
float, int, list[float], None
|
488
|
+
] = retry_delay_seconds
|
485
489
|
|
486
490
|
if isinstance(self.retry_delay_seconds, list) and (
|
487
491
|
len(self.retry_delay_seconds) > 50
|
@@ -505,11 +509,15 @@ class Task(Generic[P, R]):
|
|
505
509
|
self.result_serializer = result_serializer
|
506
510
|
self.result_storage_key = result_storage_key
|
507
511
|
self.cache_result_in_memory = cache_result_in_memory
|
508
|
-
self.timeout_seconds
|
509
|
-
|
510
|
-
|
511
|
-
self.
|
512
|
-
|
512
|
+
self.timeout_seconds: Union[float, None] = (
|
513
|
+
float(timeout_seconds) if timeout_seconds else None
|
514
|
+
)
|
515
|
+
self.on_rollback_hooks: list[Callable[["Transaction"], None]] = (
|
516
|
+
on_rollback or []
|
517
|
+
)
|
518
|
+
self.on_commit_hooks: list[Callable[["Transaction"], None]] = on_commit or []
|
519
|
+
self.on_completion_hooks: list[StateHookCallable] = on_completion or []
|
520
|
+
self.on_failure_hooks: list[StateHookCallable] = on_failure or []
|
513
521
|
|
514
522
|
# retry_condition_fn must be a callable or None. If it is neither, raise a TypeError
|
515
523
|
if retry_condition_fn is not None and not (callable(retry_condition_fn)):
|
@@ -525,7 +533,7 @@ class Task(Generic[P, R]):
|
|
525
533
|
def ismethod(self) -> bool:
|
526
534
|
return hasattr(self.fn, "__prefect_self__")
|
527
535
|
|
528
|
-
def __get__(self, instance: Any, owner: Any):
|
536
|
+
def __get__(self, instance: Any, owner: Any) -> "Task[P, R]":
|
529
537
|
"""
|
530
538
|
Implement the descriptor protocol so that the task can be used as an instance method.
|
531
539
|
When an instance method is loaded, this method is called with the "self" instance as
|
@@ -580,7 +588,7 @@ class Task(Generic[P, R]):
|
|
580
588
|
Callable[["Task[..., Any]", TaskRun, State], bool]
|
581
589
|
] = None,
|
582
590
|
viz_return_value: Optional[Any] = None,
|
583
|
-
):
|
591
|
+
) -> "Task[P, R]":
|
584
592
|
"""
|
585
593
|
Create a new task from the current object, updating provided options.
|
586
594
|
|
prefect/telemetry/bootstrap.py
CHANGED
@@ -2,6 +2,12 @@ from typing import TYPE_CHECKING, Union
|
|
2
2
|
|
3
3
|
import prefect.settings
|
4
4
|
from prefect.client.base import ServerType, determine_server_type
|
5
|
+
from prefect.logging.loggers import get_logger
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
import logging
|
9
|
+
|
10
|
+
logger: "logging.Logger" = get_logger(__name__)
|
5
11
|
|
6
12
|
if TYPE_CHECKING:
|
7
13
|
from opentelemetry.sdk._logs import LoggerProvider
|
@@ -16,30 +22,28 @@ def setup_telemetry() -> (
|
|
16
22
|
]
|
17
23
|
):
|
18
24
|
settings = prefect.settings.get_current_settings()
|
19
|
-
if not settings.experiments.telemetry_enabled:
|
20
|
-
return None, None, None
|
21
25
|
|
22
26
|
server_type = determine_server_type()
|
23
27
|
if server_type != ServerType.CLOUD:
|
24
28
|
return None, None, None
|
25
29
|
|
30
|
+
if not settings.cloud.enable_orchestration_telemetry:
|
31
|
+
return None, None, None
|
32
|
+
|
26
33
|
if not settings.api.key:
|
27
|
-
|
34
|
+
logger.warning(
|
28
35
|
"A Prefect Cloud API key is required to enable telemetry. Please set "
|
29
36
|
"the `PREFECT_API_KEY` environment variable or authenticate with "
|
30
37
|
"Prefect Cloud via the `prefect cloud login` command."
|
31
38
|
)
|
39
|
+
return None, None, None
|
32
40
|
|
33
41
|
assert settings.api.url
|
34
42
|
|
35
43
|
# This import is here to defer importing of the `opentelemetry` packages.
|
36
44
|
try:
|
37
45
|
from .instrumentation import setup_exporters
|
38
|
-
except ImportError
|
39
|
-
|
40
|
-
"Unable to import OpenTelemetry instrumentation libraries. Please "
|
41
|
-
"ensure you have installed the `otel` extra when installing Prefect: "
|
42
|
-
"`pip install 'prefect[otel]'`"
|
43
|
-
) from exc
|
46
|
+
except ImportError:
|
47
|
+
return None, None, None
|
44
48
|
|
45
49
|
return setup_exporters(settings.api.url, settings.api.key.get_secret_value())
|
@@ -1,6 +1,8 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import time
|
2
4
|
from dataclasses import dataclass, field
|
3
|
-
from typing import TYPE_CHECKING, Any,
|
5
|
+
from typing import TYPE_CHECKING, Any, Union
|
4
6
|
|
5
7
|
from opentelemetry import propagate, trace
|
6
8
|
from opentelemetry.context import Context
|
@@ -47,14 +49,14 @@ class RunTelemetry:
|
|
47
49
|
_tracer: "Tracer" = field(
|
48
50
|
default_factory=lambda: get_tracer("prefect", prefect.__version__)
|
49
51
|
)
|
50
|
-
span:
|
52
|
+
span: Span | None = None
|
51
53
|
|
52
54
|
async def async_start_span(
|
53
55
|
self,
|
54
56
|
run: FlowOrTaskRun,
|
55
57
|
client: PrefectClient,
|
56
|
-
parameters:
|
57
|
-
):
|
58
|
+
parameters: dict[str, Any] | None = None,
|
59
|
+
) -> Span:
|
58
60
|
traceparent, span = self._start_span(run, parameters)
|
59
61
|
|
60
62
|
if self._run_type(run) == "flow" and traceparent:
|
@@ -70,8 +72,8 @@ class RunTelemetry:
|
|
70
72
|
self,
|
71
73
|
run: FlowOrTaskRun,
|
72
74
|
client: SyncPrefectClient,
|
73
|
-
parameters:
|
74
|
-
):
|
75
|
+
parameters: dict[str, Any] | None = None,
|
76
|
+
) -> Span:
|
75
77
|
traceparent, span = self._start_span(run, parameters)
|
76
78
|
|
77
79
|
if self._run_type(run) == "flow" and traceparent:
|
@@ -84,8 +86,8 @@ class RunTelemetry:
|
|
84
86
|
def _start_span(
|
85
87
|
self,
|
86
88
|
run: FlowOrTaskRun,
|
87
|
-
parameters:
|
88
|
-
) -> tuple[
|
89
|
+
parameters: dict[str, Any] | None = None,
|
90
|
+
) -> tuple[str | None, Span]:
|
89
91
|
"""
|
90
92
|
Start a span for a run.
|
91
93
|
"""
|
@@ -139,8 +141,8 @@ class RunTelemetry:
|
|
139
141
|
return "task" if isinstance(run, TaskRun) else "flow"
|
140
142
|
|
141
143
|
def _trace_context_from_labels(
|
142
|
-
self, labels:
|
143
|
-
) ->
|
144
|
+
self, labels: KeyValueLabels | None
|
145
|
+
) -> Context | None:
|
144
146
|
"""Get trace context from run labels if it exists."""
|
145
147
|
if not labels or LABELS_TRACEPARENT_KEY not in labels:
|
146
148
|
return None
|
@@ -148,7 +150,7 @@ class RunTelemetry:
|
|
148
150
|
carrier = {TRACEPARENT_KEY: traceparent}
|
149
151
|
return propagate.extract(carrier)
|
150
152
|
|
151
|
-
def _traceparent_from_span(self, span: Span) ->
|
153
|
+
def _traceparent_from_span(self, span: Span) -> str | None:
|
152
154
|
carrier: dict[str, Any] = {}
|
153
155
|
propagate.inject(carrier, context=trace.set_span_in_context(span))
|
154
156
|
return carrier.get(TRACEPARENT_KEY)
|
@@ -162,7 +164,7 @@ class RunTelemetry:
|
|
162
164
|
self.span.end(time.time_ns())
|
163
165
|
self.span = None
|
164
166
|
|
165
|
-
def end_span_on_failure(self, terminal_message:
|
167
|
+
def end_span_on_failure(self, terminal_message: str | None = None) -> None:
|
166
168
|
"""
|
167
169
|
End a span for a run on failure.
|
168
170
|
"""
|
@@ -203,7 +205,7 @@ class RunTelemetry:
|
|
203
205
|
self.span.update_name(name=name)
|
204
206
|
self.span.set_attribute("prefect.run.name", name)
|
205
207
|
|
206
|
-
def _parent_run(self) ->
|
208
|
+
def _parent_run(self) -> FlowOrTaskRun | None:
|
207
209
|
"""
|
208
210
|
Identify the "parent run" for the current execution context.
|
209
211
|
|
prefect/telemetry/services.py
CHANGED
@@ -53,6 +53,8 @@ class QueueingSpanExporter(BaseQueueingExporter[ReadableSpan], SpanExporter):
|
|
53
53
|
|
54
54
|
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
|
55
55
|
for item in spans:
|
56
|
+
if self._stopped:
|
57
|
+
break
|
56
58
|
self.send(item)
|
57
59
|
return SpanExportResult.SUCCESS
|
58
60
|
|
@@ -65,4 +67,6 @@ class QueueingLogExporter(BaseQueueingExporter[LogData], LogExporter):
|
|
65
67
|
|
66
68
|
def export(self, batch: Sequence[LogData]) -> None:
|
67
69
|
for item in batch:
|
70
|
+
if self._stopped:
|
71
|
+
break
|
68
72
|
self.send(item)
|
prefect/transactions.py
CHANGED
@@ -173,7 +173,7 @@ class Transaction(ContextModel):
|
|
173
173
|
def is_active(self) -> bool:
|
174
174
|
return self.state == TransactionState.ACTIVE
|
175
175
|
|
176
|
-
def __enter__(self):
|
176
|
+
def __enter__(self) -> Self:
|
177
177
|
if self._token is not None:
|
178
178
|
raise RuntimeError(
|
179
179
|
"Context already entered. Context enter calls cannot be nested."
|
@@ -206,7 +206,7 @@ class Transaction(ContextModel):
|
|
206
206
|
self._token = self.__var__.set(self)
|
207
207
|
return self
|
208
208
|
|
209
|
-
def __exit__(self, *exc_info: Any):
|
209
|
+
def __exit__(self, *exc_info: Any) -> None:
|
210
210
|
exc_type, exc_val, _ = exc_info
|
211
211
|
if not self._token:
|
212
212
|
raise RuntimeError(
|
@@ -235,7 +235,7 @@ class Transaction(ContextModel):
|
|
235
235
|
|
236
236
|
self.reset()
|
237
237
|
|
238
|
-
def begin(self):
|
238
|
+
def begin(self) -> None:
|
239
239
|
if (
|
240
240
|
self.store
|
241
241
|
and self.key
|
prefect/types/__init__.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
from functools import partial
|
2
4
|
from typing import Annotated, Any, Dict, List, Optional, Set, TypeVar, Union
|
3
5
|
from typing_extensions import Literal, TypeAlias
|
@@ -114,7 +116,7 @@ class SecretDict(pydantic.Secret[Dict[str, Any]]):
|
|
114
116
|
|
115
117
|
|
116
118
|
def validate_set_T_from_delim_string(
|
117
|
-
value: Union[str, T, Set[T], None], type_, delim=None
|
119
|
+
value: Union[str, T, Set[T], None], type_: type[T], delim: str | None = None
|
118
120
|
) -> Set[T]:
|
119
121
|
"""
|
120
122
|
"no-info" before validator useful in scooping env vars
|
@@ -0,0 +1,38 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
from jsonpatch import ( # type: ignore # no typing stubs available, see https://github.com/stefankoegl/python-json-patch/issues/158
|
4
|
+
JsonPatch as JsonPatchBase,
|
5
|
+
)
|
6
|
+
from pydantic import GetJsonSchemaHandler
|
7
|
+
from pydantic.json_schema import JsonSchemaValue
|
8
|
+
from pydantic_core import core_schema
|
9
|
+
|
10
|
+
|
11
|
+
class JsonPatch(JsonPatchBase):
|
12
|
+
@classmethod
|
13
|
+
def __get_pydantic_core_schema__(
|
14
|
+
cls, source_type: Any, handler: GetJsonSchemaHandler
|
15
|
+
) -> core_schema.CoreSchema:
|
16
|
+
return core_schema.typed_dict_schema(
|
17
|
+
{"patch": core_schema.typed_dict_field(core_schema.dict_schema())}
|
18
|
+
)
|
19
|
+
|
20
|
+
@classmethod
|
21
|
+
def __get_pydantic_json_schema__(
|
22
|
+
cls, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
|
23
|
+
) -> JsonSchemaValue:
|
24
|
+
json_schema = handler(core_schema)
|
25
|
+
json_schema = handler.resolve_ref_schema(json_schema)
|
26
|
+
json_schema.pop("required", None)
|
27
|
+
json_schema.pop("properties", None)
|
28
|
+
json_schema.update(
|
29
|
+
{
|
30
|
+
"type": "array",
|
31
|
+
"format": "rfc6902",
|
32
|
+
"items": {
|
33
|
+
"type": "object",
|
34
|
+
"additionalProperties": {"type": "string"},
|
35
|
+
},
|
36
|
+
}
|
37
|
+
)
|
38
|
+
return json_schema
|
prefect/utilities/engine.py
CHANGED
@@ -757,12 +757,19 @@ def resolve_to_final_result(expr: Any, context: dict[str, Any]) -> Any:
|
|
757
757
|
parameter_context = propagate.extract(
|
758
758
|
{"traceparent": state.state_details.traceparent}
|
759
759
|
)
|
760
|
-
|
761
|
-
|
762
|
-
|
760
|
+
attributes = {}
|
761
|
+
|
762
|
+
# If this future is being used as a parameter (as opposed to just a wait_for),
|
763
|
+
# add attributes to the span to indicate the parameter name and type
|
764
|
+
if "parameter_name" in context:
|
765
|
+
attributes = {
|
763
766
|
"prefect.input.name": context["parameter_name"],
|
764
767
|
"prefect.input.type": type(result).__name__,
|
765
|
-
}
|
768
|
+
}
|
769
|
+
|
770
|
+
trace.get_current_span().add_link(
|
771
|
+
context=trace.get_current_span(parameter_context).get_span_context(),
|
772
|
+
attributes=attributes,
|
766
773
|
)
|
767
774
|
|
768
775
|
return result
|
prefect/utilities/filesystem.py
CHANGED
@@ -8,7 +8,7 @@ import threading
|
|
8
8
|
from collections.abc import Iterable
|
9
9
|
from contextlib import contextmanager
|
10
10
|
from pathlib import Path, PureWindowsPath
|
11
|
-
from typing import AnyStr, Optional, Union, cast
|
11
|
+
from typing import Any, AnyStr, Optional, Union, cast
|
12
12
|
|
13
13
|
# fsspec has no stubs, see https://github.com/fsspec/filesystem_spec/issues/625
|
14
14
|
import fsspec # type: ignore
|
@@ -114,7 +114,7 @@ def filename(path: str) -> str:
|
|
114
114
|
return path.split(sep)[-1]
|
115
115
|
|
116
116
|
|
117
|
-
def is_local_path(path: Union[str, pathlib.Path,
|
117
|
+
def is_local_path(path: Union[str, pathlib.Path, Any]) -> bool:
|
118
118
|
"""Check if the given path points to a local or remote file system"""
|
119
119
|
if isinstance(path, str):
|
120
120
|
try:
|
prefect/utilities/generics.py
CHANGED
@@ -5,7 +5,7 @@ from pydantic_core import SchemaValidator, core_schema
|
|
5
5
|
|
6
6
|
T = TypeVar("T", bound=BaseModel)
|
7
7
|
|
8
|
-
ListValidator = SchemaValidator(
|
8
|
+
ListValidator: SchemaValidator = SchemaValidator(
|
9
9
|
schema=core_schema.list_schema(
|
10
10
|
items_schema=core_schema.dict_schema(
|
11
11
|
keys_schema=core_schema.str_schema(), values_schema=core_schema.any_schema()
|
prefect/utilities/pydantic.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import warnings
|
1
2
|
from typing import (
|
2
3
|
Any,
|
3
4
|
Callable,
|
@@ -10,18 +11,13 @@ from typing import (
|
|
10
11
|
overload,
|
11
12
|
)
|
12
13
|
|
13
|
-
from jsonpatch import ( # type: ignore # no typing stubs available, see https://github.com/stefankoegl/python-json-patch/issues/158
|
14
|
-
JsonPatch as JsonPatchBase,
|
15
|
-
)
|
16
14
|
from pydantic import (
|
17
15
|
BaseModel,
|
18
|
-
GetJsonSchemaHandler,
|
19
16
|
Secret,
|
20
17
|
TypeAdapter,
|
21
18
|
ValidationError,
|
22
19
|
)
|
23
|
-
from
|
24
|
-
from pydantic_core import core_schema, to_jsonable_python
|
20
|
+
from pydantic_core import to_jsonable_python
|
25
21
|
from typing_extensions import Literal
|
26
22
|
|
27
23
|
from prefect.utilities.dispatch import get_dispatch_key, lookup_type, register_base_type
|
@@ -262,36 +258,6 @@ class PartialModel(Generic[M]):
|
|
262
258
|
return f"PartialModel(cls={self.model_cls.__name__}, {dsp_fields})"
|
263
259
|
|
264
260
|
|
265
|
-
class JsonPatch(JsonPatchBase):
|
266
|
-
@classmethod
|
267
|
-
def __get_pydantic_core_schema__(
|
268
|
-
cls, source_type: Any, handler: GetJsonSchemaHandler
|
269
|
-
) -> core_schema.CoreSchema:
|
270
|
-
return core_schema.typed_dict_schema(
|
271
|
-
{"patch": core_schema.typed_dict_field(core_schema.dict_schema())}
|
272
|
-
)
|
273
|
-
|
274
|
-
@classmethod
|
275
|
-
def __get_pydantic_json_schema__(
|
276
|
-
cls, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
|
277
|
-
) -> JsonSchemaValue:
|
278
|
-
json_schema = handler(core_schema)
|
279
|
-
json_schema = handler.resolve_ref_schema(json_schema)
|
280
|
-
json_schema.pop("required", None)
|
281
|
-
json_schema.pop("properties", None)
|
282
|
-
json_schema.update(
|
283
|
-
{
|
284
|
-
"type": "array",
|
285
|
-
"format": "rfc6902",
|
286
|
-
"items": {
|
287
|
-
"type": "object",
|
288
|
-
"additionalProperties": {"type": "string"},
|
289
|
-
},
|
290
|
-
}
|
291
|
-
)
|
292
|
-
return json_schema
|
293
|
-
|
294
|
-
|
295
261
|
def custom_pydantic_encoder(
|
296
262
|
type_encoders: dict[Any, Callable[[type[Any]], Any]], obj: Any
|
297
263
|
) -> Any:
|
@@ -382,3 +348,22 @@ def handle_secret_render(value: object, context: dict[str, Any]) -> object:
|
|
382
348
|
elif isinstance(value, BaseModel):
|
383
349
|
return value.model_dump(context=context)
|
384
350
|
return value
|
351
|
+
|
352
|
+
|
353
|
+
def __getattr__(name: str) -> Any:
|
354
|
+
"""
|
355
|
+
Handles imports from this module that are deprecated.
|
356
|
+
"""
|
357
|
+
|
358
|
+
if name == "JsonPatch":
|
359
|
+
warnings.warn(
|
360
|
+
"JsonPatch is deprecated and will be removed after March 2025. "
|
361
|
+
"Please use `JsonPatch` from the `jsonpatch` package instead.",
|
362
|
+
DeprecationWarning,
|
363
|
+
stacklevel=2,
|
364
|
+
)
|
365
|
+
from ._deprecated import JsonPatch
|
366
|
+
|
367
|
+
return JsonPatch
|
368
|
+
else:
|
369
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
prefect/utilities/templating.py
CHANGED
@@ -1,7 +1,17 @@
|
|
1
1
|
import enum
|
2
2
|
import os
|
3
3
|
import re
|
4
|
-
from typing import
|
4
|
+
from typing import (
|
5
|
+
TYPE_CHECKING,
|
6
|
+
Any,
|
7
|
+
Literal,
|
8
|
+
NamedTuple,
|
9
|
+
Optional,
|
10
|
+
TypeVar,
|
11
|
+
Union,
|
12
|
+
cast,
|
13
|
+
overload,
|
14
|
+
)
|
5
15
|
|
6
16
|
from prefect.client.utilities import inject_client
|
7
17
|
from prefect.utilities.annotations import NotSet
|
@@ -79,6 +89,20 @@ def find_placeholders(template: T) -> set[Placeholder]:
|
|
79
89
|
raise ValueError(f"Unexpected type: {type(template)}")
|
80
90
|
|
81
91
|
|
92
|
+
@overload
|
93
|
+
def apply_values(
|
94
|
+
template: T, values: dict[str, Any], remove_notset: Literal[True] = True
|
95
|
+
) -> T:
|
96
|
+
...
|
97
|
+
|
98
|
+
|
99
|
+
@overload
|
100
|
+
def apply_values(
|
101
|
+
template: T, values: dict[str, Any], remove_notset: Literal[False] = False
|
102
|
+
) -> Union[T, type[NotSet]]:
|
103
|
+
...
|
104
|
+
|
105
|
+
|
82
106
|
def apply_values(
|
83
107
|
template: T, values: dict[str, Any], remove_notset: bool = True
|
84
108
|
) -> Union[T, type[NotSet]]:
|