internetdata 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- internetdata/__init__.py +49 -0
- internetdata/_core.py +287 -0
- internetdata/_generated/__init__.py +8 -0
- internetdata/_generated/api/__init__.py +1 -0
- internetdata/_generated/api/database_v_2/__init__.py +1 -0
- internetdata/_generated/api/database_v_2/database_checksum_v2.py +203 -0
- internetdata/_generated/api/database_v_2/database_metadata_v2.py +205 -0
- internetdata/_generated/api/database_v_2/download_database_v2.py +213 -0
- internetdata/_generated/api/database_v_2/list_databases.py +162 -0
- internetdata/_generated/api/database_v_2/list_downloads.py +178 -0
- internetdata/_generated/client.py +272 -0
- internetdata/_generated/errors.py +16 -0
- internetdata/_generated/models/__init__.py +53 -0
- internetdata/_generated/models/database.py +233 -0
- internetdata/_generated/models/database_checksum_v2_format.py +9 -0
- internetdata/_generated/models/database_checksum_v2_response_200.py +85 -0
- internetdata/_generated/models/database_checksum_v2_response_200_format.py +9 -0
- internetdata/_generated/models/database_metadata.py +132 -0
- internetdata/_generated/models/database_metadata_column.py +80 -0
- internetdata/_generated/models/database_metadata_sample.py +78 -0
- internetdata/_generated/models/database_metadata_sample_additional_property_item.py +45 -0
- internetdata/_generated/models/database_metadata_schema.py +72 -0
- internetdata/_generated/models/database_metadata_size.py +47 -0
- internetdata/_generated/models/database_redistribution_type_1.py +10 -0
- internetdata/_generated/models/database_redistribution_type_2_type_1.py +10 -0
- internetdata/_generated/models/database_redistribution_type_3_type_1.py +10 -0
- internetdata/_generated/models/database_standing.py +10 -0
- internetdata/_generated/models/database_version.py +97 -0
- internetdata/_generated/models/database_version_formats_item.py +9 -0
- internetdata/_generated/models/db_checksums.py +85 -0
- internetdata/_generated/models/download.py +161 -0
- internetdata/_generated/models/download_database_v2_format.py +9 -0
- internetdata/_generated/models/download_outcome.py +13 -0
- internetdata/_generated/models/error.py +62 -0
- internetdata/_generated/models/list_databases_response_200.py +75 -0
- internetdata/_generated/models/list_downloads_response_200.py +75 -0
- internetdata/_generated/types.py +54 -0
- internetdata/aio.py +268 -0
- internetdata/client.py +278 -0
- internetdata/errors.py +128 -0
- internetdata/models.py +197 -0
- internetdata/py.typed +0 -0
- internetdata-1.0.0.dist-info/METADATA +184 -0
- internetdata-1.0.0.dist-info/RECORD +46 -0
- internetdata-1.0.0.dist-info/WHEEL +4 -0
- internetdata-1.0.0.dist-info/licenses/LICENSE +21 -0
internetdata/__init__.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""The official Python client library for the InternetData API.
|
|
2
|
+
|
|
3
|
+
from internetdata import InternetData
|
|
4
|
+
|
|
5
|
+
with InternetData(api_key) as client:
|
|
6
|
+
for db in client.database.list():
|
|
7
|
+
print(db.base, db.standing)
|
|
8
|
+
|
|
9
|
+
See https://internetdata.io for the API, and the README for downloads, checksums and
|
|
10
|
+
what the catalog does and does not show you.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from ._core import DEFAULT_BASE_URL
|
|
14
|
+
from .aio import AsyncDatabaseApi, AsyncInternetData
|
|
15
|
+
from .client import DatabaseApi, InternetData
|
|
16
|
+
from .errors import ErrorKind, InternetDataError
|
|
17
|
+
from .models import (
|
|
18
|
+
Database,
|
|
19
|
+
DatabaseMetadata,
|
|
20
|
+
DatabaseVersion,
|
|
21
|
+
Download,
|
|
22
|
+
Format,
|
|
23
|
+
MetadataColumn,
|
|
24
|
+
Outcome,
|
|
25
|
+
Redistribution,
|
|
26
|
+
Standing,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
__version__ = "1.0.0"
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"DEFAULT_BASE_URL",
|
|
33
|
+
"AsyncDatabaseApi",
|
|
34
|
+
"AsyncInternetData",
|
|
35
|
+
"Database",
|
|
36
|
+
"DatabaseApi",
|
|
37
|
+
"DatabaseMetadata",
|
|
38
|
+
"DatabaseVersion",
|
|
39
|
+
"Download",
|
|
40
|
+
"ErrorKind",
|
|
41
|
+
"Format",
|
|
42
|
+
"InternetData",
|
|
43
|
+
"InternetDataError",
|
|
44
|
+
"MetadataColumn",
|
|
45
|
+
"Outcome",
|
|
46
|
+
"Redistribution",
|
|
47
|
+
"Standing",
|
|
48
|
+
"__version__",
|
|
49
|
+
]
|
internetdata/_core.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"""Plumbing the sync and the async client both need: transport wiring, response
|
|
2
|
+
unwrapping, and the retry policy."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import contextlib
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
from collections.abc import Awaitable, Callable, Iterator
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import IO, Any, TypeVar, cast
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
|
|
15
|
+
from ._generated.client import AuthenticatedClient, Client
|
|
16
|
+
from ._generated.types import Response
|
|
17
|
+
from .errors import InternetDataError, error_from_response
|
|
18
|
+
from .models import Database, Download, to_database, to_download
|
|
19
|
+
|
|
20
|
+
DEFAULT_BASE_URL = "https://internetdata.io"
|
|
21
|
+
DEFAULT_RETRIES = 2
|
|
22
|
+
DEFAULT_TIMEOUT = 10.0
|
|
23
|
+
DEFAULT_DOWNLOADS_LIMIT = 50
|
|
24
|
+
|
|
25
|
+
# One chunk of a transfer, and therefore the ceiling on what a download of any size
|
|
26
|
+
# costs in memory.
|
|
27
|
+
TRANSFER_CHUNK_BYTES = 1 << 20
|
|
28
|
+
|
|
29
|
+
_BACKOFF_BASE = 1.0
|
|
30
|
+
|
|
31
|
+
T = TypeVar("T")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def build_client(
|
|
35
|
+
api_key: str | None,
|
|
36
|
+
base_url: str,
|
|
37
|
+
timeout: float | None,
|
|
38
|
+
transport: httpx.BaseTransport | httpx.AsyncBaseTransport | None,
|
|
39
|
+
) -> AuthenticatedClient:
|
|
40
|
+
"""The generated client, wired for one of ours.
|
|
41
|
+
|
|
42
|
+
Every generated endpoint function types `client` as `AuthenticatedClient` because
|
|
43
|
+
every operation lists a security scheme, but a keyless caller must send NO
|
|
44
|
+
`Authorization` header rather than an empty one: `Bearer ` with nothing after it is
|
|
45
|
+
a 401 that reads as a wrong key. The two classes are interchangeable where the
|
|
46
|
+
endpoints use them, so the keyless one is built as `Client` and the cast lives here
|
|
47
|
+
instead of at every call site.
|
|
48
|
+
|
|
49
|
+
The transport is injected through `httpx_args` rather than with
|
|
50
|
+
`set_httpx_client()`, which silently bypasses auth: the generated client only adds
|
|
51
|
+
the `Authorization` header when it CONSTRUCTS the httpx client itself.
|
|
52
|
+
"""
|
|
53
|
+
httpx_args: dict[str, Any] = {}
|
|
54
|
+
if transport is not None:
|
|
55
|
+
httpx_args["transport"] = transport
|
|
56
|
+
if not api_key:
|
|
57
|
+
return cast(
|
|
58
|
+
AuthenticatedClient,
|
|
59
|
+
Client(base_url=base_url, timeout=httpx.Timeout(timeout), httpx_args=httpx_args),
|
|
60
|
+
)
|
|
61
|
+
return AuthenticatedClient(
|
|
62
|
+
base_url=base_url,
|
|
63
|
+
token=api_key,
|
|
64
|
+
timeout=httpx.Timeout(timeout),
|
|
65
|
+
httpx_args=httpx_args,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def build_transfer_client(
|
|
70
|
+
timeout: float | None, transport: httpx.BaseTransport | None
|
|
71
|
+
) -> httpx.Client:
|
|
72
|
+
"""A SECOND client, holding no credential, for the object-storage leg of a download.
|
|
73
|
+
|
|
74
|
+
The API answers a download with a `302` to a presigned URL, and that URL authorizes
|
|
75
|
+
itself. Following the redirect on the API client would forward the key to a host with
|
|
76
|
+
no business holding it. Worth more than tidiness: object storage answers 400 to a
|
|
77
|
+
request carrying both a presigned signature and an `Authorization` header, so
|
|
78
|
+
forwarding the key does not merely leak it, it breaks the download.
|
|
79
|
+
|
|
80
|
+
Only the connect phase keeps the client's timeout. That timeout is a sane bound on a
|
|
81
|
+
metadata call and the wrong one on a body that reaches gigabytes, which would
|
|
82
|
+
otherwise be cut off mid-transfer. Redirects ARE followed here, unlike on the API
|
|
83
|
+
client: object storage behind a CDN answers one, and there is no credential to leak
|
|
84
|
+
by going along with it.
|
|
85
|
+
"""
|
|
86
|
+
return httpx.Client(
|
|
87
|
+
timeout=httpx.Timeout(None, connect=timeout),
|
|
88
|
+
transport=transport,
|
|
89
|
+
follow_redirects=True,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def build_async_transfer_client(
|
|
94
|
+
timeout: float | None, transport: httpx.AsyncBaseTransport | None
|
|
95
|
+
) -> httpx.AsyncClient:
|
|
96
|
+
"""`build_transfer_client`, for asyncio."""
|
|
97
|
+
return httpx.AsyncClient(
|
|
98
|
+
timeout=httpx.Timeout(None, connect=timeout),
|
|
99
|
+
transport=transport,
|
|
100
|
+
follow_redirects=True,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def storage_refusal(res: httpx.Response) -> InternetDataError:
|
|
105
|
+
"""What object storage refusing a download link becomes.
|
|
106
|
+
|
|
107
|
+
The body is deliberately left unread: the status is what separates a lapsed link from
|
|
108
|
+
a refused one, and nothing bounds the size of an error page.
|
|
109
|
+
"""
|
|
110
|
+
return error_from_response(
|
|
111
|
+
res.status_code,
|
|
112
|
+
res.headers,
|
|
113
|
+
{"rc": f"object storage refused the download link with status {res.status_code}"},
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def assert_whole_transfer(res: httpx.Response, written: int) -> None:
|
|
118
|
+
"""Check what arrived against what was promised.
|
|
119
|
+
|
|
120
|
+
A transfer that dies mid-body can reach a client as a plain end of stream, and a
|
|
121
|
+
short file that looks complete is worse than no file at all: the next run reads it as
|
|
122
|
+
a whole database.
|
|
123
|
+
|
|
124
|
+
Skipped when the body was decoded on the way in, because `Content-Length` then
|
|
125
|
+
describes the ENCODED bytes and disagreeing with it is correct rather than short. A
|
|
126
|
+
chunked response declares no length; httpx raises for itself when one of those is cut
|
|
127
|
+
off.
|
|
128
|
+
"""
|
|
129
|
+
declared = res.headers.get("content-length")
|
|
130
|
+
encoding = res.headers.get("content-encoding", "identity").strip().lower()
|
|
131
|
+
if declared is None or encoding not in ("", "identity"):
|
|
132
|
+
return
|
|
133
|
+
try:
|
|
134
|
+
expected = int(declared)
|
|
135
|
+
except ValueError:
|
|
136
|
+
return
|
|
137
|
+
if expected != written:
|
|
138
|
+
raise InternetDataError(
|
|
139
|
+
"network",
|
|
140
|
+
f"the transfer ended after {written} of {expected} bytes",
|
|
141
|
+
res.status_code,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@contextlib.contextmanager
|
|
146
|
+
def part_file(destination: str | os.PathLike[str]) -> Iterator[IO[bytes]]:
|
|
147
|
+
"""A download's bytes, landing beside `destination` and moved onto it at the end.
|
|
148
|
+
|
|
149
|
+
Two failures this prevents, and only the first is the obvious one. A transfer that
|
|
150
|
+
dies half way leaves no truncated file carrying the real name. And a refresh that
|
|
151
|
+
fails leaves yesterday's good copy untouched, which opening the destination itself
|
|
152
|
+
could not do: that truncates it before the first byte of the new one arrives.
|
|
153
|
+
"""
|
|
154
|
+
partial = os.fspath(destination) + ".part"
|
|
155
|
+
try:
|
|
156
|
+
with open(partial, "wb") as sink:
|
|
157
|
+
yield sink
|
|
158
|
+
os.replace(partial, destination)
|
|
159
|
+
except BaseException:
|
|
160
|
+
Path(partial).unlink(missing_ok=True)
|
|
161
|
+
raise
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def send(call: Callable[[], Response[Any]]) -> Response[Any]:
|
|
165
|
+
"""One generated endpoint call.
|
|
166
|
+
|
|
167
|
+
The generated code eagerly decodes the body of every DOCUMENTED status into its model
|
|
168
|
+
before anything here sees the response, and it raises three different ways doing it: a
|
|
169
|
+
503 carrying an intermediary's HTML error page is a `ValueError` out of `json()`, a
|
|
170
|
+
200 missing a required key is a `KeyError`, and a `standing` this pinned spec predates
|
|
171
|
+
is a `ValueError` out of an enum constructor. All three are the server sending
|
|
172
|
+
something this client cannot read, which is a failed request rather than a bug in the
|
|
173
|
+
caller's code, so all three become the one error type here.
|
|
174
|
+
|
|
175
|
+
The lambda does nothing but call the generated function, which is what keeps this
|
|
176
|
+
catch from swallowing a fault of our own.
|
|
177
|
+
"""
|
|
178
|
+
try:
|
|
179
|
+
return call()
|
|
180
|
+
except (KeyError, TypeError, ValueError) as exc:
|
|
181
|
+
raise malformed(exc) from exc
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
async def send_async(call: Callable[[], Awaitable[Response[Any]]]) -> Response[Any]:
|
|
185
|
+
"""`send`, awaited."""
|
|
186
|
+
try:
|
|
187
|
+
return await call()
|
|
188
|
+
except (KeyError, TypeError, ValueError) as exc:
|
|
189
|
+
raise malformed(exc) from exc
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def malformed(exc: Exception) -> InternetDataError:
|
|
193
|
+
return InternetDataError("server_error", f"malformed response from the API: {exc}")
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def unwrap(res: Response[Any]) -> dict[str, Any]:
|
|
197
|
+
"""The response body as it came off the wire, or the failure it describes."""
|
|
198
|
+
body = _decode(res.content)
|
|
199
|
+
status = int(res.status_code)
|
|
200
|
+
if not 200 <= status < 300:
|
|
201
|
+
raise error_from_response(status, _headers(res), body)
|
|
202
|
+
if not isinstance(body, dict):
|
|
203
|
+
raise InternetDataError("server_error", "the API answered with a non-object body", status)
|
|
204
|
+
return body
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def as_error(exc: InternetDataError | httpx.HTTPError) -> InternetDataError:
|
|
208
|
+
"""A transport failure, as the one error type this library raises.
|
|
209
|
+
|
|
210
|
+
Deliberately narrow: anything else is a bug rather than a failed request, and turning
|
|
211
|
+
it into a `network` error here would hide it behind a retry.
|
|
212
|
+
"""
|
|
213
|
+
if isinstance(exc, InternetDataError):
|
|
214
|
+
return exc
|
|
215
|
+
return InternetDataError("network", str(exc) or type(exc).__name__)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def parse_body(body: dict[str, Any], parse: Callable[[dict[str, Any]], T]) -> T:
|
|
219
|
+
"""A served body through the model layer, with a malformed one reported as the
|
|
220
|
+
server's failure rather than as a traceback out of a dataclass constructor."""
|
|
221
|
+
try:
|
|
222
|
+
return parse(body)
|
|
223
|
+
except (KeyError, TypeError, ValueError) as exc:
|
|
224
|
+
raise malformed(exc) from exc
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def redirect_location(res: Response[Any]) -> str:
|
|
228
|
+
"""Where a `302` points, or whatever the API said instead."""
|
|
229
|
+
location: str | None = _headers(res).get("location")
|
|
230
|
+
if int(res.status_code) == 302 and location:
|
|
231
|
+
return location
|
|
232
|
+
unwrap(res)
|
|
233
|
+
raise InternetDataError(
|
|
234
|
+
"server_error", "expected a redirect to object storage", int(res.status_code)
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def databases_of(body: dict[str, Any]) -> list[Database]:
|
|
239
|
+
"""Exactly the families the server listed for THIS key, in the order it listed them.
|
|
240
|
+
|
|
241
|
+
Nothing is added here, and nothing may be: a family built for a single customer is
|
|
242
|
+
absent from this array for every organization that does not license it, so filling a
|
|
243
|
+
gap from any other source would publish the fact that the family exists.
|
|
244
|
+
"""
|
|
245
|
+
return [to_database(d) for d in body["databases"]]
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def downloads_of(body: dict[str, Any]) -> list[Download]:
|
|
249
|
+
return [to_download(d) for d in body["downloads"]]
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def checksums_of(body: dict[str, Any]) -> dict[str, str]:
|
|
253
|
+
"""The digests, unwrapped one level past the envelope.
|
|
254
|
+
|
|
255
|
+
The response carries `id` and `format` beside them, so reading a top-level `sha256`
|
|
256
|
+
finds nothing. That exact mistake shipped in another binding's 1.0.x, which is why
|
|
257
|
+
the shared corpus pins the depth.
|
|
258
|
+
"""
|
|
259
|
+
return dict(body["checksums"])
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def retry_delay(err: InternetDataError, attempt: int, retries: int) -> float | None:
|
|
263
|
+
"""How long to wait before attempt `attempt + 1`, or None when there must not be one.
|
|
264
|
+
|
|
265
|
+
A server-supplied `Retry-After` wins over the backoff schedule outright: it is the
|
|
266
|
+
only thing that makes a 429 worth retrying at all, so second-guessing it with a
|
|
267
|
+
shorter wait would just spend the next attempt on the same rejection.
|
|
268
|
+
"""
|
|
269
|
+
if attempt >= retries or not err.retryable:
|
|
270
|
+
return None
|
|
271
|
+
if err.retry_after_seconds is not None:
|
|
272
|
+
return err.retry_after_seconds
|
|
273
|
+
return _BACKOFF_BASE * (2.0**attempt)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
# The generated Response declares a plain MutableMapping, but always carries httpx's
|
|
277
|
+
# case-insensitive Headers. Rebuilding one keeps a header lookup case-blind whichever it
|
|
278
|
+
# turns out to be, which matters for `Retry-After`.
|
|
279
|
+
def _headers(res: Response[Any]) -> httpx.Headers:
|
|
280
|
+
return httpx.Headers(res.headers)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _decode(content: bytes) -> Any:
|
|
284
|
+
try:
|
|
285
|
+
return json.loads(content)
|
|
286
|
+
except ValueError:
|
|
287
|
+
return None
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Contains methods for accessing the API"""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Contains endpoint functions for accessing the API"""
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
from http import HTTPStatus
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
from ... import errors
|
|
7
|
+
from ...client import AuthenticatedClient, Client
|
|
8
|
+
from ...models.database_checksum_v2_format import DatabaseChecksumV2Format
|
|
9
|
+
from ...models.database_checksum_v2_response_200 import DatabaseChecksumV2Response200
|
|
10
|
+
from ...models.error import Error
|
|
11
|
+
from ...types import UNSET, Response
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _get_kwargs(
|
|
15
|
+
*,
|
|
16
|
+
id: str,
|
|
17
|
+
format_: DatabaseChecksumV2Format,
|
|
18
|
+
) -> dict[str, Any]:
|
|
19
|
+
|
|
20
|
+
params: dict[str, Any] = {}
|
|
21
|
+
|
|
22
|
+
params["id"] = id
|
|
23
|
+
|
|
24
|
+
json_format_ = format_.value
|
|
25
|
+
params["format"] = json_format_
|
|
26
|
+
|
|
27
|
+
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
|
|
28
|
+
|
|
29
|
+
_kwargs: dict[str, Any] = {
|
|
30
|
+
"method": "get",
|
|
31
|
+
"url": "/api/v2/database/checksum",
|
|
32
|
+
"params": params,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return _kwargs
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _parse_response(
|
|
39
|
+
*, client: AuthenticatedClient | Client, response: httpx.Response
|
|
40
|
+
) -> DatabaseChecksumV2Response200 | Error | None:
|
|
41
|
+
if response.status_code == 200:
|
|
42
|
+
response_200 = DatabaseChecksumV2Response200.from_dict(response.json())
|
|
43
|
+
|
|
44
|
+
return response_200
|
|
45
|
+
|
|
46
|
+
if response.status_code == 400:
|
|
47
|
+
response_400 = Error.from_dict(response.json())
|
|
48
|
+
|
|
49
|
+
return response_400
|
|
50
|
+
|
|
51
|
+
if response.status_code == 401:
|
|
52
|
+
response_401 = Error.from_dict(response.json())
|
|
53
|
+
|
|
54
|
+
return response_401
|
|
55
|
+
|
|
56
|
+
if response.status_code == 403:
|
|
57
|
+
response_403 = Error.from_dict(response.json())
|
|
58
|
+
|
|
59
|
+
return response_403
|
|
60
|
+
|
|
61
|
+
if response.status_code == 404:
|
|
62
|
+
response_404 = Error.from_dict(response.json())
|
|
63
|
+
|
|
64
|
+
return response_404
|
|
65
|
+
|
|
66
|
+
if response.status_code == 503:
|
|
67
|
+
response_503 = Error.from_dict(response.json())
|
|
68
|
+
|
|
69
|
+
return response_503
|
|
70
|
+
|
|
71
|
+
if client.raise_on_unexpected_status:
|
|
72
|
+
raise errors.UnexpectedStatus(response.status_code, response.content)
|
|
73
|
+
else:
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _build_response(
|
|
78
|
+
*, client: AuthenticatedClient | Client, response: httpx.Response
|
|
79
|
+
) -> Response[DatabaseChecksumV2Response200 | Error]:
|
|
80
|
+
return Response(
|
|
81
|
+
status_code=HTTPStatus(response.status_code),
|
|
82
|
+
content=response.content,
|
|
83
|
+
headers=response.headers,
|
|
84
|
+
parsed=_parse_response(client=client, response=response),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def sync_detailed(
|
|
89
|
+
*,
|
|
90
|
+
client: AuthenticatedClient,
|
|
91
|
+
id: str,
|
|
92
|
+
format_: DatabaseChecksumV2Format,
|
|
93
|
+
) -> Response[DatabaseChecksumV2Response200 | Error]:
|
|
94
|
+
"""Checksums for one published file, to verify a download
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
id (str): Example: vpn_ip_v1.
|
|
98
|
+
format_ (DatabaseChecksumV2Format): Example: mmdb.
|
|
99
|
+
|
|
100
|
+
Raises:
|
|
101
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
102
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Response[DatabaseChecksumV2Response200 | Error]
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
kwargs = _get_kwargs(
|
|
109
|
+
id=id,
|
|
110
|
+
format_=format_,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
response = client.get_httpx_client().request(
|
|
114
|
+
**kwargs,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return _build_response(client=client, response=response)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def sync(
|
|
121
|
+
*,
|
|
122
|
+
client: AuthenticatedClient,
|
|
123
|
+
id: str,
|
|
124
|
+
format_: DatabaseChecksumV2Format,
|
|
125
|
+
) -> DatabaseChecksumV2Response200 | Error | None:
|
|
126
|
+
"""Checksums for one published file, to verify a download
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
id (str): Example: vpn_ip_v1.
|
|
130
|
+
format_ (DatabaseChecksumV2Format): Example: mmdb.
|
|
131
|
+
|
|
132
|
+
Raises:
|
|
133
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
134
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
DatabaseChecksumV2Response200 | Error
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
return sync_detailed(
|
|
141
|
+
client=client,
|
|
142
|
+
id=id,
|
|
143
|
+
format_=format_,
|
|
144
|
+
).parsed
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
async def asyncio_detailed(
|
|
148
|
+
*,
|
|
149
|
+
client: AuthenticatedClient,
|
|
150
|
+
id: str,
|
|
151
|
+
format_: DatabaseChecksumV2Format,
|
|
152
|
+
) -> Response[DatabaseChecksumV2Response200 | Error]:
|
|
153
|
+
"""Checksums for one published file, to verify a download
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
id (str): Example: vpn_ip_v1.
|
|
157
|
+
format_ (DatabaseChecksumV2Format): Example: mmdb.
|
|
158
|
+
|
|
159
|
+
Raises:
|
|
160
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
161
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Response[DatabaseChecksumV2Response200 | Error]
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
kwargs = _get_kwargs(
|
|
168
|
+
id=id,
|
|
169
|
+
format_=format_,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
response = await client.get_async_httpx_client().request(**kwargs)
|
|
173
|
+
|
|
174
|
+
return _build_response(client=client, response=response)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
async def asyncio(
|
|
178
|
+
*,
|
|
179
|
+
client: AuthenticatedClient,
|
|
180
|
+
id: str,
|
|
181
|
+
format_: DatabaseChecksumV2Format,
|
|
182
|
+
) -> DatabaseChecksumV2Response200 | Error | None:
|
|
183
|
+
"""Checksums for one published file, to verify a download
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
id (str): Example: vpn_ip_v1.
|
|
187
|
+
format_ (DatabaseChecksumV2Format): Example: mmdb.
|
|
188
|
+
|
|
189
|
+
Raises:
|
|
190
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
191
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
DatabaseChecksumV2Response200 | Error
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
await asyncio_detailed(
|
|
199
|
+
client=client,
|
|
200
|
+
id=id,
|
|
201
|
+
format_=format_,
|
|
202
|
+
)
|
|
203
|
+
).parsed
|