internetdata 2.7.2__tar.gz → 2.7.4__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.
Files changed (95) hide show
  1. {internetdata-2.7.2 → internetdata-2.7.4}/PKG-INFO +1 -1
  2. {internetdata-2.7.2 → internetdata-2.7.4}/spec/openapi.yaml +15 -18
  3. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/__init__.py +1 -1
  4. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_core.py +13 -7
  5. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/database_v_2/list_databases.py +0 -4
  6. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/organization/account_org_members.py +4 -4
  7. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/account_create_apikey_request.py +1 -1
  8. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/apikey_detail.py +1 -1
  9. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/database.py +6 -7
  10. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/database_metadata.py +1 -3
  11. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/download.py +1 -3
  12. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/models.py +5 -6
  13. {internetdata-2.7.2 → internetdata-2.7.4}/tests/test_conformance.py +36 -4
  14. {internetdata-2.7.2 → internetdata-2.7.4}/.gitignore +0 -0
  15. {internetdata-2.7.2 → internetdata-2.7.4}/LICENSE +0 -0
  16. {internetdata-2.7.2 → internetdata-2.7.4}/README.md +0 -0
  17. {internetdata-2.7.2 → internetdata-2.7.4}/pyproject.toml +0 -0
  18. {internetdata-2.7.2 → internetdata-2.7.4}/scripts/download-spec.sh +0 -0
  19. {internetdata-2.7.2 → internetdata-2.7.4}/scripts/generate.sh +0 -0
  20. {internetdata-2.7.2 → internetdata-2.7.4}/scripts/publish.sh +0 -0
  21. {internetdata-2.7.2 → internetdata-2.7.4}/scripts/v2_subset.py +0 -0
  22. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/__init__.py +0 -0
  23. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/__init__.py +0 -0
  24. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/account/__init__.py +0 -0
  25. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/account/account_identity.py +0 -0
  26. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/api_keys/__init__.py +0 -0
  27. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/api_keys/account_create_apikey.py +0 -0
  28. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/api_keys/account_list_apikeys.py +0 -0
  29. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/api_keys/account_reveal_apikey.py +0 -0
  30. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/api_keys/account_revoke_apikey.py +0 -0
  31. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/api_keys/account_rotate_apikey.py +0 -0
  32. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/authorization/__init__.py +0 -0
  33. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/authorization/oauth_authorize.py +0 -0
  34. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/authorization/oauth_device_authorization.py +0 -0
  35. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/authorization/oauth_metadata.py +0 -0
  36. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/authorization/oauth_revoke.py +0 -0
  37. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/authorization/oauth_token.py +0 -0
  38. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/database_v_2/__init__.py +0 -0
  39. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/database_v_2/database_checksum_v2.py +0 -0
  40. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/database_v_2/database_metadata_v2.py +0 -0
  41. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/database_v_2/download_database_v2.py +0 -0
  42. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/database_v_2/list_downloads.py +0 -0
  43. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/organization/__init__.py +0 -0
  44. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/api/organization/account_org.py +0 -0
  45. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/client.py +0 -0
  46. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/errors.py +0 -0
  47. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/__init__.py +0 -0
  48. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/account_created_apikey.py +0 -0
  49. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/account_org.py +0 -0
  50. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/account_org_ref.py +0 -0
  51. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/account_org_wrap.py +0 -0
  52. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/account_rc.py +0 -0
  53. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/account_revealed_apikey.py +0 -0
  54. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/account_user.py +0 -0
  55. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/apikey_list.py +0 -0
  56. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/database_checksum_v2_response_200.py +0 -0
  57. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/database_format.py +0 -0
  58. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/database_license_type_type_1.py +0 -0
  59. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/database_license_type_type_2_type_1.py +0 -0
  60. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/database_license_type_type_3_type_1.py +0 -0
  61. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/database_metadata_column.py +0 -0
  62. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/database_metadata_sample.py +0 -0
  63. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/database_metadata_sample_additional_property_item.py +0 -0
  64. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/database_metadata_sample_size.py +0 -0
  65. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/database_metadata_schema.py +0 -0
  66. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/database_metadata_size.py +0 -0
  67. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/database_version.py +0 -0
  68. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/db_checksums.py +0 -0
  69. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/device_authorization.py +0 -0
  70. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/device_authorization_request.py +0 -0
  71. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/download_outcome.py +0 -0
  72. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/error.py +0 -0
  73. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/identity.py +0 -0
  74. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/list_databases_response_200.py +0 -0
  75. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/list_downloads_response_200.py +0 -0
  76. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/oauth_authorize_code_challenge_method.py +0 -0
  77. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/oauth_authorize_response_type.py +0 -0
  78. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/oauth_error.py +0 -0
  79. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/oauth_metadata.py +0 -0
  80. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/oauth_revoke_response_200.py +0 -0
  81. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/revoke_request.py +0 -0
  82. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/standing.py +0 -0
  83. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/token_request.py +0 -0
  84. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/models/token_response.py +0 -0
  85. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/_generated/types.py +0 -0
  86. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/aio.py +0 -0
  87. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/client.py +0 -0
  88. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/errors.py +0 -0
  89. {internetdata-2.7.2 → internetdata-2.7.4}/src/internetdata/py.typed +0 -0
  90. {internetdata-2.7.2 → internetdata-2.7.4}/testdata/testdata.json +0 -0
  91. {internetdata-2.7.2 → internetdata-2.7.4}/tests/conftest.py +0 -0
  92. {internetdata-2.7.2 → internetdata-2.7.4}/tests/helpers.py +0 -0
  93. {internetdata-2.7.2 → internetdata-2.7.4}/tests/test_client.py +0 -0
  94. {internetdata-2.7.2 → internetdata-2.7.4}/tests/test_download.py +0 -0
  95. {internetdata-2.7.2 → internetdata-2.7.4}/tests/test_oauth.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: internetdata
