internetdata 2.9.4__tar.gz → 2.9.6__tar.gz
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-2.9.4 → internetdata-2.9.6}/PKG-INFO +1 -1
- {internetdata-2.9.4 → internetdata-2.9.6}/spec/openapi.yaml +25 -2
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/__init__.py +1 -1
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/database_v_2/download_database_v2.py +21 -1
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/aio.py +11 -3
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/client.py +11 -3
- {internetdata-2.9.4 → internetdata-2.9.6}/testdata/testdata.json +1 -1
- {internetdata-2.9.4 → internetdata-2.9.6}/tests/helpers.py +5 -2
- {internetdata-2.9.4 → internetdata-2.9.6}/tests/test_oauth.py +44 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/.gitignore +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/LICENSE +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/README.md +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/pyproject.toml +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/scripts/download-spec.sh +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/scripts/generate.sh +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/scripts/publish.sh +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/scripts/v2_subset.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_core.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/__init__.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/__init__.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/account/__init__.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/account/account_identity.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/api_keys/__init__.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/api_keys/account_create_apikey.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/api_keys/account_list_apikeys.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/api_keys/account_reveal_apikey.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/api_keys/account_revoke_apikey.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/api_keys/account_rotate_apikey.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/authorization/__init__.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/authorization/oauth_authorize.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/authorization/oauth_device_authorization.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/authorization/oauth_metadata.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/authorization/oauth_register.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/authorization/oauth_revoke.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/authorization/oauth_token.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/database_v_2/__init__.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/database_v_2/database_checksum_v2.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/database_v_2/database_metadata_v2.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/database_v_2/list_databases.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/database_v_2/list_downloads.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/organization/__init__.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/organization/account_org.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/organization/account_org_members.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/client.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/errors.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/__init__.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/account_create_apikey_request.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/account_created_apikey.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/account_org.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/account_org_ref.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/account_org_wrap.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/account_rc.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/account_revealed_apikey.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/account_user.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/apikey_detail.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/apikey_list.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/client_registration.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/client_registration_request.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_checksum_v2_response_200.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_format.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_license_type_type_1.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_license_type_type_2_type_1.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_license_type_type_3_type_1.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_metadata.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_metadata_column.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_metadata_sample.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_metadata_sample_additional_property_item.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_metadata_sample_size.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_metadata_schema.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_metadata_size.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_version.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/db_checksums.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/device_authorization.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/device_authorization_request.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/download.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/download_outcome.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/error.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/identity.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/list_databases_response_200.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/list_downloads_response_200.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/oauth_authorize_code_challenge_method.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/oauth_authorize_response_type.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/oauth_error.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/oauth_metadata.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/oauth_revoke_response_200.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/revoke_request.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/standing.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/token_request.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/token_response.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/types.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/errors.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/models.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/py.typed +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/tests/conftest.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/tests/test_client.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/tests/test_conformance.py +0 -0
- {internetdata-2.9.4 → internetdata-2.9.6}/tests/test_download.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: internetdata
|
|
3
|
-
Version: 2.9.
|
|
3
|
+
Version: 2.9.6
|
|
4
4
|
Summary: Official Python client library for the InternetData API. Download and verify licensed IP datasets.
|
|
5
5
|
Project-URL: Homepage, https://internetdata.io
|
|
6
6
|
Project-URL: Documentation, https://docs.internetdata.io
|
|
@@ -12,7 +12,7 @@ info:
|
|
|
12
12
|
email: support@internetdata.io
|
|
13
13
|
url: https://internetdata.io
|
|
14
14
|
termsOfService: https://internetdata.io/terms
|
|
15
|
-
version: 2026.11.
|
|
15
|
+
version: 2026.11.05
|
|
16
16
|
servers:
|
|
17
17
|
- url: https://internetdata.io
|
|
18
18
|
tags:
|
|
@@ -99,6 +99,7 @@ paths:
|
|
|
99
99
|
parameters:
|
|
100
100
|
- $ref: '#/components/parameters/DbId'
|
|
101
101
|
- $ref: '#/components/parameters/DbFormat'
|
|
102
|
+
- $ref: '#/components/parameters/DbBuild'
|
|
102
103
|
responses:
|
|
103
104
|
'302':
|
|
104
105
|
description: Follow the Location header to object storage.
|
|
@@ -107,7 +108,9 @@ paths:
|
|
|
107
108
|
schema:
|
|
108
109
|
type: string
|
|
109
110
|
'400':
|
|
110
|
-
description:
|
|
111
|
+
description: |
|
|
112
|
+
The database is not built in the requested format (`INVALID_FORMAT`),
|
|
113
|
+
or `build` is not a build's name (`INVALID_BUILD`).
|
|
111
114
|
content:
|
|
112
115
|
application/json:
|
|
113
116
|
schema:
|
|
@@ -118,6 +121,14 @@ paths:
|
|
|
118
121
|
$ref: '#/components/responses/V2Forbidden'
|
|
119
122
|
'404':
|
|
120
123
|
$ref: '#/components/responses/V2UnknownDatabase'
|
|
124
|
+
'410':
|
|
125
|
+
description: |
|
|
126
|
+
The build named in `build` is no longer kept (`BUILD_GONE`): builds are
|
|
127
|
+
kept 7 days. Download without `build` for the live one.
|
|
128
|
+
content:
|
|
129
|
+
application/json:
|
|
130
|
+
schema:
|
|
131
|
+
$ref: '#/components/schemas/Error'
|
|
121
132
|
'503':
|
|
122
133
|
$ref: '#/components/responses/V2NotAvailable'
|
|
123
134
|
/api/v2/database/metadata:
|
|
@@ -932,6 +943,18 @@ components:
|
|
|
932
943
|
description: Database file format.
|
|
933
944
|
schema:
|
|
934
945
|
$ref: '#/components/schemas/DatabaseFormat'
|
|
946
|
+
DbBuild:
|
|
947
|
+
name: build
|
|
948
|
+
in: query
|
|
949
|
+
required: false
|
|
950
|
+
description: |
|
|
951
|
+
One build of the database, by the name a `database.updated` webhook gives
|
|
952
|
+
it, so the file is the one whose `sha256` the webhook carries. Without
|
|
953
|
+
it, the build that is live.
|
|
954
|
+
schema:
|
|
955
|
+
type: string
|
|
956
|
+
pattern: ^[0-9]{8}\.[a-z0-9]{6}$
|
|
957
|
+
example: 20261105.0t53s5
|
|
935
958
|
ApikeyIdParam:
|
|
936
959
|
name: id
|
|
937
960
|
in: path
|
|
@@ -7,13 +7,14 @@ from ... import errors
|
|
|
7
7
|
from ...client import AuthenticatedClient, Client
|
|
8
8
|
from ...models.database_format import DatabaseFormat
|
|
9
9
|
from ...models.error import Error
|
|
10
|
-
from ...types import UNSET, Response
|
|
10
|
+
from ...types import UNSET, Response, Unset
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def _get_kwargs(
|
|
14
14
|
*,
|
|
15
15
|
id: str,
|
|
16
16
|
format_: DatabaseFormat,
|
|
17
|
+
build: str | Unset = UNSET,
|
|
17
18
|
) -> dict[str, Any]:
|
|
18
19
|
|
|
19
20
|
params: dict[str, Any] = {}
|
|
@@ -23,6 +24,8 @@ def _get_kwargs(
|
|
|
23
24
|
json_format_ = format_.value
|
|
24
25
|
params["format"] = json_format_
|
|
25
26
|
|
|
27
|
+
params["build"] = build
|
|
28
|
+
|
|
26
29
|
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
|
|
27
30
|
|
|
28
31
|
_kwargs: dict[str, Any] = {
|
|
@@ -61,6 +64,11 @@ def _parse_response(
|
|
|
61
64
|
|
|
62
65
|
return response_404
|
|
63
66
|
|
|
67
|
+
if response.status_code == 410:
|
|
68
|
+
response_410 = Error.from_dict(response.json())
|
|
69
|
+
|
|
70
|
+
return response_410
|
|
71
|
+
|
|
64
72
|
if response.status_code == 503:
|
|
65
73
|
response_503 = Error.from_dict(response.json())
|
|
66
74
|
|
|
@@ -88,6 +96,7 @@ def sync_detailed(
|
|
|
88
96
|
client: AuthenticatedClient,
|
|
89
97
|
id: str,
|
|
90
98
|
format_: DatabaseFormat,
|
|
99
|
+
build: str | Unset = UNSET,
|
|
91
100
|
) -> Response[Any | Error]:
|
|
92
101
|
"""Download
|
|
93
102
|
|
|
@@ -97,6 +106,7 @@ def sync_detailed(
|
|
|
97
106
|
Args:
|
|
98
107
|
id (str): Example: vpn_ip_v1.
|
|
99
108
|
format_ (DatabaseFormat): A file format a database version is published in. Example: mmdb.
|
|
109
|
+
build (str | Unset): Example: 20261105.0t53s5.
|
|
100
110
|
|
|
101
111
|
Raises:
|
|
102
112
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
@@ -109,6 +119,7 @@ def sync_detailed(
|
|
|
109
119
|
kwargs = _get_kwargs(
|
|
110
120
|
id=id,
|
|
111
121
|
format_=format_,
|
|
122
|
+
build=build,
|
|
112
123
|
)
|
|
113
124
|
|
|
114
125
|
response = client.get_httpx_client().request(
|
|
@@ -123,6 +134,7 @@ def sync(
|
|
|
123
134
|
client: AuthenticatedClient,
|
|
124
135
|
id: str,
|
|
125
136
|
format_: DatabaseFormat,
|
|
137
|
+
build: str | Unset = UNSET,
|
|
126
138
|
) -> Any | Error | None:
|
|
127
139
|
"""Download
|
|
128
140
|
|
|
@@ -132,6 +144,7 @@ def sync(
|
|
|
132
144
|
Args:
|
|
133
145
|
id (str): Example: vpn_ip_v1.
|
|
134
146
|
format_ (DatabaseFormat): A file format a database version is published in. Example: mmdb.
|
|
147
|
+
build (str | Unset): Example: 20261105.0t53s5.
|
|
135
148
|
|
|
136
149
|
Raises:
|
|
137
150
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
@@ -145,6 +158,7 @@ def sync(
|
|
|
145
158
|
client=client,
|
|
146
159
|
id=id,
|
|
147
160
|
format_=format_,
|
|
161
|
+
build=build,
|
|
148
162
|
).parsed
|
|
149
163
|
|
|
150
164
|
|
|
@@ -153,6 +167,7 @@ async def asyncio_detailed(
|
|
|
153
167
|
client: AuthenticatedClient,
|
|
154
168
|
id: str,
|
|
155
169
|
format_: DatabaseFormat,
|
|
170
|
+
build: str | Unset = UNSET,
|
|
156
171
|
) -> Response[Any | Error]:
|
|
157
172
|
"""Download
|
|
158
173
|
|
|
@@ -162,6 +177,7 @@ async def asyncio_detailed(
|
|
|
162
177
|
Args:
|
|
163
178
|
id (str): Example: vpn_ip_v1.
|
|
164
179
|
format_ (DatabaseFormat): A file format a database version is published in. Example: mmdb.
|
|
180
|
+
build (str | Unset): Example: 20261105.0t53s5.
|
|
165
181
|
|
|
166
182
|
Raises:
|
|
167
183
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
@@ -174,6 +190,7 @@ async def asyncio_detailed(
|
|
|
174
190
|
kwargs = _get_kwargs(
|
|
175
191
|
id=id,
|
|
176
192
|
format_=format_,
|
|
193
|
+
build=build,
|
|
177
194
|
)
|
|
178
195
|
|
|
179
196
|
response = await client.get_async_httpx_client().request(**kwargs)
|
|
@@ -186,6 +203,7 @@ async def asyncio(
|
|
|
186
203
|
client: AuthenticatedClient,
|
|
187
204
|
id: str,
|
|
188
205
|
format_: DatabaseFormat,
|
|
206
|
+
build: str | Unset = UNSET,
|
|
189
207
|
) -> Any | Error | None:
|
|
190
208
|
"""Download
|
|
191
209
|
|
|
@@ -195,6 +213,7 @@ async def asyncio(
|
|
|
195
213
|
Args:
|
|
196
214
|
id (str): Example: vpn_ip_v1.
|
|
197
215
|
format_ (DatabaseFormat): A file format a database version is published in. Example: mmdb.
|
|
216
|
+
build (str | Unset): Example: 20261105.0t53s5.
|
|
198
217
|
|
|
199
218
|
Raises:
|
|
200
219
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
@@ -209,5 +228,6 @@ async def asyncio(
|
|
|
209
228
|
client=client,
|
|
210
229
|
id=id,
|
|
211
230
|
format_=format_,
|
|
231
|
+
build=build,
|
|
212
232
|
)
|
|
213
233
|
).parsed
|
|
@@ -478,9 +478,17 @@ class AsyncOauthApi:
|
|
|
478
478
|
while True:
|
|
479
479
|
# No wait runs past the deadline: an interval ending after it waits only the
|
|
480
480
|
# time left, and the expiry follows with nothing sent. In full, an interval of
|
|
481
|
-
# 2147483647 held a poll with 2 s left for 68 years (2.6.0).
|
|
482
|
-
|
|
483
|
-
|
|
481
|
+
# 2147483647 held a poll with 2 s left for 68 years (2.6.0). A remainder not
|
|
482
|
+
# above 0 is none left: a hand-built `expires_in` of NaN, which compares false
|
|
483
|
+
# with everything, polled every interval for good (2.9.5). `max(0, ...)` is 0
|
|
484
|
+
# for NaN only in that order; `max(nan, 0)` is NaN.
|
|
485
|
+
left = max(0, deadline - self._clock.now())
|
|
486
|
+
to_deadline = left <= interval
|
|
487
|
+
await self._clock.sleep(left if to_deadline else interval)
|
|
488
|
+
# A wait that ran to the deadline ends the poll without reading the clock again:
|
|
489
|
+
# a timer may wake before the clock has moved the whole wait, and the deadline
|
|
490
|
+
# would then read as not yet reached, so one more request would go out at it.
|
|
491
|
+
if to_deadline or self._clock.now() >= deadline:
|
|
484
492
|
raise OauthExpiredTokenError()
|
|
485
493
|
try:
|
|
486
494
|
return await self.exchange_device_code(
|
|
@@ -537,9 +537,17 @@ class OauthApi:
|
|
|
537
537
|
while True:
|
|
538
538
|
# No wait runs past the deadline: an interval ending after it waits only the
|
|
539
539
|
# time left, and the expiry follows with nothing sent. In full, an interval of
|
|
540
|
-
# 2147483647 held a poll with 2 s left for 68 years (2.6.0).
|
|
541
|
-
|
|
542
|
-
|
|
540
|
+
# 2147483647 held a poll with 2 s left for 68 years (2.6.0). A remainder not
|
|
541
|
+
# above 0 is none left: a hand-built `expires_in` of NaN, which compares false
|
|
542
|
+
# with everything, polled every interval for good (2.9.5). `max(0, ...)` is 0
|
|
543
|
+
# for NaN only in that order; `max(nan, 0)` is NaN.
|
|
544
|
+
left = max(0, deadline - self._clock.now())
|
|
545
|
+
to_deadline = left <= interval
|
|
546
|
+
self._clock.sleep(left if to_deadline else interval)
|
|
547
|
+
# A wait that ran to the deadline ends the poll without reading the clock again:
|
|
548
|
+
# a timer may wake before the clock has moved the whole wait, and the deadline
|
|
549
|
+
# would then read as not yet reached, so one more request would go out at it.
|
|
550
|
+
if to_deadline or self._clock.now() >= deadline:
|
|
543
551
|
raise OauthExpiredTokenError()
|
|
544
552
|
try:
|
|
545
553
|
return self.exchange_device_code(client_id, device.device_code, timeout=timeout)
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
"mmdb"
|
|
151
151
|
],
|
|
152
152
|
"visibility": {
|
|
153
|
-
"why": "A private family is one built for a single customer. It must be ABSENT from a listing for an org that does not license it, not present with standing 'unlicensed' - the standing field is the discovery surface for public datasets only. The server enforces this; an SDK must not reconstruct a catalog from any other source and must not assume the listing is the same for every key. The private
|
|
153
|
+
"why": "A private family is one built for a single customer. It must be ABSENT from a listing for an org that does not license it, not present with standing 'unlicensed' - the standing field is the discovery surface for public datasets only. The server enforces this; an SDK must not reconstruct a catalog from any other source and must not assume the listing is the same for every key. The private IDs are deliberately NOT named here: this corpus is committed into twelve PUBLIC repositories, so naming them would publish the customer relationship the rule exists to keep private.",
|
|
154
154
|
"clientRules": [
|
|
155
155
|
"listing-is-returned-as-served",
|
|
156
156
|
"no-catalog-is-compiled-into-the-client",
|
|
@@ -222,11 +222,14 @@ class FakeClock(Clock):
|
|
|
222
222
|
"""A poll's sleep and monotonic clock, replaced together, recording every wait in
|
|
223
223
|
seconds. Past LOOP_BOUND waits, or twice that many clock reads, it trips `bound`."""
|
|
224
224
|
|
|
225
|
-
def __init__(self, bound: LoopBound) -> None:
|
|
225
|
+
def __init__(self, bound: LoopBound, short: float = 0.0) -> None:
|
|
226
226
|
self.waits: list[float] = []
|
|
227
227
|
self._elapsed = 0.0
|
|
228
228
|
self._reads = 0
|
|
229
229
|
self._bound = bound
|
|
230
|
+
# Each wait of at least 1 ms wakes this many seconds before the clock has moved
|
|
231
|
+
# its delay, as a runtime timer may.
|
|
232
|
+
self._short = short
|
|
230
233
|
|
|
231
234
|
def now(self) -> float:
|
|
232
235
|
self._reads += 1
|
|
@@ -238,7 +241,7 @@ class FakeClock(Clock):
|
|
|
238
241
|
if len(self.waits) == LOOP_BOUND:
|
|
239
242
|
self._bound.trip(f"waited more than {LOOP_BOUND} times")
|
|
240
243
|
self.waits.append(seconds)
|
|
241
|
-
self._elapsed += seconds
|
|
244
|
+
self._elapsed += seconds - self._short if seconds >= 0.001 else seconds
|
|
242
245
|
|
|
243
246
|
|
|
244
247
|
class AsyncFakeClock(AsyncClock):
|
|
@@ -9,6 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
import asyncio
|
|
11
11
|
import json
|
|
12
|
+
import math
|
|
12
13
|
import re
|
|
13
14
|
import time
|
|
14
15
|
import urllib.parse
|
|
@@ -389,6 +390,49 @@ def test_an_expires_in_past_any_float_still_polls(make_client: ClientFactory) ->
|
|
|
389
390
|
assert clock.waits == [1]
|
|
390
391
|
|
|
391
392
|
|
|
393
|
+
@pytest.mark.parametrize("interval", [5, 3])
|
|
394
|
+
def test_a_wait_that_ran_to_the_deadline_ends_the_poll_however_early_the_timer_woke(
|
|
395
|
+
make_client: ClientFactory, interval: int
|
|
396
|
+
) -> None:
|
|
397
|
+
"""A timer that wakes before the clock has moved its delay leaves the deadline reading as
|
|
398
|
+
not yet reached, so a poll that read the clock again would ask once more at it. A wait
|
|
399
|
+
cut to the deadline, or ending at it, ends the poll."""
|
|
400
|
+
bound = LoopBound()
|
|
401
|
+
stub = OauthStub([{"status": 400, "body": {"error": "authorization_pending"}}], bound)
|
|
402
|
+
client = make_client(base_url=BASE_URL, transport=stub.transport)
|
|
403
|
+
clock = FakeClock(bound, short=0.0005)
|
|
404
|
+
client.oauth.use_clock(clock)
|
|
405
|
+
device = DeviceAuthorization(**{**_device_fields(), "expires_in": 3, "interval": interval})
|
|
406
|
+
|
|
407
|
+
outcome = settle(lambda: client.oauth.poll_device_token("internetdata-cli", device), bound)
|
|
408
|
+
|
|
409
|
+
assert isinstance(outcome, OauthExpiredTokenError), f"settled with {outcome!r}"
|
|
410
|
+
assert outcome.status is None, "the local deadline, not the server's"
|
|
411
|
+
assert stub.requests == []
|
|
412
|
+
assert clock.waits == [3]
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
@pytest.mark.parametrize("expires_in", [math.nan, -math.nan, math.inf - math.inf])
|
|
416
|
+
def test_a_lifetime_that_is_not_a_number_ends_the_poll_at_once(
|
|
417
|
+
make_client: ClientFactory, expires_in: float
|
|
418
|
+
) -> None:
|
|
419
|
+
"""A hand-built device can carry NaN, which compares false with everything, so the
|
|
420
|
+
deadline was never reached and the poll asked every interval for good."""
|
|
421
|
+
bound = LoopBound()
|
|
422
|
+
stub = OauthStub([{"status": 400, "body": {"error": "authorization_pending"}}], bound)
|
|
423
|
+
client = make_client(base_url=BASE_URL, transport=stub.transport)
|
|
424
|
+
clock = FakeClock(bound)
|
|
425
|
+
client.oauth.use_clock(clock)
|
|
426
|
+
device = DeviceAuthorization(**{**_device_fields(), "expires_in": expires_in, "interval": 1})
|
|
427
|
+
|
|
428
|
+
outcome = settle(lambda: client.oauth.poll_device_token("internetdata-cli", device), bound)
|
|
429
|
+
|
|
430
|
+
assert isinstance(outcome, OauthExpiredTokenError), f"settled with {outcome!r}"
|
|
431
|
+
assert outcome.status is None, "the local deadline, not the server's"
|
|
432
|
+
assert stub.requests == []
|
|
433
|
+
assert clock.waits == [0]
|
|
434
|
+
|
|
435
|
+
|
|
392
436
|
def _device_fields() -> dict[str, Any]:
|
|
393
437
|
return {
|
|
394
438
|
"device_code": "mo_dc_x",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/account/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/api_keys/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/authorization/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/database_v_2/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/api/organization/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/account_org_ref.py
RENAMED
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/account_org_wrap.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/account_user.py
RENAMED
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/apikey_detail.py
RENAMED
|
File without changes
|
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/client_registration.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_format.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_metadata.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/database_version.py
RENAMED
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/db_checksums.py
RENAMED
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/device_authorization.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/download_outcome.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/oauth_metadata.py
RENAMED
|
File without changes
|
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/revoke_request.py
RENAMED
|
File without changes
|
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/token_request.py
RENAMED
|
File without changes
|
{internetdata-2.9.4 → internetdata-2.9.6}/src/internetdata/_generated/models/token_response.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|