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
@@ -0,0 +1,447 @@
1
+ import warnings
2
+ from pathlib import Path
3
+ from typing import (
4
+ TYPE_CHECKING,
5
+ Annotated,
6
+ Any,
7
+ Iterable,
8
+ Mapping,
9
+ Optional,
10
+ )
11
+ from urllib.parse import urlparse
12
+
13
+ from pydantic import BeforeValidator, Field, SecretStr, model_validator
14
+ from pydantic_settings import SettingsConfigDict
15
+ from typing_extensions import Self
16
+
17
+ from prefect.settings.base import PrefectBaseSettings
18
+ from prefect.settings.models.tasks import TasksSettings
19
+ from prefect.settings.models.testing import TestingSettings
20
+ from prefect.settings.models.worker import WorkerSettings
21
+ from prefect.utilities.collections import deep_merge_dicts, set_in_dict
22
+
23
+ from .api import APISettings
24
+ from .cli import CLISettings
25
+ from .client import ClientSettings
26
+ from .cloud import CloudSettings
27
+ from .deployments import DeploymentsSettings
28
+ from .flows import FlowsSettings
29
+ from .internal import InternalSettings
30
+ from .logging import LoggingSettings
31
+ from .results import ResultsSettings
32
+ from .runner import RunnerSettings
33
+ from .server import ServerSettings
34
+
35
+ if TYPE_CHECKING:
36
+ from prefect.settings.legacy import Setting
37
+
38
+
39
+ class Settings(PrefectBaseSettings):
40
+ """
41
+ Settings for Prefect using Pydantic settings.
42
+
43
+ See https://docs.pydantic.dev/latest/concepts/pydantic_settings
44
+ """
45
+
46
+ model_config = SettingsConfigDict(
47
+ env_file=".env",
48
+ env_prefix="PREFECT_",
49
+ env_nested_delimiter=None,
50
+ extra="ignore",
51
+ )
52
+
53
+ home: Annotated[Path, BeforeValidator(lambda x: Path(x).expanduser())] = Field(
54
+ default=Path("~") / ".prefect",
55
+ description="The path to the Prefect home directory. Defaults to ~/.prefect",
56
+ )
57
+
58
+ profiles_path: Optional[Path] = Field(
59
+ default=None,
60
+ description="The path to a profiles configuration file.",
61
+ )
62
+
63
+ debug_mode: bool = Field(
64
+ default=False,
65
+ description="If True, enables debug mode which may provide additional logging and debugging features.",
66
+ )
67
+
68
+ api: APISettings = Field(
69
+ default_factory=APISettings,
70
+ description="Settings for interacting with the Prefect API",
71
+ )
72
+
73
+ cli: CLISettings = Field(
74
+ default_factory=CLISettings,
75
+ description="Settings for controlling CLI behavior",
76
+ )
77
+
78
+ client: ClientSettings = Field(
79
+ default_factory=ClientSettings,
80
+ description="Settings for for controlling API client behavior",
81
+ )
82
+
83
+ cloud: CloudSettings = Field(
84
+ default_factory=CloudSettings,
85
+ description="Settings for interacting with Prefect Cloud",
86
+ )
87
+
88
+ deployments: DeploymentsSettings = Field(
89
+ default_factory=DeploymentsSettings,
90
+ description="Settings for configuring deployments defaults",
91
+ )
92
+
93
+ flows: FlowsSettings = Field(
94
+ default_factory=FlowsSettings,
95
+ description="Settings for controlling flow behavior",
96
+ )
97
+
98
+ internal: InternalSettings = Field(
99
+ default_factory=InternalSettings,
100
+ description="Settings for internal Prefect machinery",
101
+ )
102
+
103
+ logging: LoggingSettings = Field(
104
+ default_factory=LoggingSettings,
105
+ description="Settings for controlling logging behavior",
106
+ )
107
+
108
+ results: ResultsSettings = Field(
109
+ default_factory=ResultsSettings,
110
+ description="Settings for controlling result storage behavior",
111
+ )
112
+
113
+ runner: RunnerSettings = Field(
114
+ default_factory=RunnerSettings,
115
+ description="Settings for controlling runner behavior",
116
+ )
117
+
118
+ server: ServerSettings = Field(
119
+ default_factory=ServerSettings,
120
+ description="Settings for controlling server behavior",
121
+ )
122
+
123
+ tasks: TasksSettings = Field(
124
+ default_factory=TasksSettings,
125
+ description="Settings for controlling task behavior",
126
+ )
127
+
128
+ testing: TestingSettings = Field(
129
+ default_factory=TestingSettings,
130
+ description="Settings used during testing",
131
+ )
132
+
133
+ worker: WorkerSettings = Field(
134
+ default_factory=WorkerSettings,
135
+ description="Settings for controlling worker behavior",
136
+ )
137
+
138
+ ui_url: Optional[str] = Field(
139
+ default=None,
140
+ description="The URL of the Prefect UI. If not set, the client will attempt to infer it.",
141
+ )
142
+
143
+ silence_api_url_misconfiguration: bool = Field(
144
+ default=False,
145
+ description="""
146
+ If `True`, disable the warning when a user accidentally misconfigure its `PREFECT_API_URL`
147
+ Sometimes when a user manually set `PREFECT_API_URL` to a custom url,reverse-proxy for example,
148
+ we would like to silence this warning so we will set it to `FALSE`.
149
+ """,
150
+ )
151
+
152
+ experimental_warn: bool = Field(
153
+ default=True,
154
+ description="If `True`, warn on usage of experimental features.",
155
+ )
156
+
157
+ # this setting needs to be removed
158
+ async_fetch_state_result: bool = Field(
159
+ default=False,
160
+ description="""
161
+ Determines whether `State.result()` fetches results automatically or not.
162
+ In Prefect 2.6.0, the `State.result()` method was updated to be async
163
+ to facilitate automatic retrieval of results from storage which means when
164
+ writing async code you must `await` the call. For backwards compatibility,
165
+ the result is not retrieved by default for async users. You may opt into this
166
+ per call by passing `fetch=True` or toggle this setting to change the behavior
167
+ globally.
168
+ """,
169
+ )
170
+
171
+ experimental_enable_schedule_concurrency: bool = Field(
172
+ default=False,
173
+ description="Whether or not to enable concurrency for scheduled tasks.",
174
+ )
175
+
176
+ ###########################################################################
177
+ # allow deprecated access to PREFECT_SOME_SETTING_NAME
178
+
179
+ def __getattribute__(self, name: str) -> Any:
180
+ from prefect.settings.legacy import _env_var_to_accessor
181
+
182
+ if name.startswith("PREFECT_"):
183
+ accessor = _env_var_to_accessor(name)
184
+ warnings.warn(
185
+ f"Accessing `Settings().{name}` is deprecated. Use `Settings().{accessor}` instead.",
186
+ DeprecationWarning,
187
+ stacklevel=2,
188
+ )
189
+ path = accessor.split(".")
190
+ value = super().__getattribute__(path[0])
191
+ for key in path[1:]:
192
+ value = getattr(value, key)
193
+ return value
194
+ return super().__getattribute__(name)
195
+
196
+ ###########################################################################
197
+
198
+ @model_validator(mode="after")
199
+ def post_hoc_settings(self) -> Self:
200
+ """refactor on resolution of https://github.com/pydantic/pydantic/issues/9789
201
+
202
+ we should not be modifying __pydantic_fields_set__ directly, but until we can
203
+ define dependencies between defaults in a first-class way, we need clean up
204
+ post-hoc default assignments to keep set/unset fields correct after instantiation.
205
+ """
206
+ if self.ui_url is None:
207
+ self.ui_url = _default_ui_url(self)
208
+ self.__pydantic_fields_set__.remove("ui_url")
209
+ if self.server.ui.api_url is None:
210
+ if self.api.url:
211
+ self.server.ui.api_url = self.api.url
212
+ self.server.ui.__pydantic_fields_set__.remove("api_url")
213
+ else:
214
+ self.server.ui.api_url = (
215
+ f"http://{self.server.api.host}:{self.server.api.port}/api"
216
+ )
217
+ self.server.ui.__pydantic_fields_set__.remove("api_url")
218
+ if self.profiles_path is None or "PREFECT_HOME" in str(self.profiles_path):
219
+ self.profiles_path = Path(f"{self.home}/profiles.toml")
220
+ self.__pydantic_fields_set__.remove("profiles_path")
221
+ if self.results.local_storage_path is None:
222
+ self.results.local_storage_path = Path(f"{self.home}/storage")
223
+ self.results.__pydantic_fields_set__.remove("local_storage_path")
224
+ if self.server.memo_store_path is None:
225
+ self.server.memo_store_path = Path(f"{self.home}/memo_store.toml")
226
+ self.server.__pydantic_fields_set__.remove("memo_store_path")
227
+ if self.debug_mode or self.testing.test_mode:
228
+ self.logging.level = "DEBUG"
229
+ self.internal.logging_level = "DEBUG"
230
+ self.logging.__pydantic_fields_set__.remove("level")
231
+ self.internal.__pydantic_fields_set__.remove("logging_level")
232
+
233
+ if self.logging.config_path is None:
234
+ self.logging.config_path = Path(f"{self.home}/logging.yml")
235
+ self.logging.__pydantic_fields_set__.remove("config_path")
236
+ # Set default database connection URL if not provided
237
+ if self.server.database.connection_url is None:
238
+ self.server.database.connection_url = _default_database_connection_url(self)
239
+ self.server.database.__pydantic_fields_set__.remove("connection_url")
240
+ db_url = (
241
+ self.server.database.connection_url.get_secret_value()
242
+ if isinstance(self.server.database.connection_url, SecretStr)
243
+ else self.server.database.connection_url
244
+ )
245
+ if (
246
+ "PREFECT_API_DATABASE_PASSWORD" in db_url
247
+ or "PREFECT_SERVER_DATABASE_PASSWORD" in db_url
248
+ ):
249
+ if self.server.database.password is None:
250
+ raise ValueError(
251
+ "database password is None - please set PREFECT_SERVER_DATABASE_PASSWORD"
252
+ )
253
+ db_url = db_url.replace(
254
+ "${PREFECT_API_DATABASE_PASSWORD}",
255
+ self.server.database.password.get_secret_value()
256
+ if self.server.database.password
257
+ else "",
258
+ )
259
+ db_url = db_url.replace(
260
+ "${PREFECT_SERVER_DATABASE_PASSWORD}",
261
+ self.server.database.password.get_secret_value()
262
+ if self.server.database.password
263
+ else "",
264
+ )
265
+ self.server.database.connection_url = SecretStr(db_url)
266
+ self.server.database.__pydantic_fields_set__.remove("connection_url")
267
+ return self
268
+
269
+ @model_validator(mode="after")
270
+ def emit_warnings(self) -> Self:
271
+ """More post-hoc validation of settings, including warnings for misconfigurations."""
272
+ if not self.silence_api_url_misconfiguration:
273
+ _warn_on_misconfigured_api_url(self)
274
+ return self
275
+
276
+ ##########################################################################
277
+ # Settings methods
278
+
279
+ def copy_with_update(
280
+ self: Self,
281
+ updates: Optional[Mapping["Setting", Any]] = None,
282
+ set_defaults: Optional[Mapping["Setting", Any]] = None,
283
+ restore_defaults: Optional[Iterable["Setting"]] = None,
284
+ ) -> Self:
285
+ """
286
+ Create a new Settings object with validation.
287
+
288
+ Arguments:
289
+ updates: A mapping of settings to new values. Existing values for the
290
+ given settings will be overridden.
291
+ set_defaults: A mapping of settings to new default values. Existing values for
292
+ the given settings will only be overridden if they were not set.
293
+ restore_defaults: An iterable of settings to restore to their default values.
294
+
295
+ Returns:
296
+ A new Settings object.
297
+ """
298
+ restore_defaults_obj = {}
299
+ for r in restore_defaults or []:
300
+ set_in_dict(restore_defaults_obj, r.accessor, True)
301
+ updates = updates or {}
302
+ set_defaults = set_defaults or {}
303
+
304
+ set_defaults_obj = {}
305
+ for setting, value in set_defaults.items():
306
+ set_in_dict(set_defaults_obj, setting.accessor, value)
307
+
308
+ updates_obj = {}
309
+ for setting, value in updates.items():
310
+ set_in_dict(updates_obj, setting.accessor, value)
311
+
312
+ new_settings = self.__class__.model_validate(
313
+ deep_merge_dicts(
314
+ set_defaults_obj,
315
+ self.model_dump(exclude_unset=True, exclude=restore_defaults_obj),
316
+ updates_obj,
317
+ )
318
+ )
319
+ return new_settings
320
+
321
+ def hash_key(self) -> str:
322
+ """
323
+ Return a hash key for the settings object. This is needed since some
324
+ settings may be unhashable, like lists.
325
+ """
326
+ env_variables = self.to_environment_variables()
327
+ return str(hash(tuple((key, value) for key, value in env_variables.items())))
328
+
329
+
330
+ def _default_ui_url(settings: "Settings") -> Optional[str]:
331
+ value = settings.ui_url
332
+ if value is not None:
333
+ return value
334
+
335
+ # Otherwise, infer a value from the API URL
336
+ ui_url = api_url = settings.api.url
337
+
338
+ if not api_url:
339
+ return None
340
+ assert ui_url is not None
341
+
342
+ cloud_url = settings.cloud.api_url
343
+ cloud_ui_url = settings.cloud.ui_url
344
+ if api_url.startswith(cloud_url) and cloud_ui_url:
345
+ ui_url = ui_url.replace(cloud_url, cloud_ui_url)
346
+
347
+ if ui_url.endswith("/api"):
348
+ # Handles open-source APIs
349
+ ui_url = ui_url[:-4]
350
+
351
+ # Handles Cloud APIs with content after `/api`
352
+ ui_url = ui_url.replace("/api/", "/")
353
+
354
+ # Update routing
355
+ ui_url = ui_url.replace("/accounts/", "/account/")
356
+ ui_url = ui_url.replace("/workspaces/", "/workspace/")
357
+
358
+ return ui_url
359
+
360
+
361
+ def _warn_on_misconfigured_api_url(settings: "Settings"):
362
+ """
363
+ Validator for settings warning if the API URL is misconfigured.
364
+ """
365
+ api_url = settings.api.url
366
+ if api_url is not None:
367
+ misconfigured_mappings = {
368
+ "app.prefect.cloud": (
369
+ "`PREFECT_API_URL` points to `app.prefect.cloud`. Did you"
370
+ " mean `api.prefect.cloud`?"
371
+ ),
372
+ "account/": (
373
+ "`PREFECT_API_URL` uses `/account/` but should use `/accounts/`."
374
+ ),
375
+ "workspace/": (
376
+ "`PREFECT_API_URL` uses `/workspace/` but should use `/workspaces/`."
377
+ ),
378
+ }
379
+ warnings_list = []
380
+
381
+ for misconfig, warning in misconfigured_mappings.items():
382
+ if misconfig in api_url:
383
+ warnings_list.append(warning)
384
+
385
+ parsed_url = urlparse(api_url)
386
+ if parsed_url.path and not parsed_url.path.startswith("/api"):
387
+ warnings_list.append(
388
+ "`PREFECT_API_URL` should have `/api` after the base URL."
389
+ )
390
+
391
+ if warnings_list:
392
+ example = 'e.g. PREFECT_API_URL="https://api.prefect.cloud/api/accounts/[ACCOUNT-ID]/workspaces/[WORKSPACE-ID]"'
393
+ warnings_list.append(example)
394
+
395
+ warnings.warn("\n".join(warnings_list), stacklevel=2)
396
+
397
+ return settings
398
+
399
+
400
+ def _default_database_connection_url(settings: "Settings") -> SecretStr:
401
+ value = None
402
+ if settings.server.database.driver == "postgresql+asyncpg":
403
+ required = [
404
+ "host",
405
+ "user",
406
+ "name",
407
+ "password",
408
+ ]
409
+ missing = [
410
+ attr for attr in required if getattr(settings.server.database, attr) is None
411
+ ]
412
+ if missing:
413
+ raise ValueError(
414
+ f"Missing required database connection settings: {', '.join(missing)}"
415
+ )
416
+
417
+ from sqlalchemy import URL
418
+
419
+ return URL(
420
+ drivername=settings.server.database.driver,
421
+ host=settings.server.database.host,
422
+ port=settings.server.database.port or 5432,
423
+ username=settings.server.database.user,
424
+ password=(
425
+ settings.server.database.password.get_secret_value()
426
+ if settings.server.database.password
427
+ else None
428
+ ),
429
+ database=settings.server.database.name,
430
+ query=[], # type: ignore
431
+ ).render_as_string(hide_password=False)
432
+
433
+ elif settings.server.database.driver == "sqlite+aiosqlite":
434
+ if settings.server.database.name:
435
+ value = (
436
+ f"{settings.server.database.driver}:///{settings.server.database.name}"
437
+ )
438
+ else:
439
+ value = f"sqlite+aiosqlite:///{settings.home}/prefect.db"
440
+
441
+ elif settings.server.database.driver:
442
+ raise ValueError(
443
+ f"Unsupported database driver: {settings.server.database.driver}"
444
+ )
445
+
446
+ value = value if value else f"sqlite+aiosqlite:///{settings.home}/prefect.db"
447
+ return SecretStr(value)
@@ -0,0 +1,65 @@
1
+ from pydantic import Field
2
+ from pydantic_settings import SettingsConfigDict
3
+
4
+ from prefect.settings.base import PrefectBaseSettings
5
+ from prefect.types import LogLevel
6
+
7
+
8
+ class RunnerServerSettings(PrefectBaseSettings):
9
+ """
10
+ Settings for controlling runner server behavior
11
+ """
12
+
13
+ model_config = SettingsConfigDict(
14
+ env_prefix="PREFECT_RUNNER_SERVER_", env_file=".env", extra="ignore"
15
+ )
16
+
17
+ enable: bool = Field(
18
+ default=False,
19
+ description="Whether or not to enable the runner's webserver.",
20
+ )
21
+
22
+ host: str = Field(
23
+ default="localhost",
24
+ description="The host address the runner's webserver should bind to.",
25
+ )
26
+
27
+ port: int = Field(
28
+ default=8080,
29
+ description="The port the runner's webserver should bind to.",
30
+ )
31
+
32
+ log_level: LogLevel = Field(
33
+ default="error",
34
+ description="The log level of the runner's webserver.",
35
+ )
36
+
37
+ missed_polls_tolerance: int = Field(
38
+ default=2,
39
+ description="Number of missed polls before a runner is considered unhealthy by its webserver.",
40
+ )
41
+
42
+
43
+ class RunnerSettings(PrefectBaseSettings):
44
+ """
45
+ Settings for controlling runner behavior
46
+ """
47
+
48
+ model_config = SettingsConfigDict(
49
+ env_prefix="PREFECT_RUNNER_", env_file=".env", extra="ignore"
50
+ )
51
+
52
+ process_limit: int = Field(
53
+ default=5,
54
+ description="Maximum number of processes a runner will execute in parallel.",
55
+ )
56
+
57
+ poll_frequency: int = Field(
58
+ default=10,
59
+ description="Number of seconds a runner should wait between queries for scheduled work.",
60
+ )
61
+
62
+ server: RunnerServerSettings = Field(
63
+ default_factory=RunnerServerSettings,
64
+ description="Settings for controlling runner server behavior",
65
+ )
@@ -0,0 +1 @@
1
+ from .root import ServerSettings
@@ -0,0 +1,133 @@
1
+ from datetime import timedelta
2
+
3
+ from pydantic import AliasChoices, AliasPath, Field
4
+ from pydantic_settings import SettingsConfigDict
5
+
6
+ from prefect.settings.base import PrefectBaseSettings
7
+
8
+
9
+ class ServerAPISettings(PrefectBaseSettings):
10
+ """
11
+ Settings for controlling API server behavior
12
+ """
13
+
14
+ model_config = SettingsConfigDict(
15
+ env_prefix="PREFECT_SERVER_API_", env_file=".env", extra="ignore"
16
+ )
17
+
18
+ host: str = Field(
19
+ default="127.0.0.1",
20
+ description="The API's host address (defaults to `127.0.0.1`).",
21
+ )
22
+
23
+ port: int = Field(
24
+ default=4200,
25
+ description="The API's port address (defaults to `4200`).",
26
+ )
27
+
28
+ default_limit: int = Field(
29
+ default=200,
30
+ description="The default limit applied to queries that can return multiple objects, such as `POST /flow_runs/filter`.",
31
+ validation_alias=AliasChoices(
32
+ AliasPath("default_limit"),
33
+ "prefect_server_api_default_limit",
34
+ "prefect_api_default_limit",
35
+ ),
36
+ )
37
+
38
+ keepalive_timeout: int = Field(
39
+ default=5,
40
+ description="""
41
+ The API's keep alive timeout (defaults to `5`).
42
+ Refer to https://www.uvicorn.org/settings/#timeouts for details.
43
+
44
+ When the API is hosted behind a load balancer, you may want to set this to a value
45
+ greater than the load balancer's idle timeout.
46
+
47
+ Note this setting only applies when calling `prefect server start`; if hosting the
48
+ API with another tool you will need to configure this there instead.
49
+ """,
50
+ )
51
+
52
+ csrf_protection_enabled: bool = Field(
53
+ default=False,
54
+ description="""
55
+ Controls the activation of CSRF protection for the Prefect server API.
56
+
57
+ When enabled (`True`), the server enforces CSRF validation checks on incoming
58
+ state-changing requests (POST, PUT, PATCH, DELETE), requiring a valid CSRF
59
+ token to be included in the request headers or body. This adds a layer of
60
+ security by preventing unauthorized or malicious sites from making requests on
61
+ behalf of authenticated users.
62
+
63
+ It is recommended to enable this setting in production environments where the
64
+ API is exposed to web clients to safeguard against CSRF attacks.
65
+
66
+ Note: Enabling this setting requires corresponding support in the client for
67
+ CSRF token management. See PREFECT_CLIENT_CSRF_SUPPORT_ENABLED for more.
68
+ """,
69
+ validation_alias=AliasChoices(
70
+ AliasPath("csrf_protection_enabled"),
71
+ "prefect_server_api_csrf_protection_enabled",
72
+ "prefect_server_csrf_protection_enabled",
73
+ ),
74
+ )
75
+
76
+ csrf_token_expiration: timedelta = Field(
77
+ default=timedelta(hours=1),
78
+ description="""
79
+ Specifies the duration for which a CSRF token remains valid after being issued
80
+ by the server.
81
+
82
+ The default expiration time is set to 1 hour, which offers a reasonable
83
+ compromise. Adjust this setting based on your specific security requirements
84
+ and usage patterns.
85
+ """,
86
+ validation_alias=AliasChoices(
87
+ AliasPath("csrf_token_expiration"),
88
+ "prefect_server_api_csrf_token_expiration",
89
+ "prefect_server_csrf_token_expiration",
90
+ ),
91
+ )
92
+
93
+ cors_allowed_origins: str = Field(
94
+ default="*",
95
+ description="""
96
+ A comma-separated list of origins that are authorized to make cross-origin requests to the API.
97
+
98
+ By default, this is set to `*`, which allows requests from all origins.
99
+ """,
100
+ validation_alias=AliasChoices(
101
+ AliasPath("cors_allowed_origins"),
102
+ "prefect_server_api_cors_allowed_origins",
103
+ "prefect_server_cors_allowed_origins",
104
+ ),
105
+ )
106
+
107
+ cors_allowed_methods: str = Field(
108
+ default="*",
109
+ description="""
110
+ A comma-separated list of methods that are authorized to make cross-origin requests to the API.
111
+
112
+ By default, this is set to `*`, which allows requests from all methods.
113
+ """,
114
+ validation_alias=AliasChoices(
115
+ AliasPath("cors_allowed_methods"),
116
+ "prefect_server_api_cors_allowed_methods",
117
+ "prefect_server_cors_allowed_methods",
118
+ ),
119
+ )
120
+
121
+ cors_allowed_headers: str = Field(
122
+ default="*",
123
+ description="""
124
+ A comma-separated list of headers that are authorized to make cross-origin requests to the API.
125
+
126
+ By default, this is set to `*`, which allows requests from all headers.
127
+ """,
128
+ validation_alias=AliasChoices(
129
+ AliasPath("cors_allowed_headers"),
130
+ "prefect_server_api_cors_allowed_headers",
131
+ "prefect_server_cors_allowed_headers",
132
+ ),
133
+ )