3
- Version: 2.7.2
3
+ Version: 2.7.4
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.10.09
15
+ version: 2026.10.15
16
16
  servers:
17
17
  - url: https://internetdata.io
18
18
  tags:
@@ -29,8 +29,9 @@ tags:
29
29
 
30
30
  Access is granted by CONTRACT, one dataset family at a time: there is no
31
31
  self-service purchase and no subscription behind it. The Open databases
32
- are the exception: any organization downloads one with its key, licensed
33
- or not, under CC BY-SA 4.0. `list` marks them `open`.
32
+ are licensed to every organization from the start, a `standard` license
33
+ to each, and are free under CC BY-SA 4.0 besides. `list` marks them
34
+ `open`.
34
35
 
35
36
  A successful download answers `302` with a time-limited URL pointing
36
37
  straight at object storage. Follow the redirect (`curl -L`); the link
@@ -60,7 +61,6 @@ paths:
60
61
  The whole published catalog, with your organization's license beside
61
62
  each entry, so `standing` says whether a database is yours today
62
63
  (`licensed`), was (`expired`), or has never been bought (`unlicensed`).
63
- An `open` database downloads whatever its `standing`.
64
64
  security:
65
65
  - ApiKeyQueryAuth: []
66
66
  - ApiKeyHeaderAuth: []
@@ -416,7 +416,7 @@ paths:
416
416
  summary: Members
417
417
  description: |
418
418
  Read-only. Adding or removing a member is an invitation flow with email
419
- in the middle rather than a POST, and modelling it as one here would
419
+ in the middle rather than a POST, and modeling it as one here would
420
420
  promise something this API does not do.
421
421
  security:
422
422
  - BearerAuth:
@@ -902,8 +902,7 @@ components:
902
902
  description: |
903
903
  Where your license for a database family stands today. `licensed` is a
904
904
  live grant, `expired` one whose term has ended, and `unlicensed` a
905
- database published but never bought. An Open database (`open`)
906
- downloads whatever this says.
905
+ database published but never bought.
907
906
  enum:
908
907
  - licensed
