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