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.
Files changed (67) hide show
  1. prefect/__init__.py +17 -14
  2. prefect/_internal/schemas/bases.py +1 -0
  3. prefect/_internal/schemas/validators.py +5 -3
  4. prefect/_version.py +3 -3
  5. prefect/client/cloud.py +2 -2
  6. prefect/client/orchestration.py +4 -4
  7. prefect/client/schemas/filters.py +14 -0
  8. prefect/context.py +3 -2
  9. prefect/deployments/runner.py +15 -6
  10. prefect/events/schemas/automations.py +3 -3
  11. prefect/events/schemas/deployment_triggers.py +10 -5
  12. prefect/flow_engine.py +4 -4
  13. prefect/flows.py +24 -9
  14. prefect/futures.py +4 -4
  15. prefect/logging/handlers.py +1 -1
  16. prefect/logging/highlighters.py +2 -0
  17. prefect/logging/logging.yml +82 -83
  18. prefect/runner/runner.py +1 -2
  19. prefect/runner/server.py +12 -1
  20. prefect/settings/__init__.py +59 -0
  21. prefect/settings/base.py +131 -0
  22. prefect/settings/constants.py +8 -0
  23. prefect/settings/context.py +65 -0
  24. prefect/settings/legacy.py +167 -0
  25. prefect/settings/models/__init__.py +0 -0
  26. prefect/settings/models/api.py +41 -0
  27. prefect/settings/models/cli.py +31 -0
  28. prefect/settings/models/client.py +90 -0
  29. prefect/settings/models/cloud.py +58 -0
  30. prefect/settings/models/deployments.py +40 -0
  31. prefect/settings/models/flows.py +37 -0
  32. prefect/settings/models/internal.py +21 -0
  33. prefect/settings/models/logging.py +137 -0
  34. prefect/settings/models/results.py +47 -0
  35. prefect/settings/models/root.py +447 -0
  36. prefect/settings/models/runner.py +65 -0
  37. prefect/settings/models/server/__init__.py +1 -0
  38. prefect/settings/models/server/api.py +133 -0
  39. prefect/settings/models/server/database.py +202 -0
  40. prefect/settings/models/server/deployments.py +24 -0
  41. prefect/settings/models/server/ephemeral.py +34 -0
  42. prefect/settings/models/server/events.py +140 -0
  43. prefect/settings/models/server/flow_run_graph.py +34 -0
  44. prefect/settings/models/server/root.py +143 -0
  45. prefect/settings/models/server/services.py +485 -0
  46. prefect/settings/models/server/tasks.py +86 -0
  47. prefect/settings/models/server/ui.py +52 -0
  48. prefect/settings/models/tasks.py +91 -0
  49. prefect/settings/models/testing.py +52 -0
  50. prefect/settings/models/ui.py +0 -0
  51. prefect/settings/models/worker.py +46 -0
  52. prefect/settings/profiles.py +390 -0
  53. prefect/settings/sources.py +162 -0
  54. prefect/task_engine.py +24 -29
  55. prefect/task_runners.py +6 -1
  56. prefect/tasks.py +63 -28
  57. prefect/utilities/asyncutils.py +1 -1
  58. prefect/utilities/engine.py +11 -3
  59. prefect/utilities/services.py +3 -3
  60. prefect/workers/base.py +8 -2
  61. {prefect_client-3.0.10.dist-info → prefect_client-3.0.11.dist-info}/METADATA +2 -2
  62. {prefect_client-3.0.10.dist-info → prefect_client-3.0.11.dist-info}/RECORD +66 -33
  63. prefect/settings.py +0 -2172
  64. /prefect/{profiles.toml → settings/profiles.toml} +0 -0
  65. {prefect_client-3.0.10.dist-info → prefect_client-3.0.11.dist-info}/LICENSE +0 -0
  66. {prefect_client-3.0.10.dist-info → prefect_client-3.0.11.dist-info}/WHEEL +0 -0
  67. {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 inspect.iscoroutine(result):
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(webserver, host=host, port=port, log_level=log_level)
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
+ ]
@@ -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
+ )