prefect-client 2.20.2__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.
Files changed (288) hide show
  1. prefect/__init__.py +74 -110
  2. prefect/_internal/compatibility/deprecated.py +6 -115
  3. prefect/_internal/compatibility/experimental.py +4 -79
  4. prefect/_internal/compatibility/migration.py +166 -0
  5. prefect/_internal/concurrency/__init__.py +2 -2
  6. prefect/_internal/concurrency/api.py +1 -35
  7. prefect/_internal/concurrency/calls.py +0 -6
  8. prefect/_internal/concurrency/cancellation.py +0 -3
  9. prefect/_internal/concurrency/event_loop.py +0 -20
  10. prefect/_internal/concurrency/inspection.py +3 -3
  11. prefect/_internal/concurrency/primitives.py +1 -0
  12. prefect/_internal/concurrency/services.py +23 -0
  13. prefect/_internal/concurrency/threads.py +35 -0
  14. prefect/_internal/concurrency/waiters.py +0 -28
  15. prefect/_internal/integrations.py +7 -0
  16. prefect/_internal/pydantic/__init__.py +0 -45
  17. prefect/_internal/pydantic/annotations/pendulum.py +2 -2
  18. prefect/_internal/pydantic/v1_schema.py +21 -22
  19. prefect/_internal/pydantic/v2_schema.py +0 -2
  20. prefect/_internal/pydantic/v2_validated_func.py +18 -23
  21. prefect/_internal/pytz.py +1 -1
  22. prefect/_internal/retries.py +61 -0
  23. prefect/_internal/schemas/bases.py +45 -177
  24. prefect/_internal/schemas/fields.py +1 -43
  25. prefect/_internal/schemas/validators.py +47 -233
  26. prefect/agent.py +3 -695
  27. prefect/artifacts.py +173 -14
  28. prefect/automations.py +39 -4
  29. prefect/blocks/abstract.py +1 -1
  30. prefect/blocks/core.py +423 -164
  31. prefect/blocks/fields.py +2 -57
  32. prefect/blocks/notifications.py +43 -28
  33. prefect/blocks/redis.py +168 -0
  34. prefect/blocks/system.py +67 -20
  35. prefect/blocks/webhook.py +2 -9
  36. prefect/cache_policies.py +239 -0
  37. prefect/client/__init__.py +4 -0
  38. prefect/client/base.py +33 -27
  39. prefect/client/cloud.py +65 -20
  40. prefect/client/collections.py +1 -1
  41. prefect/client/orchestration.py +667 -440
  42. prefect/client/schemas/actions.py +115 -100
  43. prefect/client/schemas/filters.py +46 -52
  44. prefect/client/schemas/objects.py +228 -178
  45. prefect/client/schemas/responses.py +18 -36
  46. prefect/client/schemas/schedules.py +55 -36
  47. prefect/client/schemas/sorting.py +2 -0
  48. prefect/client/subscriptions.py +8 -7
  49. prefect/client/types/flexible_schedule_list.py +11 -0
  50. prefect/client/utilities.py +9 -6
  51. prefect/concurrency/asyncio.py +60 -11
  52. prefect/concurrency/context.py +24 -0
  53. prefect/concurrency/events.py +2 -2
  54. prefect/concurrency/services.py +46 -16
  55. prefect/concurrency/sync.py +51 -7
  56. prefect/concurrency/v1/asyncio.py +143 -0
  57. prefect/concurrency/v1/context.py +27 -0
  58. prefect/concurrency/v1/events.py +61 -0
  59. prefect/concurrency/v1/services.py +116 -0
  60. prefect/concurrency/v1/sync.py +92 -0
  61. prefect/context.py +246 -149
  62. prefect/deployments/__init__.py +33 -18
  63. prefect/deployments/base.py +10 -15
  64. prefect/deployments/deployments.py +2 -1048
  65. prefect/deployments/flow_runs.py +178 -0
  66. prefect/deployments/runner.py +72 -173
  67. prefect/deployments/schedules.py +31 -25
  68. prefect/deployments/steps/__init__.py +0 -1
  69. prefect/deployments/steps/core.py +7 -0
  70. prefect/deployments/steps/pull.py +15 -21
  71. prefect/deployments/steps/utility.py +2 -1
  72. prefect/docker/__init__.py +20 -0
  73. prefect/docker/docker_image.py +82 -0
  74. prefect/engine.py +15 -2466
  75. prefect/events/actions.py +17 -23
  76. prefect/events/cli/automations.py +20 -7
  77. prefect/events/clients.py +142 -80
  78. prefect/events/filters.py +14 -18
  79. prefect/events/related.py +74 -75
  80. prefect/events/schemas/__init__.py +0 -5
  81. prefect/events/schemas/automations.py +55 -46
  82. prefect/events/schemas/deployment_triggers.py +7 -197
  83. prefect/events/schemas/events.py +46 -65
  84. prefect/events/schemas/labelling.py +10 -14
  85. prefect/events/utilities.py +4 -5
  86. prefect/events/worker.py +23 -8
  87. prefect/exceptions.py +15 -0
  88. prefect/filesystems.py +30 -529
  89. prefect/flow_engine.py +827 -0
  90. prefect/flow_runs.py +379 -7
  91. prefect/flows.py +470 -360
  92. prefect/futures.py +382 -331
  93. prefect/infrastructure/__init__.py +5 -26
  94. prefect/infrastructure/base.py +3 -320
  95. prefect/infrastructure/provisioners/__init__.py +5 -3
  96. prefect/infrastructure/provisioners/cloud_run.py +13 -8
  97. prefect/infrastructure/provisioners/container_instance.py +14 -9
  98. prefect/infrastructure/provisioners/ecs.py +10 -8
  99. prefect/infrastructure/provisioners/modal.py +8 -5
  100. prefect/input/__init__.py +4 -0
  101. prefect/input/actions.py +2 -4
  102. prefect/input/run_input.py +9 -9
  103. prefect/logging/formatters.py +2 -4
  104. prefect/logging/handlers.py +9 -14
  105. prefect/logging/loggers.py +5 -5
  106. prefect/main.py +72 -0
  107. prefect/plugins.py +2 -64
  108. prefect/profiles.toml +16 -2
  109. prefect/records/__init__.py +1 -0
  110. prefect/records/base.py +223 -0
  111. prefect/records/filesystem.py +207 -0
  112. prefect/records/memory.py +178 -0
  113. prefect/records/result_store.py +64 -0
  114. prefect/results.py +577 -504
  115. prefect/runner/runner.py +124 -51
  116. prefect/runner/server.py +32 -34
  117. prefect/runner/storage.py +3 -12
  118. prefect/runner/submit.py +2 -10
  119. prefect/runner/utils.py +2 -2
  120. prefect/runtime/__init__.py +1 -0
  121. prefect/runtime/deployment.py +1 -0
  122. prefect/runtime/flow_run.py +40 -5
  123. prefect/runtime/task_run.py +1 -0
  124. prefect/serializers.py +28 -39
  125. prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
  126. prefect/settings.py +209 -332
  127. prefect/states.py +160 -63
  128. prefect/task_engine.py +1478 -57
  129. prefect/task_runners.py +383 -287
  130. prefect/task_runs.py +240 -0
  131. prefect/task_worker.py +463 -0
  132. prefect/tasks.py +684 -374
  133. prefect/transactions.py +410 -0
  134. prefect/types/__init__.py +72 -86
  135. prefect/types/entrypoint.py +13 -0
  136. prefect/utilities/annotations.py +4 -3
  137. prefect/utilities/asyncutils.py +227 -148
  138. prefect/utilities/callables.py +138 -48
  139. prefect/utilities/collections.py +134 -86
  140. prefect/utilities/dispatch.py +27 -14
  141. prefect/utilities/dockerutils.py +11 -4
  142. prefect/utilities/engine.py +186 -32
  143. prefect/utilities/filesystem.py +4 -5
  144. prefect/utilities/importtools.py +26 -27
  145. prefect/utilities/pydantic.py +128 -38
  146. prefect/utilities/schema_tools/hydration.py +18 -1
  147. prefect/utilities/schema_tools/validation.py +30 -0
  148. prefect/utilities/services.py +35 -9
  149. prefect/utilities/templating.py +12 -2
  150. prefect/utilities/timeout.py +20 -5
  151. prefect/utilities/urls.py +195 -0
  152. prefect/utilities/visualization.py +1 -0
  153. prefect/variables.py +78 -59
  154. prefect/workers/__init__.py +0 -1
  155. prefect/workers/base.py +237 -244
  156. prefect/workers/block.py +5 -226
  157. prefect/workers/cloud.py +6 -0
  158. prefect/workers/process.py +265 -12
  159. prefect/workers/server.py +29 -11
  160. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/METADATA +30 -26
  161. prefect_client-3.0.0.dist-info/RECORD +201 -0
  162. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/WHEEL +1 -1
  163. prefect/_internal/pydantic/_base_model.py +0 -51
  164. prefect/_internal/pydantic/_compat.py +0 -82
  165. prefect/_internal/pydantic/_flags.py +0 -20
  166. prefect/_internal/pydantic/_types.py +0 -8
  167. prefect/_internal/pydantic/utilities/config_dict.py +0 -72
  168. prefect/_internal/pydantic/utilities/field_validator.py +0 -150
  169. prefect/_internal/pydantic/utilities/model_construct.py +0 -56
  170. prefect/_internal/pydantic/utilities/model_copy.py +0 -55
  171. prefect/_internal/pydantic/utilities/model_dump.py +0 -136
  172. prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
  173. prefect/_internal/pydantic/utilities/model_fields.py +0 -50
  174. prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
  175. prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
  176. prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
  177. prefect/_internal/pydantic/utilities/model_validate.py +0 -75
  178. prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
  179. prefect/_internal/pydantic/utilities/model_validator.py +0 -87
  180. prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
  181. prefect/_vendor/fastapi/__init__.py +0 -25
  182. prefect/_vendor/fastapi/applications.py +0 -946
  183. prefect/_vendor/fastapi/background.py +0 -3
  184. prefect/_vendor/fastapi/concurrency.py +0 -44
  185. prefect/_vendor/fastapi/datastructures.py +0 -58
  186. prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
  187. prefect/_vendor/fastapi/dependencies/models.py +0 -64
  188. prefect/_vendor/fastapi/dependencies/utils.py +0 -877
  189. prefect/_vendor/fastapi/encoders.py +0 -177
  190. prefect/_vendor/fastapi/exception_handlers.py +0 -40
  191. prefect/_vendor/fastapi/exceptions.py +0 -46
  192. prefect/_vendor/fastapi/logger.py +0 -3
  193. prefect/_vendor/fastapi/middleware/__init__.py +0 -1
  194. prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
  195. prefect/_vendor/fastapi/middleware/cors.py +0 -3
  196. prefect/_vendor/fastapi/middleware/gzip.py +0 -3
  197. prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
  198. prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
  199. prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
  200. prefect/_vendor/fastapi/openapi/__init__.py +0 -0
  201. prefect/_vendor/fastapi/openapi/constants.py +0 -2
  202. prefect/_vendor/fastapi/openapi/docs.py +0 -203
  203. prefect/_vendor/fastapi/openapi/models.py +0 -480
  204. prefect/_vendor/fastapi/openapi/utils.py +0 -485
  205. prefect/_vendor/fastapi/param_functions.py +0 -340
  206. prefect/_vendor/fastapi/params.py +0 -453
  207. prefect/_vendor/fastapi/py.typed +0 -0
  208. prefect/_vendor/fastapi/requests.py +0 -4
  209. prefect/_vendor/fastapi/responses.py +0 -40
  210. prefect/_vendor/fastapi/routing.py +0 -1331
  211. prefect/_vendor/fastapi/security/__init__.py +0 -15
  212. prefect/_vendor/fastapi/security/api_key.py +0 -98
  213. prefect/_vendor/fastapi/security/base.py +0 -6
  214. prefect/_vendor/fastapi/security/http.py +0 -172
  215. prefect/_vendor/fastapi/security/oauth2.py +0 -227
  216. prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
  217. prefect/_vendor/fastapi/security/utils.py +0 -10
  218. prefect/_vendor/fastapi/staticfiles.py +0 -1
  219. prefect/_vendor/fastapi/templating.py +0 -3
  220. prefect/_vendor/fastapi/testclient.py +0 -1
  221. prefect/_vendor/fastapi/types.py +0 -3
  222. prefect/_vendor/fastapi/utils.py +0 -235
  223. prefect/_vendor/fastapi/websockets.py +0 -7
  224. prefect/_vendor/starlette/__init__.py +0 -1
  225. prefect/_vendor/starlette/_compat.py +0 -28
  226. prefect/_vendor/starlette/_exception_handler.py +0 -80
  227. prefect/_vendor/starlette/_utils.py +0 -88
  228. prefect/_vendor/starlette/applications.py +0 -261
  229. prefect/_vendor/starlette/authentication.py +0 -159
  230. prefect/_vendor/starlette/background.py +0 -43
  231. prefect/_vendor/starlette/concurrency.py +0 -59
  232. prefect/_vendor/starlette/config.py +0 -151
  233. prefect/_vendor/starlette/convertors.py +0 -87
  234. prefect/_vendor/starlette/datastructures.py +0 -707
  235. prefect/_vendor/starlette/endpoints.py +0 -130
  236. prefect/_vendor/starlette/exceptions.py +0 -60
  237. prefect/_vendor/starlette/formparsers.py +0 -276
  238. prefect/_vendor/starlette/middleware/__init__.py +0 -17
  239. prefect/_vendor/starlette/middleware/authentication.py +0 -52
  240. prefect/_vendor/starlette/middleware/base.py +0 -220
  241. prefect/_vendor/starlette/middleware/cors.py +0 -176
  242. prefect/_vendor/starlette/middleware/errors.py +0 -265
  243. prefect/_vendor/starlette/middleware/exceptions.py +0 -74
  244. prefect/_vendor/starlette/middleware/gzip.py +0 -113
  245. prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
  246. prefect/_vendor/starlette/middleware/sessions.py +0 -82
  247. prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
  248. prefect/_vendor/starlette/middleware/wsgi.py +0 -147
  249. prefect/_vendor/starlette/py.typed +0 -0
  250. prefect/_vendor/starlette/requests.py +0 -328
  251. prefect/_vendor/starlette/responses.py +0 -347
  252. prefect/_vendor/starlette/routing.py +0 -933
  253. prefect/_vendor/starlette/schemas.py +0 -154
  254. prefect/_vendor/starlette/staticfiles.py +0 -248
  255. prefect/_vendor/starlette/status.py +0 -199
  256. prefect/_vendor/starlette/templating.py +0 -231
  257. prefect/_vendor/starlette/testclient.py +0 -804
  258. prefect/_vendor/starlette/types.py +0 -30
  259. prefect/_vendor/starlette/websockets.py +0 -193
  260. prefect/blocks/kubernetes.py +0 -119
  261. prefect/deprecated/__init__.py +0 -0
  262. prefect/deprecated/data_documents.py +0 -350
  263. prefect/deprecated/packaging/__init__.py +0 -12
  264. prefect/deprecated/packaging/base.py +0 -96
  265. prefect/deprecated/packaging/docker.py +0 -146
  266. prefect/deprecated/packaging/file.py +0 -92
  267. prefect/deprecated/packaging/orion.py +0 -80
  268. prefect/deprecated/packaging/serializers.py +0 -171
  269. prefect/events/instrument.py +0 -135
  270. prefect/infrastructure/container.py +0 -824
  271. prefect/infrastructure/kubernetes.py +0 -920
  272. prefect/infrastructure/process.py +0 -289
  273. prefect/manifests.py +0 -20
  274. prefect/new_flow_engine.py +0 -449
  275. prefect/new_task_engine.py +0 -423
  276. prefect/pydantic/__init__.py +0 -76
  277. prefect/pydantic/main.py +0 -39
  278. prefect/software/__init__.py +0 -2
  279. prefect/software/base.py +0 -50
  280. prefect/software/conda.py +0 -199
  281. prefect/software/pip.py +0 -122
  282. prefect/software/python.py +0 -52
  283. prefect/task_server.py +0 -322
  284. prefect_client-2.20.2.dist-info/RECORD +0 -294
  285. /prefect/{_internal/pydantic/utilities → client/types}/__init__.py +0 -0
  286. /prefect/{_vendor → concurrency/v1}/__init__.py +0 -0
  287. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
  288. {prefect_client-2.20.2.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