prefect-client 3.0.10__py3-none-any.whl → 3.0.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.
- prefect/__init__.py +17 -14
- prefect/_internal/schemas/bases.py +1 -0
- prefect/_internal/schemas/validators.py +5 -3
- prefect/_version.py +3 -3
- prefect/client/cloud.py +2 -2
- prefect/client/orchestration.py +4 -4
- prefect/client/schemas/filters.py +14 -0
- prefect/context.py +3 -2
- prefect/deployments/runner.py +15 -6
- prefect/events/schemas/automations.py +3 -3
- prefect/events/schemas/deployment_triggers.py +10 -5
- prefect/flow_engine.py +4 -4
- prefect/flows.py +24 -9
- prefect/futures.py +4 -4
- prefect/logging/handlers.py +1 -1
- prefect/logging/highlighters.py +2 -0
- prefect/logging/logging.yml +82 -83
- prefect/runner/runner.py +1 -2
- prefect/runner/server.py +12 -1
- prefect/settings/__init__.py +59 -0
- prefect/settings/base.py +131 -0
- prefect/settings/constants.py +8 -0
- prefect/settings/context.py +65 -0
- prefect/settings/legacy.py +167 -0
- prefect/settings/models/__init__.py +0 -0
- prefect/settings/models/api.py +41 -0
- prefect/settings/models/cli.py +31 -0
- prefect/settings/models/client.py +90 -0
- prefect/settings/models/cloud.py +58 -0
- prefect/settings/models/deployments.py +40 -0
- prefect/settings/models/flows.py +37 -0
- prefect/settings/models/internal.py +21 -0
- prefect/settings/models/logging.py +137 -0
- prefect/settings/models/results.py +47 -0
- prefect/settings/models/root.py +447 -0
- prefect/settings/models/runner.py +65 -0
- prefect/settings/models/server/__init__.py +1 -0
- prefect/settings/models/server/api.py +133 -0
- prefect/settings/models/server/database.py +202 -0
- prefect/settings/models/server/deployments.py +24 -0
- prefect/settings/models/server/ephemeral.py +34 -0
- prefect/settings/models/server/events.py +140 -0
- prefect/settings/models/server/flow_run_graph.py +34 -0
- prefect/settings/models/server/root.py +143 -0
- prefect/settings/models/server/services.py +485 -0
- prefect/settings/models/server/tasks.py +86 -0
- prefect/settings/models/server/ui.py +52 -0
- prefect/settings/models/tasks.py +91 -0
- prefect/settings/models/testing.py +52 -0
- prefect/settings/models/ui.py +0 -0
- prefect/settings/models/worker.py +46 -0
- prefect/settings/profiles.py +390 -0
- prefect/settings/sources.py +162 -0
- prefect/task_engine.py +24 -29
- prefect/task_runners.py +6 -1
- prefect/tasks.py +63 -28
- prefect/utilities/asyncutils.py +1 -1
- prefect/utilities/engine.py +11 -3
- prefect/utilities/services.py +3 -3
- prefect/workers/base.py +8 -2
- {prefect_client-3.0.10.dist-info → prefect_client-3.0.11.dist-info}/METADATA +2 -2
- {prefect_client-3.0.10.dist-info → prefect_client-3.0.11.dist-info}/RECORD +66 -33
- prefect/settings.py +0 -2172
- /prefect/{profiles.toml → settings/profiles.toml} +0 -0
- {prefect_client-3.0.10.dist-info → prefect_client-3.0.11.dist-info}/LICENSE +0 -0
- {prefect_client-3.0.10.dist-info → prefect_client-3.0.11.dist-info}/WHEEL +0 -0
- {prefect_client-3.0.10.dist-info → prefect_client-3.0.11.dist-info}/top_level.txt +0 -0
prefect/runner/runner.py
CHANGED
@@ -32,7 +32,6 @@ Example:
|
|
32
32
|
|
33
33
|
import asyncio
|
34
34
|
import datetime
|
35
|
-
import inspect
|
36
35
|
import logging
|
37
36
|
import os
|
38
37
|
import shutil
|
@@ -1213,7 +1212,7 @@ class Runner:
|
|
1213
1212
|
task_status.started()
|
1214
1213
|
|
1215
1214
|
result = fn(*args, **kwargs)
|
1216
|
-
if
|
1215
|
+
if asyncio.iscoroutine(result):
|
1217
1216
|
await result
|
1218
1217
|
|
1219
1218
|
await self._runs_task_group.start(wrapper)
|
prefect/runner/server.py
CHANGED
@@ -298,4 +298,15 @@ def start_webserver(runner: "Runner", log_level: Optional[str] = None) -> None:
|
|
298
298
|
port = PREFECT_RUNNER_SERVER_PORT.value()
|
299
299
|
log_level = log_level or PREFECT_RUNNER_SERVER_LOG_LEVEL.value()
|
300
300
|
webserver = build_server(runner)
|
301
|
-
uvicorn.run(
|
301
|
+
uvicorn.run(
|
302
|
+
webserver, host=host, port=port, log_level=log_level.lower()
|
303
|
+
) # Uvicorn supports only lowercase log_level
|
304
|
+
# From the Uvicorn config file:
|
305
|
+
# LOG_LEVELS: dict[str, int] = {
|
306
|
+
# "critical": logging.CRITICAL,
|
307
|
+
# "error": logging.ERROR,
|
308
|
+
# "warning": logging.WARNING,
|
309
|
+
# "info": logging.INFO,
|
310
|
+
# "debug": logging.DEBUG,
|
311
|
+
# "trace": TRACE_LOG_LEVEL,
|
312
|
+
# }
|
@@ -0,0 +1,59 @@
|
|
1
|
+
"""
|
2
|
+
Prefect settings are defined using `BaseSettings` from `pydantic_settings`. `BaseSettings` can load setting values
|
3
|
+
from system environment variables and each additionally specified `env_file`.
|
4
|
+
|
5
|
+
The recommended user-facing way to access Prefect settings at this time is to import specific setting objects directly,
|
6
|
+
like `from prefect.settings import PREFECT_API_URL; print(PREFECT_API_URL.value())`.
|
7
|
+
|
8
|
+
Importantly, we replace the `callback` mechanism for updating settings with an "after" model_validator that updates dependent settings.
|
9
|
+
After https://github.com/pydantic/pydantic/issues/9789 is resolved, we will be able to define context-aware defaults
|
10
|
+
for settings, at which point we will not need to use the "after" model_validator.
|
11
|
+
"""
|
12
|
+
|
13
|
+
from prefect.settings.legacy import (
|
14
|
+
Setting,
|
15
|
+
_get_settings_fields,
|
16
|
+
_get_valid_setting_names,
|
17
|
+
)
|
18
|
+
from prefect.settings.models.root import Settings
|
19
|
+
|
20
|
+
from prefect.settings.profiles import (
|
21
|
+
Profile,
|
22
|
+
ProfilesCollection,
|
23
|
+
load_current_profile,
|
24
|
+
update_current_profile,
|
25
|
+
load_profile,
|
26
|
+
save_profiles,
|
27
|
+
load_profiles,
|
28
|
+
)
|
29
|
+
from prefect.settings.context import get_current_settings, temporary_settings
|
30
|
+
from prefect.settings.constants import DEFAULT_PROFILES_PATH
|
31
|
+
|
32
|
+
############################################################################
|
33
|
+
# Allow traditional env var access
|
34
|
+
|
35
|
+
|
36
|
+
def __getattr__(name: str) -> Setting:
|
37
|
+
if name in _get_valid_setting_names(Settings):
|
38
|
+
return _get_settings_fields(Settings)[name]
|
39
|
+
raise AttributeError(f"{name} is not a Prefect setting.")
|
40
|
+
|
41
|
+
|
42
|
+
__all__ = [ # noqa: F822
|
43
|
+
"Profile",
|
44
|
+
"ProfilesCollection",
|
45
|
+
"Setting",
|
46
|
+
"Settings",
|
47
|
+
"load_current_profile",
|
48
|
+
"update_current_profile",
|
49
|
+
"load_profile",
|
50
|
+
"save_profiles",
|
51
|
+
"load_profiles",
|
52
|
+
"get_current_settings",
|
53
|
+
"temporary_settings",
|
54
|
+
"DEFAULT_PROFILES_PATH",
|
55
|
+
# add public settings here for auto-completion
|
56
|
+
"PREFECT_API_KEY", # type: ignore
|
57
|
+
"PREFECT_API_URL", # type: ignore
|
58
|
+
"PREFECT_UI_URL", # type: ignore
|
59
|
+
]
|
prefect/settings/base.py
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
from functools import partial
|
2
|
+
from typing import Any, Dict, Tuple, Type
|
3
|
+
|
4
|
+
from pydantic import (
|
5
|
+
AliasChoices,
|
6
|
+
AliasPath,
|
7
|
+
SerializationInfo,
|
8
|
+
SerializerFunctionWrapHandler,
|
9
|
+
model_serializer,
|
10
|
+
)
|
11
|
+
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource
|
12
|
+
|
13
|
+
from prefect.settings.sources import EnvFilterSettingsSource, ProfileSettingsTomlLoader
|
14
|
+
from prefect.utilities.collections import visit_collection
|
15
|
+
from prefect.utilities.pydantic import handle_secret_render
|
16
|
+
|
17
|
+
|
18
|
+
class PrefectBaseSettings(BaseSettings):
|
19
|
+
@classmethod
|
20
|
+
def settings_customise_sources(
|
21
|
+
cls,
|
22
|
+
settings_cls: Type[BaseSettings],
|
23
|
+
init_settings: PydanticBaseSettingsSource,
|
24
|
+
env_settings: PydanticBaseSettingsSource,
|
25
|
+
dotenv_settings: PydanticBaseSettingsSource,
|
26
|
+
file_secret_settings: PydanticBaseSettingsSource,
|
27
|
+
) -> Tuple[PydanticBaseSettingsSource, ...]:
|
28
|
+
"""
|
29
|
+
Define an order for Prefect settings sources.
|
30
|
+
|
31
|
+
The order of the returned callables decides the priority of inputs; first item is the highest priority.
|
32
|
+
|
33
|
+
See https://docs.pydantic.dev/latest/concepts/pydantic_settings/#customise-settings-sources
|
34
|
+
"""
|
35
|
+
env_filter = set()
|
36
|
+
for field in settings_cls.model_fields.values():
|
37
|
+
if field.validation_alias is not None and isinstance(
|
38
|
+
field.validation_alias, AliasChoices
|
39
|
+
):
|
40
|
+
for alias in field.validation_alias.choices:
|
41
|
+
if isinstance(alias, AliasPath) and len(alias.path) > 0:
|
42
|
+
env_filter.add(alias.path[0])
|
43
|
+
return (
|
44
|
+
init_settings,
|
45
|
+
EnvFilterSettingsSource(
|
46
|
+
settings_cls,
|
47
|
+
case_sensitive=cls.model_config.get("case_sensitive"),
|
48
|
+
env_prefix=cls.model_config.get("env_prefix"),
|
49
|
+
env_nested_delimiter=cls.model_config.get("env_nested_delimiter"),
|
50
|
+
env_ignore_empty=cls.model_config.get("env_ignore_empty"),
|
51
|
+
env_parse_none_str=cls.model_config.get("env_parse_none_str"),
|
52
|
+
env_parse_enums=cls.model_config.get("env_parse_enums"),
|
53
|
+
env_filter=list(env_filter),
|
54
|
+
),
|
55
|
+
dotenv_settings,
|
56
|
+
file_secret_settings,
|
57
|
+
ProfileSettingsTomlLoader(settings_cls),
|
58
|
+
)
|
59
|
+
|
60
|
+
def to_environment_variables(
|
61
|
+
self,
|
62
|
+
exclude_unset: bool = False,
|
63
|
+
include_secrets: bool = True,
|
64
|
+
) -> Dict[str, str]:
|
65
|
+
"""Convert the settings object to a dictionary of environment variables."""
|
66
|
+
|
67
|
+
env: Dict[str, Any] = self.model_dump(
|
68
|
+
exclude_unset=exclude_unset,
|
69
|
+
mode="json",
|
70
|
+
context={"include_secrets": include_secrets},
|
71
|
+
)
|
72
|
+
env_variables = {}
|
73
|
+
for key in self.model_fields.keys():
|
74
|
+
if isinstance(child_settings := getattr(self, key), PrefectBaseSettings):
|
75
|
+
child_env = child_settings.to_environment_variables(
|
76
|
+
exclude_unset=exclude_unset,
|
77
|
+
include_secrets=include_secrets,
|
78
|
+
)
|
79
|
+
env_variables.update(child_env)
|
80
|
+
elif (value := env.get(key)) is not None:
|
81
|
+
env_variables[
|
82
|
+
f"{self.model_config.get('env_prefix')}{key.upper()}"
|
83
|
+
] = str(value)
|
84
|
+
return env_variables
|
85
|
+
|
86
|
+
@model_serializer(
|
87
|
+
mode="wrap", when_used="always"
|
88
|
+
) # TODO: reconsider `when_used` default for more control
|
89
|
+
def ser_model(
|
90
|
+
self, handler: SerializerFunctionWrapHandler, info: SerializationInfo
|
91
|
+
) -> Any:
|
92
|
+
jsonable_self = handler(self)
|
93
|
+
# iterate over fields to ensure child models that have been updated are also included
|
94
|
+
for key in self.model_fields.keys():
|
95
|
+
if info.exclude and key in info.exclude:
|
96
|
+
continue
|
97
|
+
if info.include and key not in info.include:
|
98
|
+
continue
|
99
|
+
|
100
|
+
child_include = None
|
101
|
+
child_exclude = None
|
102
|
+
if info.include and key in info.include and isinstance(info.include, dict):
|
103
|
+
child_include = info.include[key]
|
104
|
+
|
105
|
+
if isinstance(child_settings := getattr(self, key), PrefectBaseSettings):
|
106
|
+
child_jsonable = child_settings.model_dump(
|
107
|
+
mode=info.mode,
|
108
|
+
include=child_include,
|
109
|
+
exclude=child_exclude,
|
110
|
+
exclude_unset=info.exclude_unset,
|
111
|
+
context=info.context,
|
112
|
+
by_alias=info.by_alias,
|
113
|
+
exclude_none=info.exclude_none,
|
114
|
+
exclude_defaults=info.exclude_defaults,
|
115
|
+
serialize_as_any=info.serialize_as_any,
|
116
|
+
)
|
117
|
+
if child_jsonable:
|
118
|
+
jsonable_self[key] = child_jsonable
|
119
|
+
if info.context and info.context.get("include_secrets") is True:
|
120
|
+
jsonable_self.update(
|
121
|
+
{
|
122
|
+
field_name: visit_collection(
|
123
|
+
expr=getattr(self, field_name),
|
124
|
+
visit_fn=partial(handle_secret_render, context=info.context),
|
125
|
+
return_data=True,
|
126
|
+
)
|
127
|
+
for field_name in set(jsonable_self.keys()) # type: ignore
|
128
|
+
}
|
129
|
+
)
|
130
|
+
|
131
|
+
return jsonable_self
|
@@ -0,0 +1,8 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from typing import Tuple, Type
|
3
|
+
|
4
|
+
from pydantic import Secret, SecretStr
|
5
|
+
|
6
|
+
DEFAULT_PREFECT_HOME = Path.home() / ".prefect"
|
7
|
+
DEFAULT_PROFILES_PATH = Path(__file__).parent.joinpath("profiles.toml")
|
8
|
+
_SECRET_TYPES: Tuple[Type, ...] = (Secret, SecretStr)
|
@@ -0,0 +1,65 @@
|
|
1
|
+
from contextlib import contextmanager
|
2
|
+
from typing import TYPE_CHECKING, Any, Generator, Iterable, Mapping, Optional
|
3
|
+
|
4
|
+
from prefect.settings.models.root import Settings
|
5
|
+
|
6
|
+
if TYPE_CHECKING:
|
7
|
+
from prefect.settings.legacy import Setting
|
8
|
+
|
9
|
+
|
10
|
+
def get_current_settings() -> Settings:
|
11
|
+
"""
|
12
|
+
Returns a settings object populated with values from the current settings context
|
13
|
+
or, if no settings context is active, the environment.
|
14
|
+
"""
|
15
|
+
from prefect.context import SettingsContext
|
16
|
+
|
17
|
+
settings_context = SettingsContext.get()
|
18
|
+
if settings_context is not None:
|
19
|
+
return settings_context.settings
|
20
|
+
|
21
|
+
return Settings()
|
22
|
+
|
23
|
+
|
24
|
+
@contextmanager
|
25
|
+
def temporary_settings(
|
26
|
+
updates: Optional[Mapping["Setting", Any]] = None,
|
27
|
+
set_defaults: Optional[Mapping["Setting", Any]] = None,
|
28
|
+
restore_defaults: Optional[Iterable["Setting"]] = None,
|
29
|
+
) -> Generator[Settings, None, None]:
|
30
|
+
"""
|
31
|
+
Temporarily override the current settings by entering a new profile.
|
32
|
+
|
33
|
+
See `Settings.copy_with_update` for details on different argument behavior.
|
34
|
+
|
35
|
+
Examples:
|
36
|
+
>>> from prefect.settings import PREFECT_API_URL
|
37
|
+
>>>
|
38
|
+
>>> with temporary_settings(updates={PREFECT_API_URL: "foo"}):
|
39
|
+
>>> assert PREFECT_API_URL.value() == "foo"
|
40
|
+
>>>
|
41
|
+
>>> with temporary_settings(set_defaults={PREFECT_API_URL: "bar"}):
|
42
|
+
>>> assert PREFECT_API_URL.value() == "foo"
|
43
|
+
>>>
|
44
|
+
>>> with temporary_settings(restore_defaults={PREFECT_API_URL}):
|
45
|
+
>>> assert PREFECT_API_URL.value() is None
|
46
|
+
>>>
|
47
|
+
>>> with temporary_settings(set_defaults={PREFECT_API_URL: "bar"})
|
48
|
+
>>> assert PREFECT_API_URL.value() == "bar"
|
49
|
+
>>> assert PREFECT_API_URL.value() is None
|
50
|
+
"""
|
51
|
+
import prefect.context
|
52
|
+
|
53
|
+
context = prefect.context.get_settings_context()
|
54
|
+
|
55
|
+
if not restore_defaults:
|
56
|
+
restore_defaults = []
|
57
|
+
|
58
|
+
new_settings = context.settings.copy_with_update(
|
59
|
+
updates=updates, set_defaults=set_defaults, restore_defaults=restore_defaults
|
60
|
+
)
|
61
|
+
|
62
|
+
with prefect.context.SettingsContext(
|
63
|
+
profile=context.profile, settings=new_settings
|
64
|
+
):
|
65
|
+
yield new_settings
|
@@ -0,0 +1,167 @@
|
|
1
|
+
import inspect
|
2
|
+
import os
|
3
|
+
from functools import cache
|
4
|
+
from typing import Any, Dict, Optional, Set, Type, get_args
|
5
|
+
|
6
|
+
from pydantic import AliasChoices
|
7
|
+
from pydantic_settings import BaseSettings
|
8
|
+
from typing_extensions import Self
|
9
|
+
|
10
|
+
from prefect.settings.base import PrefectBaseSettings
|
11
|
+
from prefect.settings.constants import _SECRET_TYPES
|
12
|
+
from prefect.settings.context import get_current_settings
|
13
|
+
from prefect.settings.models.root import Settings
|
14
|
+
|
15
|
+
|
16
|
+
class Setting:
|
17
|
+
"""Mimics the old Setting object for compatibility with existing code."""
|
18
|
+
|
19
|
+
def __init__(
|
20
|
+
self, name: str, default: Any, type_: Any, accessor: Optional[str] = None
|
21
|
+
):
|
22
|
+
self._name = name
|
23
|
+
self._default = default
|
24
|
+
self._type = type_
|
25
|
+
if accessor is None:
|
26
|
+
self.accessor = _env_var_to_accessor(name)
|
27
|
+
else:
|
28
|
+
self.accessor = accessor
|
29
|
+
|
30
|
+
@property
|
31
|
+
def name(self):
|
32
|
+
return self._name
|
33
|
+
|
34
|
+
@property
|
35
|
+
def is_secret(self):
|
36
|
+
if self._type in _SECRET_TYPES:
|
37
|
+
return True
|
38
|
+
for secret_type in _SECRET_TYPES:
|
39
|
+
if secret_type in get_args(self._type):
|
40
|
+
return True
|
41
|
+
return False
|
42
|
+
|
43
|
+
def default(self):
|
44
|
+
return self._default
|
45
|
+
|
46
|
+
def value(self: Self) -> Any:
|
47
|
+
if (
|
48
|
+
self.name == "PREFECT_TEST_SETTING"
|
49
|
+
or self.name == "PREFECT_TESTING_TEST_SETTING"
|
50
|
+
):
|
51
|
+
if (
|
52
|
+
"PREFECT_TEST_MODE" in os.environ
|
53
|
+
or "PREFECT_TESTING_TEST_MODE" in os.environ
|
54
|
+
):
|
55
|
+
return get_current_settings().testing.test_setting
|
56
|
+
else:
|
57
|
+
return None
|
58
|
+
|
59
|
+
return self.value_from(get_current_settings())
|
60
|
+
|
61
|
+
def value_from(self: Self, settings: "Settings") -> Any:
|
62
|
+
path = self.accessor.split(".")
|
63
|
+
current_value = settings
|
64
|
+
for key in path:
|
65
|
+
current_value = getattr(current_value, key, None)
|
66
|
+
if isinstance(current_value, _SECRET_TYPES):
|
67
|
+
return current_value.get_secret_value()
|
68
|
+
return current_value
|
69
|
+
|
70
|
+
def __bool__(self) -> bool:
|
71
|
+
return bool(self.value())
|
72
|
+
|
73
|
+
def __str__(self) -> str:
|
74
|
+
return str(self.value())
|
75
|
+
|
76
|
+
def __repr__(self) -> str:
|
77
|
+
return f"<{self.name}: {self._type!r}>"
|
78
|
+
|
79
|
+
def __eq__(self, __o: object) -> bool:
|
80
|
+
return __o.__eq__(self.value())
|
81
|
+
|
82
|
+
def __hash__(self) -> int:
|
83
|
+
return hash((type(self), self.name))
|
84
|
+
|
85
|
+
|
86
|
+
def _env_var_to_accessor(env_var: str) -> str:
|
87
|
+
"""
|
88
|
+
Convert an environment variable name to a settings accessor.
|
89
|
+
"""
|
90
|
+
if (field := _get_settings_fields(Settings).get(env_var)) is not None:
|
91
|
+
return field.accessor
|
92
|
+
return env_var.replace("PREFECT_", "").lower()
|
93
|
+
|
94
|
+
|
95
|
+
@cache
|
96
|
+
def _get_valid_setting_names(cls: type[BaseSettings]) -> Set[str]:
|
97
|
+
"""
|
98
|
+
A set of valid setting names, e.g. "PREFECT_API_URL" or "PREFECT_API_KEY".
|
99
|
+
"""
|
100
|
+
settings_fields = set()
|
101
|
+
for field_name, field in cls.model_fields.items():
|
102
|
+
if inspect.isclass(field.annotation) and issubclass(
|
103
|
+
field.annotation, PrefectBaseSettings
|
104
|
+
):
|
105
|
+
settings_fields.update(_get_valid_setting_names(field.annotation))
|
106
|
+
else:
|
107
|
+
if field.validation_alias and isinstance(
|
108
|
+
field.validation_alias, AliasChoices
|
109
|
+
):
|
110
|
+
for alias in field.validation_alias.choices:
|
111
|
+
if not isinstance(alias, str):
|
112
|
+
continue
|
113
|
+
settings_fields.add(alias.upper())
|
114
|
+
else:
|
115
|
+
settings_fields.add(
|
116
|
+
f"{cls.model_config.get('env_prefix')}{field_name.upper()}"
|
117
|
+
)
|
118
|
+
return settings_fields
|
119
|
+
|
120
|
+
|
121
|
+
@cache
|
122
|
+
def _get_settings_fields(
|
123
|
+
settings: Type[BaseSettings], accessor_prefix: Optional[str] = None
|
124
|
+
) -> Dict[str, "Setting"]:
|
125
|
+
"""Get the settings fields for the settings object"""
|
126
|
+
settings_fields: Dict[str, Setting] = {}
|
127
|
+
for field_name, field in settings.model_fields.items():
|
128
|
+
if inspect.isclass(field.annotation) and issubclass(
|
129
|
+
field.annotation, PrefectBaseSettings
|
130
|
+
):
|
131
|
+
accessor = (
|
132
|
+
field_name
|
133
|
+
if accessor_prefix is None
|
134
|
+
else f"{accessor_prefix}.{field_name}"
|
135
|
+
)
|
136
|
+
settings_fields.update(_get_settings_fields(field.annotation, accessor))
|
137
|
+
else:
|
138
|
+
accessor = (
|
139
|
+
field_name
|
140
|
+
if accessor_prefix is None
|
141
|
+
else f"{accessor_prefix}.{field_name}"
|
142
|
+
)
|
143
|
+
if field.validation_alias and isinstance(
|
144
|
+
field.validation_alias, AliasChoices
|
145
|
+
):
|
146
|
+
for alias in field.validation_alias.choices:
|
147
|
+
if not isinstance(alias, str):
|
148
|
+
continue
|
149
|
+
setting = Setting(
|
150
|
+
name=alias.upper(),
|
151
|
+
default=field.default,
|
152
|
+
type_=field.annotation,
|
153
|
+
accessor=accessor,
|
154
|
+
)
|
155
|
+
settings_fields[setting.name] = setting
|
156
|
+
settings_fields[setting.accessor] = setting
|
157
|
+
else:
|
158
|
+
setting = Setting(
|
159
|
+
name=f"{settings.model_config.get('env_prefix')}{field_name.upper()}",
|
160
|
+
default=field.default,
|
161
|
+
type_=field.annotation,
|
162
|
+
accessor=accessor,
|
163
|
+
)
|
164
|
+
settings_fields[setting.name] = setting
|
165
|
+
settings_fields[setting.accessor] = setting
|
166
|
+
|
167
|
+
return settings_fields
|
File without changes
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import os
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
from pydantic import Field, SecretStr
|
5
|
+
from pydantic_settings import SettingsConfigDict
|
6
|
+
|
7
|
+
from prefect.settings.base import PrefectBaseSettings
|
8
|
+
|
9
|
+
|
10
|
+
class APISettings(PrefectBaseSettings):
|
11
|
+
"""
|
12
|
+
Settings for interacting with the Prefect API
|
13
|
+
"""
|
14
|
+
|
15
|
+
model_config = SettingsConfigDict(
|
16
|
+
env_prefix="PREFECT_API_", env_file=".env", extra="ignore"
|
17
|
+
)
|
18
|
+
url: Optional[str] = Field(
|
19
|
+
default=None,
|
20
|
+
description="The URL of the Prefect API. If not set, the client will attempt to infer it.",
|
21
|
+
)
|
22
|
+
key: Optional[SecretStr] = Field(
|
23
|
+
default=None,
|
24
|
+
description="The API key used for authentication with the Prefect API. Should be kept secret.",
|
25
|
+
)
|
26
|
+
tls_insecure_skip_verify: bool = Field(
|
27
|
+
default=False,
|
28
|
+
description="If `True`, disables SSL checking to allow insecure requests. This is recommended only during development, e.g. when using self-signed certificates.",
|
29
|
+
)
|
30
|
+
ssl_cert_file: Optional[str] = Field(
|
31
|
+
default=os.environ.get("SSL_CERT_FILE"),
|
32
|
+
description="This configuration settings option specifies the path to an SSL certificate file.",
|
33
|
+
)
|
34
|
+
enable_http2: bool = Field(
|
35
|
+
default=False,
|
36
|
+
description="If true, enable support for HTTP/2 for communicating with an API. If the API does not support HTTP/2, this will have no effect and connections will be made via HTTP/1.1.",
|
37
|
+
)
|
38
|
+
request_timeout: float = Field(
|
39
|
+
default=60.0,
|
40
|
+
description="The default timeout for requests to the API",
|
41
|
+
)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
from pydantic import Field
|
4
|
+
from pydantic_settings import SettingsConfigDict
|
5
|
+
|
6
|
+
from prefect.settings.base import PrefectBaseSettings
|
7
|
+
|
8
|
+
|
9
|
+
class CLISettings(PrefectBaseSettings):
|
10
|
+
"""
|
11
|
+
Settings for controlling CLI behavior
|
12
|
+
"""
|
13
|
+
|
14
|
+
model_config = SettingsConfigDict(
|
15
|
+
env_prefix="PREFECT_CLI_", env_file=".env", extra="ignore"
|
16
|
+
)
|
17
|
+
|
18
|
+
colors: bool = Field(
|
19
|
+
default=True,
|
20
|
+
description="If True, use colors in CLI output. If `False`, output will not include colors codes.",
|
21
|
+
)
|
22
|
+
|
23
|
+
prompt: Optional[bool] = Field(
|
24
|
+
default=None,
|
25
|
+
description="If `True`, use interactive prompts in CLI commands. If `False`, no interactive prompts will be used. If `None`, the value will be dynamically determined based on the presence of an interactive-enabled terminal.",
|
26
|
+
)
|
27
|
+
|
28
|
+
wrap_lines: bool = Field(
|
29
|
+
default=True,
|
30
|
+
description="If `True`, wrap text by inserting new lines in long lines in CLI output. If `False`, output will not be wrapped.",
|
31
|
+
)
|
@@ -0,0 +1,90 @@
|
|
1
|
+
from pydantic import AliasChoices, AliasPath, Field
|
2
|
+
from pydantic_settings import SettingsConfigDict
|
3
|
+
|
4
|
+
from prefect.settings.base import PrefectBaseSettings
|
5
|
+
from prefect.types import ClientRetryExtraCodes
|
6
|
+
|
7
|
+
|
8
|
+
class ClientMetricsSettings(PrefectBaseSettings):
|
9
|
+
"""
|
10
|
+
Settings for controlling metrics reporting from the client
|
11
|
+
"""
|
12
|
+
|
13
|
+
model_config = SettingsConfigDict(
|
14
|
+
env_prefix="PREFECT_CLIENT_METRICS_", env_file=".env", extra="ignore"
|
15
|
+
)
|
16
|
+
|
17
|
+
enabled: bool = Field(
|
18
|
+
default=False,
|
19
|
+
description="Whether or not to enable Prometheus metrics in the client.",
|
20
|
+
# Using alias for backwards compatibility. Need to duplicate the prefix because
|
21
|
+
# Pydantic does not allow the alias to be prefixed with the env_prefix. The AliasPath
|
22
|
+
# needs to be first to ensure that init kwargs take precedence over env vars.
|
23
|
+
validation_alias=AliasChoices(
|
24
|
+
AliasPath("enabled"),
|
25
|
+
"prefect_client_metrics_enabled",
|
26
|
+
"prefect_client_enable_metrics",
|
27
|
+
),
|
28
|
+
)
|
29
|
+
|
30
|
+
port: int = Field(
|
31
|
+
default=4201, description="The port to expose the client Prometheus metrics on."
|
32
|
+
)
|
33
|
+
|
34
|
+
|
35
|
+
class ClientSettings(PrefectBaseSettings):
|
36
|
+
"""
|
37
|
+
Settings for controlling API client behavior
|
38
|
+
"""
|
39
|
+
|
40
|
+
model_config = SettingsConfigDict(
|
41
|
+
env_prefix="PREFECT_CLIENT_", env_file=".env", extra="ignore"
|
42
|
+
)
|
43
|
+
|
44
|
+
max_retries: int = Field(
|
45
|
+
default=5,
|
46
|
+
ge=0,
|
47
|
+
description="""
|
48
|
+
The maximum number of retries to perform on failed HTTP requests.
|
49
|
+
Defaults to 5. Set to 0 to disable retries.
|
50
|
+
See `PREFECT_CLIENT_RETRY_EXTRA_CODES` for details on which HTTP status codes are
|
51
|
+
retried.
|
52
|
+
""",
|
53
|
+
)
|
54
|
+
|
55
|
+
retry_jitter_factor: float = Field(
|
56
|
+
default=0.2,
|
57
|
+
ge=0.0,
|
58
|
+
description="""
|
59
|
+
A value greater than or equal to zero to control the amount of jitter added to retried
|
60
|
+
client requests. Higher values introduce larger amounts of jitter.
|
61
|
+
Set to 0 to disable jitter. See `clamped_poisson_interval` for details on the how jitter
|
62
|
+
can affect retry lengths.
|
63
|
+
""",
|
64
|
+
)
|
65
|
+
|
66
|
+
retry_extra_codes: ClientRetryExtraCodes = Field(
|
67
|
+
default_factory=set,
|
68
|
+
description="""
|
69
|
+
A list of extra HTTP status codes to retry on. Defaults to an empty list.
|
70
|
+
429, 502 and 503 are always retried. Please note that not all routes are idempotent and retrying
|
71
|
+
may result in unexpected behavior.
|
72
|
+
""",
|
73
|
+
examples=["404,429,503", "429", {404, 429, 503}],
|
74
|
+
)
|
75
|
+
|
76
|
+
csrf_support_enabled: bool = Field(
|
77
|
+
default=True,
|
78
|
+
description="""
|
79
|
+
Determines if CSRF token handling is active in the Prefect client for API
|
80
|
+
requests.
|
81
|
+
|
82
|
+
When enabled (`True`), the client automatically manages CSRF tokens by
|
83
|
+
retrieving, storing, and including them in applicable state-changing requests
|
84
|
+
""",
|
85
|
+
)
|
86
|
+
|
87
|
+
metrics: ClientMetricsSettings = Field(
|
88
|
+
default_factory=ClientMetricsSettings,
|
89
|
+
description="Settings for controlling metrics reporting from the client",
|
90
|
+
)
|