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.
- prefect/__init__.py +8 -56
- prefect/_internal/compatibility/deprecated.py +6 -115
- prefect/_internal/compatibility/experimental.py +4 -79
- prefect/_internal/concurrency/api.py +0 -34
- prefect/_internal/concurrency/calls.py +0 -6
- prefect/_internal/concurrency/cancellation.py +0 -3
- prefect/_internal/concurrency/event_loop.py +0 -20
- prefect/_internal/concurrency/inspection.py +3 -3
- prefect/_internal/concurrency/threads.py +35 -0
- prefect/_internal/concurrency/waiters.py +0 -28
- prefect/_internal/pydantic/__init__.py +0 -45
- prefect/_internal/pydantic/v1_schema.py +21 -22
- prefect/_internal/pydantic/v2_schema.py +0 -2
- prefect/_internal/pydantic/v2_validated_func.py +18 -23
- prefect/_internal/schemas/bases.py +44 -177
- prefect/_internal/schemas/fields.py +1 -43
- prefect/_internal/schemas/validators.py +60 -158
- prefect/artifacts.py +161 -14
- prefect/automations.py +39 -4
- prefect/blocks/abstract.py +1 -1
- prefect/blocks/core.py +268 -148
- prefect/blocks/fields.py +2 -57
- prefect/blocks/kubernetes.py +8 -12
- prefect/blocks/notifications.py +40 -20
- prefect/blocks/system.py +22 -11
- prefect/blocks/webhook.py +2 -9
- prefect/client/base.py +4 -4
- prefect/client/cloud.py +8 -13
- prefect/client/orchestration.py +347 -341
- prefect/client/schemas/actions.py +92 -86
- prefect/client/schemas/filters.py +20 -40
- prefect/client/schemas/objects.py +147 -145
- prefect/client/schemas/responses.py +16 -24
- prefect/client/schemas/schedules.py +47 -35
- prefect/client/subscriptions.py +2 -2
- prefect/client/utilities.py +5 -2
- prefect/concurrency/asyncio.py +3 -1
- prefect/concurrency/events.py +1 -1
- prefect/concurrency/services.py +6 -3
- prefect/context.py +195 -27
- prefect/deployments/__init__.py +5 -6
- prefect/deployments/base.py +7 -5
- prefect/deployments/flow_runs.py +185 -0
- prefect/deployments/runner.py +50 -45
- prefect/deployments/schedules.py +28 -23
- prefect/deployments/steps/__init__.py +0 -1
- prefect/deployments/steps/core.py +1 -0
- prefect/deployments/steps/pull.py +7 -21
- prefect/engine.py +12 -2422
- prefect/events/actions.py +17 -23
- prefect/events/cli/automations.py +19 -6
- prefect/events/clients.py +14 -37
- prefect/events/filters.py +14 -18
- prefect/events/related.py +2 -2
- prefect/events/schemas/__init__.py +0 -5
- prefect/events/schemas/automations.py +55 -46
- prefect/events/schemas/deployment_triggers.py +7 -197
- prefect/events/schemas/events.py +34 -65
- prefect/events/schemas/labelling.py +10 -14
- prefect/events/utilities.py +2 -3
- prefect/events/worker.py +2 -3
- prefect/filesystems.py +6 -517
- prefect/{new_flow_engine.py → flow_engine.py} +313 -72
- prefect/flow_runs.py +377 -5
- prefect/flows.py +248 -165
- prefect/futures.py +186 -345
- prefect/infrastructure/__init__.py +0 -27
- prefect/infrastructure/provisioners/__init__.py +5 -3
- prefect/infrastructure/provisioners/cloud_run.py +11 -6
- prefect/infrastructure/provisioners/container_instance.py +11 -7
- prefect/infrastructure/provisioners/ecs.py +6 -4
- prefect/infrastructure/provisioners/modal.py +8 -5
- prefect/input/actions.py +2 -4
- prefect/input/run_input.py +5 -7
- prefect/logging/formatters.py +0 -2
- prefect/logging/handlers.py +3 -11
- prefect/logging/loggers.py +2 -2
- prefect/manifests.py +2 -1
- prefect/records/__init__.py +1 -0
- prefect/records/result_store.py +42 -0
- prefect/records/store.py +9 -0
- prefect/results.py +43 -39
- prefect/runner/runner.py +9 -9
- prefect/runner/server.py +6 -10
- prefect/runner/storage.py +3 -8
- prefect/runner/submit.py +2 -2
- prefect/runner/utils.py +2 -2
- prefect/serializers.py +24 -35
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
- prefect/settings.py +70 -133
- prefect/states.py +17 -47
- prefect/task_engine.py +697 -58
- prefect/task_runners.py +269 -301
- prefect/task_server.py +53 -34
- prefect/tasks.py +327 -337
- prefect/transactions.py +220 -0
- prefect/types/__init__.py +61 -82
- prefect/utilities/asyncutils.py +195 -136
- prefect/utilities/callables.py +121 -41
- prefect/utilities/collections.py +23 -38
- prefect/utilities/dispatch.py +11 -3
- prefect/utilities/dockerutils.py +4 -0
- prefect/utilities/engine.py +140 -20
- prefect/utilities/importtools.py +26 -27
- prefect/utilities/pydantic.py +128 -38
- prefect/utilities/schema_tools/hydration.py +5 -1
- prefect/utilities/templating.py +12 -2
- prefect/variables.py +78 -61
- prefect/workers/__init__.py +0 -1
- prefect/workers/base.py +15 -17
- prefect/workers/process.py +3 -8
- prefect/workers/server.py +2 -2
- {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/METADATA +22 -21
- prefect_client-3.0.0rc1.dist-info/RECORD +176 -0
- prefect/_internal/pydantic/_base_model.py +0 -51
- prefect/_internal/pydantic/_compat.py +0 -82
- prefect/_internal/pydantic/_flags.py +0 -20
- prefect/_internal/pydantic/_types.py +0 -8
- prefect/_internal/pydantic/utilities/__init__.py +0 -0
- prefect/_internal/pydantic/utilities/config_dict.py +0 -72
- prefect/_internal/pydantic/utilities/field_validator.py +0 -150
- prefect/_internal/pydantic/utilities/model_construct.py +0 -56
- prefect/_internal/pydantic/utilities/model_copy.py +0 -55
- prefect/_internal/pydantic/utilities/model_dump.py +0 -136
- prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
- prefect/_internal/pydantic/utilities/model_fields.py +0 -50
- prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
- prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
- prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
- prefect/_internal/pydantic/utilities/model_validate.py +0 -75
- prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
- prefect/_internal/pydantic/utilities/model_validator.py +0 -87
- prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
- prefect/_vendor/__init__.py +0 -0
- prefect/_vendor/fastapi/__init__.py +0 -25
- prefect/_vendor/fastapi/applications.py +0 -946
- prefect/_vendor/fastapi/background.py +0 -3
- prefect/_vendor/fastapi/concurrency.py +0 -44
- prefect/_vendor/fastapi/datastructures.py +0 -58
- prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
- prefect/_vendor/fastapi/dependencies/models.py +0 -64
- prefect/_vendor/fastapi/dependencies/utils.py +0 -877
- prefect/_vendor/fastapi/encoders.py +0 -177
- prefect/_vendor/fastapi/exception_handlers.py +0 -40
- prefect/_vendor/fastapi/exceptions.py +0 -46
- prefect/_vendor/fastapi/logger.py +0 -3
- prefect/_vendor/fastapi/middleware/__init__.py +0 -1
- prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
- prefect/_vendor/fastapi/middleware/cors.py +0 -3
- prefect/_vendor/fastapi/middleware/gzip.py +0 -3
- prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
- prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
- prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
- prefect/_vendor/fastapi/openapi/__init__.py +0 -0
- prefect/_vendor/fastapi/openapi/constants.py +0 -2
- prefect/_vendor/fastapi/openapi/docs.py +0 -203
- prefect/_vendor/fastapi/openapi/models.py +0 -480
- prefect/_vendor/fastapi/openapi/utils.py +0 -485
- prefect/_vendor/fastapi/param_functions.py +0 -340
- prefect/_vendor/fastapi/params.py +0 -453
- prefect/_vendor/fastapi/requests.py +0 -4
- prefect/_vendor/fastapi/responses.py +0 -40
- prefect/_vendor/fastapi/routing.py +0 -1331
- prefect/_vendor/fastapi/security/__init__.py +0 -15
- prefect/_vendor/fastapi/security/api_key.py +0 -98
- prefect/_vendor/fastapi/security/base.py +0 -6
- prefect/_vendor/fastapi/security/http.py +0 -172
- prefect/_vendor/fastapi/security/oauth2.py +0 -227
- prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
- prefect/_vendor/fastapi/security/utils.py +0 -10
- prefect/_vendor/fastapi/staticfiles.py +0 -1
- prefect/_vendor/fastapi/templating.py +0 -3
- prefect/_vendor/fastapi/testclient.py +0 -1
- prefect/_vendor/fastapi/types.py +0 -3
- prefect/_vendor/fastapi/utils.py +0 -235
- prefect/_vendor/fastapi/websockets.py +0 -7
- prefect/_vendor/starlette/__init__.py +0 -1
- prefect/_vendor/starlette/_compat.py +0 -28
- prefect/_vendor/starlette/_exception_handler.py +0 -80
- prefect/_vendor/starlette/_utils.py +0 -88
- prefect/_vendor/starlette/applications.py +0 -261
- prefect/_vendor/starlette/authentication.py +0 -159
- prefect/_vendor/starlette/background.py +0 -43
- prefect/_vendor/starlette/concurrency.py +0 -59
- prefect/_vendor/starlette/config.py +0 -151
- prefect/_vendor/starlette/convertors.py +0 -87
- prefect/_vendor/starlette/datastructures.py +0 -707
- prefect/_vendor/starlette/endpoints.py +0 -130
- prefect/_vendor/starlette/exceptions.py +0 -60
- prefect/_vendor/starlette/formparsers.py +0 -276
- prefect/_vendor/starlette/middleware/__init__.py +0 -17
- prefect/_vendor/starlette/middleware/authentication.py +0 -52
- prefect/_vendor/starlette/middleware/base.py +0 -220
- prefect/_vendor/starlette/middleware/cors.py +0 -176
- prefect/_vendor/starlette/middleware/errors.py +0 -265
- prefect/_vendor/starlette/middleware/exceptions.py +0 -74
- prefect/_vendor/starlette/middleware/gzip.py +0 -113
- prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
- prefect/_vendor/starlette/middleware/sessions.py +0 -82
- prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
- prefect/_vendor/starlette/middleware/wsgi.py +0 -147
- prefect/_vendor/starlette/requests.py +0 -328
- prefect/_vendor/starlette/responses.py +0 -347
- prefect/_vendor/starlette/routing.py +0 -933
- prefect/_vendor/starlette/schemas.py +0 -154
- prefect/_vendor/starlette/staticfiles.py +0 -248
- prefect/_vendor/starlette/status.py +0 -199
- prefect/_vendor/starlette/templating.py +0 -231
- prefect/_vendor/starlette/testclient.py +0 -804
- prefect/_vendor/starlette/types.py +0 -30
- prefect/_vendor/starlette/websockets.py +0 -193
- prefect/agent.py +0 -698
- prefect/deployments/deployments.py +0 -1042
- prefect/deprecated/__init__.py +0 -0
- prefect/deprecated/data_documents.py +0 -350
- prefect/deprecated/packaging/__init__.py +0 -12
- prefect/deprecated/packaging/base.py +0 -96
- prefect/deprecated/packaging/docker.py +0 -146
- prefect/deprecated/packaging/file.py +0 -92
- prefect/deprecated/packaging/orion.py +0 -80
- prefect/deprecated/packaging/serializers.py +0 -171
- prefect/events/instrument.py +0 -135
- prefect/infrastructure/base.py +0 -323
- prefect/infrastructure/container.py +0 -818
- prefect/infrastructure/kubernetes.py +0 -920
- prefect/infrastructure/process.py +0 -289
- prefect/new_task_engine.py +0 -423
- prefect/pydantic/__init__.py +0 -76
- prefect/pydantic/main.py +0 -39
- prefect/software/__init__.py +0 -2
- prefect/software/base.py +0 -50
- prefect/software/conda.py +0 -199
- prefect/software/pip.py +0 -122
- prefect/software/python.py +0 -52
- prefect/workers/block.py +0 -218
- prefect_client-2.19.3.dist-info/RECORD +0 -292
- {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/LICENSE +0 -0
- {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/WHEEL +0 -0
- {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/top_level.txt +0 -0
@@ -1,707 +0,0 @@
|
|
1
|
-
import typing
|
2
|
-
from shlex import shlex
|
3
|
-
from urllib.parse import SplitResult, parse_qsl, urlencode, urlsplit
|
4
|
-
|
5
|
-
from prefect._vendor.starlette.concurrency import run_in_threadpool
|
6
|
-
from prefect._vendor.starlette.types import Scope
|
7
|
-
|
8
|
-
|
9
|
-
class Address(typing.NamedTuple):
|
10
|
-
host: str
|
11
|
-
port: int
|
12
|
-
|
13
|
-
|
14
|
-
_KeyType = typing.TypeVar("_KeyType")
|
15
|
-
# Mapping keys are invariant but their values are covariant since
|
16
|
-
# you can only read them
|
17
|
-
# that is, you can't do `Mapping[str, Animal]()["fido"] = Dog()`
|
18
|
-
_CovariantValueType = typing.TypeVar("_CovariantValueType", covariant=True)
|
19
|
-
|
20
|
-
|
21
|
-
class URL:
|
22
|
-
def __init__(
|
23
|
-
self,
|
24
|
-
url: str = "",
|
25
|
-
scope: typing.Optional[Scope] = None,
|
26
|
-
**components: typing.Any,
|
27
|
-
) -> None:
|
28
|
-
if scope is not None:
|
29
|
-
assert not url, 'Cannot set both "url" and "scope".'
|
30
|
-
assert not components, 'Cannot set both "scope" and "**components".'
|
31
|
-
scheme = scope.get("scheme", "http")
|
32
|
-
server = scope.get("server", None)
|
33
|
-
path = scope.get("root_path", "") + scope["path"]
|
34
|
-
query_string = scope.get("query_string", b"")
|
35
|
-
|
36
|
-
host_header = None
|
37
|
-
for key, value in scope["headers"]:
|
38
|
-
if key == b"host":
|
39
|
-
host_header = value.decode("latin-1")
|
40
|
-
break
|
41
|
-
|
42
|
-
if host_header is not None:
|
43
|
-
url = f"{scheme}://{host_header}{path}"
|
44
|
-
elif server is None:
|
45
|
-
url = path
|
46
|
-
else:
|
47
|
-
host, port = server
|
48
|
-
default_port = {"http": 80, "https": 443, "ws": 80, "wss": 443}[scheme]
|
49
|
-
if port == default_port:
|
50
|
-
url = f"{scheme}://{host}{path}"
|
51
|
-
else:
|
52
|
-
url = f"{scheme}://{host}:{port}{path}"
|
53
|
-
|
54
|
-
if query_string:
|
55
|
-
url += "?" + query_string.decode()
|
56
|
-
elif components:
|
57
|
-
assert not url, 'Cannot set both "url" and "**components".'
|
58
|
-
url = URL("").replace(**components).components.geturl()
|
59
|
-
|
60
|
-
self._url = url
|
61
|
-
|
62
|
-
@property
|
63
|
-
def components(self) -> SplitResult:
|
64
|
-
if not hasattr(self, "_components"):
|
65
|
-
self._components = urlsplit(self._url)
|
66
|
-
return self._components
|
67
|
-
|
68
|
-
@property
|
69
|
-
def scheme(self) -> str:
|
70
|
-
return self.components.scheme
|
71
|
-
|
72
|
-
@property
|
73
|
-
def netloc(self) -> str:
|
74
|
-
return self.components.netloc
|
75
|
-
|
76
|
-
@property
|
77
|
-
def path(self) -> str:
|
78
|
-
return self.components.path
|
79
|
-
|
80
|
-
@property
|
81
|
-
def query(self) -> str:
|
82
|
-
return self.components.query
|
83
|
-
|
84
|
-
@property
|
85
|
-
def fragment(self) -> str:
|
86
|
-
return self.components.fragment
|
87
|
-
|
88
|
-
@property
|
89
|
-
def username(self) -> typing.Union[None, str]:
|
90
|
-
return self.components.username
|
91
|
-
|
92
|
-
@property
|
93
|
-
def password(self) -> typing.Union[None, str]:
|
94
|
-
return self.components.password
|
95
|
-
|
96
|
-
@property
|
97
|
-
def hostname(self) -> typing.Union[None, str]:
|
98
|
-
return self.components.hostname
|
99
|
-
|
100
|
-
@property
|
101
|
-
def port(self) -> typing.Optional[int]:
|
102
|
-
return self.components.port
|
103
|
-
|
104
|
-
@property
|
105
|
-
def is_secure(self) -> bool:
|
106
|
-
return self.scheme in ("https", "wss")
|
107
|
-
|
108
|
-
def replace(self, **kwargs: typing.Any) -> "URL":
|
109
|
-
if (
|
110
|
-
"username" in kwargs
|
111
|
-
or "password" in kwargs
|
112
|
-
or "hostname" in kwargs
|
113
|
-
or "port" in kwargs
|
114
|
-
):
|
115
|
-
hostname = kwargs.pop("hostname", None)
|
116
|
-
port = kwargs.pop("port", self.port)
|
117
|
-
username = kwargs.pop("username", self.username)
|
118
|
-
password = kwargs.pop("password", self.password)
|
119
|
-
|
120
|
-
if hostname is None:
|
121
|
-
netloc = self.netloc
|
122
|
-
_, _, hostname = netloc.rpartition("@")
|
123
|
-
|
124
|
-
if hostname[-1] != "]":
|
125
|
-
hostname = hostname.rsplit(":", 1)[0]
|
126
|
-
|
127
|
-
netloc = hostname
|
128
|
-
if port is not None:
|
129
|
-
netloc += f":{port}"
|
130
|
-
if username is not None:
|
131
|
-
userpass = username
|
132
|
-
if password is not None:
|
133
|
-
userpass += f":{password}"
|
134
|
-
netloc = f"{userpass}@{netloc}"
|
135
|
-
|
136
|
-
kwargs["netloc"] = netloc
|
137
|
-
|
138
|
-
components = self.components._replace(**kwargs)
|
139
|
-
return self.__class__(components.geturl())
|
140
|
-
|
141
|
-
def include_query_params(self, **kwargs: typing.Any) -> "URL":
|
142
|
-
params = MultiDict(parse_qsl(self.query, keep_blank_values=True))
|
143
|
-
params.update({str(key): str(value) for key, value in kwargs.items()})
|
144
|
-
query = urlencode(params.multi_items())
|
145
|
-
return self.replace(query=query)
|
146
|
-
|
147
|
-
def replace_query_params(self, **kwargs: typing.Any) -> "URL":
|
148
|
-
query = urlencode([(str(key), str(value)) for key, value in kwargs.items()])
|
149
|
-
return self.replace(query=query)
|
150
|
-
|
151
|
-
def remove_query_params(
|
152
|
-
self, keys: typing.Union[str, typing.Sequence[str]]
|
153
|
-
) -> "URL":
|
154
|
-
if isinstance(keys, str):
|
155
|
-
keys = [keys]
|
156
|
-
params = MultiDict(parse_qsl(self.query, keep_blank_values=True))
|
157
|
-
for key in keys:
|
158
|
-
params.pop(key, None)
|
159
|
-
query = urlencode(params.multi_items())
|
160
|
-
return self.replace(query=query)
|
161
|
-
|
162
|
-
def __eq__(self, other: typing.Any) -> bool:
|
163
|
-
return str(self) == str(other)
|
164
|
-
|
165
|
-
def __str__(self) -> str:
|
166
|
-
return self._url
|
167
|
-
|
168
|
-
def __repr__(self) -> str:
|
169
|
-
url = str(self)
|
170
|
-
if self.password:
|
171
|
-
url = str(self.replace(password="********"))
|
172
|
-
return f"{self.__class__.__name__}({repr(url)})"
|
173
|
-
|
174
|
-
|
175
|
-
class URLPath(str):
|
176
|
-
"""
|
177
|
-
A URL path string that may also hold an associated protocol and/or host.
|
178
|
-
Used by the routing to return `url_path_for` matches.
|
179
|
-
"""
|
180
|
-
|
181
|
-
def __new__(cls, path: str, protocol: str = "", host: str = "") -> "URLPath":
|
182
|
-
assert protocol in ("http", "websocket", "")
|
183
|
-
return str.__new__(cls, path)
|
184
|
-
|
185
|
-
def __init__(self, path: str, protocol: str = "", host: str = "") -> None:
|
186
|
-
self.protocol = protocol
|
187
|
-
self.host = host
|
188
|
-
|
189
|
-
def make_absolute_url(self, base_url: typing.Union[str, URL]) -> URL:
|
190
|
-
if isinstance(base_url, str):
|
191
|
-
base_url = URL(base_url)
|
192
|
-
if self.protocol:
|
193
|
-
scheme = {
|
194
|
-
"http": {True: "https", False: "http"},
|
195
|
-
"websocket": {True: "wss", False: "ws"},
|
196
|
-
}[self.protocol][base_url.is_secure]
|
197
|
-
else:
|
198
|
-
scheme = base_url.scheme
|
199
|
-
|
200
|
-
netloc = self.host or base_url.netloc
|
201
|
-
path = base_url.path.rstrip("/") + str(self)
|
202
|
-
return URL(scheme=scheme, netloc=netloc, path=path)
|
203
|
-
|
204
|
-
|
205
|
-
class Secret:
|
206
|
-
"""
|
207
|
-
Holds a string value that should not be revealed in tracebacks etc.
|
208
|
-
You should cast the value to `str` at the point it is required.
|
209
|
-
"""
|
210
|
-
|
211
|
-
def __init__(self, value: str):
|
212
|
-
self._value = value
|
213
|
-
|
214
|
-
def __repr__(self) -> str:
|
215
|
-
class_name = self.__class__.__name__
|
216
|
-
return f"{class_name}('**********')"
|
217
|
-
|
218
|
-
def __str__(self) -> str:
|
219
|
-
return self._value
|
220
|
-
|
221
|
-
def __bool__(self) -> bool:
|
222
|
-
return bool(self._value)
|
223
|
-
|
224
|
-
|
225
|
-
class CommaSeparatedStrings(typing.Sequence[str]):
|
226
|
-
def __init__(self, value: typing.Union[str, typing.Sequence[str]]):
|
227
|
-
if isinstance(value, str):
|
228
|
-
splitter = shlex(value, posix=True)
|
229
|
-
splitter.whitespace = ","
|
230
|
-
splitter.whitespace_split = True
|
231
|
-
self._items = [item.strip() for item in splitter]
|
232
|
-
else:
|
233
|
-
self._items = list(value)
|
234
|
-
|
235
|
-
def __len__(self) -> int:
|
236
|
-
return len(self._items)
|
237
|
-
|
238
|
-
def __getitem__(self, index: typing.Union[int, slice]) -> typing.Any:
|
239
|
-
return self._items[index]
|
240
|
-
|
241
|
-
def __iter__(self) -> typing.Iterator[str]:
|
242
|
-
return iter(self._items)
|
243
|
-
|
244
|
-
def __repr__(self) -> str:
|
245
|
-
class_name = self.__class__.__name__
|
246
|
-
items = [item for item in self]
|
247
|
-
return f"{class_name}({items!r})"
|
248
|
-
|
249
|
-
def __str__(self) -> str:
|
250
|
-
return ", ".join(repr(item) for item in self)
|
251
|
-
|
252
|
-
|
253
|
-
class ImmutableMultiDict(typing.Mapping[_KeyType, _CovariantValueType]):
|
254
|
-
_dict: typing.Dict[_KeyType, _CovariantValueType]
|
255
|
-
|
256
|
-
def __init__(
|
257
|
-
self,
|
258
|
-
*args: typing.Union[
|
259
|
-
"ImmutableMultiDict[_KeyType, _CovariantValueType]",
|
260
|
-
typing.Mapping[_KeyType, _CovariantValueType],
|
261
|
-
typing.Iterable[typing.Tuple[_KeyType, _CovariantValueType]],
|
262
|
-
],
|
263
|
-
**kwargs: typing.Any,
|
264
|
-
) -> None:
|
265
|
-
assert len(args) < 2, "Too many arguments."
|
266
|
-
|
267
|
-
value: typing.Any = args[0] if args else []
|
268
|
-
if kwargs:
|
269
|
-
value = (
|
270
|
-
ImmutableMultiDict(value).multi_items()
|
271
|
-
+ ImmutableMultiDict(kwargs).multi_items()
|
272
|
-
)
|
273
|
-
|
274
|
-
if not value:
|
275
|
-
_items: typing.List[typing.Tuple[typing.Any, typing.Any]] = []
|
276
|
-
elif hasattr(value, "multi_items"):
|
277
|
-
value = typing.cast(
|
278
|
-
ImmutableMultiDict[_KeyType, _CovariantValueType], value
|
279
|
-
)
|
280
|
-
_items = list(value.multi_items())
|
281
|
-
elif hasattr(value, "items"):
|
282
|
-
value = typing.cast(typing.Mapping[_KeyType, _CovariantValueType], value)
|
283
|
-
_items = list(value.items())
|
284
|
-
else:
|
285
|
-
value = typing.cast(
|
286
|
-
typing.List[typing.Tuple[typing.Any, typing.Any]], value
|
287
|
-
)
|
288
|
-
_items = list(value)
|
289
|
-
|
290
|
-
self._dict = {k: v for k, v in _items}
|
291
|
-
self._list = _items
|
292
|
-
|
293
|
-
def getlist(self, key: typing.Any) -> typing.List[_CovariantValueType]:
|
294
|
-
return [item_value for item_key, item_value in self._list if item_key == key]
|
295
|
-
|
296
|
-
def keys(self) -> typing.KeysView[_KeyType]:
|
297
|
-
return self._dict.keys()
|
298
|
-
|
299
|
-
def values(self) -> typing.ValuesView[_CovariantValueType]:
|
300
|
-
return self._dict.values()
|
301
|
-
|
302
|
-
def items(self) -> typing.ItemsView[_KeyType, _CovariantValueType]:
|
303
|
-
return self._dict.items()
|
304
|
-
|
305
|
-
def multi_items(self) -> typing.List[typing.Tuple[_KeyType, _CovariantValueType]]:
|
306
|
-
return list(self._list)
|
307
|
-
|
308
|
-
def __getitem__(self, key: _KeyType) -> _CovariantValueType:
|
309
|
-
return self._dict[key]
|
310
|
-
|
311
|
-
def __contains__(self, key: typing.Any) -> bool:
|
312
|
-
return key in self._dict
|
313
|
-
|
314
|
-
def __iter__(self) -> typing.Iterator[_KeyType]:
|
315
|
-
return iter(self.keys())
|
316
|
-
|
317
|
-
def __len__(self) -> int:
|
318
|
-
return len(self._dict)
|
319
|
-
|
320
|
-
def __eq__(self, other: typing.Any) -> bool:
|
321
|
-
if not isinstance(other, self.__class__):
|
322
|
-
return False
|
323
|
-
return sorted(self._list) == sorted(other._list)
|
324
|
-
|
325
|
-
def __repr__(self) -> str:
|
326
|
-
class_name = self.__class__.__name__
|
327
|
-
items = self.multi_items()
|
328
|
-
return f"{class_name}({items!r})"
|
329
|
-
|
330
|
-
|
331
|
-
class MultiDict(ImmutableMultiDict[typing.Any, typing.Any]):
|
332
|
-
def __setitem__(self, key: typing.Any, value: typing.Any) -> None:
|
333
|
-
self.setlist(key, [value])
|
334
|
-
|
335
|
-
def __delitem__(self, key: typing.Any) -> None:
|
336
|
-
self._list = [(k, v) for k, v in self._list if k != key]
|
337
|
-
del self._dict[key]
|
338
|
-
|
339
|
-
def pop(self, key: typing.Any, default: typing.Any = None) -> typing.Any:
|
340
|
-
self._list = [(k, v) for k, v in self._list if k != key]
|
341
|
-
return self._dict.pop(key, default)
|
342
|
-
|
343
|
-
def popitem(self) -> typing.Tuple[typing.Any, typing.Any]:
|
344
|
-
key, value = self._dict.popitem()
|
345
|
-
self._list = [(k, v) for k, v in self._list if k != key]
|
346
|
-
return key, value
|
347
|
-
|
348
|
-
def poplist(self, key: typing.Any) -> typing.List[typing.Any]:
|
349
|
-
values = [v for k, v in self._list if k == key]
|
350
|
-
self.pop(key)
|
351
|
-
return values
|
352
|
-
|
353
|
-
def clear(self) -> None:
|
354
|
-
self._dict.clear()
|
355
|
-
self._list.clear()
|
356
|
-
|
357
|
-
def setdefault(self, key: typing.Any, default: typing.Any = None) -> typing.Any:
|
358
|
-
if key not in self:
|
359
|
-
self._dict[key] = default
|
360
|
-
self._list.append((key, default))
|
361
|
-
|
362
|
-
return self[key]
|
363
|
-
|
364
|
-
def setlist(self, key: typing.Any, values: typing.List[typing.Any]) -> None:
|
365
|
-
if not values:
|
366
|
-
self.pop(key, None)
|
367
|
-
else:
|
368
|
-
existing_items = [(k, v) for (k, v) in self._list if k != key]
|
369
|
-
self._list = existing_items + [(key, value) for value in values]
|
370
|
-
self._dict[key] = values[-1]
|
371
|
-
|
372
|
-
def append(self, key: typing.Any, value: typing.Any) -> None:
|
373
|
-
self._list.append((key, value))
|
374
|
-
self._dict[key] = value
|
375
|
-
|
376
|
-
def update(
|
377
|
-
self,
|
378
|
-
*args: typing.Union[
|
379
|
-
"MultiDict",
|
380
|
-
typing.Mapping[typing.Any, typing.Any],
|
381
|
-
typing.List[typing.Tuple[typing.Any, typing.Any]],
|
382
|
-
],
|
383
|
-
**kwargs: typing.Any,
|
384
|
-
) -> None:
|
385
|
-
value = MultiDict(*args, **kwargs)
|
386
|
-
existing_items = [(k, v) for (k, v) in self._list if k not in value.keys()]
|
387
|
-
self._list = existing_items + value.multi_items()
|
388
|
-
self._dict.update(value)
|
389
|
-
|
390
|
-
|
391
|
-
class QueryParams(ImmutableMultiDict[str, str]):
|
392
|
-
"""
|
393
|
-
An immutable multidict.
|
394
|
-
"""
|
395
|
-
|
396
|
-
def __init__(
|
397
|
-
self,
|
398
|
-
*args: typing.Union[
|
399
|
-
"ImmutableMultiDict[typing.Any, typing.Any]",
|
400
|
-
typing.Mapping[typing.Any, typing.Any],
|
401
|
-
typing.List[typing.Tuple[typing.Any, typing.Any]],
|
402
|
-
str,
|
403
|
-
bytes,
|
404
|
-
],
|
405
|
-
**kwargs: typing.Any,
|
406
|
-
) -> None:
|
407
|
-
assert len(args) < 2, "Too many arguments."
|
408
|
-
|
409
|
-
value = args[0] if args else []
|
410
|
-
|
411
|
-
if isinstance(value, str):
|
412
|
-
super().__init__(parse_qsl(value, keep_blank_values=True), **kwargs)
|
413
|
-
elif isinstance(value, bytes):
|
414
|
-
super().__init__(
|
415
|
-
parse_qsl(value.decode("latin-1"), keep_blank_values=True), **kwargs
|
416
|
-
)
|
417
|
-
else:
|
418
|
-
super().__init__(*args, **kwargs) # type: ignore[arg-type]
|
419
|
-
self._list = [(str(k), str(v)) for k, v in self._list]
|
420
|
-
self._dict = {str(k): str(v) for k, v in self._dict.items()}
|
421
|
-
|
422
|
-
def __str__(self) -> str:
|
423
|
-
return urlencode(self._list)
|
424
|
-
|
425
|
-
def __repr__(self) -> str:
|
426
|
-
class_name = self.__class__.__name__
|
427
|
-
query_string = str(self)
|
428
|
-
return f"{class_name}({query_string!r})"
|
429
|
-
|
430
|
-
|
431
|
-
class UploadFile:
|
432
|
-
"""
|
433
|
-
An uploaded file included as part of the request data.
|
434
|
-
"""
|
435
|
-
|
436
|
-
def __init__(
|
437
|
-
self,
|
438
|
-
file: typing.BinaryIO,
|
439
|
-
*,
|
440
|
-
size: typing.Optional[int] = None,
|
441
|
-
filename: typing.Optional[str] = None,
|
442
|
-
headers: "typing.Optional[Headers]" = None,
|
443
|
-
) -> None:
|
444
|
-
self.filename = filename
|
445
|
-
self.file = file
|
446
|
-
self.size = size
|
447
|
-
self.headers = headers or Headers()
|
448
|
-
|
449
|
-
@property
|
450
|
-
def content_type(self) -> typing.Optional[str]:
|
451
|
-
return self.headers.get("content-type", None)
|
452
|
-
|
453
|
-
@property
|
454
|
-
def _in_memory(self) -> bool:
|
455
|
-
# check for SpooledTemporaryFile._rolled
|
456
|
-
rolled_to_disk = getattr(self.file, "_rolled", True)
|
457
|
-
return not rolled_to_disk
|
458
|
-
|
459
|
-
async def write(self, data: bytes) -> None:
|
460
|
-
if self.size is not None:
|
461
|
-
self.size += len(data)
|
462
|
-
|
463
|
-
if self._in_memory:
|
464
|
-
self.file.write(data)
|
465
|
-
else:
|
466
|
-
await run_in_threadpool(self.file.write, data)
|
467
|
-
|
468
|
-
async def read(self, size: int = -1) -> bytes:
|
469
|
-
if self._in_memory:
|
470
|
-
return self.file.read(size)
|
471
|
-
return await run_in_threadpool(self.file.read, size)
|
472
|
-
|
473
|
-
async def seek(self, offset: int) -> None:
|
474
|
-
if self._in_memory:
|
475
|
-
self.file.seek(offset)
|
476
|
-
else:
|
477
|
-
await run_in_threadpool(self.file.seek, offset)
|
478
|
-
|
479
|
-
async def close(self) -> None:
|
480
|
-
if self._in_memory:
|
481
|
-
self.file.close()
|
482
|
-
else:
|
483
|
-
await run_in_threadpool(self.file.close)
|
484
|
-
|
485
|
-
|
486
|
-
class FormData(ImmutableMultiDict[str, typing.Union[UploadFile, str]]):
|
487
|
-
"""
|
488
|
-
An immutable multidict, containing both file uploads and text input.
|
489
|
-
"""
|
490
|
-
|
491
|
-
def __init__(
|
492
|
-
self,
|
493
|
-
*args: typing.Union[
|
494
|
-
"FormData",
|
495
|
-
typing.Mapping[str, typing.Union[str, UploadFile]],
|
496
|
-
typing.List[typing.Tuple[str, typing.Union[str, UploadFile]]],
|
497
|
-
],
|
498
|
-
**kwargs: typing.Union[str, UploadFile],
|
499
|
-
) -> None:
|
500
|
-
super().__init__(*args, **kwargs)
|
501
|
-
|
502
|
-
async def close(self) -> None:
|
503
|
-
for key, value in self.multi_items():
|
504
|
-
if isinstance(value, UploadFile):
|
505
|
-
await value.close()
|
506
|
-
|
507
|
-
|
508
|
-
class Headers(typing.Mapping[str, str]):
|
509
|
-
"""
|
510
|
-
An immutable, case-insensitive multidict.
|
511
|
-
"""
|
512
|
-
|
513
|
-
def __init__(
|
514
|
-
self,
|
515
|
-
headers: typing.Optional[typing.Mapping[str, str]] = None,
|
516
|
-
raw: typing.Optional[typing.List[typing.Tuple[bytes, bytes]]] = None,
|
517
|
-
scope: typing.Optional[typing.MutableMapping[str, typing.Any]] = None,
|
518
|
-
) -> None:
|
519
|
-
self._list: typing.List[typing.Tuple[bytes, bytes]] = []
|
520
|
-
if headers is not None:
|
521
|
-
assert raw is None, 'Cannot set both "headers" and "raw".'
|
522
|
-
assert scope is None, 'Cannot set both "headers" and "scope".'
|
523
|
-
self._list = [
|
524
|
-
(key.lower().encode("latin-1"), value.encode("latin-1"))
|
525
|
-
for key, value in headers.items()
|
526
|
-
]
|
527
|
-
elif raw is not None:
|
528
|
-
assert scope is None, 'Cannot set both "raw" and "scope".'
|
529
|
-
self._list = raw
|
530
|
-
elif scope is not None:
|
531
|
-
# scope["headers"] isn't necessarily a list
|
532
|
-
# it might be a tuple or other iterable
|
533
|
-
self._list = scope["headers"] = list(scope["headers"])
|
534
|
-
|
535
|
-
@property
|
536
|
-
def raw(self) -> typing.List[typing.Tuple[bytes, bytes]]:
|
537
|
-
return list(self._list)
|
538
|
-
|
539
|
-
def keys(self) -> typing.List[str]: # type: ignore[override]
|
540
|
-
return [key.decode("latin-1") for key, value in self._list]
|
541
|
-
|
542
|
-
def values(self) -> typing.List[str]: # type: ignore[override]
|
543
|
-
return [value.decode("latin-1") for key, value in self._list]
|
544
|
-
|
545
|
-
def items(self) -> typing.List[typing.Tuple[str, str]]: # type: ignore[override]
|
546
|
-
return [
|
547
|
-
(key.decode("latin-1"), value.decode("latin-1"))
|
548
|
-
for key, value in self._list
|
549
|
-
]
|
550
|
-
|
551
|
-
def getlist(self, key: str) -> typing.List[str]:
|
552
|
-
get_header_key = key.lower().encode("latin-1")
|
553
|
-
return [
|
554
|
-
item_value.decode("latin-1")
|
555
|
-
for item_key, item_value in self._list
|
556
|
-
if item_key == get_header_key
|
557
|
-
]
|
558
|
-
|
559
|
-
def mutablecopy(self) -> "MutableHeaders":
|
560
|
-
return MutableHeaders(raw=self._list[:])
|
561
|
-
|
562
|
-
def __getitem__(self, key: str) -> str:
|
563
|
-
get_header_key = key.lower().encode("latin-1")
|
564
|
-
for header_key, header_value in self._list:
|
565
|
-
if header_key == get_header_key:
|
566
|
-
return header_value.decode("latin-1")
|
567
|
-
raise KeyError(key)
|
568
|
-
|
569
|
-
def __contains__(self, key: typing.Any) -> bool:
|
570
|
-
get_header_key = key.lower().encode("latin-1")
|
571
|
-
for header_key, header_value in self._list:
|
572
|
-
if header_key == get_header_key:
|
573
|
-
return True
|
574
|
-
return False
|
575
|
-
|
576
|
-
def __iter__(self) -> typing.Iterator[typing.Any]:
|
577
|
-
return iter(self.keys())
|
578
|
-
|
579
|
-
def __len__(self) -> int:
|
580
|
-
return len(self._list)
|
581
|
-
|
582
|
-
def __eq__(self, other: typing.Any) -> bool:
|
583
|
-
if not isinstance(other, Headers):
|
584
|
-
return False
|
585
|
-
return sorted(self._list) == sorted(other._list)
|
586
|
-
|
587
|
-
def __repr__(self) -> str:
|
588
|
-
class_name = self.__class__.__name__
|
589
|
-
as_dict = dict(self.items())
|
590
|
-
if len(as_dict) == len(self):
|
591
|
-
return f"{class_name}({as_dict!r})"
|
592
|
-
return f"{class_name}(raw={self.raw!r})"
|
593
|
-
|
594
|
-
|
595
|
-
class MutableHeaders(Headers):
|
596
|
-
def __setitem__(self, key: str, value: str) -> None:
|
597
|
-
"""
|
598
|
-
Set the header `key` to `value`, removing any duplicate entries.
|
599
|
-
Retains insertion order.
|
600
|
-
"""
|
601
|
-
set_key = key.lower().encode("latin-1")
|
602
|
-
set_value = value.encode("latin-1")
|
603
|
-
|
604
|
-
found_indexes: "typing.List[int]" = []
|
605
|
-
for idx, (item_key, item_value) in enumerate(self._list):
|
606
|
-
if item_key == set_key:
|
607
|
-
found_indexes.append(idx)
|
608
|
-
|
609
|
-
for idx in reversed(found_indexes[1:]):
|
610
|
-
del self._list[idx]
|
611
|
-
|
612
|
-
if found_indexes:
|
613
|
-
idx = found_indexes[0]
|
614
|
-
self._list[idx] = (set_key, set_value)
|
615
|
-
else:
|
616
|
-
self._list.append((set_key, set_value))
|
617
|
-
|
618
|
-
def __delitem__(self, key: str) -> None:
|
619
|
-
"""
|
620
|
-
Remove the header `key`.
|
621
|
-
"""
|
622
|
-
del_key = key.lower().encode("latin-1")
|
623
|
-
|
624
|
-
pop_indexes: "typing.List[int]" = []
|
625
|
-
for idx, (item_key, item_value) in enumerate(self._list):
|
626
|
-
if item_key == del_key:
|
627
|
-
pop_indexes.append(idx)
|
628
|
-
|
629
|
-
for idx in reversed(pop_indexes):
|
630
|
-
del self._list[idx]
|
631
|
-
|
632
|
-
def __ior__(self, other: typing.Mapping[str, str]) -> "MutableHeaders":
|
633
|
-
if not isinstance(other, typing.Mapping):
|
634
|
-
raise TypeError(f"Expected a mapping but got {other.__class__.__name__}")
|
635
|
-
self.update(other)
|
636
|
-
return self
|
637
|
-
|
638
|
-
def __or__(self, other: typing.Mapping[str, str]) -> "MutableHeaders":
|
639
|
-
if not isinstance(other, typing.Mapping):
|
640
|
-
raise TypeError(f"Expected a mapping but got {other.__class__.__name__}")
|
641
|
-
new = self.mutablecopy()
|
642
|
-
new.update(other)
|
643
|
-
return new
|
644
|
-
|
645
|
-
@property
|
646
|
-
def raw(self) -> typing.List[typing.Tuple[bytes, bytes]]:
|
647
|
-
return self._list
|
648
|
-
|
649
|
-
def setdefault(self, key: str, value: str) -> str:
|
650
|
-
"""
|
651
|
-
If the header `key` does not exist, then set it to `value`.
|
652
|
-
Returns the header value.
|
653
|
-
"""
|
654
|
-
set_key = key.lower().encode("latin-1")
|
655
|
-
set_value = value.encode("latin-1")
|
656
|
-
|
657
|
-
for idx, (item_key, item_value) in enumerate(self._list):
|
658
|
-
if item_key == set_key:
|
659
|
-
return item_value.decode("latin-1")
|
660
|
-
self._list.append((set_key, set_value))
|
661
|
-
return value
|
662
|
-
|
663
|
-
def update(self, other: typing.Mapping[str, str]) -> None:
|
664
|
-
for key, val in other.items():
|
665
|
-
self[key] = val
|
666
|
-
|
667
|
-
def append(self, key: str, value: str) -> None:
|
668
|
-
"""
|
669
|
-
Append a header, preserving any duplicate entries.
|
670
|
-
"""
|
671
|
-
append_key = key.lower().encode("latin-1")
|
672
|
-
append_value = value.encode("latin-1")
|
673
|
-
self._list.append((append_key, append_value))
|
674
|
-
|
675
|
-
def add_vary_header(self, vary: str) -> None:
|
676
|
-
existing = self.get("vary")
|
677
|
-
if existing is not None:
|
678
|
-
vary = ", ".join([existing, vary])
|
679
|
-
self["vary"] = vary
|
680
|
-
|
681
|
-
|
682
|
-
class State:
|
683
|
-
"""
|
684
|
-
An object that can be used to store arbitrary state.
|
685
|
-
|
686
|
-
Used for `request.state` and `app.state`.
|
687
|
-
"""
|
688
|
-
|
689
|
-
_state: typing.Dict[str, typing.Any]
|
690
|
-
|
691
|
-
def __init__(self, state: typing.Optional[typing.Dict[str, typing.Any]] = None):
|
692
|
-
if state is None:
|
693
|
-
state = {}
|
694
|
-
super().__setattr__("_state", state)
|
695
|
-
|
696
|
-
def __setattr__(self, key: typing.Any, value: typing.Any) -> None:
|
697
|
-
self._state[key] = value
|
698
|
-
|
699
|
-
def __getattr__(self, key: typing.Any) -> typing.Any:
|
700
|
-
try:
|
701
|
-
return self._state[key]
|
702
|
-
except KeyError:
|
703
|
-
message = "'{}' object has no attribute '{}'"
|
704
|
-
raise AttributeError(message.format(self.__class__.__name__, key))
|
705
|
-
|
706
|
-
def __delattr__(self, key: typing.Any) -> None:
|
707
|
-
del self._state[key]
|