909
908
  - expired
@@ -942,11 +941,11 @@ components:
942
941
  open:
943
942
  type: boolean
944
943
  description: |
945
- An Open database: any organization downloads it, and fetches its
946
- checksums, with no license, under CC BY-SA 4.0 (credit
947
- InternetData, https://internetdata.io, and share what you build
948
- from it under the same license). `standing` still reports your
949
- own license, which grants more where you hold one.
944
+ An Open database: free under CC BY-SA 4.0 (credit InternetData,
945
+ https://internetdata.io, and share what you build from it under
946
+ the same license). Every organization holds a `standard` license
947
+ to it from the start, which `standing` and `license_type` report
948
+ like any other.
950
949
  license_type:
951
950
  type: string
952
951
  nullable: true
@@ -1056,9 +1055,7 @@ components:
1056
1055
  open:
1057
1056
  type: boolean
1058
1057
  description: |
1059
- Taken under the Open license rather than one of your licenses: an
1060
- Open database downloaded while your organization held no license
1061
- in term for it.
1058
+ A download of an Open database.
1062
1059
  bytes:
1063
1060
  type: integer
1064
1061
  format: int64
@@ -1368,7 +1365,7 @@ components:
1368
1365
  type: string
1369
1366
  key_prefix:
1370
1367
  type: string
1371
- description: The leading, non-secret part, so a key is recognisable without storing it.
1368
+ description: The leading, non-secret part, so a key is recognizable without storing it.
1372
1369
  created:
1373
1370
  type: string
1374
1371
  format: date-time
@@ -1408,7 +1405,7 @@ components:
1408
1405
  properties:
1409
1406
  name:
1410
1407
  type: string
1411
- description: A label you will recognise later. Shown wherever the key is listed.
1408
+ description: A label you will recognize later. Shown wherever the key is listed.
1412
1409
  allowed_scopes:
1413
1410
  type: array
1414
1411
  items:
@@ -1647,7 +1644,7 @@ components:
1647
1644
  V2NotLicensed:
1648
1645
  description: |
1649
1646
  Your organization holds no license for this database, or its term has
1650
- ended. Never for an Open database.
1647
+ ended.
1651
1648
  `rc`: `NOT_LICENSED`, `LICENSE_EXPIRED`.
1652
1649
  content:
1653
1650
  application/json:
@@ -39,7 +39,7 @@ from .models import (
39
39
  TokenResponse,
40
40
  )
41
41
 
42
- __version__ = "2.7.2"
42
+ __version__ = "2.7.4"
43
43
 
