prefect-client 2.19.3__py3-none-any.whl → 3.0.0rc1__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 (239) hide show
  1. prefect/__init__.py +8 -56
  2. prefect/_internal/compatibility/deprecated.py +6 -115
  3. prefect/_internal/compatibility/experimental.py +4 -79
  4. prefect/_internal/concurrency/api.py +0 -34
  5. prefect/_internal/concurrency/calls.py +0 -6
  6. prefect/_internal/concurrency/cancellation.py +0 -3
  7. prefect/_internal/concurrency/event_loop.py +0 -20
  8. prefect/_internal/concurrency/inspection.py +3 -3
  9. prefect/_internal/concurrency/threads.py +35 -0
  10. prefect/_internal/concurrency/waiters.py +0 -28
  11. prefect/_internal/pydantic/__init__.py +0 -45
  12. prefect/_internal/pydantic/v1_schema.py +21 -22
  13. prefect/_internal/pydantic/v2_schema.py +0 -2
  14. prefect/_internal/pydantic/v2_validated_func.py +18 -23
  15. prefect/_internal/schemas/bases.py +44 -177
  16. prefect/_internal/schemas/fields.py +1 -43
  17. prefect/_internal/schemas/validators.py +60 -158
  18. prefect/artifacts.py +161 -14
  19. prefect/automations.py +39 -4
  20. prefect/blocks/abstract.py +1 -1
  21. prefect/blocks/core.py +268 -148
  22. prefect/blocks/fields.py +2 -57
  23. prefect/blocks/kubernetes.py +8 -12
  24. prefect/blocks/notifications.py +40 -20
  25. prefect/blocks/system.py +22 -11
  26. prefect/blocks/webhook.py +2 -9
  27. prefect/client/base.py +4 -4
  28. prefect/client/cloud.py +8 -13
  29. prefect/client/orchestration.py +347 -341
  30. prefect/client/schemas/actions.py +92 -86
  31. prefect/client/schemas/filters.py +20 -40
  32. prefect/client/schemas/objects.py +147 -145
  33. prefect/client/schemas/responses.py +16 -24
  34. prefect/client/schemas/schedules.py +47 -35
  35. prefect/client/subscriptions.py +2 -2
  36. prefect/client/utilities.py +5 -2
  37. prefect/concurrency/asyncio.py +3 -1
  38. prefect/concurrency/events.py +1 -1
  39. prefect/concurrency/services.py +6 -3
  40. prefect/context.py +195 -27
  41. prefect/deployments/__init__.py +5 -6
  42. prefect/deployments/base.py +7 -5
  43. prefect/deployments/flow_runs.py +185 -0
  44. prefect/deployments/runner.py +50 -45
  45. prefect/deployments/schedules.py +28 -23
  46. prefect/deployments/steps/__init__.py +0 -1
  47. prefect/deployments/steps/core.py +1 -0
  48. prefect/deployments/steps/pull.py +7 -21
  49. prefect/engine.py +12 -2422
  50. prefect/events/actions.py +17 -23
  51. prefect/events/cli/automations.py +19 -6
  52. prefect/events/clients.py +14 -37
  53. prefect/events/filters.py +14 -18
  54. prefect/events/related.py +2 -2
  55. prefect/events/schemas/__init__.py +0 -5
  56. prefect/events/schemas/automations.py +55 -46
  57. prefect/events/schemas/deployment_triggers.py +7 -197
  58. prefect/events/schemas/events.py +34 -65
  59. prefect/events/schemas/labelling.py +10 -14
  60. prefect/events/utilities.py +2 -3
  61. prefect/events/worker.py +2 -3
  62. prefect/filesystems.py +6 -517
  63. prefect/{new_flow_engine.py → flow_engine.py} +313 -72
  64. prefect/flow_runs.py +377 -5
  65. prefect/flows.py +248 -165
  66. prefect/futures.py +186 -345
  67. prefect/infrastructure/__init__.py +0 -27
  68. prefect/infrastructure/provisioners/__init__.py +5 -3
  69. prefect/infrastructure/provisioners/cloud_run.py +11 -6
  70. prefect/infrastructure/provisioners/container_instance.py +11 -7
  71. prefect/infrastructure/provisioners/ecs.py +6 -4
  72. prefect/infrastructure/provisioners/modal.py +8 -5
  73. prefect/input/actions.py +2 -4
  74. prefect/input/run_input.py +5 -7
  75. prefect/logging/formatters.py +0 -2
  76. prefect/logging/handlers.py +3 -11
  77. prefect/logging/loggers.py +2 -2
  78. prefect/manifests.py +2 -1
  79. prefect/records/__init__.py +1 -0
  80. prefect/records/result_store.py +42 -0
  81. prefect/records/store.py +9 -0
  82. prefect/results.py +43 -39
  83. prefect/runner/runner.py +9 -9
  84. prefect/runner/server.py +6 -10
  85. prefect/runner/storage.py +3 -8
  86. prefect/runner/submit.py +2 -2
  87. prefect/runner/utils.py +2 -2
  88. prefect/serializers.py +24 -35
  89. prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
  90. prefect/settings.py +70 -133
  91. prefect/states.py +17 -47
  92. prefect/task_engine.py +697 -58
  93. prefect/task_runners.py +269 -301
  94. prefect/task_server.py +53 -34
  95. prefect/tasks.py +327 -337
  96. prefect/transactions.py +220 -0
  97. prefect/types/__init__.py +61 -82
  98. prefect/utilities/asyncutils.py +195 -136
  99. prefect/utilities/callables.py +121 -41
  100. prefect/utilities/collections.py +23 -38
  101. prefect/utilities/dispatch.py +11 -3
  102. prefect/utilities/dockerutils.py +4 -0
  103. prefect/utilities/engine.py +140 -20
  104. prefect/utilities/importtools.py +26 -27
  105. prefect/utilities/pydantic.py +128 -38
  106. prefect/utilities/schema_tools/hydration.py +5 -1
  107. prefect/utilities/templating.py +12 -2
  108. prefect/variables.py +78 -61
  109. prefect/workers/__init__.py +0 -1
  110. prefect/workers/base.py +15 -17
  111. prefect/workers/process.py +3 -8
  112. prefect/workers/server.py +2 -2
  113. {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/METADATA +22 -21
  114. prefect_client-3.0.0rc1.dist-info/RECORD +176 -0
  115. prefect/_internal/pydantic/_base_model.py +0 -51
  116. prefect/_internal/pydantic/_compat.py +0 -82
  117. prefect/_internal/pydantic/_flags.py +0 -20
  118. prefect/_internal/pydantic/_types.py +0 -8
  119. prefect/_internal/pydantic/utilities/__init__.py +0 -0
  120. prefect/_internal/pydantic/utilities/config_dict.py +0 -72
  121. prefect/_internal/pydantic/utilities/field_validator.py +0 -150
  122. prefect/_internal/pydantic/utilities/model_construct.py +0 -56
  123. prefect/_internal/pydantic/utilities/model_copy.py +0 -55
  124. prefect/_internal/pydantic/utilities/model_dump.py +0 -136
  125. prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
  126. prefect/_internal/pydantic/utilities/model_fields.py +0 -50
  127. prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
  128. prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
  129. prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
  130. prefect/_internal/pydantic/utilities/model_validate.py +0 -75
  131. prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
  132. prefect/_internal/pydantic/utilities/model_validator.py +0 -87
  133. prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
  134. prefect/_vendor/__init__.py +0 -0
  135. prefect/_vendor/fastapi/__init__.py +0 -25
  136. prefect/_vendor/fastapi/applications.py +0 -946
  137. prefect/_vendor/fastapi/background.py +0 -3
  138. prefect/_vendor/fastapi/concurrency.py +0 -44
  139. prefect/_vendor/fastapi/datastructures.py +0 -58
  140. prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
  141. prefect/_vendor/fastapi/dependencies/models.py +0 -64
  142. prefect/_vendor/fastapi/dependencies/utils.py +0 -877
  143. prefect/_vendor/fastapi/encoders.py +0 -177
  144. prefect/_vendor/fastapi/exception_handlers.py +0 -40
  145. prefect/_vendor/fastapi/exceptions.py +0 -46
  146. prefect/_vendor/fastapi/logger.py +0 -3
  147. prefect/_vendor/fastapi/middleware/__init__.py +0 -1
  148. prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
  149. prefect/_vendor/fastapi/middleware/cors.py +0 -3
  150. prefect/_vendor/fastapi/middleware/gzip.py +0 -3
  151. prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
  152. prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
  153. prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
  154. prefect/_vendor/fastapi/openapi/__init__.py +0 -0
  155. prefect/_vendor/fastapi/openapi/constants.py +0 -2
  156. prefect/_vendor/fastapi/openapi/docs.py +0 -203
  157. prefect/_vendor/fastapi/openapi/models.py +0 -480
  158. prefect/_vendor/fastapi/openapi/utils.py +0 -485
  159. prefect/_vendor/fastapi/param_functions.py +0 -340
  160. prefect/_vendor/fastapi/params.py +0 -453
  161. prefect/_vendor/fastapi/requests.py +0 -4
  162. prefect/_vendor/fastapi/responses.py +0 -40
  163. prefect/_vendor/fastapi/routing.py +0 -1331
  164. prefect/_vendor/fastapi/security/__init__.py +0 -15
  165. prefect/_vendor/fastapi/security/api_key.py +0 -98
  166. prefect/_vendor/fastapi/security/base.py +0 -6
  167. prefect/_vendor/fastapi/security/http.py +0 -172
  168. prefect/_vendor/fastapi/security/oauth2.py +0 -227
  169. prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
  170. prefect/_vendor/fastapi/security/utils.py +0 -10
  171. prefect/_vendor/fastapi/staticfiles.py +0 -1
  172. prefect/_vendor/fastapi/templating.py +0 -3
  173. prefect/_vendor/fastapi/testclient.py +0 -1
  174. prefect/_vendor/fastapi/types.py +0 -3
  175. prefect/_vendor/fastapi/utils.py +0 -235
  176. prefect/_vendor/fastapi/websockets.py +0 -7
  177. prefect/_vendor/starlette/__init__.py +0 -1
  178. prefect/_vendor/starlette/_compat.py +0 -28
  179. prefect/_vendor/starlette/_exception_handler.py +0 -80
  180. prefect/_vendor/starlette/_utils.py +0 -88
  181. prefect/_vendor/starlette/applications.py +0 -261
  182. prefect/_vendor/starlette/authentication.py +0 -159
  183. prefect/_vendor/starlette/background.py +0 -43
  184. prefect/_vendor/starlette/concurrency.py +0 -59
  185. prefect/_vendor/starlette/config.py +0 -151
  186. prefect/_vendor/starlette/convertors.py +0 -87
  187. prefect/_vendor/starlette/datastructures.py +0 -707
  188. prefect/_vendor/starlette/endpoints.py +0 -130
  189. prefect/_vendor/starlette/exceptions.py +0 -60
  190. prefect/_vendor/starlette/formparsers.py +0 -276
  191. prefect/_vendor/starlette/middleware/__init__.py +0 -17
  192. prefect/_vendor/starlette/middleware/authentication.py +0 -52
  193. prefect/_vendor/starlette/middleware/base.py +0 -220
  194. prefect/_vendor/starlette/middleware/cors.py +0 -176
  195. prefect/_vendor/starlette/middleware/errors.py +0 -265
  196. prefect/_vendor/starlette/middleware/exceptions.py +0 -74
  197. prefect/_vendor/starlette/middleware/gzip.py +0 -113
  198. prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
  199. prefect/_vendor/starlette/middleware/sessions.py +0 -82
  200. prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
  201. prefect/_vendor/starlette/middleware/wsgi.py +0 -147
  202. prefect/_vendor/starlette/requests.py +0 -328
  203. prefect/_vendor/starlette/responses.py +0 -347
  204. prefect/_vendor/starlette/routing.py +0 -933
  205. prefect/_vendor/starlette/schemas.py +0 -154
  206. prefect/_vendor/starlette/staticfiles.py +0 -248
  207. prefect/_vendor/starlette/status.py +0 -199
  208. prefect/_vendor/starlette/templating.py +0 -231
  209. prefect/_vendor/starlette/testclient.py +0 -804
  210. prefect/_vendor/starlette/types.py +0 -30
  211. prefect/_vendor/starlette/websockets.py +0 -193
  212. prefect/agent.py +0 -698
  213. prefect/deployments/deployments.py +0 -1042
  214. prefect/deprecated/__init__.py +0 -0
  215. prefect/deprecated/data_documents.py +0 -350
  216. prefect/deprecated/packaging/__init__.py +0 -12
  217. prefect/deprecated/packaging/base.py +0 -96
  218. prefect/deprecated/packaging/docker.py +0 -146
  219. prefect/deprecated/packaging/file.py +0 -92
  220. prefect/deprecated/packaging/orion.py +0 -80
  221. prefect/deprecated/packaging/serializers.py +0 -171
  222. prefect/events/instrument.py +0 -135
  223. prefect/infrastructure/base.py +0 -323
  224. prefect/infrastructure/container.py +0 -818
  225. prefect/infrastructure/kubernetes.py +0 -920
  226. prefect/infrastructure/process.py +0 -289
  227. prefect/new_task_engine.py +0 -423
  228. prefect/pydantic/__init__.py +0 -76
  229. prefect/pydantic/main.py +0 -39
  230. prefect/software/__init__.py +0 -2
  231. prefect/software/base.py +0 -50
  232. prefect/software/conda.py +0 -199
  233. prefect/software/pip.py +0 -122
  234. prefect/software/python.py +0 -52
  235. prefect/workers/block.py +0 -218
  236. prefect_client-2.19.3.dist-info/RECORD +0 -292
  237. {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/LICENSE +0 -0
  238. {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/WHEEL +0 -0
  239. {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.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
- )