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,74 +0,0 @@
|
|
1
|
-
import typing
|
2
|
-
|
3
|
-
from prefect._vendor.starlette._exception_handler import (
|
4
|
-
ExceptionHandlers,
|
5
|
-
StatusHandlers,
|
6
|
-
wrap_app_handling_exceptions,
|
7
|
-
)
|
8
|
-
from prefect._vendor.starlette.exceptions import HTTPException, WebSocketException
|
9
|
-
from prefect._vendor.starlette.requests import Request
|
10
|
-
from prefect._vendor.starlette.responses import PlainTextResponse, Response
|
11
|
-
from prefect._vendor.starlette.types import ASGIApp, Receive, Scope, Send
|
12
|
-
from prefect._vendor.starlette.websockets import WebSocket
|
13
|
-
|
14
|
-
|
15
|
-
class ExceptionMiddleware:
|
16
|
-
def __init__(
|
17
|
-
self,
|
18
|
-
app: ASGIApp,
|
19
|
-
handlers: typing.Optional[
|
20
|
-
typing.Mapping[typing.Any, typing.Callable[[Request, Exception], Response]]
|
21
|
-
] = None,
|
22
|
-
debug: bool = False,
|
23
|
-
) -> None:
|
24
|
-
self.app = app
|
25
|
-
self.debug = debug # TODO: We ought to handle 404 cases if debug is set.
|
26
|
-
self._status_handlers: StatusHandlers = {}
|
27
|
-
self._exception_handlers: ExceptionHandlers = {
|
28
|
-
HTTPException: self.http_exception,
|
29
|
-
WebSocketException: self.websocket_exception,
|
30
|
-
}
|
31
|
-
if handlers is not None:
|
32
|
-
for key, value in handlers.items():
|
33
|
-
self.add_exception_handler(key, value)
|
34
|
-
|
35
|
-
def add_exception_handler(
|
36
|
-
self,
|
37
|
-
exc_class_or_status_code: typing.Union[int, typing.Type[Exception]],
|
38
|
-
handler: typing.Callable[[Request, Exception], Response],
|
39
|
-
) -> None:
|
40
|
-
if isinstance(exc_class_or_status_code, int):
|
41
|
-
self._status_handlers[exc_class_or_status_code] = handler
|
42
|
-
else:
|
43
|
-
assert issubclass(exc_class_or_status_code, Exception)
|
44
|
-
self._exception_handlers[exc_class_or_status_code] = handler
|
45
|
-
|
46
|
-
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
47
|
-
if scope["type"] not in ("http", "websocket"):
|
48
|
-
await self.app(scope, receive, send)
|
49
|
-
return
|
50
|
-
|
51
|
-
scope["starlette.exception_handlers"] = (
|
52
|
-
self._exception_handlers,
|
53
|
-
self._status_handlers,
|
54
|
-
)
|
55
|
-
|
56
|
-
conn: typing.Union[Request, WebSocket]
|
57
|
-
if scope["type"] == "http":
|
58
|
-
conn = Request(scope, receive, send)
|
59
|
-
else:
|
60
|
-
conn = WebSocket(scope, receive, send)
|
61
|
-
|
62
|
-
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
|
63
|
-
|
64
|
-
def http_exception(self, request: Request, exc: Exception) -> Response:
|
65
|
-
assert isinstance(exc, HTTPException)
|
66
|
-
if exc.status_code in {204, 304}:
|
67
|
-
return Response(status_code=exc.status_code, headers=exc.headers)
|
68
|
-
return PlainTextResponse(
|
69
|
-
exc.detail, status_code=exc.status_code, headers=exc.headers
|
70
|
-
)
|
71
|
-
|
72
|
-
async def websocket_exception(self, websocket: WebSocket, exc: Exception) -> None:
|
73
|
-
assert isinstance(exc, WebSocketException)
|
74
|
-
await websocket.close(code=exc.code, reason=exc.reason) # pragma: no cover
|
@@ -1,113 +0,0 @@
|
|
1
|
-
import gzip
|
2
|
-
import io
|
3
|
-
import typing
|
4
|
-
|
5
|
-
from prefect._vendor.starlette.datastructures import Headers, MutableHeaders
|
6
|
-
from prefect._vendor.starlette.types import ASGIApp, Message, Receive, Scope, Send
|
7
|
-
|
8
|
-
|
9
|
-
class GZipMiddleware:
|
10
|
-
def __init__(
|
11
|
-
self, app: ASGIApp, minimum_size: int = 500, compresslevel: int = 9
|
12
|
-
) -> None:
|
13
|
-
self.app = app
|
14
|
-
self.minimum_size = minimum_size
|
15
|
-
self.compresslevel = compresslevel
|
16
|
-
|
17
|
-
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
18
|
-
if scope["type"] == "http":
|
19
|
-
headers = Headers(scope=scope)
|
20
|
-
if "gzip" in headers.get("Accept-Encoding", ""):
|
21
|
-
responder = GZipResponder(
|
22
|
-
self.app, self.minimum_size, compresslevel=self.compresslevel
|
23
|
-
)
|
24
|
-
await responder(scope, receive, send)
|
25
|
-
return
|
26
|
-
await self.app(scope, receive, send)
|
27
|
-
|
28
|
-
|
29
|
-
class GZipResponder:
|
30
|
-
def __init__(self, app: ASGIApp, minimum_size: int, compresslevel: int = 9) -> None:
|
31
|
-
self.app = app
|
32
|
-
self.minimum_size = minimum_size
|
33
|
-
self.send: Send = unattached_send
|
34
|
-
self.initial_message: Message = {}
|
35
|
-
self.started = False
|
36
|
-
self.content_encoding_set = False
|
37
|
-
self.gzip_buffer = io.BytesIO()
|
38
|
-
self.gzip_file = gzip.GzipFile(
|
39
|
-
mode="wb", fileobj=self.gzip_buffer, compresslevel=compresslevel
|
40
|
-
)
|
41
|
-
|
42
|
-
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
43
|
-
self.send = send
|
44
|
-
await self.app(scope, receive, self.send_with_gzip)
|
45
|
-
|
46
|
-
async def send_with_gzip(self, message: Message) -> None:
|
47
|
-
message_type = message["type"]
|
48
|
-
if message_type == "http.response.start":
|
49
|
-
# Don't send the initial message until we've determined how to
|
50
|
-
# modify the outgoing headers correctly.
|
51
|
-
self.initial_message = message
|
52
|
-
headers = Headers(raw=self.initial_message["headers"])
|
53
|
-
self.content_encoding_set = "content-encoding" in headers
|
54
|
-
elif message_type == "http.response.body" and self.content_encoding_set:
|
55
|
-
if not self.started:
|
56
|
-
self.started = True
|
57
|
-
await self.send(self.initial_message)
|
58
|
-
await self.send(message)
|
59
|
-
elif message_type == "http.response.body" and not self.started:
|
60
|
-
self.started = True
|
61
|
-
body = message.get("body", b"")
|
62
|
-
more_body = message.get("more_body", False)
|
63
|
-
if len(body) < self.minimum_size and not more_body:
|
64
|
-
# Don't apply GZip to small outgoing responses.
|
65
|
-
await self.send(self.initial_message)
|
66
|
-
await self.send(message)
|
67
|
-
elif not more_body:
|
68
|
-
# Standard GZip response.
|
69
|
-
self.gzip_file.write(body)
|
70
|
-
self.gzip_file.close()
|
71
|
-
body = self.gzip_buffer.getvalue()
|
72
|
-
|
73
|
-
headers = MutableHeaders(raw=self.initial_message["headers"])
|
74
|
-
headers["Content-Encoding"] = "gzip"
|
75
|
-
headers["Content-Length"] = str(len(body))
|
76
|
-
headers.add_vary_header("Accept-Encoding")
|
77
|
-
message["body"] = body
|
78
|
-
|
79
|
-
await self.send(self.initial_message)
|
80
|
-
await self.send(message)
|
81
|
-
else:
|
82
|
-
# Initial body in streaming GZip response.
|
83
|
-
headers = MutableHeaders(raw=self.initial_message["headers"])
|
84
|
-
headers["Content-Encoding"] = "gzip"
|
85
|
-
headers.add_vary_header("Accept-Encoding")
|
86
|
-
del headers["Content-Length"]
|
87
|
-
|
88
|
-
self.gzip_file.write(body)
|
89
|
-
message["body"] = self.gzip_buffer.getvalue()
|
90
|
-
self.gzip_buffer.seek(0)
|
91
|
-
self.gzip_buffer.truncate()
|
92
|
-
|
93
|
-
await self.send(self.initial_message)
|
94
|
-
await self.send(message)
|
95
|
-
|
96
|
-
elif message_type == "http.response.body":
|
97
|
-
# Remaining body in streaming GZip response.
|
98
|
-
body = message.get("body", b"")
|
99
|
-
more_body = message.get("more_body", False)
|
100
|
-
|
101
|
-
self.gzip_file.write(body)
|
102
|
-
if not more_body:
|
103
|
-
self.gzip_file.close()
|
104
|
-
|
105
|
-
message["body"] = self.gzip_buffer.getvalue()
|
106
|
-
self.gzip_buffer.seek(0)
|
107
|
-
self.gzip_buffer.truncate()
|
108
|
-
|
109
|
-
await self.send(message)
|
110
|
-
|
111
|
-
|
112
|
-
async def unattached_send(message: Message) -> typing.NoReturn:
|
113
|
-
raise RuntimeError("send awaitable not set") # pragma: no cover
|
@@ -1,19 +0,0 @@
|
|
1
|
-
from prefect._vendor.starlette.datastructures import URL
|
2
|
-
from prefect._vendor.starlette.responses import RedirectResponse
|
3
|
-
from prefect._vendor.starlette.types import ASGIApp, Receive, Scope, Send
|
4
|
-
|
5
|
-
|
6
|
-
class HTTPSRedirectMiddleware:
|
7
|
-
def __init__(self, app: ASGIApp) -> None:
|
8
|
-
self.app = app
|
9
|
-
|
10
|
-
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
11
|
-
if scope["type"] in ("http", "websocket") and scope["scheme"] in ("http", "ws"):
|
12
|
-
url = URL(scope=scope)
|
13
|
-
redirect_scheme = {"http": "https", "ws": "wss"}[url.scheme]
|
14
|
-
netloc = url.hostname if url.port in (80, 443) else url.netloc
|
15
|
-
url = url.replace(scheme=redirect_scheme, netloc=netloc)
|
16
|
-
response = RedirectResponse(url, status_code=307)
|
17
|
-
await response(scope, receive, send)
|
18
|
-
else:
|
19
|
-
await self.app(scope, receive, send)
|
@@ -1,82 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
import typing
|
3
|
-
from base64 import b64decode, b64encode
|
4
|
-
|
5
|
-
import itsdangerous
|
6
|
-
from itsdangerous.exc import BadSignature
|
7
|
-
from prefect._vendor.starlette.datastructures import MutableHeaders, Secret
|
8
|
-
from prefect._vendor.starlette.requests import HTTPConnection
|
9
|
-
from prefect._vendor.starlette.types import ASGIApp, Message, Receive, Scope, Send
|
10
|
-
|
11
|
-
|
12
|
-
class SessionMiddleware:
|
13
|
-
def __init__(
|
14
|
-
self,
|
15
|
-
app: ASGIApp,
|
16
|
-
secret_key: typing.Union[str, Secret],
|
17
|
-
session_cookie: str = "session",
|
18
|
-
max_age: typing.Optional[int] = 14 * 24 * 60 * 60, # 14 days, in seconds
|
19
|
-
path: str = "/",
|
20
|
-
same_site: typing.Literal["lax", "strict", "none"] = "lax",
|
21
|
-
https_only: bool = False,
|
22
|
-
domain: typing.Optional[str] = None,
|
23
|
-
) -> None:
|
24
|
-
self.app = app
|
25
|
-
self.signer = itsdangerous.TimestampSigner(str(secret_key))
|
26
|
-
self.session_cookie = session_cookie
|
27
|
-
self.max_age = max_age
|
28
|
-
self.path = path
|
29
|
-
self.security_flags = "httponly; samesite=" + same_site
|
30
|
-
if https_only: # Secure flag can be used with HTTPS only
|
31
|
-
self.security_flags += "; secure"
|
32
|
-
if domain is not None:
|
33
|
-
self.security_flags += f"; domain={domain}"
|
34
|
-
|
35
|
-
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
36
|
-
if scope["type"] not in ("http", "websocket"): # pragma: no cover
|
37
|
-
await self.app(scope, receive, send)
|
38
|
-
return
|
39
|
-
|
40
|
-
connection = HTTPConnection(scope)
|
41
|
-
initial_session_was_empty = True
|
42
|
-
|
43
|
-
if self.session_cookie in connection.cookies:
|
44
|
-
data = connection.cookies[self.session_cookie].encode("utf-8")
|
45
|
-
try:
|
46
|
-
data = self.signer.unsign(data, max_age=self.max_age)
|
47
|
-
scope["session"] = json.loads(b64decode(data))
|
48
|
-
initial_session_was_empty = False
|
49
|
-
except BadSignature:
|
50
|
-
scope["session"] = {}
|
51
|
-
else:
|
52
|
-
scope["session"] = {}
|
53
|
-
|
54
|
-
async def send_wrapper(message: Message) -> None:
|
55
|
-
if message["type"] == "http.response.start":
|
56
|
-
if scope["session"]:
|
57
|
-
# We have session data to persist.
|
58
|
-
data = b64encode(json.dumps(scope["session"]).encode("utf-8"))
|
59
|
-
data = self.signer.sign(data)
|
60
|
-
headers = MutableHeaders(scope=message)
|
61
|
-
header_value = "{session_cookie}={data}; path={path}; {max_age}{security_flags}".format( # noqa E501
|
62
|
-
session_cookie=self.session_cookie,
|
63
|
-
data=data.decode("utf-8"),
|
64
|
-
path=self.path,
|
65
|
-
max_age=f"Max-Age={self.max_age}; " if self.max_age else "",
|
66
|
-
security_flags=self.security_flags,
|
67
|
-
)
|
68
|
-
headers.append("Set-Cookie", header_value)
|
69
|
-
elif not initial_session_was_empty:
|
70
|
-
# The session has been cleared.
|
71
|
-
headers = MutableHeaders(scope=message)
|
72
|
-
header_value = "{session_cookie}={data}; path={path}; {expires}{security_flags}".format( # noqa E501
|
73
|
-
session_cookie=self.session_cookie,
|
74
|
-
data="null",
|
75
|
-
path=self.path,
|
76
|
-
expires="expires=Thu, 01 Jan 1970 00:00:00 GMT; ",
|
77
|
-
security_flags=self.security_flags,
|
78
|
-
)
|
79
|
-
headers.append("Set-Cookie", header_value)
|
80
|
-
await send(message)
|
81
|
-
|
82
|
-
await self.app(scope, receive, send_wrapper)
|
@@ -1,64 +0,0 @@
|
|
1
|
-
import typing
|
2
|
-
|
3
|
-
from prefect._vendor.starlette.datastructures import URL, Headers
|
4
|
-
from prefect._vendor.starlette.responses import (
|
5
|
-
PlainTextResponse,
|
6
|
-
RedirectResponse,
|
7
|
-
Response,
|
8
|
-
)
|
9
|
-
from prefect._vendor.starlette.types import ASGIApp, Receive, Scope, Send
|
10
|
-
|
11
|
-
ENFORCE_DOMAIN_WILDCARD = "Domain wildcard patterns must be like '*.example.com'."
|
12
|
-
|
13
|
-
|
14
|
-
class TrustedHostMiddleware:
|
15
|
-
def __init__(
|
16
|
-
self,
|
17
|
-
app: ASGIApp,
|
18
|
-
allowed_hosts: typing.Optional[typing.Sequence[str]] = None,
|
19
|
-
www_redirect: bool = True,
|
20
|
-
) -> None:
|
21
|
-
if allowed_hosts is None:
|
22
|
-
allowed_hosts = ["*"]
|
23
|
-
|
24
|
-
for pattern in allowed_hosts:
|
25
|
-
assert "*" not in pattern[1:], ENFORCE_DOMAIN_WILDCARD
|
26
|
-
if pattern.startswith("*") and pattern != "*":
|
27
|
-
assert pattern.startswith("*."), ENFORCE_DOMAIN_WILDCARD
|
28
|
-
self.app = app
|
29
|
-
self.allowed_hosts = list(allowed_hosts)
|
30
|
-
self.allow_any = "*" in allowed_hosts
|
31
|
-
self.www_redirect = www_redirect
|
32
|
-
|
33
|
-
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
34
|
-
if self.allow_any or scope["type"] not in (
|
35
|
-
"http",
|
36
|
-
"websocket",
|
37
|
-
): # pragma: no cover
|
38
|
-
await self.app(scope, receive, send)
|
39
|
-
return
|
40
|
-
|
41
|
-
headers = Headers(scope=scope)
|
42
|
-
host = headers.get("host", "").split(":")[0]
|
43
|
-
is_valid_host = False
|
44
|
-
found_www_redirect = False
|
45
|
-
for pattern in self.allowed_hosts:
|
46
|
-
if host == pattern or (
|
47
|
-
pattern.startswith("*") and host.endswith(pattern[1:])
|
48
|
-
):
|
49
|
-
is_valid_host = True
|
50
|
-
break
|
51
|
-
elif "www." + host == pattern:
|
52
|
-
found_www_redirect = True
|
53
|
-
|
54
|
-
if is_valid_host:
|
55
|
-
await self.app(scope, receive, send)
|
56
|
-
else:
|
57
|
-
response: Response
|
58
|
-
if found_www_redirect and self.www_redirect:
|
59
|
-
url = URL(scope=scope)
|
60
|
-
redirect_url = url.replace(netloc="www." + url.netloc)
|
61
|
-
response = RedirectResponse(url=str(redirect_url))
|
62
|
-
else:
|
63
|
-
response = PlainTextResponse("Invalid host header", status_code=400)
|
64
|
-
await response(scope, receive, send)
|
@@ -1,147 +0,0 @@
|
|
1
|
-
import io
|
2
|
-
import math
|
3
|
-
import sys
|
4
|
-
import typing
|
5
|
-
import warnings
|
6
|
-
|
7
|
-
import anyio
|
8
|
-
from anyio.abc import ObjectReceiveStream, ObjectSendStream
|
9
|
-
from prefect._vendor.starlette.types import Receive, Scope, Send
|
10
|
-
|
11
|
-
warnings.warn(
|
12
|
-
"starlette.middleware.wsgi is deprecated and will be removed in a future release. "
|
13
|
-
"Please refer to https://github.com/abersheeran/a2wsgi as a replacement.",
|
14
|
-
DeprecationWarning,
|
15
|
-
)
|
16
|
-
|
17
|
-
|
18
|
-
def build_environ(scope: Scope, body: bytes) -> typing.Dict[str, typing.Any]:
|
19
|
-
"""
|
20
|
-
Builds a scope and request body into a WSGI environ object.
|
21
|
-
"""
|
22
|
-
environ = {
|
23
|
-
"REQUEST_METHOD": scope["method"],
|
24
|
-
"SCRIPT_NAME": scope.get("root_path", "").encode("utf8").decode("latin1"),
|
25
|
-
"PATH_INFO": scope["path"].encode("utf8").decode("latin1"),
|
26
|
-
"QUERY_STRING": scope["query_string"].decode("ascii"),
|
27
|
-
"SERVER_PROTOCOL": f"HTTP/{scope['http_version']}",
|
28
|
-
"wsgi.version": (1, 0),
|
29
|
-
"wsgi.url_scheme": scope.get("scheme", "http"),
|
30
|
-
"wsgi.input": io.BytesIO(body),
|
31
|
-
"wsgi.errors": sys.stdout,
|
32
|
-
"wsgi.multithread": True,
|
33
|
-
"wsgi.multiprocess": True,
|
34
|
-
"wsgi.run_once": False,
|
35
|
-
}
|
36
|
-
|
37
|
-
# Get server name and port - required in WSGI, not in ASGI
|
38
|
-
server = scope.get("server") or ("localhost", 80)
|
39
|
-
environ["SERVER_NAME"] = server[0]
|
40
|
-
environ["SERVER_PORT"] = server[1]
|
41
|
-
|
42
|
-
# Get client IP address
|
43
|
-
if scope.get("client"):
|
44
|
-
environ["REMOTE_ADDR"] = scope["client"][0]
|
45
|
-
|
46
|
-
# Go through headers and make them into environ entries
|
47
|
-
for name, value in scope.get("headers", []):
|
48
|
-
name = name.decode("latin1")
|
49
|
-
if name == "content-length":
|
50
|
-
corrected_name = "CONTENT_LENGTH"
|
51
|
-
elif name == "content-type":
|
52
|
-
corrected_name = "CONTENT_TYPE"
|
53
|
-
else:
|
54
|
-
corrected_name = f"HTTP_{name}".upper().replace("-", "_")
|
55
|
-
# HTTPbis say only ASCII chars are allowed in headers, but we latin1 just in
|
56
|
-
# case
|
57
|
-
value = value.decode("latin1")
|
58
|
-
if corrected_name in environ:
|
59
|
-
value = environ[corrected_name] + "," + value
|
60
|
-
environ[corrected_name] = value
|
61
|
-
return environ
|
62
|
-
|
63
|
-
|
64
|
-
class WSGIMiddleware:
|
65
|
-
def __init__(self, app: typing.Callable[..., typing.Any]) -> None:
|
66
|
-
self.app = app
|
67
|
-
|
68
|
-
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
69
|
-
assert scope["type"] == "http"
|
70
|
-
responder = WSGIResponder(self.app, scope)
|
71
|
-
await responder(receive, send)
|
72
|
-
|
73
|
-
|
74
|
-
class WSGIResponder:
|
75
|
-
stream_send: ObjectSendStream[typing.MutableMapping[str, typing.Any]]
|
76
|
-
stream_receive: ObjectReceiveStream[typing.MutableMapping[str, typing.Any]]
|
77
|
-
|
78
|
-
def __init__(self, app: typing.Callable[..., typing.Any], scope: Scope) -> None:
|
79
|
-
self.app = app
|
80
|
-
self.scope = scope
|
81
|
-
self.status = None
|
82
|
-
self.response_headers = None
|
83
|
-
self.stream_send, self.stream_receive = anyio.create_memory_object_stream(
|
84
|
-
math.inf
|
85
|
-
)
|
86
|
-
self.response_started = False
|
87
|
-
self.exc_info: typing.Any = None
|
88
|
-
|
89
|
-
async def __call__(self, receive: Receive, send: Send) -> None:
|
90
|
-
body = b""
|
91
|
-
more_body = True
|
92
|
-
while more_body:
|
93
|
-
message = await receive()
|
94
|
-
body += message.get("body", b"")
|
95
|
-
more_body = message.get("more_body", False)
|
96
|
-
environ = build_environ(self.scope, body)
|
97
|
-
|
98
|
-
async with anyio.create_task_group() as task_group:
|
99
|
-
task_group.start_soon(self.sender, send)
|
100
|
-
async with self.stream_send:
|
101
|
-
await anyio.to_thread.run_sync(self.wsgi, environ, self.start_response)
|
102
|
-
if self.exc_info is not None:
|
103
|
-
raise self.exc_info[0].with_traceback(self.exc_info[1], self.exc_info[2])
|
104
|
-
|
105
|
-
async def sender(self, send: Send) -> None:
|
106
|
-
async with self.stream_receive:
|
107
|
-
async for message in self.stream_receive:
|
108
|
-
await send(message)
|
109
|
-
|
110
|
-
def start_response(
|
111
|
-
self,
|
112
|
-
status: str,
|
113
|
-
response_headers: typing.List[typing.Tuple[str, str]],
|
114
|
-
exc_info: typing.Any = None,
|
115
|
-
) -> None:
|
116
|
-
self.exc_info = exc_info
|
117
|
-
if not self.response_started:
|
118
|
-
self.response_started = True
|
119
|
-
status_code_string, _ = status.split(" ", 1)
|
120
|
-
status_code = int(status_code_string)
|
121
|
-
headers = [
|
122
|
-
(name.strip().encode("ascii").lower(), value.strip().encode("ascii"))
|
123
|
-
for name, value in response_headers
|
124
|
-
]
|
125
|
-
anyio.from_thread.run(
|
126
|
-
self.stream_send.send,
|
127
|
-
{
|
128
|
-
"type": "http.response.start",
|
129
|
-
"status": status_code,
|
130
|
-
"headers": headers,
|
131
|
-
},
|
132
|
-
)
|
133
|
-
|
134
|
-
def wsgi(
|
135
|
-
self,
|
136
|
-
environ: typing.Dict[str, typing.Any],
|
137
|
-
start_response: typing.Callable[..., typing.Any],
|
138
|
-
) -> None:
|
139
|
-
for chunk in self.app(environ, start_response):
|
140
|
-
anyio.from_thread.run(
|
141
|
-
self.stream_send.send,
|
142
|
-
{"type": "http.response.body", "body": chunk, "more_body": True},
|
143
|
-
)
|
144
|
-
|
145
|
-
anyio.from_thread.run(
|
146
|
-
self.stream_send.send, {"type": "http.response.body", "body": b""}
|
147
|
-
)
|
File without changes
|