44
44
  __all__ = [
45
45
  "DATABASE_FORMATS",
@@ -81,6 +81,11 @@ def build_client(
81
81
  the `Authorization` header when it CONSTRUCTS the httpx client itself.
82
82
  """
83
83
  api_key = check_api_key(api_key)
84
+ # httpx drops one trailing slash when it joins a path onto the base URL and keeps the
85
+ # rest, so a second or third reached the API as `//api/v2/database/list`, which prod
86
+ # answers with a redirect this client does not follow: every call failed (2.7.3,
87
+ # measured 2026-10-16).
88
+ base_url = base_url.rstrip("/")
84
89
  httpx_args: dict[str, Any] = {}
85
90
  if transport is not None:
86
91
  httpx_args["transport"] = transport
@@ -135,19 +140,20 @@ def build_async_transfer_client(
135
140
  def check_api_key(api_key: str | None) -> str | None:
136
141
  """`api_key` without the blanks around it, or None when nothing else is left.
137
142
 
138
- Refused where it is SET when a control character remains: httpx refuses a header
139
- holding a CR, LF or NUL, so every call failed as a retried `network` error with nothing
140
- sent, while a tab or DEL inside the key went out on the wire. Untrimmed, a key of blanks
141
- was sent as one rather than as no key, and failed every call the same way (2.7.1,
142
- measured 2026-10-12).
143
+ Refused where it is SET when a control character remains, or a character outside
144
+ ASCII, which httpx cannot put in a header: a CR, LF or NUL failed every call as a
145
+ retried `network` error with nothing sent, a C1 control or any other non-ASCII
146
+ character as a raw `UnicodeEncodeError` (2.7.3, measured 2026-10-16), while a tab or
147
+ DEL went out on the wire. Untrimmed, a key of blanks was sent as one rather than as no
148
+ key, and failed every call the same way (2.7.1, measured 2026-10-12).
143
149
  """
144
150
  if api_key is None:
145
151
  return None
146
152
  if not isinstance(api_key, str):
147
153
  raise TypeError(f"api_key must be a string or None, not {type(api_key).__name__}")
148
154
  key = api_key.strip()
149
- if any(ord(char) < 0x20 or ord(char) == 0x7F for char in key):
150
- raise ValueError("api_key holds a control character")
155
+ if any(ord(char) < 0x20 or ord(char) >= 0x7F for char in key):
156
+ raise ValueError("api_key holds a control character or one outside ASCII")
151
157
  return key or None
152
158
 
153
159
 
@@ -59,7 +59,6 @@ def sync_detailed(
59
59
  The whole published catalog, with your organization's license beside
60
60
  each entry, so `standing` says whether a database is yours today
61
61
  (`licensed`), was (`expired`), or has never been bought (`unlicensed`).
62
- An `open` database downloads whatever its `standing`.
63
62
 
64
63
  Raises:
65
64
  errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
@@ -87,7 +86,6 @@ def sync(
87
86
  The whole published catalog, with your organization's license beside
88
87
  each entry, so `standing` says whether a database is yours today
89
88
  (`licensed`), was (`expired`), or has never been bought (`unlicensed`).
90
- An `open` database downloads whatever its `standing`.
91
89
 
92
90
  Raises:
93
91
  errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
@@ -111,7 +109,6 @@ async def asyncio_detailed(
111
109
  The whole published catalog, with your organization's license beside
112
110
  each entry, so `standing` says whether a database is yours today
113
111
  (`licensed`), was (`expired`), or has never been bought (`unlicensed`).
114
- An `open` database downloads whatever its `standing`.
115
112
 
116
113
  Raises:
117
114
  errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
@@ -137,7 +134,6 @@ async def asyncio(
137
134
  The whole published catalog, with your organization's license beside
138
135
  each entry, so `standing` says whether a database is yours today
139
136
  (`licensed`), was (`expired`), or has never been bought (`unlicensed`).
140
- An `open` database downloads whatever its `standing`.
141
137
 
142
138
  Raises:
143
139
  errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
@@ -61,7 +61,7 @@ def sync_detailed(
61
61
  """Members
62
62
 
63
63
  Read-only. Adding or removing a member is an invitation flow with email
64
- in the middle rather than a POST, and modelling it as one here would
64
+ in the middle rather than a POST, and modeling it as one here would
65
65
  promise something this API does not do.
66
66
 
67
67
  Raises:
@@ -88,7 +88,7 @@ def sync(
88
88
  """Members
89
89
 
90
90
  Read-only. Adding or removing a member is an invitation flow with email
91
- in the middle rather than a POST, and modelling it as one here would
91
+ in the middle rather than a POST, and modeling it as one here would
92
92
  promise something this API does not do.
93
93
 
94
94
  Raises:
@@ -111,7 +111,7 @@ async def asyncio_detailed(
111
111
  """Members
112
112
 
113
113
  Read-only. Adding or removing a member is an invitation flow with email
114
- in the middle rather than a POST, and modelling it as one here would
114
+ in the middle rather than a POST, and modeling it as one here would
115
115
  promise something this API does not do.
116
116
 
117
117
  Raises:
@@ -136,7 +136,7 @@ async def asyncio(
136
136
  """Members
137
137
 
138
138
  Read-only. Adding or removing a member is an invitation flow with email
139
- in the middle rather than a POST, and modelling it as one here would
139
+ in the middle rather than a POST, and modeling it as one here would
140
140
  promise something this API does not do.
141
141
 
142
142
  Raises:
@@ -15,7 +15,7 @@ T = TypeVar("T", bound="AccountCreateApikeyRequest")
15
15
  class AccountCreateApikeyRequest:
16
16
  """
17
17
  Attributes:
18
- name (str): A label you will recognise later. Shown wherever the key is listed.
18
+ name (str): A label you will recognize later. Shown wherever the key is listed.
19
19
  allowed_scopes (list[str] | Unset): What the new key may do. Omit for a key that carries no named scope, which
20
20
  is the safe default.
21
21
  """
@@ -20,7 +20,7 @@ class ApikeyDetail:
20
20
  Attributes:
21
21
  id (UUID):
22
22
  name (str):
23
- key_prefix (str): The leading, non-secret part, so a key is recognisable without storing it.
23
+ key_prefix (str): The leading, non-secret part, so a key is recognizable without storing it.
24
24
  created (datetime.datetime):
25
25
  expires (datetime.datetime | None | Unset):
26
26
  last_used_at (datetime.datetime | None | Unset):
@@ -31,13 +31,12 @@ class Database:
31
31
  summary (str): One line on what the newest version contains.
32
32
  standing (Standing): Where your license for a database family stands today. `licensed` is a
33
33
  live grant, `expired` one whose term has ended, and `unlicensed` a
34
- database published but never bought. An Open database (`open`)
35
- downloads whatever this says.
36
- open_ (bool): An Open database: any organization downloads it, and fetches its
37
- checksums, with no license, under CC BY-SA 4.0 (credit
38
- InternetData, https://internetdata.io, and share what you build
39
- from it under the same license). `standing` still reports your
40
- own license, which grants more where you hold one.
34
+ database published but never bought.
35
+ open_ (bool): An Open database: free under CC BY-SA 4.0 (credit InternetData,
36
+ https://internetdata.io, and share what you build from it under
37
+ the same license). Every organization holds a `standard` license
38
+ to it from the start, which `standing` and `license_type` report
39
+ like any other.
41
40
  license_type (DatabaseLicenseTypeType1 | DatabaseLicenseTypeType2Type1 | DatabaseLicenseTypeType3Type1 | None):
42
41
  What your license permits you to do with the data. Null when there
43
42
  is no license, which is every family with standing `unlicensed`.
@@ -95,9 +95,7 @@ class DatabaseMetadata:
95
95
  @classmethod
96
96
  def from_dict(cls, src_dict: Mapping[str, Any]) -> Self:
97
97
  from ..models.database_metadata_sample import DatabaseMetadataSample
98
- from ..models.database_metadata_sample_size import (
99
- DatabaseMetadataSampleSize,
100
- )
98
+ from ..models.database_metadata_sample_size import DatabaseMetadataSampleSize
101
99
  from ..models.database_metadata_schema import DatabaseMetadataSchema
102
100
  from ..models.database_metadata_size import DatabaseMetadataSize
103
101
 
@@ -21,9 +21,7 @@ class Download:
21
21
  format_ (str):
22
22
  outcome (DownloadOutcome):
23
23
  sample (bool): The evaluation sample rather than the database itself.
24
- open_ (bool): Taken under the Open license rather than one of your licenses: an
25
- Open database downloaded while your organization held no license
26
- in term for it.
24
+ open_ (bool): A download of an Open database.
27
25
  bytes_ (int | None): Object size at redirect time, NOT bytes delivered: the transfer is a
28
26
  presigned redirect straight to object storage, so we never observe
29
27
  how much of it was taken.
@@ -86,9 +86,9 @@ class Database:
86
86
  """One database FAMILY, with your organization's license beside it.
87
87
 
88
88
  A family your organization has never licensed is still listed, with `standing` set to
89
- `unlicensed`, so you can see what else exists. One that is `open` downloads with no
90
- license at all, under CC BY-SA 4.0, whatever its `standing`, which still reports your
91
- own license.
89
+ `unlicensed`, so you can see what else exists. One that is `open` is free under
90
+ CC BY-SA 4.0, and every organization holds a `standard` license to it from the start,
91
+ which `standing` and `license_type` report like any other.
92
92
 
93
93
  A rolling license carries `renews_at`, when it next renews, and `notice_due_at`, the
94
94
  last day notice of non-renewal can be given for the term ending then. Both are None
@@ -167,9 +167,8 @@ class Download:
167
167
  # with a default, so a Download built by hand before 2.5.0 still builds; the parser
168
168
  # always sets it.
169
169
  sample: bool = field(default=False, kw_only=True)
170
- # Taken under the Open license rather than one of your licenses: an Open database
171
- # downloaded while your organization held no license in term for it. Keyword-only
172
- # with a default, so a Download built by hand before 2.7.0 still builds.
170
+ # A download of an Open database. Keyword-only with a default, so a Download built
171
+ # by hand before 2.7.0 still builds.
173
172
  open: bool = field(default=False, kw_only=True)
174
173
 
175
174
 
@@ -274,12 +274,44 @@ def test_the_api_key_is_trimmed(make_client: ClientFactory) -> None:
274
274
 
275
275
 
276
276
  @pytest.mark.parametrize(
277
- "api_key", ["key\r\nX-Injected: 1", "ke\ty", "key\x00", "key\x7f", "k\ney"]
277
+ "api_key", ["key\r\nX-Injected: 1", "ke\ty", "key\x00", "key\x7f", "k\ney", "k\x85ey", "kéy"]
278
278
  )
279
- def test_a_control_character_in_the_api_key_is_refused_where_it_is_set(
279
+ def test_a_control_character_or_one_outside_ascii_is_refused_where_the_key_is_set(
280
280
  make_client: ClientFactory, api_key: str
281
281
  ) -> None:
282
282
  """Accepted, a CR, LF or NUL failed every call as a retried network error with nothing
283
- sent, and a tab or DEL went out on the wire inside the header (2.7.1)."""
284
- with pytest.raises(ValueError, match="control character"):
283
+ sent, and a tab or DEL went out on the wire inside the header (2.7.1). A C1 control or
284
+ any other character outside ASCII failed every call with a raw UnicodeEncodeError and
285
+ nothing sent, since httpx encodes a header value as ASCII (2.7.3)."""
286
+ with pytest.raises(ValueError, match="control character or one outside ASCII"):
285
287
  make_client(api_key=api_key)
288
+
289
+
290
+ @pytest.mark.parametrize("slashes", [1, 2, 3])
291
+ def test_every_trailing_slash_on_the_base_url_is_dropped(
292
+ make_client: ClientFactory, slashes: int
293
+ ) -> None:
294
+ """httpx drops one when it joins a path on and keeps the rest, so through 2.7.3 a
295
+ second reached the API as `//api/v2/database/list`, which prod answers with a
296
+ redirect this client does not follow."""
297
+ digests = {"md5": "m", "sha1": "s1", "sha256": "s256", "sha512": "s512"}
298
+ stub = Stub(
299
+ {
300
+ LIST_PATH: {"body": {"databases": []}},
301
+ CHECKSUM_PATH: {"body": {"id": "bogon_ip_v1", "format": "csvgz", "checksums": digests}},
302
+ "/.well-known/oauth-authorization-server": {
303
+ "body": {
304
+ "issuer": "https://api.example",
305
+ "authorization_endpoint": "https://api.example/oauth/authorize",
306
+ "token_endpoint": "https://api.example/oauth/token",
307
+ }
308
+ },
309
+ }
310
+ )
311
+ client = make_client(transport=stub.transport, base_url="https://api.example" + "/" * slashes)
312
+
313
+ client.database.list()
314
+ client.database.checksums("bogon_ip_v1", "csvgz")
315
+ client.oauth.metadata()
316
+
317
+ assert stub.paths() == [LIST_PATH, CHECKSUM_PATH, "/.well-known/oauth-authorization-server"]
File without changes
File without changes
File without changes