prefect-client 3.1.10__py3-none-any.whl → 3.1.12__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/lineage.py +7 -8
- prefect/_experimental/sla/__init__.py +0 -0
- prefect/_experimental/sla/client.py +66 -0
- prefect/_experimental/sla/objects.py +53 -0
- prefect/_internal/_logging.py +15 -3
- prefect/_internal/compatibility/async_dispatch.py +22 -16
- prefect/_internal/compatibility/deprecated.py +42 -18
- prefect/_internal/compatibility/migration.py +2 -2
- prefect/_internal/concurrency/inspection.py +12 -14
- prefect/_internal/concurrency/primitives.py +2 -2
- prefect/_internal/concurrency/services.py +154 -80
- prefect/_internal/concurrency/waiters.py +13 -9
- prefect/_internal/pydantic/annotations/pendulum.py +7 -7
- prefect/_internal/pytz.py +4 -3
- prefect/_internal/retries.py +10 -5
- prefect/_internal/schemas/bases.py +19 -10
- prefect/_internal/schemas/validators.py +227 -388
- prefect/_version.py +3 -3
- prefect/automations.py +236 -30
- prefect/blocks/__init__.py +3 -3
- prefect/blocks/abstract.py +53 -30
- prefect/blocks/core.py +183 -84
- prefect/blocks/notifications.py +133 -73
- prefect/blocks/redis.py +13 -9
- prefect/blocks/system.py +24 -11
- prefect/blocks/webhook.py +7 -5
- prefect/cache_policies.py +3 -2
- prefect/client/orchestration/__init__.py +1957 -0
- prefect/client/orchestration/_artifacts/__init__.py +0 -0
- prefect/client/orchestration/_artifacts/client.py +239 -0
- 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/_concurrency_limits/__init__.py +0 -0
- prefect/client/orchestration/_concurrency_limits/client.py +762 -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/__init__.py +0 -0
- prefect/client/orchestration/_logs/client.py +97 -0
- prefect/client/orchestration/_variables/__init__.py +0 -0
- prefect/client/orchestration/_variables/client.py +157 -0
- prefect/client/orchestration/base.py +46 -0
- prefect/client/orchestration/routes.py +145 -0
- prefect/client/schemas/__init__.py +68 -28
- prefect/client/schemas/actions.py +2 -2
- prefect/client/schemas/filters.py +5 -0
- prefect/client/schemas/objects.py +8 -15
- prefect/client/schemas/schedules.py +22 -10
- prefect/concurrency/_asyncio.py +87 -0
- prefect/concurrency/{events.py → _events.py} +10 -10
- prefect/concurrency/asyncio.py +20 -104
- prefect/concurrency/context.py +6 -4
- prefect/concurrency/services.py +26 -74
- prefect/concurrency/sync.py +23 -44
- prefect/concurrency/v1/_asyncio.py +63 -0
- prefect/concurrency/v1/{events.py → _events.py} +13 -15
- prefect/concurrency/v1/asyncio.py +27 -80
- prefect/concurrency/v1/context.py +6 -4
- prefect/concurrency/v1/services.py +33 -79
- prefect/concurrency/v1/sync.py +18 -37
- prefect/context.py +66 -45
- prefect/deployments/base.py +10 -144
- prefect/deployments/flow_runs.py +12 -2
- prefect/deployments/runner.py +53 -4
- prefect/deployments/steps/pull.py +13 -0
- prefect/engine.py +17 -4
- prefect/events/clients.py +7 -1
- prefect/events/schemas/events.py +3 -2
- prefect/filesystems.py +6 -2
- prefect/flow_engine.py +101 -85
- prefect/flows.py +10 -1
- prefect/input/run_input.py +2 -1
- prefect/logging/logging.yml +1 -1
- prefect/main.py +1 -3
- prefect/results.py +2 -307
- prefect/runner/runner.py +4 -2
- prefect/runner/storage.py +87 -21
- prefect/serializers.py +32 -25
- prefect/settings/legacy.py +4 -4
- prefect/settings/models/api.py +3 -3
- prefect/settings/models/cli.py +3 -3
- prefect/settings/models/client.py +5 -3
- prefect/settings/models/cloud.py +8 -3
- prefect/settings/models/deployments.py +3 -3
- prefect/settings/models/experiments.py +4 -7
- prefect/settings/models/flows.py +3 -3
- prefect/settings/models/internal.py +4 -2
- prefect/settings/models/logging.py +4 -3
- prefect/settings/models/results.py +3 -3
- prefect/settings/models/root.py +3 -2
- prefect/settings/models/runner.py +4 -4
- prefect/settings/models/server/api.py +3 -3
- prefect/settings/models/server/database.py +11 -4
- prefect/settings/models/server/deployments.py +6 -2
- prefect/settings/models/server/ephemeral.py +4 -2
- prefect/settings/models/server/events.py +3 -2
- prefect/settings/models/server/flow_run_graph.py +6 -2
- prefect/settings/models/server/root.py +3 -3
- prefect/settings/models/server/services.py +26 -11
- prefect/settings/models/server/tasks.py +6 -3
- prefect/settings/models/server/ui.py +3 -3
- prefect/settings/models/tasks.py +5 -5
- prefect/settings/models/testing.py +3 -3
- prefect/settings/models/worker.py +5 -3
- prefect/settings/profiles.py +15 -2
- prefect/states.py +61 -45
- prefect/task_engine.py +54 -75
- prefect/task_runners.py +56 -55
- prefect/task_worker.py +2 -2
- prefect/tasks.py +90 -36
- prefect/telemetry/bootstrap.py +10 -9
- prefect/telemetry/run_telemetry.py +13 -8
- prefect/telemetry/services.py +4 -0
- prefect/transactions.py +4 -15
- prefect/utilities/_git.py +34 -0
- prefect/utilities/asyncutils.py +1 -1
- prefect/utilities/engine.py +3 -19
- prefect/utilities/generics.py +18 -0
- prefect/utilities/templating.py +25 -1
- prefect/workers/base.py +6 -3
- prefect/workers/process.py +1 -1
- {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/METADATA +2 -2
- {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/RECORD +135 -109
- prefect/client/orchestration.py +0 -4523
- prefect/records/__init__.py +0 -1
- prefect/records/base.py +0 -235
- prefect/records/filesystem.py +0 -213
- prefect/records/memory.py +0 -184
- prefect/records/result_store.py +0 -70
- {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/WHEEL +0 -0
- {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/top_level.txt +0 -0
prefect/deployments/base.py
CHANGED
@@ -5,27 +5,21 @@ build system for managing flows and deployments.
|
|
5
5
|
To get started, follow along with [the deloyments tutorial](/tutorials/deployments/).
|
6
6
|
"""
|
7
7
|
|
8
|
-
import
|
9
|
-
|
10
|
-
import math
|
8
|
+
from __future__ import annotations
|
9
|
+
|
11
10
|
import os
|
12
|
-
import subprocess
|
13
|
-
import sys
|
14
11
|
from copy import deepcopy
|
15
12
|
from pathlib import Path
|
16
13
|
from typing import Any, Dict, List, Optional, cast
|
17
14
|
|
18
|
-
import anyio
|
19
15
|
import yaml
|
20
16
|
from ruamel.yaml import YAML
|
21
17
|
|
22
18
|
from prefect.client.schemas.actions import DeploymentScheduleCreate
|
23
19
|
from prefect.client.schemas.objects import ConcurrencyLimitStrategy
|
24
20
|
from prefect.client.schemas.schedules import IntervalSchedule
|
25
|
-
from prefect.
|
26
|
-
from prefect.
|
27
|
-
from prefect.utilities.asyncutils import LazySemaphore
|
28
|
-
from prefect.utilities.filesystem import create_default_ignore_file, get_open_file_limit
|
21
|
+
from prefect.utilities._git import get_git_branch, get_git_remote_origin_url
|
22
|
+
from prefect.utilities.filesystem import create_default_ignore_file
|
29
23
|
from prefect.utilities.templating import apply_values
|
30
24
|
|
31
25
|
|
@@ -146,36 +140,6 @@ def configure_project_by_recipe(recipe: str, **formatting_kwargs) -> dict:
|
|
146
140
|
return config
|
147
141
|
|
148
142
|
|
149
|
-
def _get_git_remote_origin_url() -> Optional[str]:
|
150
|
-
"""
|
151
|
-
Returns the git remote origin URL for the current directory.
|
152
|
-
"""
|
153
|
-
try:
|
154
|
-
origin_url = subprocess.check_output(
|
155
|
-
["git", "config", "--get", "remote.origin.url"],
|
156
|
-
shell=sys.platform == "win32",
|
157
|
-
stderr=subprocess.DEVNULL,
|
158
|
-
)
|
159
|
-
origin_url = origin_url.decode().strip()
|
160
|
-
except subprocess.CalledProcessError:
|
161
|
-
return None
|
162
|
-
|
163
|
-
return origin_url
|
164
|
-
|
165
|
-
|
166
|
-
def _get_git_branch() -> Optional[str]:
|
167
|
-
"""
|
168
|
-
Returns the git branch for the current directory.
|
169
|
-
"""
|
170
|
-
try:
|
171
|
-
branch = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
|
172
|
-
branch = branch.decode().strip()
|
173
|
-
except subprocess.CalledProcessError:
|
174
|
-
return None
|
175
|
-
|
176
|
-
return branch
|
177
|
-
|
178
|
-
|
179
143
|
def initialize_project(
|
180
144
|
name: Optional[str] = None,
|
181
145
|
recipe: Optional[str] = None,
|
@@ -198,11 +162,11 @@ def initialize_project(
|
|
198
162
|
formatting_kwargs = {"directory": str(Path(".").absolute().resolve())}
|
199
163
|
dir_name = os.path.basename(os.getcwd())
|
200
164
|
|
201
|
-
remote_url =
|
165
|
+
remote_url = get_git_remote_origin_url()
|
202
166
|
if remote_url:
|
203
167
|
formatting_kwargs["repository"] = remote_url
|
204
168
|
is_git_based = True
|
205
|
-
branch =
|
169
|
+
branch = get_git_branch()
|
206
170
|
formatting_kwargs["branch"] = branch or "main"
|
207
171
|
|
208
172
|
formatting_kwargs["name"] = dir_name
|
@@ -313,6 +277,7 @@ def _save_deployment_to_prefect_file(
|
|
313
277
|
push_steps: Optional[List[Dict]] = None,
|
314
278
|
pull_steps: Optional[List[Dict]] = None,
|
315
279
|
triggers: Optional[List[Dict]] = None,
|
280
|
+
sla: Optional[list[dict]] = None,
|
316
281
|
prefect_file: Path = Path("prefect.yaml"),
|
317
282
|
):
|
318
283
|
"""
|
@@ -357,6 +322,9 @@ def _save_deployment_to_prefect_file(
|
|
357
322
|
if triggers and triggers != parsed_prefect_file_contents.get("triggers"):
|
358
323
|
deployment["triggers"] = triggers
|
359
324
|
|
325
|
+
if sla and sla != parsed_prefect_file_contents.get("sla"):
|
326
|
+
deployment["sla"] = sla
|
327
|
+
|
360
328
|
deployments = parsed_prefect_file_contents.get("deployments")
|
361
329
|
if deployments is None:
|
362
330
|
parsed_prefect_file_contents["deployments"] = [deployment]
|
@@ -373,105 +341,3 @@ def _save_deployment_to_prefect_file(
|
|
373
341
|
|
374
342
|
with prefect_file.open(mode="w") as f:
|
375
343
|
ryaml.dump(parsed_prefect_file_contents, f)
|
376
|
-
|
377
|
-
|
378
|
-
# Only allow half of the open file limit to be open at once to allow for other
|
379
|
-
# actors to open files.
|
380
|
-
OPEN_FILE_SEMAPHORE = LazySemaphore(lambda: math.floor(get_open_file_limit() * 0.5))
|
381
|
-
|
382
|
-
|
383
|
-
async def _find_flow_functions_in_file(filename: str) -> List[Dict]:
|
384
|
-
decorator_name = "flow"
|
385
|
-
decorator_module = "prefect"
|
386
|
-
decorated_functions = []
|
387
|
-
async with OPEN_FILE_SEMAPHORE:
|
388
|
-
try:
|
389
|
-
async with await anyio.open_file(filename) as f:
|
390
|
-
try:
|
391
|
-
tree = ast.parse(await f.read())
|
392
|
-
except SyntaxError:
|
393
|
-
if PREFECT_DEBUG_MODE:
|
394
|
-
get_logger().debug(
|
395
|
-
f"Could not parse {filename} as a Python file. Skipping."
|
396
|
-
)
|
397
|
-
return decorated_functions
|
398
|
-
except Exception as exc:
|
399
|
-
if PREFECT_DEBUG_MODE:
|
400
|
-
get_logger().debug(f"Could not open {filename}: {exc}. Skipping.")
|
401
|
-
return decorated_functions
|
402
|
-
|
403
|
-
for node in ast.walk(tree):
|
404
|
-
if isinstance(
|
405
|
-
node,
|
406
|
-
(
|
407
|
-
ast.FunctionDef,
|
408
|
-
ast.AsyncFunctionDef,
|
409
|
-
),
|
410
|
-
):
|
411
|
-
for decorator in node.decorator_list:
|
412
|
-
# handles @flow
|
413
|
-
is_name_match = (
|
414
|
-
isinstance(decorator, ast.Name) and decorator.id == decorator_name
|
415
|
-
)
|
416
|
-
# handles @flow()
|
417
|
-
is_func_name_match = (
|
418
|
-
isinstance(decorator, ast.Call)
|
419
|
-
and isinstance(decorator.func, ast.Name)
|
420
|
-
and decorator.func.id == decorator_name
|
421
|
-
)
|
422
|
-
# handles @prefect.flow
|
423
|
-
is_module_attribute_match = (
|
424
|
-
isinstance(decorator, ast.Attribute)
|
425
|
-
and isinstance(decorator.value, ast.Name)
|
426
|
-
and decorator.value.id == decorator_module
|
427
|
-
and decorator.attr == decorator_name
|
428
|
-
)
|
429
|
-
# handles @prefect.flow()
|
430
|
-
is_module_attribute_func_match = (
|
431
|
-
isinstance(decorator, ast.Call)
|
432
|
-
and isinstance(decorator.func, ast.Attribute)
|
433
|
-
and decorator.func.attr == decorator_name
|
434
|
-
and isinstance(decorator.func.value, ast.Name)
|
435
|
-
and decorator.func.value.id == decorator_module
|
436
|
-
)
|
437
|
-
if is_name_match or is_module_attribute_match:
|
438
|
-
decorated_functions.append(
|
439
|
-
{
|
440
|
-
"flow_name": node.name,
|
441
|
-
"function_name": node.name,
|
442
|
-
"filepath": str(filename),
|
443
|
-
}
|
444
|
-
)
|
445
|
-
if is_func_name_match or is_module_attribute_func_match:
|
446
|
-
name_kwarg_node = next(
|
447
|
-
(kw for kw in decorator.keywords if kw.arg == "name"), None
|
448
|
-
)
|
449
|
-
flow_name = (
|
450
|
-
name_kwarg_node.value.value
|
451
|
-
if isinstance(name_kwarg_node, ast.Constant)
|
452
|
-
else node.name
|
453
|
-
)
|
454
|
-
decorated_functions.append(
|
455
|
-
{
|
456
|
-
"flow_name": flow_name,
|
457
|
-
"function_name": node.name,
|
458
|
-
"filepath": str(filename),
|
459
|
-
}
|
460
|
-
)
|
461
|
-
return decorated_functions
|
462
|
-
|
463
|
-
|
464
|
-
async def _search_for_flow_functions(directory: str = ".") -> List[Dict]:
|
465
|
-
"""
|
466
|
-
Search for flow functions in the provided directory. If no directory is provided,
|
467
|
-
the current working directory is used.
|
468
|
-
|
469
|
-
Returns:
|
470
|
-
List[Dict]: the flow name, function name, and filepath of all flow functions found
|
471
|
-
"""
|
472
|
-
path = anyio.Path(directory)
|
473
|
-
coros = []
|
474
|
-
async for file in path.rglob("*.py"):
|
475
|
-
coros.append(_find_flow_functions_in_file(file))
|
476
|
-
|
477
|
-
return [fn for file_fns in await asyncio.gather(*coros) for fn in file_fns]
|
prefect/deployments/flow_runs.py
CHANGED
@@ -10,9 +10,12 @@ from prefect.client.schemas import FlowRun
|
|
10
10
|
from prefect.client.utilities import inject_client
|
11
11
|
from prefect.context import FlowRunContext, TaskRunContext
|
12
12
|
from prefect.logging import get_logger
|
13
|
-
from prefect.results import
|
13
|
+
from prefect.results import ResultRecordMetadata
|
14
14
|
from prefect.states import Pending, Scheduled
|
15
15
|
from prefect.tasks import Task
|
16
|
+
from prefect.telemetry.run_telemetry import (
|
17
|
+
LABELS_TRACEPARENT_KEY,
|
18
|
+
)
|
16
19
|
from prefect.utilities.asyncutils import sync_compatible
|
17
20
|
from prefect.utilities.slugify import slugify
|
18
21
|
|
@@ -22,7 +25,6 @@ if TYPE_CHECKING:
|
|
22
25
|
|
23
26
|
prefect.client.schemas.StateCreate.model_rebuild(
|
24
27
|
_types_namespace={
|
25
|
-
"BaseResult": BaseResult,
|
26
28
|
"ResultRecordMetadata": ResultRecordMetadata,
|
27
29
|
}
|
28
30
|
)
|
@@ -156,6 +158,13 @@ async def run_deployment(
|
|
156
158
|
else:
|
157
159
|
parent_task_run_id = None
|
158
160
|
|
161
|
+
if flow_run_ctx and flow_run_ctx.flow_run:
|
162
|
+
traceparent = flow_run_ctx.flow_run.labels.get(LABELS_TRACEPARENT_KEY)
|
163
|
+
else:
|
164
|
+
traceparent = None
|
165
|
+
|
166
|
+
trace_labels = {LABELS_TRACEPARENT_KEY: traceparent} if traceparent else {}
|
167
|
+
|
159
168
|
flow_run = await client.create_flow_run_from_deployment(
|
160
169
|
deployment.id,
|
161
170
|
parameters=parameters,
|
@@ -166,6 +175,7 @@ async def run_deployment(
|
|
166
175
|
parent_task_run_id=parent_task_run_id,
|
167
176
|
work_queue_name=work_queue_name,
|
168
177
|
job_variables=job_variables,
|
178
|
+
labels=trace_labels,
|
169
179
|
)
|
170
180
|
|
171
181
|
flow_run_id = flow_run.id
|
prefect/deployments/runner.py
CHANGED
@@ -33,26 +33,30 @@ import importlib
|
|
33
33
|
import tempfile
|
34
34
|
from datetime import datetime, timedelta
|
35
35
|
from pathlib import Path
|
36
|
-
from typing import TYPE_CHECKING, Any, Iterable, List, Optional, Union
|
36
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Iterable, List, Optional, Union
|
37
37
|
from uuid import UUID
|
38
38
|
|
39
|
+
from exceptiongroup import ExceptionGroup # novermin
|
39
40
|
from pydantic import (
|
40
41
|
BaseModel,
|
41
42
|
ConfigDict,
|
42
43
|
Field,
|
43
44
|
PrivateAttr,
|
45
|
+
field_validator,
|
44
46
|
model_validator,
|
45
47
|
)
|
46
48
|
from rich.console import Console
|
47
49
|
from rich.progress import Progress, SpinnerColumn, TextColumn, track
|
48
50
|
from rich.table import Table
|
49
51
|
|
52
|
+
from prefect._experimental.sla.objects import SlaTypes
|
50
53
|
from prefect._internal.concurrency.api import create_call, from_async
|
51
54
|
from prefect._internal.schemas.validators import (
|
52
55
|
reconcile_paused_deployment,
|
53
56
|
reconcile_schedules_runner,
|
54
57
|
)
|
55
|
-
from prefect.client.
|
58
|
+
from prefect.client.base import ServerType
|
59
|
+
from prefect.client.orchestration import PrefectClient, get_client
|
56
60
|
from prefect.client.schemas.actions import DeploymentScheduleCreate
|
57
61
|
from prefect.client.schemas.filters import WorkerFilter, WorkerFilterStatus
|
58
62
|
from prefect.client.schemas.objects import (
|
@@ -127,9 +131,10 @@ class RunnerDeployment(BaseModel):
|
|
127
131
|
job_variables: Settings used to override the values specified default base job template
|
128
132
|
of the chosen work pool. Refer to the base job template of the chosen work pool for
|
129
133
|
available settings.
|
134
|
+
_sla: (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
|
130
135
|
"""
|
131
136
|
|
132
|
-
model_config = ConfigDict(arbitrary_types_allowed=True)
|
137
|
+
model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True)
|
133
138
|
|
134
139
|
name: str = Field(..., description="The name of the deployment.")
|
135
140
|
flow_name: Optional[str] = Field(
|
@@ -206,6 +211,10 @@ class RunnerDeployment(BaseModel):
|
|
206
211
|
" a built runner."
|
207
212
|
),
|
208
213
|
)
|
214
|
+
# (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
|
215
|
+
_sla: Optional[Union[SlaTypes, list[SlaTypes]]] = PrivateAttr(
|
216
|
+
default=None,
|
217
|
+
)
|
209
218
|
_entrypoint_type: EntrypointType = PrivateAttr(
|
210
219
|
default=EntrypointType.FILE_PATH,
|
211
220
|
)
|
@@ -220,6 +229,13 @@ class RunnerDeployment(BaseModel):
|
|
220
229
|
def entrypoint_type(self) -> EntrypointType:
|
221
230
|
return self._entrypoint_type
|
222
231
|
|
232
|
+
@field_validator("name", mode="before")
|
233
|
+
@classmethod
|
234
|
+
def validate_name(cls, value: str) -> str:
|
235
|
+
if value.endswith(".py"):
|
236
|
+
return Path(value).stem
|
237
|
+
return value
|
238
|
+
|
223
239
|
@model_validator(mode="after")
|
224
240
|
def validate_automation_names(self):
|
225
241
|
"""Ensure that each trigger has a name for its automation if none is provided."""
|
@@ -343,8 +359,32 @@ class RunnerDeployment(BaseModel):
|
|
343
359
|
trigger.set_deployment_id(deployment_id)
|
344
360
|
await client.create_automation(trigger.as_automation())
|
345
361
|
|
362
|
+
# We plan to support SLA configuration on the Prefect Server in the future.
|
363
|
+
# For now, we only support it on Prefect Cloud.
|
364
|
+
if self._sla:
|
365
|
+
await self._create_slas(deployment_id, client)
|
366
|
+
|
346
367
|
return deployment_id
|
347
368
|
|
369
|
+
async def _create_slas(self, deployment_id: UUID, client: PrefectClient):
|
370
|
+
if not isinstance(self._sla, list):
|
371
|
+
self._sla = [self._sla]
|
372
|
+
|
373
|
+
if client.server_type == ServerType.CLOUD:
|
374
|
+
exceptions = []
|
375
|
+
for sla in self._sla:
|
376
|
+
try:
|
377
|
+
sla.set_deployment_id(deployment_id)
|
378
|
+
await client.create_sla(sla)
|
379
|
+
except Exception as e:
|
380
|
+
exceptions.append(e)
|
381
|
+
if exceptions:
|
382
|
+
raise ExceptionGroup("Failed to create SLAs", exceptions) # novermin
|
383
|
+
else:
|
384
|
+
raise ValueError(
|
385
|
+
"SLA configuration is currently only supported on Prefect Cloud."
|
386
|
+
)
|
387
|
+
|
348
388
|
@staticmethod
|
349
389
|
def _construct_deployment_schedules(
|
350
390
|
interval: Optional[
|
@@ -459,6 +499,7 @@ class RunnerDeployment(BaseModel):
|
|
459
499
|
work_queue_name: Optional[str] = None,
|
460
500
|
job_variables: Optional[dict[str, Any]] = None,
|
461
501
|
entrypoint_type: EntrypointType = EntrypointType.FILE_PATH,
|
502
|
+
_sla: Optional[Union[SlaTypes, list[SlaTypes]]] = None, # experimental
|
462
503
|
) -> "RunnerDeployment":
|
463
504
|
"""
|
464
505
|
Configure a deployment for a given flow.
|
@@ -489,6 +530,7 @@ class RunnerDeployment(BaseModel):
|
|
489
530
|
job_variables: Settings used to override the values specified default base job template
|
490
531
|
of the chosen work pool. Refer to the base job template of the chosen work pool for
|
491
532
|
available settings.
|
533
|
+
_sla: (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
|
492
534
|
"""
|
493
535
|
constructed_schedules = cls._construct_deployment_schedules(
|
494
536
|
interval=interval,
|
@@ -508,7 +550,7 @@ class RunnerDeployment(BaseModel):
|
|
508
550
|
concurrency_options = None
|
509
551
|
|
510
552
|
deployment = cls(
|
511
|
-
name=
|
553
|
+
name=name,
|
512
554
|
flow_name=flow.name,
|
513
555
|
schedules=constructed_schedules,
|
514
556
|
concurrency_limit=concurrency_limit,
|
@@ -524,6 +566,7 @@ class RunnerDeployment(BaseModel):
|
|
524
566
|
work_queue_name=work_queue_name,
|
525
567
|
job_variables=job_variables,
|
526
568
|
)
|
569
|
+
deployment._sla = _sla
|
527
570
|
|
528
571
|
if not deployment.entrypoint:
|
529
572
|
no_file_location_error = (
|
@@ -599,6 +642,7 @@ class RunnerDeployment(BaseModel):
|
|
599
642
|
work_pool_name: Optional[str] = None,
|
600
643
|
work_queue_name: Optional[str] = None,
|
601
644
|
job_variables: Optional[dict[str, Any]] = None,
|
645
|
+
_sla: Optional[Union[SlaTypes, list[SlaTypes]]] = None, # experimental
|
602
646
|
) -> "RunnerDeployment":
|
603
647
|
"""
|
604
648
|
Configure a deployment for a given flow located at a given entrypoint.
|
@@ -630,6 +674,7 @@ class RunnerDeployment(BaseModel):
|
|
630
674
|
job_variables: Settings used to override the values specified default base job template
|
631
675
|
of the chosen work pool. Refer to the base job template of the chosen work pool for
|
632
676
|
available settings.
|
677
|
+
_sla: (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
|
633
678
|
"""
|
634
679
|
from prefect.flows import load_flow_from_entrypoint
|
635
680
|
|
@@ -669,6 +714,7 @@ class RunnerDeployment(BaseModel):
|
|
669
714
|
work_queue_name=work_queue_name,
|
670
715
|
job_variables=job_variables,
|
671
716
|
)
|
717
|
+
deployment._sla = _sla
|
672
718
|
deployment._path = str(Path.cwd())
|
673
719
|
|
674
720
|
cls._set_defaults_from_flow(deployment, flow)
|
@@ -700,6 +746,7 @@ class RunnerDeployment(BaseModel):
|
|
700
746
|
work_pool_name: Optional[str] = None,
|
701
747
|
work_queue_name: Optional[str] = None,
|
702
748
|
job_variables: Optional[dict[str, Any]] = None,
|
749
|
+
_sla: Optional[Union[SlaTypes, list[SlaTypes]]] = None, # experimental
|
703
750
|
):
|
704
751
|
"""
|
705
752
|
Create a RunnerDeployment from a flow located at a given entrypoint and stored in a
|
@@ -731,6 +778,7 @@ class RunnerDeployment(BaseModel):
|
|
731
778
|
job_variables: Settings used to override the values specified default base job template
|
732
779
|
of the chosen work pool. Refer to the base job template of the chosen work pool for
|
733
780
|
available settings.
|
781
|
+
_sla: (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
|
734
782
|
"""
|
735
783
|
from prefect.flows import load_flow_from_entrypoint
|
736
784
|
|
@@ -779,6 +827,7 @@ class RunnerDeployment(BaseModel):
|
|
779
827
|
work_queue_name=work_queue_name,
|
780
828
|
job_variables=job_variables,
|
781
829
|
)
|
830
|
+
deployment._sla = _sla
|
782
831
|
deployment._path = str(storage.destination).replace(
|
783
832
|
tmpdir, "$STORAGE_BASE_PATH"
|
784
833
|
)
|
@@ -50,6 +50,7 @@ async def agit_clone(
|
|
50
50
|
include_submodules: bool = False,
|
51
51
|
access_token: Optional[str] = None,
|
52
52
|
credentials: Optional["Block"] = None,
|
53
|
+
directories: Optional[list[str]] = None,
|
53
54
|
) -> dict[str, str]:
|
54
55
|
"""
|
55
56
|
Asynchronously clones a git repository into the current working directory.
|
@@ -81,6 +82,7 @@ async def agit_clone(
|
|
81
82
|
credentials=_credentials,
|
82
83
|
branch=branch,
|
83
84
|
include_submodules=include_submodules,
|
85
|
+
directories=directories,
|
84
86
|
)
|
85
87
|
|
86
88
|
await _pull_git_repository_with_retries(storage)
|
@@ -95,6 +97,7 @@ def git_clone(
|
|
95
97
|
include_submodules: bool = False,
|
96
98
|
access_token: Optional[str] = None,
|
97
99
|
credentials: Optional["Block"] = None,
|
100
|
+
directories: Optional[list[str]] = None,
|
98
101
|
) -> dict[str, str]:
|
99
102
|
"""
|
100
103
|
Clones a git repository into the current working directory.
|
@@ -107,6 +110,7 @@ def git_clone(
|
|
107
110
|
the repository will be cloned using the default git credentials
|
108
111
|
credentials: a GitHubCredentials, GitLabCredentials, or BitBucketCredentials block can be used to specify the
|
109
112
|
credentials to use for cloning the repository.
|
113
|
+
directories: Specify directories you want to be included (uses git sparse-checkout)
|
110
114
|
|
111
115
|
Returns:
|
112
116
|
dict: a dictionary containing a `directory` key of the new directory that was created
|
@@ -164,6 +168,14 @@ def git_clone(
|
|
164
168
|
- prefect.deployments.steps.git_clone:
|
165
169
|
repository: git@github.com:org/repo.git
|
166
170
|
```
|
171
|
+
|
172
|
+
Clone a repository using sparse-checkout (allows specific folders of the repository to be checked out)
|
173
|
+
```yaml
|
174
|
+
pull:
|
175
|
+
- prefect.deployments.steps.git_clone:
|
176
|
+
repository: https://github.com/org/repo.git
|
177
|
+
directories: ["dir_1", "dir_2", "prefect"]
|
178
|
+
```
|
167
179
|
"""
|
168
180
|
if access_token and credentials:
|
169
181
|
raise ValueError(
|
@@ -177,6 +189,7 @@ def git_clone(
|
|
177
189
|
credentials=_credentials,
|
178
190
|
branch=branch,
|
179
191
|
include_submodules=include_submodules,
|
192
|
+
directories=directories,
|
180
193
|
)
|
181
194
|
|
182
195
|
run_coro_as_sync(_pull_git_repository_with_retries(storage))
|
prefect/engine.py
CHANGED
@@ -31,16 +31,29 @@ if __name__ == "__main__":
|
|
31
31
|
|
32
32
|
try:
|
33
33
|
from prefect.flow_engine import (
|
34
|
-
|
34
|
+
flow_run_logger,
|
35
|
+
load_flow,
|
36
|
+
load_flow_run,
|
35
37
|
run_flow,
|
36
38
|
)
|
37
39
|
|
38
|
-
flow_run
|
40
|
+
flow_run = load_flow_run(flow_run_id=flow_run_id)
|
41
|
+
run_logger = flow_run_logger(flow_run=flow_run)
|
42
|
+
|
43
|
+
try:
|
44
|
+
flow = load_flow(flow_run)
|
45
|
+
except Exception:
|
46
|
+
run_logger.error(
|
47
|
+
"Unexpected exception encountered when trying to load flow",
|
48
|
+
exc_info=True,
|
49
|
+
)
|
50
|
+
raise
|
51
|
+
|
39
52
|
# run the flow
|
40
53
|
if flow.isasync:
|
41
|
-
run_coro_as_sync(run_flow(flow, flow_run=flow_run))
|
54
|
+
run_coro_as_sync(run_flow(flow, flow_run=flow_run, error_logger=run_logger))
|
42
55
|
else:
|
43
|
-
run_flow(flow, flow_run=flow_run)
|
56
|
+
run_flow(flow, flow_run=flow_run, error_logger=run_logger)
|
44
57
|
|
45
58
|
except Abort as exc:
|
46
59
|
engine_logger.info(
|
prefect/events/clients.py
CHANGED
@@ -16,6 +16,7 @@ from typing import (
|
|
16
16
|
cast,
|
17
17
|
)
|
18
18
|
from urllib.parse import urlparse
|
19
|
+
from urllib.request import proxy_bypass
|
19
20
|
from uuid import UUID
|
20
21
|
|
21
22
|
import orjson
|
@@ -95,6 +96,9 @@ class WebsocketProxyConnect(Connect):
|
|
95
96
|
u = urlparse(uri)
|
96
97
|
host = u.hostname
|
97
98
|
|
99
|
+
if not host:
|
100
|
+
raise ValueError(f"Invalid URI {uri}, no hostname found")
|
101
|
+
|
98
102
|
if u.scheme == "ws":
|
99
103
|
port = u.port or 80
|
100
104
|
proxy_url = os.environ.get("HTTP_PROXY")
|
@@ -107,7 +111,9 @@ class WebsocketProxyConnect(Connect):
|
|
107
111
|
"Unsupported scheme %s. Expected 'ws' or 'wss'. " % u.scheme
|
108
112
|
)
|
109
113
|
|
110
|
-
self._proxy =
|
114
|
+
self._proxy = (
|
115
|
+
Proxy.from_url(proxy_url) if proxy_url and not proxy_bypass(host) else None
|
116
|
+
)
|
111
117
|
self._host = host
|
112
118
|
self._port = port
|
113
119
|
|
prefect/events/schemas/events.py
CHANGED
@@ -2,6 +2,7 @@ import copy
|
|
2
2
|
from collections import defaultdict
|
3
3
|
from typing import (
|
4
4
|
Any,
|
5
|
+
ClassVar,
|
5
6
|
Dict,
|
6
7
|
Iterable,
|
7
8
|
List,
|
@@ -108,7 +109,7 @@ def _validate_related_resources(value) -> List:
|
|
108
109
|
class Event(PrefectBaseModel):
|
109
110
|
"""The client-side view of an event that has happened to a Resource"""
|
110
111
|
|
111
|
-
model_config = ConfigDict(extra="ignore")
|
112
|
+
model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore")
|
112
113
|
|
113
114
|
occurred: DateTime = Field(
|
114
115
|
default_factory=lambda: DateTime.now("UTC"),
|
@@ -177,7 +178,7 @@ class ReceivedEvent(Event):
|
|
177
178
|
"""The server-side view of an event that has happened to a Resource after it has
|
178
179
|
been received by the server"""
|
179
180
|
|
180
|
-
model_config = ConfigDict(from_attributes=True)
|
181
|
+
model_config: ClassVar[ConfigDict] = ConfigDict(from_attributes=True)
|
181
182
|
|
182
183
|
received: DateTime = Field(
|
183
184
|
...,
|
prefect/filesystems.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import abc
|
2
4
|
import urllib.parse
|
3
5
|
from pathlib import Path
|
@@ -92,7 +94,9 @@ class LocalFileSystem(WritableFileSystem, WritableDeploymentStorage):
|
|
92
94
|
)
|
93
95
|
|
94
96
|
@field_validator("basepath", mode="before")
|
95
|
-
def cast_pathlib(cls, value):
|
97
|
+
def cast_pathlib(cls, value: str | Path | None) -> str | None:
|
98
|
+
if value is None:
|
99
|
+
return value
|
96
100
|
return stringify_path(value)
|
97
101
|
|
98
102
|
def _resolve_path(self, path: str, validate: bool = False) -> Path:
|
@@ -132,7 +136,7 @@ class LocalFileSystem(WritableFileSystem, WritableDeploymentStorage):
|
|
132
136
|
Defaults to copying the entire contents of the block's basepath to the current working directory.
|
133
137
|
"""
|
134
138
|
if not from_path:
|
135
|
-
from_path = Path(self.basepath).expanduser().resolve()
|
139
|
+
from_path = Path(self.basepath or ".").expanduser().resolve()
|
136
140
|
else:
|
137
141
|
from_path = self._resolve_path(from_path)
|
138
142
|
|