prefect-client 2.20.4__py3-none-any.whl → 3.0.0__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 +74 -110
- prefect/_internal/compatibility/deprecated.py +6 -115
- prefect/_internal/compatibility/experimental.py +4 -79
- prefect/_internal/compatibility/migration.py +166 -0
- prefect/_internal/concurrency/__init__.py +2 -2
- prefect/_internal/concurrency/api.py +1 -35
- prefect/_internal/concurrency/calls.py +0 -6
- prefect/_internal/concurrency/cancellation.py +0 -3
- prefect/_internal/concurrency/event_loop.py +0 -20
- prefect/_internal/concurrency/inspection.py +3 -3
- prefect/_internal/concurrency/primitives.py +1 -0
- prefect/_internal/concurrency/services.py +23 -0
- prefect/_internal/concurrency/threads.py +35 -0
- prefect/_internal/concurrency/waiters.py +0 -28
- prefect/_internal/integrations.py +7 -0
- prefect/_internal/pydantic/__init__.py +0 -45
- prefect/_internal/pydantic/annotations/pendulum.py +2 -2
- prefect/_internal/pydantic/v1_schema.py +21 -22
- prefect/_internal/pydantic/v2_schema.py +0 -2
- prefect/_internal/pydantic/v2_validated_func.py +18 -23
- prefect/_internal/pytz.py +1 -1
- prefect/_internal/retries.py +61 -0
- prefect/_internal/schemas/bases.py +45 -177
- prefect/_internal/schemas/fields.py +1 -43
- prefect/_internal/schemas/validators.py +47 -233
- prefect/agent.py +3 -695
- prefect/artifacts.py +173 -14
- prefect/automations.py +39 -4
- prefect/blocks/abstract.py +1 -1
- prefect/blocks/core.py +405 -153
- prefect/blocks/fields.py +2 -57
- prefect/blocks/notifications.py +43 -28
- prefect/blocks/redis.py +168 -0
- prefect/blocks/system.py +67 -20
- prefect/blocks/webhook.py +2 -9
- prefect/cache_policies.py +239 -0
- prefect/client/__init__.py +4 -0
- prefect/client/base.py +33 -27
- prefect/client/cloud.py +65 -20
- prefect/client/collections.py +1 -1
- prefect/client/orchestration.py +650 -442
- prefect/client/schemas/actions.py +115 -100
- prefect/client/schemas/filters.py +46 -52
- prefect/client/schemas/objects.py +228 -178
- prefect/client/schemas/responses.py +18 -36
- prefect/client/schemas/schedules.py +55 -36
- prefect/client/schemas/sorting.py +2 -0
- prefect/client/subscriptions.py +8 -7
- prefect/client/types/flexible_schedule_list.py +11 -0
- prefect/client/utilities.py +9 -6
- prefect/concurrency/asyncio.py +60 -11
- prefect/concurrency/context.py +24 -0
- prefect/concurrency/events.py +2 -2
- prefect/concurrency/services.py +46 -16
- prefect/concurrency/sync.py +51 -7
- prefect/concurrency/v1/asyncio.py +143 -0
- prefect/concurrency/v1/context.py +27 -0
- prefect/concurrency/v1/events.py +61 -0
- prefect/concurrency/v1/services.py +116 -0
- prefect/concurrency/v1/sync.py +92 -0
- prefect/context.py +246 -149
- prefect/deployments/__init__.py +33 -18
- prefect/deployments/base.py +10 -15
- prefect/deployments/deployments.py +2 -1048
- prefect/deployments/flow_runs.py +178 -0
- prefect/deployments/runner.py +72 -173
- prefect/deployments/schedules.py +31 -25
- prefect/deployments/steps/__init__.py +0 -1
- prefect/deployments/steps/core.py +7 -0
- prefect/deployments/steps/pull.py +15 -21
- prefect/deployments/steps/utility.py +2 -1
- prefect/docker/__init__.py +20 -0
- prefect/docker/docker_image.py +82 -0
- prefect/engine.py +15 -2475
- prefect/events/actions.py +17 -23
- prefect/events/cli/automations.py +20 -7
- prefect/events/clients.py +142 -80
- prefect/events/filters.py +14 -18
- prefect/events/related.py +74 -75
- prefect/events/schemas/__init__.py +0 -5
- prefect/events/schemas/automations.py +55 -46
- prefect/events/schemas/deployment_triggers.py +7 -197
- prefect/events/schemas/events.py +46 -65
- prefect/events/schemas/labelling.py +10 -14
- prefect/events/utilities.py +4 -5
- prefect/events/worker.py +23 -8
- prefect/exceptions.py +15 -0
- prefect/filesystems.py +30 -529
- prefect/flow_engine.py +827 -0
- prefect/flow_runs.py +379 -7
- prefect/flows.py +470 -360
- prefect/futures.py +382 -331
- prefect/infrastructure/__init__.py +5 -26
- prefect/infrastructure/base.py +3 -320
- prefect/infrastructure/provisioners/__init__.py +5 -3
- prefect/infrastructure/provisioners/cloud_run.py +13 -8
- prefect/infrastructure/provisioners/container_instance.py +14 -9
- prefect/infrastructure/provisioners/ecs.py +10 -8
- prefect/infrastructure/provisioners/modal.py +8 -5
- prefect/input/__init__.py +4 -0
- prefect/input/actions.py +2 -4
- prefect/input/run_input.py +9 -9
- prefect/logging/formatters.py +2 -4
- prefect/logging/handlers.py +9 -14
- prefect/logging/loggers.py +5 -5
- prefect/main.py +72 -0
- prefect/plugins.py +2 -64
- prefect/profiles.toml +16 -2
- prefect/records/__init__.py +1 -0
- prefect/records/base.py +223 -0
- prefect/records/filesystem.py +207 -0
- prefect/records/memory.py +178 -0
- prefect/records/result_store.py +64 -0
- prefect/results.py +577 -504
- prefect/runner/runner.py +117 -47
- prefect/runner/server.py +32 -34
- prefect/runner/storage.py +3 -12
- prefect/runner/submit.py +2 -10
- prefect/runner/utils.py +2 -2
- prefect/runtime/__init__.py +1 -0
- prefect/runtime/deployment.py +1 -0
- prefect/runtime/flow_run.py +40 -5
- prefect/runtime/task_run.py +1 -0
- prefect/serializers.py +28 -39
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
- prefect/settings.py +209 -332
- prefect/states.py +160 -63
- prefect/task_engine.py +1478 -57
- prefect/task_runners.py +383 -287
- prefect/task_runs.py +240 -0
- prefect/task_worker.py +463 -0
- prefect/tasks.py +684 -374
- prefect/transactions.py +410 -0
- prefect/types/__init__.py +72 -86
- prefect/types/entrypoint.py +13 -0
- prefect/utilities/annotations.py +4 -3
- prefect/utilities/asyncutils.py +227 -148
- prefect/utilities/callables.py +137 -45
- prefect/utilities/collections.py +134 -86
- prefect/utilities/dispatch.py +27 -14
- prefect/utilities/dockerutils.py +11 -4
- prefect/utilities/engine.py +186 -32
- prefect/utilities/filesystem.py +4 -5
- prefect/utilities/importtools.py +26 -27
- prefect/utilities/pydantic.py +128 -38
- prefect/utilities/schema_tools/hydration.py +18 -1
- prefect/utilities/schema_tools/validation.py +30 -0
- prefect/utilities/services.py +35 -9
- prefect/utilities/templating.py +12 -2
- prefect/utilities/timeout.py +20 -5
- prefect/utilities/urls.py +195 -0
- prefect/utilities/visualization.py +1 -0
- prefect/variables.py +78 -59
- prefect/workers/__init__.py +0 -1
- prefect/workers/base.py +237 -244
- prefect/workers/block.py +5 -226
- prefect/workers/cloud.py +6 -0
- prefect/workers/process.py +265 -12
- prefect/workers/server.py +29 -11
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/METADATA +28 -24
- prefect_client-3.0.0.dist-info/RECORD +201 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/WHEEL +1 -1
- prefect/_internal/pydantic/_base_model.py +0 -51
- prefect/_internal/pydantic/_compat.py +0 -82
- prefect/_internal/pydantic/_flags.py +0 -20
- prefect/_internal/pydantic/_types.py +0 -8
- prefect/_internal/pydantic/utilities/config_dict.py +0 -72
- prefect/_internal/pydantic/utilities/field_validator.py +0 -150
- prefect/_internal/pydantic/utilities/model_construct.py +0 -56
- prefect/_internal/pydantic/utilities/model_copy.py +0 -55
- prefect/_internal/pydantic/utilities/model_dump.py +0 -136
- prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
- prefect/_internal/pydantic/utilities/model_fields.py +0 -50
- prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
- prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
- prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
- prefect/_internal/pydantic/utilities/model_validate.py +0 -75
- prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
- prefect/_internal/pydantic/utilities/model_validator.py +0 -87
- prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
- prefect/_vendor/fastapi/__init__.py +0 -25
- prefect/_vendor/fastapi/applications.py +0 -946
- prefect/_vendor/fastapi/background.py +0 -3
- prefect/_vendor/fastapi/concurrency.py +0 -44
- prefect/_vendor/fastapi/datastructures.py +0 -58
- prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
- prefect/_vendor/fastapi/dependencies/models.py +0 -64
- prefect/_vendor/fastapi/dependencies/utils.py +0 -877
- prefect/_vendor/fastapi/encoders.py +0 -177
- prefect/_vendor/fastapi/exception_handlers.py +0 -40
- prefect/_vendor/fastapi/exceptions.py +0 -46
- prefect/_vendor/fastapi/logger.py +0 -3
- prefect/_vendor/fastapi/middleware/__init__.py +0 -1
- prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
- prefect/_vendor/fastapi/middleware/cors.py +0 -3
- prefect/_vendor/fastapi/middleware/gzip.py +0 -3
- prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
- prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
- prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
- prefect/_vendor/fastapi/openapi/__init__.py +0 -0
- prefect/_vendor/fastapi/openapi/constants.py +0 -2
- prefect/_vendor/fastapi/openapi/docs.py +0 -203
- prefect/_vendor/fastapi/openapi/models.py +0 -480
- prefect/_vendor/fastapi/openapi/utils.py +0 -485
- prefect/_vendor/fastapi/param_functions.py +0 -340
- prefect/_vendor/fastapi/params.py +0 -453
- prefect/_vendor/fastapi/py.typed +0 -0
- prefect/_vendor/fastapi/requests.py +0 -4
- prefect/_vendor/fastapi/responses.py +0 -40
- prefect/_vendor/fastapi/routing.py +0 -1331
- prefect/_vendor/fastapi/security/__init__.py +0 -15
- prefect/_vendor/fastapi/security/api_key.py +0 -98
- prefect/_vendor/fastapi/security/base.py +0 -6
- prefect/_vendor/fastapi/security/http.py +0 -172
- prefect/_vendor/fastapi/security/oauth2.py +0 -227
- prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
- prefect/_vendor/fastapi/security/utils.py +0 -10
- prefect/_vendor/fastapi/staticfiles.py +0 -1
- prefect/_vendor/fastapi/templating.py +0 -3
- prefect/_vendor/fastapi/testclient.py +0 -1
- prefect/_vendor/fastapi/types.py +0 -3
- prefect/_vendor/fastapi/utils.py +0 -235
- prefect/_vendor/fastapi/websockets.py +0 -7
- prefect/_vendor/starlette/__init__.py +0 -1
- prefect/_vendor/starlette/_compat.py +0 -28
- prefect/_vendor/starlette/_exception_handler.py +0 -80
- prefect/_vendor/starlette/_utils.py +0 -88
- prefect/_vendor/starlette/applications.py +0 -261
- prefect/_vendor/starlette/authentication.py +0 -159
- prefect/_vendor/starlette/background.py +0 -43
- prefect/_vendor/starlette/concurrency.py +0 -59
- prefect/_vendor/starlette/config.py +0 -151
- prefect/_vendor/starlette/convertors.py +0 -87
- prefect/_vendor/starlette/datastructures.py +0 -707
- prefect/_vendor/starlette/endpoints.py +0 -130
- prefect/_vendor/starlette/exceptions.py +0 -60
- prefect/_vendor/starlette/formparsers.py +0 -276
- prefect/_vendor/starlette/middleware/__init__.py +0 -17
- prefect/_vendor/starlette/middleware/authentication.py +0 -52
- prefect/_vendor/starlette/middleware/base.py +0 -220
- prefect/_vendor/starlette/middleware/cors.py +0 -176
- prefect/_vendor/starlette/middleware/errors.py +0 -265
- prefect/_vendor/starlette/middleware/exceptions.py +0 -74
- prefect/_vendor/starlette/middleware/gzip.py +0 -113
- prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
- prefect/_vendor/starlette/middleware/sessions.py +0 -82
- prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
- prefect/_vendor/starlette/middleware/wsgi.py +0 -147
- prefect/_vendor/starlette/py.typed +0 -0
- prefect/_vendor/starlette/requests.py +0 -328
- prefect/_vendor/starlette/responses.py +0 -347
- prefect/_vendor/starlette/routing.py +0 -933
- prefect/_vendor/starlette/schemas.py +0 -154
- prefect/_vendor/starlette/staticfiles.py +0 -248
- prefect/_vendor/starlette/status.py +0 -199
- prefect/_vendor/starlette/templating.py +0 -231
- prefect/_vendor/starlette/testclient.py +0 -804
- prefect/_vendor/starlette/types.py +0 -30
- prefect/_vendor/starlette/websockets.py +0 -193
- prefect/blocks/kubernetes.py +0 -119
- prefect/deprecated/__init__.py +0 -0
- prefect/deprecated/data_documents.py +0 -350
- prefect/deprecated/packaging/__init__.py +0 -12
- prefect/deprecated/packaging/base.py +0 -96
- prefect/deprecated/packaging/docker.py +0 -146
- prefect/deprecated/packaging/file.py +0 -92
- prefect/deprecated/packaging/orion.py +0 -80
- prefect/deprecated/packaging/serializers.py +0 -171
- prefect/events/instrument.py +0 -135
- prefect/infrastructure/container.py +0 -824
- prefect/infrastructure/kubernetes.py +0 -920
- prefect/infrastructure/process.py +0 -289
- prefect/manifests.py +0 -20
- prefect/new_flow_engine.py +0 -449
- prefect/new_task_engine.py +0 -423
- prefect/pydantic/__init__.py +0 -76
- prefect/pydantic/main.py +0 -39
- prefect/software/__init__.py +0 -2
- prefect/software/base.py +0 -50
- prefect/software/conda.py +0 -199
- prefect/software/pip.py +0 -122
- prefect/software/python.py +0 -52
- prefect/task_server.py +0 -322
- prefect_client-2.20.4.dist-info/RECORD +0 -294
- /prefect/{_internal/pydantic/utilities → client/types}/__init__.py +0 -0
- /prefect/{_vendor → concurrency/v1}/__init__.py +0 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/top_level.txt +0 -0
@@ -1,261 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import typing
|
4
|
-
import warnings
|
5
|
-
|
6
|
-
from prefect._vendor.starlette.datastructures import State, URLPath
|
7
|
-
from prefect._vendor.starlette.middleware import Middleware
|
8
|
-
from prefect._vendor.starlette.middleware.base import BaseHTTPMiddleware
|
9
|
-
from prefect._vendor.starlette.middleware.errors import ServerErrorMiddleware
|
10
|
-
from prefect._vendor.starlette.middleware.exceptions import ExceptionMiddleware
|
11
|
-
from prefect._vendor.starlette.requests import Request
|
12
|
-
from prefect._vendor.starlette.responses import Response
|
13
|
-
from prefect._vendor.starlette.routing import BaseRoute, Router
|
14
|
-
from prefect._vendor.starlette.types import (
|
15
|
-
ASGIApp,
|
16
|
-
ExceptionHandler,
|
17
|
-
Lifespan,
|
18
|
-
Receive,
|
19
|
-
Scope,
|
20
|
-
Send,
|
21
|
-
)
|
22
|
-
from prefect._vendor.starlette.websockets import WebSocket
|
23
|
-
|
24
|
-
AppType = typing.TypeVar("AppType", bound="Starlette")
|
25
|
-
|
26
|
-
|
27
|
-
class Starlette:
|
28
|
-
"""
|
29
|
-
Creates an application instance.
|
30
|
-
|
31
|
-
**Parameters:**
|
32
|
-
|
33
|
-
* **debug** - Boolean indicating if debug tracebacks should be returned on errors.
|
34
|
-
* **routes** - A list of routes to serve incoming HTTP and WebSocket requests.
|
35
|
-
* **middleware** - A list of middleware to run for every request. A starlette
|
36
|
-
application will always automatically include two middleware classes.
|
37
|
-
`ServerErrorMiddleware` is added as the very outermost middleware, to handle
|
38
|
-
any uncaught errors occurring anywhere in the entire stack.
|
39
|
-
`ExceptionMiddleware` is added as the very innermost middleware, to deal
|
40
|
-
with handled exception cases occurring in the routing or endpoints.
|
41
|
-
* **exception_handlers** - A mapping of either integer status codes,
|
42
|
-
or exception class types onto callables which handle the exceptions.
|
43
|
-
Exception handler callables should be of the form
|
44
|
-
`handler(request, exc) -> response` and may be either standard functions, or
|
45
|
-
async functions.
|
46
|
-
* **on_startup** - A list of callables to run on application startup.
|
47
|
-
Startup handler callables do not take any arguments, and may be either
|
48
|
-
standard functions, or async functions.
|
49
|
-
* **on_shutdown** - A list of callables to run on application shutdown.
|
50
|
-
Shutdown handler callables do not take any arguments, and may be either
|
51
|
-
standard functions, or async functions.
|
52
|
-
* **lifespan** - A lifespan context function, which can be used to perform
|
53
|
-
startup and shutdown tasks. This is a newer style that replaces the
|
54
|
-
`on_startup` and `on_shutdown` handlers. Use one or the other, not both.
|
55
|
-
"""
|
56
|
-
|
57
|
-
def __init__(
|
58
|
-
self: "AppType",
|
59
|
-
debug: bool = False,
|
60
|
-
routes: typing.Sequence[BaseRoute] | None = None,
|
61
|
-
middleware: typing.Sequence[Middleware] | None = None,
|
62
|
-
exception_handlers: typing.Mapping[typing.Any, ExceptionHandler] | None = None,
|
63
|
-
on_startup: typing.Sequence[typing.Callable[[], typing.Any]] | None = None,
|
64
|
-
on_shutdown: typing.Sequence[typing.Callable[[], typing.Any]] | None = None,
|
65
|
-
lifespan: typing.Optional[Lifespan["AppType"]] = None,
|
66
|
-
) -> None:
|
67
|
-
# The lifespan context function is a newer style that replaces
|
68
|
-
# on_startup / on_shutdown handlers. Use one or the other, not both.
|
69
|
-
assert lifespan is None or (
|
70
|
-
on_startup is None and on_shutdown is None
|
71
|
-
), "Use either 'lifespan' or 'on_startup'/'on_shutdown', not both."
|
72
|
-
|
73
|
-
self.debug = debug
|
74
|
-
self.state = State()
|
75
|
-
self.router = Router(
|
76
|
-
routes, on_startup=on_startup, on_shutdown=on_shutdown, lifespan=lifespan
|
77
|
-
)
|
78
|
-
self.exception_handlers = (
|
79
|
-
{} if exception_handlers is None else dict(exception_handlers)
|
80
|
-
)
|
81
|
-
self.user_middleware = [] if middleware is None else list(middleware)
|
82
|
-
self.middleware_stack: typing.Optional[ASGIApp] = None
|
83
|
-
|
84
|
-
def build_middleware_stack(self) -> ASGIApp:
|
85
|
-
debug = self.debug
|
86
|
-
error_handler = None
|
87
|
-
exception_handlers: typing.Dict[
|
88
|
-
typing.Any, typing.Callable[[Request, Exception], Response]
|
89
|
-
] = {}
|
90
|
-
|
91
|
-
for key, value in self.exception_handlers.items():
|
92
|
-
if key in (500, Exception):
|
93
|
-
error_handler = value
|
94
|
-
else:
|
95
|
-
exception_handlers[key] = value
|
96
|
-
|
97
|
-
middleware = (
|
98
|
-
[Middleware(ServerErrorMiddleware, handler=error_handler, debug=debug)]
|
99
|
-
+ self.user_middleware
|
100
|
-
+ [
|
101
|
-
Middleware(
|
102
|
-
ExceptionMiddleware, handlers=exception_handlers, debug=debug
|
103
|
-
)
|
104
|
-
]
|
105
|
-
)
|
106
|
-
|
107
|
-
app = self.router
|
108
|
-
for cls, options in reversed(middleware):
|
109
|
-
app = cls(app=app, **options)
|
110
|
-
return app
|
111
|
-
|
112
|
-
@property
|
113
|
-
def routes(self) -> typing.List[BaseRoute]:
|
114
|
-
return self.router.routes
|
115
|
-
|
116
|
-
def url_path_for(self, name: str, /, **path_params: typing.Any) -> URLPath:
|
117
|
-
return self.router.url_path_for(name, **path_params)
|
118
|
-
|
119
|
-
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
120
|
-
scope["app"] = self
|
121
|
-
if self.middleware_stack is None:
|
122
|
-
self.middleware_stack = self.build_middleware_stack()
|
123
|
-
await self.middleware_stack(scope, receive, send)
|
124
|
-
|
125
|
-
def on_event(self, event_type: str) -> typing.Callable: # type: ignore[type-arg]
|
126
|
-
return self.router.on_event(event_type) # pragma: nocover
|
127
|
-
|
128
|
-
def mount(self, path: str, app: ASGIApp, name: str | None = None) -> None:
|
129
|
-
self.router.mount(path, app=app, name=name) # pragma: no cover
|
130
|
-
|
131
|
-
def host(self, host: str, app: ASGIApp, name: str | None = None) -> None:
|
132
|
-
self.router.host(host, app=app, name=name) # pragma: no cover
|
133
|
-
|
134
|
-
def add_middleware(self, middleware_class: type, **options: typing.Any) -> None:
|
135
|
-
if self.middleware_stack is not None: # pragma: no cover
|
136
|
-
raise RuntimeError("Cannot add middleware after an application has started")
|
137
|
-
self.user_middleware.insert(0, Middleware(middleware_class, **options))
|
138
|
-
|
139
|
-
def add_exception_handler(
|
140
|
-
self,
|
141
|
-
exc_class_or_status_code: int | typing.Type[Exception],
|
142
|
-
handler: ExceptionHandler,
|
143
|
-
) -> None: # pragma: no cover
|
144
|
-
self.exception_handlers[exc_class_or_status_code] = handler
|
145
|
-
|
146
|
-
def add_event_handler(
|
147
|
-
self,
|
148
|
-
event_type: str,
|
149
|
-
func: typing.Callable, # type: ignore[type-arg]
|
150
|
-
) -> None: # pragma: no cover
|
151
|
-
self.router.add_event_handler(event_type, func)
|
152
|
-
|
153
|
-
def add_route(
|
154
|
-
self,
|
155
|
-
path: str,
|
156
|
-
route: typing.Callable[[Request], typing.Awaitable[Response] | Response],
|
157
|
-
methods: typing.Optional[typing.List[str]] = None,
|
158
|
-
name: typing.Optional[str] = None,
|
159
|
-
include_in_schema: bool = True,
|
160
|
-
) -> None: # pragma: no cover
|
161
|
-
self.router.add_route(
|
162
|
-
path, route, methods=methods, name=name, include_in_schema=include_in_schema
|
163
|
-
)
|
164
|
-
|
165
|
-
def add_websocket_route(
|
166
|
-
self,
|
167
|
-
path: str,
|
168
|
-
route: typing.Callable[[WebSocket], typing.Awaitable[None]],
|
169
|
-
name: str | None = None,
|
170
|
-
) -> None: # pragma: no cover
|
171
|
-
self.router.add_websocket_route(path, route, name=name)
|
172
|
-
|
173
|
-
def exception_handler(
|
174
|
-
self, exc_class_or_status_code: int | typing.Type[Exception]
|
175
|
-
) -> typing.Callable: # type: ignore[type-arg]
|
176
|
-
warnings.warn(
|
177
|
-
"The `exception_handler` decorator is deprecated, and will be removed in version 1.0.0. " # noqa: E501
|
178
|
-
"Refer to https://www.starlette.io/exceptions/ for the recommended approach.", # noqa: E501
|
179
|
-
DeprecationWarning,
|
180
|
-
)
|
181
|
-
|
182
|
-
def decorator(func: typing.Callable) -> typing.Callable: # type: ignore[type-arg] # noqa: E501
|
183
|
-
self.add_exception_handler(exc_class_or_status_code, func)
|
184
|
-
return func
|
185
|
-
|
186
|
-
return decorator
|
187
|
-
|
188
|
-
def route(
|
189
|
-
self,
|
190
|
-
path: str,
|
191
|
-
methods: typing.List[str] | None = None,
|
192
|
-
name: str | None = None,
|
193
|
-
include_in_schema: bool = True,
|
194
|
-
) -> typing.Callable: # type: ignore[type-arg]
|
195
|
-
"""
|
196
|
-
We no longer document this decorator style API, and its usage is discouraged.
|
197
|
-
Instead you should use the following approach:
|
198
|
-
|
199
|
-
>>> routes = [Route(path, endpoint=...), ...]
|
200
|
-
>>> app = Starlette(routes=routes)
|
201
|
-
"""
|
202
|
-
warnings.warn(
|
203
|
-
"The `route` decorator is deprecated, and will be removed in version 1.0.0. " # noqa: E501
|
204
|
-
"Refer to https://www.starlette.io/routing/ for the recommended approach.", # noqa: E501
|
205
|
-
DeprecationWarning,
|
206
|
-
)
|
207
|
-
|
208
|
-
def decorator(func: typing.Callable) -> typing.Callable: # type: ignore[type-arg] # noqa: E501
|
209
|
-
self.router.add_route(
|
210
|
-
path,
|
211
|
-
func,
|
212
|
-
methods=methods,
|
213
|
-
name=name,
|
214
|
-
include_in_schema=include_in_schema,
|
215
|
-
)
|
216
|
-
return func
|
217
|
-
|
218
|
-
return decorator
|
219
|
-
|
220
|
-
def websocket_route(self, path: str, name: str | None = None) -> typing.Callable: # type: ignore[type-arg]
|
221
|
-
"""
|
222
|
-
We no longer document this decorator style API, and its usage is discouraged.
|
223
|
-
Instead you should use the following approach:
|
224
|
-
|
225
|
-
>>> routes = [WebSocketRoute(path, endpoint=...), ...]
|
226
|
-
>>> app = Starlette(routes=routes)
|
227
|
-
"""
|
228
|
-
warnings.warn(
|
229
|
-
"The `websocket_route` decorator is deprecated, and will be removed in version 1.0.0. " # noqa: E501
|
230
|
-
"Refer to https://www.starlette.io/routing/#websocket-routing for the recommended approach.", # noqa: E501
|
231
|
-
DeprecationWarning,
|
232
|
-
)
|
233
|
-
|
234
|
-
def decorator(func: typing.Callable) -> typing.Callable: # type: ignore[type-arg] # noqa: E501
|
235
|
-
self.router.add_websocket_route(path, func, name=name)
|
236
|
-
return func
|
237
|
-
|
238
|
-
return decorator
|
239
|
-
|
240
|
-
def middleware(self, middleware_type: str) -> typing.Callable: # type: ignore[type-arg] # noqa: E501
|
241
|
-
"""
|
242
|
-
We no longer document this decorator style API, and its usage is discouraged.
|
243
|
-
Instead you should use the following approach:
|
244
|
-
|
245
|
-
>>> middleware = [Middleware(...), ...]
|
246
|
-
>>> app = Starlette(middleware=middleware)
|
247
|
-
"""
|
248
|
-
warnings.warn(
|
249
|
-
"The `middleware` decorator is deprecated, and will be removed in version 1.0.0. " # noqa: E501
|
250
|
-
"Refer to https://www.starlette.io/middleware/#using-middleware for recommended approach.", # noqa: E501
|
251
|
-
DeprecationWarning,
|
252
|
-
)
|
253
|
-
assert (
|
254
|
-
middleware_type == "http"
|
255
|
-
), 'Currently only middleware("http") is supported.'
|
256
|
-
|
257
|
-
def decorator(func: typing.Callable) -> typing.Callable: # type: ignore[type-arg] # noqa: E501
|
258
|
-
self.add_middleware(BaseHTTPMiddleware, dispatch=func)
|
259
|
-
return func
|
260
|
-
|
261
|
-
return decorator
|
@@ -1,159 +0,0 @@
|
|
1
|
-
import functools
|
2
|
-
import inspect
|
3
|
-
import sys
|
4
|
-
import typing
|
5
|
-
from urllib.parse import urlencode
|
6
|
-
|
7
|
-
if sys.version_info >= (3, 10): # pragma: no cover
|
8
|
-
from typing import ParamSpec
|
9
|
-
else: # pragma: no cover
|
10
|
-
from typing_extensions import ParamSpec
|
11
|
-
|
12
|
-
from prefect._vendor.starlette._utils import is_async_callable
|
13
|
-
from prefect._vendor.starlette.exceptions import HTTPException
|
14
|
-
from prefect._vendor.starlette.requests import HTTPConnection, Request
|
15
|
-
from prefect._vendor.starlette.responses import RedirectResponse
|
16
|
-
from prefect._vendor.starlette.websockets import WebSocket
|
17
|
-
|
18
|
-
_P = ParamSpec("_P")
|
19
|
-
|
20
|
-
|
21
|
-
def has_required_scope(conn: HTTPConnection, scopes: typing.Sequence[str]) -> bool:
|
22
|
-
for scope in scopes:
|
23
|
-
if scope not in conn.auth.scopes:
|
24
|
-
return False
|
25
|
-
return True
|
26
|
-
|
27
|
-
|
28
|
-
def requires(
|
29
|
-
scopes: typing.Union[str, typing.Sequence[str]],
|
30
|
-
status_code: int = 403,
|
31
|
-
redirect: typing.Optional[str] = None,
|
32
|
-
) -> typing.Callable[
|
33
|
-
[typing.Callable[_P, typing.Any]], typing.Callable[_P, typing.Any]
|
34
|
-
]:
|
35
|
-
scopes_list = [scopes] if isinstance(scopes, str) else list(scopes)
|
36
|
-
|
37
|
-
def decorator(
|
38
|
-
func: typing.Callable[_P, typing.Any],
|
39
|
-
) -> typing.Callable[_P, typing.Any]:
|
40
|
-
sig = inspect.signature(func)
|
41
|
-
for idx, parameter in enumerate(sig.parameters.values()):
|
42
|
-
if parameter.name == "request" or parameter.name == "websocket":
|
43
|
-
type_ = parameter.name
|
44
|
-
break
|
45
|
-
else:
|
46
|
-
raise Exception(
|
47
|
-
f'No "request" or "websocket" argument on function "{func}"'
|
48
|
-
)
|
49
|
-
|
50
|
-
if type_ == "websocket":
|
51
|
-
# Handle websocket functions. (Always async)
|
52
|
-
@functools.wraps(func)
|
53
|
-
async def websocket_wrapper(*args: _P.args, **kwargs: _P.kwargs) -> None:
|
54
|
-
websocket = kwargs.get(
|
55
|
-
"websocket", args[idx] if idx < len(args) else None
|
56
|
-
)
|
57
|
-
assert isinstance(websocket, WebSocket)
|
58
|
-
|
59
|
-
if not has_required_scope(websocket, scopes_list):
|
60
|
-
await websocket.close()
|
61
|
-
else:
|
62
|
-
await func(*args, **kwargs)
|
63
|
-
|
64
|
-
return websocket_wrapper
|
65
|
-
|
66
|
-
elif is_async_callable(func):
|
67
|
-
# Handle async request/response functions.
|
68
|
-
@functools.wraps(func)
|
69
|
-
async def async_wrapper(*args: _P.args, **kwargs: _P.kwargs) -> typing.Any:
|
70
|
-
request = kwargs.get("request", args[idx] if idx < len(args) else None)
|
71
|
-
assert isinstance(request, Request)
|
72
|
-
|
73
|
-
if not has_required_scope(request, scopes_list):
|
74
|
-
if redirect is not None:
|
75
|
-
orig_request_qparam = urlencode({"next": str(request.url)})
|
76
|
-
next_url = "{redirect_path}?{orig_request}".format(
|
77
|
-
redirect_path=request.url_for(redirect),
|
78
|
-
orig_request=orig_request_qparam,
|
79
|
-
)
|
80
|
-
return RedirectResponse(url=next_url, status_code=303)
|
81
|
-
raise HTTPException(status_code=status_code)
|
82
|
-
return await func(*args, **kwargs)
|
83
|
-
|
84
|
-
return async_wrapper
|
85
|
-
|
86
|
-
else:
|
87
|
-
# Handle sync request/response functions.
|
88
|
-
@functools.wraps(func)
|
89
|
-
def sync_wrapper(*args: _P.args, **kwargs: _P.kwargs) -> typing.Any:
|
90
|
-
request = kwargs.get("request", args[idx] if idx < len(args) else None)
|
91
|
-
assert isinstance(request, Request)
|
92
|
-
|
93
|
-
if not has_required_scope(request, scopes_list):
|
94
|
-
if redirect is not None:
|
95
|
-
orig_request_qparam = urlencode({"next": str(request.url)})
|
96
|
-
next_url = "{redirect_path}?{orig_request}".format(
|
97
|
-
redirect_path=request.url_for(redirect),
|
98
|
-
orig_request=orig_request_qparam,
|
99
|
-
)
|
100
|
-
return RedirectResponse(url=next_url, status_code=303)
|
101
|
-
raise HTTPException(status_code=status_code)
|
102
|
-
return func(*args, **kwargs)
|
103
|
-
|
104
|
-
return sync_wrapper
|
105
|
-
|
106
|
-
return decorator
|
107
|
-
|
108
|
-
|
109
|
-
class AuthenticationError(Exception):
|
110
|
-
pass
|
111
|
-
|
112
|
-
|
113
|
-
class AuthenticationBackend:
|
114
|
-
async def authenticate(
|
115
|
-
self, conn: HTTPConnection
|
116
|
-
) -> typing.Optional[typing.Tuple["AuthCredentials", "BaseUser"]]:
|
117
|
-
raise NotImplementedError() # pragma: no cover
|
118
|
-
|
119
|
-
|
120
|
-
class AuthCredentials:
|
121
|
-
def __init__(self, scopes: typing.Optional[typing.Sequence[str]] = None):
|
122
|
-
self.scopes = [] if scopes is None else list(scopes)
|
123
|
-
|
124
|
-
|
125
|
-
class BaseUser:
|
126
|
-
@property
|
127
|
-
def is_authenticated(self) -> bool:
|
128
|
-
raise NotImplementedError() # pragma: no cover
|
129
|
-
|
130
|
-
@property
|
131
|
-
def display_name(self) -> str:
|
132
|
-
raise NotImplementedError() # pragma: no cover
|
133
|
-
|
134
|
-
@property
|
135
|
-
def identity(self) -> str:
|
136
|
-
raise NotImplementedError() # pragma: no cover
|
137
|
-
|
138
|
-
|
139
|
-
class SimpleUser(BaseUser):
|
140
|
-
def __init__(self, username: str) -> None:
|
141
|
-
self.username = username
|
142
|
-
|
143
|
-
@property
|
144
|
-
def is_authenticated(self) -> bool:
|
145
|
-
return True
|
146
|
-
|
147
|
-
@property
|
148
|
-
def display_name(self) -> str:
|
149
|
-
return self.username
|
150
|
-
|
151
|
-
|
152
|
-
class UnauthenticatedUser(BaseUser):
|
153
|
-
@property
|
154
|
-
def is_authenticated(self) -> bool:
|
155
|
-
return False
|
156
|
-
|
157
|
-
@property
|
158
|
-
def display_name(self) -> str:
|
159
|
-
return ""
|
@@ -1,43 +0,0 @@
|
|
1
|
-
import sys
|
2
|
-
import typing
|
3
|
-
|
4
|
-
if sys.version_info >= (3, 10): # pragma: no cover
|
5
|
-
from typing import ParamSpec
|
6
|
-
else: # pragma: no cover
|
7
|
-
from typing_extensions import ParamSpec
|
8
|
-
|
9
|
-
from prefect._vendor.starlette._utils import is_async_callable
|
10
|
-
from prefect._vendor.starlette.concurrency import run_in_threadpool
|
11
|
-
|
12
|
-
P = ParamSpec("P")
|
13
|
-
|
14
|
-
|
15
|
-
class BackgroundTask:
|
16
|
-
def __init__(
|
17
|
-
self, func: typing.Callable[P, typing.Any], *args: P.args, **kwargs: P.kwargs
|
18
|
-
) -> None:
|
19
|
-
self.func = func
|
20
|
-
self.args = args
|
21
|
-
self.kwargs = kwargs
|
22
|
-
self.is_async = is_async_callable(func)
|
23
|
-
|
24
|
-
async def __call__(self) -> None:
|
25
|
-
if self.is_async:
|
26
|
-
await self.func(*self.args, **self.kwargs)
|
27
|
-
else:
|
28
|
-
await run_in_threadpool(self.func, *self.args, **self.kwargs)
|
29
|
-
|
30
|
-
|
31
|
-
class BackgroundTasks(BackgroundTask):
|
32
|
-
def __init__(self, tasks: typing.Optional[typing.Sequence[BackgroundTask]] = None):
|
33
|
-
self.tasks = list(tasks) if tasks else []
|
34
|
-
|
35
|
-
def add_task(
|
36
|
-
self, func: typing.Callable[P, typing.Any], *args: P.args, **kwargs: P.kwargs
|
37
|
-
) -> None:
|
38
|
-
task = BackgroundTask(func, *args, **kwargs)
|
39
|
-
self.tasks.append(task)
|
40
|
-
|
41
|
-
async def __call__(self) -> None:
|
42
|
-
for task in self.tasks:
|
43
|
-
await task()
|
@@ -1,59 +0,0 @@
|
|
1
|
-
import functools
|
2
|
-
import typing
|
3
|
-
import warnings
|
4
|
-
|
5
|
-
import anyio.to_thread
|
6
|
-
|
7
|
-
T = typing.TypeVar("T")
|
8
|
-
|
9
|
-
|
10
|
-
async def run_until_first_complete(*args: typing.Tuple[typing.Callable, dict]) -> None: # type: ignore[type-arg] # noqa: E501
|
11
|
-
warnings.warn(
|
12
|
-
"run_until_first_complete is deprecated "
|
13
|
-
"and will be removed in a future version.",
|
14
|
-
DeprecationWarning,
|
15
|
-
)
|
16
|
-
|
17
|
-
async with anyio.create_task_group() as task_group:
|
18
|
-
|
19
|
-
async def run(func: typing.Callable[[], typing.Coroutine]) -> None: # type: ignore[type-arg] # noqa: E501
|
20
|
-
await func()
|
21
|
-
task_group.cancel_scope.cancel()
|
22
|
-
|
23
|
-
for func, kwargs in args:
|
24
|
-
task_group.start_soon(run, functools.partial(func, **kwargs))
|
25
|
-
|
26
|
-
|
27
|
-
# TODO: We should use `ParamSpec` here, but mypy doesn't support it yet.
|
28
|
-
# Check https://github.com/python/mypy/issues/12278 for more details.
|
29
|
-
async def run_in_threadpool(
|
30
|
-
func: typing.Callable[..., T], *args: typing.Any, **kwargs: typing.Any
|
31
|
-
) -> T:
|
32
|
-
if kwargs: # pragma: no cover
|
33
|
-
# run_sync doesn't accept 'kwargs', so bind them in here
|
34
|
-
func = functools.partial(func, **kwargs)
|
35
|
-
return await anyio.to_thread.run_sync(func, *args)
|
36
|
-
|
37
|
-
|
38
|
-
class _StopIteration(Exception):
|
39
|
-
pass
|
40
|
-
|
41
|
-
|
42
|
-
def _next(iterator: typing.Iterator[T]) -> T:
|
43
|
-
# We can't raise `StopIteration` from within the threadpool iterator
|
44
|
-
# and catch it outside that context, so we coerce them into a different
|
45
|
-
# exception type.
|
46
|
-
try:
|
47
|
-
return next(iterator)
|
48
|
-
except StopIteration:
|
49
|
-
raise _StopIteration
|
50
|
-
|
51
|
-
|
52
|
-
async def iterate_in_threadpool(
|
53
|
-
iterator: typing.Iterator[T],
|
54
|
-
) -> typing.AsyncIterator[T]:
|
55
|
-
while True:
|
56
|
-
try:
|
57
|
-
yield await anyio.to_thread.run_sync(_next, iterator)
|
58
|
-
except _StopIteration:
|
59
|
-
break
|
@@ -1,151 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import typing
|
3
|
-
from pathlib import Path
|
4
|
-
|
5
|
-
|
6
|
-
class undefined:
|
7
|
-
pass
|
8
|
-
|
9
|
-
|
10
|
-
class EnvironError(Exception):
|
11
|
-
pass
|
12
|
-
|
13
|
-
|
14
|
-
class Environ(typing.MutableMapping[str, str]):
|
15
|
-
def __init__(self, environ: typing.MutableMapping[str, str] = os.environ):
|
16
|
-
self._environ = environ
|
17
|
-
self._has_been_read: typing.Set[str] = set()
|
18
|
-
|
19
|
-
def __getitem__(self, key: str) -> str:
|
20
|
-
self._has_been_read.add(key)
|
21
|
-
return self._environ.__getitem__(key)
|
22
|
-
|
23
|
-
def __setitem__(self, key: str, value: str) -> None:
|
24
|
-
if key in self._has_been_read:
|
25
|
-
raise EnvironError(
|
26
|
-
f"Attempting to set environ['{key}'], but the value has already been "
|
27
|
-
"read."
|
28
|
-
)
|
29
|
-
self._environ.__setitem__(key, value)
|
30
|
-
|
31
|
-
def __delitem__(self, key: str) -> None:
|
32
|
-
if key in self._has_been_read:
|
33
|
-
raise EnvironError(
|
34
|
-
f"Attempting to delete environ['{key}'], but the value has already "
|
35
|
-
"been read."
|
36
|
-
)
|
37
|
-
self._environ.__delitem__(key)
|
38
|
-
|
39
|
-
def __iter__(self) -> typing.Iterator[str]:
|
40
|
-
return iter(self._environ)
|
41
|
-
|
42
|
-
def __len__(self) -> int:
|
43
|
-
return len(self._environ)
|
44
|
-
|
45
|
-
|
46
|
-
environ = Environ()
|
47
|
-
|
48
|
-
T = typing.TypeVar("T")
|
49
|
-
|
50
|
-
|
51
|
-
class Config:
|
52
|
-
def __init__(
|
53
|
-
self,
|
54
|
-
env_file: typing.Optional[typing.Union[str, Path]] = None,
|
55
|
-
environ: typing.Mapping[str, str] = environ,
|
56
|
-
env_prefix: str = "",
|
57
|
-
) -> None:
|
58
|
-
self.environ = environ
|
59
|
-
self.env_prefix = env_prefix
|
60
|
-
self.file_values: typing.Dict[str, str] = {}
|
61
|
-
if env_file is not None and os.path.isfile(env_file):
|
62
|
-
self.file_values = self._read_file(env_file)
|
63
|
-
|
64
|
-
@typing.overload
|
65
|
-
def __call__(self, key: str, *, default: None) -> typing.Optional[str]:
|
66
|
-
...
|
67
|
-
|
68
|
-
@typing.overload
|
69
|
-
def __call__(self, key: str, cast: typing.Type[T], default: T = ...) -> T:
|
70
|
-
...
|
71
|
-
|
72
|
-
@typing.overload
|
73
|
-
def __call__(
|
74
|
-
self, key: str, cast: typing.Type[str] = ..., default: str = ...
|
75
|
-
) -> str:
|
76
|
-
...
|
77
|
-
|
78
|
-
@typing.overload
|
79
|
-
def __call__(
|
80
|
-
self,
|
81
|
-
key: str,
|
82
|
-
cast: typing.Callable[[typing.Any], T] = ...,
|
83
|
-
default: typing.Any = ...,
|
84
|
-
) -> T:
|
85
|
-
...
|
86
|
-
|
87
|
-
@typing.overload
|
88
|
-
def __call__(
|
89
|
-
self, key: str, cast: typing.Type[str] = ..., default: T = ...
|
90
|
-
) -> typing.Union[T, str]:
|
91
|
-
...
|
92
|
-
|
93
|
-
def __call__(
|
94
|
-
self,
|
95
|
-
key: str,
|
96
|
-
cast: typing.Optional[typing.Callable[[typing.Any], typing.Any]] = None,
|
97
|
-
default: typing.Any = undefined,
|
98
|
-
) -> typing.Any:
|
99
|
-
return self.get(key, cast, default)
|
100
|
-
|
101
|
-
def get(
|
102
|
-
self,
|
103
|
-
key: str,
|
104
|
-
cast: typing.Optional[typing.Callable[[typing.Any], typing.Any]] = None,
|
105
|
-
default: typing.Any = undefined,
|
106
|
-
) -> typing.Any:
|
107
|
-
key = self.env_prefix + key
|
108
|
-
if key in self.environ:
|
109
|
-
value = self.environ[key]
|
110
|
-
return self._perform_cast(key, value, cast)
|
111
|
-
if key in self.file_values:
|
112
|
-
value = self.file_values[key]
|
113
|
-
return self._perform_cast(key, value, cast)
|
114
|
-
if default is not undefined:
|
115
|
-
return self._perform_cast(key, default, cast)
|
116
|
-
raise KeyError(f"Config '{key}' is missing, and has no default.")
|
117
|
-
|
118
|
-
def _read_file(self, file_name: typing.Union[str, Path]) -> typing.Dict[str, str]:
|
119
|
-
file_values: typing.Dict[str, str] = {}
|
120
|
-
with open(file_name) as input_file:
|
121
|
-
for line in input_file.readlines():
|
122
|
-
line = line.strip()
|
123
|
-
if "=" in line and not line.startswith("#"):
|
124
|
-
key, value = line.split("=", 1)
|
125
|
-
key = key.strip()
|
126
|
-
value = value.strip().strip("\"'")
|
127
|
-
file_values[key] = value
|
128
|
-
return file_values
|
129
|
-
|
130
|
-
def _perform_cast(
|
131
|
-
self,
|
132
|
-
key: str,
|
133
|
-
value: typing.Any,
|
134
|
-
cast: typing.Optional[typing.Callable[[typing.Any], typing.Any]] = None,
|
135
|
-
) -> typing.Any:
|
136
|
-
if cast is None or value is None:
|
137
|
-
return value
|
138
|
-
elif cast is bool and isinstance(value, str):
|
139
|
-
mapping = {"true": True, "1": True, "false": False, "0": False}
|
140
|
-
value = value.lower()
|
141
|
-
if value not in mapping:
|
142
|
-
raise ValueError(
|
143
|
-
f"Config '{key}' has value '{value}'. Not a valid bool."
|
144
|
-
)
|
145
|
-
return mapping[value]
|
146
|
-
try:
|
147
|
-
return cast(value)
|
148
|
-
except (TypeError, ValueError):
|
149
|
-
raise ValueError(
|
150
|
-
f"Config '{key}' has value '{value}'. Not a valid {cast.__name__}."
|
151
|
-
)
|