internetdata 2.6.1__tar.gz → 2.7.0__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.6.1 → internetdata-2.7.0}/PKG-INFO +3 -3
  2. {internetdata-2.6.1 → internetdata-2.7.0}/README.md +2 -2
  3. {internetdata-2.6.1 → internetdata-2.7.0}/spec/openapi.yaml +27 -8
  4. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/__init__.py +1 -1
  5. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/database_v_2/database_metadata_v2.py +8 -9
  6. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/database_v_2/list_databases.py +4 -0
  7. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/database.py +14 -1
  8. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/download.py +10 -0
  9. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/models.py +12 -1
  10. {internetdata-2.6.1 → internetdata-2.7.0}/tests/helpers.py +1 -0
  11. {internetdata-2.6.1 → internetdata-2.7.0}/tests/test_client.py +16 -0
  12. {internetdata-2.6.1 → internetdata-2.7.0}/.gitignore +0 -0
  13. {internetdata-2.6.1 → internetdata-2.7.0}/LICENSE +0 -0
  14. {internetdata-2.6.1 → internetdata-2.7.0}/pyproject.toml +0 -0
  15. {internetdata-2.6.1 → internetdata-2.7.0}/scripts/download-spec.sh +0 -0
  16. {internetdata-2.6.1 → internetdata-2.7.0}/scripts/generate.sh +0 -0
  17. {internetdata-2.6.1 → internetdata-2.7.0}/scripts/publish.sh +0 -0
  18. {internetdata-2.6.1 → internetdata-2.7.0}/scripts/v2_subset.py +0 -0
  19. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_core.py +0 -0
  20. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/__init__.py +0 -0
  21. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/__init__.py +0 -0
  22. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/account/__init__.py +0 -0
  23. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/account/account_identity.py +0 -0
  24. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/api_keys/__init__.py +0 -0
  25. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/api_keys/account_create_apikey.py +0 -0
  26. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/api_keys/account_list_apikeys.py +0 -0
  27. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/api_keys/account_reveal_apikey.py +0 -0
  28. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/api_keys/account_revoke_apikey.py +0 -0
  29. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/api_keys/account_rotate_apikey.py +0 -0
  30. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/authorization/__init__.py +0 -0
  31. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/authorization/oauth_authorize.py +0 -0
  32. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/authorization/oauth_device_authorization.py +0 -0
  33. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/authorization/oauth_metadata.py +0 -0
  34. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/authorization/oauth_revoke.py +0 -0
  35. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/authorization/oauth_token.py +0 -0
  36. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/database_v_2/__init__.py +0 -0
  37. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/database_v_2/database_checksum_v2.py +0 -0
  38. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/database_v_2/download_database_v2.py +0 -0
  39. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/database_v_2/list_downloads.py +0 -0
  40. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/organization/__init__.py +0 -0
  41. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/organization/account_org.py +0 -0
  42. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/api/organization/account_org_members.py +0 -0
  43. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/client.py +0 -0
  44. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/errors.py +0 -0
  45. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/__init__.py +0 -0
  46. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/account_create_apikey_request.py +0 -0
  47. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/account_created_apikey.py +0 -0
  48. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/account_org.py +0 -0
  49. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/account_org_ref.py +0 -0
  50. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/account_org_wrap.py +0 -0
  51. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/account_rc.py +0 -0
  52. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/account_revealed_apikey.py +0 -0
  53. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/account_user.py +0 -0
  54. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/apikey_detail.py +0 -0
  55. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/apikey_list.py +0 -0
  56. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/database_checksum_v2_response_200.py +0 -0
  57. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/database_format.py +0 -0
  58. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/database_license_type_type_1.py +0 -0
  59. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/database_license_type_type_2_type_1.py +0 -0
  60. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/database_license_type_type_3_type_1.py +0 -0
  61. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/database_metadata.py +0 -0
  62. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/database_metadata_column.py +0 -0
  63. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/database_metadata_sample.py +0 -0
  64. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/database_metadata_sample_additional_property_item.py +0 -0
  65. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/database_metadata_sample_size.py +0 -0
  66. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/database_metadata_schema.py +0 -0
  67. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/database_metadata_size.py +0 -0
  68. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/database_version.py +0 -0
  69. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/db_checksums.py +0 -0
  70. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/device_authorization.py +0 -0
  71. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/device_authorization_request.py +0 -0
  72. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/download_outcome.py +0 -0
  73. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/error.py +0 -0
  74. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/identity.py +0 -0
  75. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/list_databases_response_200.py +0 -0
  76. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/list_downloads_response_200.py +0 -0
  77. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/oauth_authorize_code_challenge_method.py +0 -0
  78. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/oauth_authorize_response_type.py +0 -0
  79. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/oauth_error.py +0 -0
  80. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/oauth_metadata.py +0 -0
  81. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/oauth_revoke_response_200.py +0 -0
  82. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/revoke_request.py +0 -0
  83. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/standing.py +0 -0
  84. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/token_request.py +0 -0
  85. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/models/token_response.py +0 -0
  86. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/_generated/types.py +0 -0
  87. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/aio.py +0 -0
  88. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/client.py +0 -0
  89. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/errors.py +0 -0
  90. {internetdata-2.6.1 → internetdata-2.7.0}/src/internetdata/py.typed +0 -0
  91. {internetdata-2.6.1 → internetdata-2.7.0}/testdata/testdata.json +0 -0
  92. {internetdata-2.6.1 → internetdata-2.7.0}/tests/conftest.py +0 -0
  93. {internetdata-2.6.1 → internetdata-2.7.0}/tests/test_conformance.py +0 -0
  94. {internetdata-2.6.1 → internetdata-2.7.0}/tests/test_download.py +0 -0
  95. {internetdata-2.6.1 → internetdata-2.7.0}/tests/test_oauth.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: internetdata
3
- Version: 2.6.1
3
+ Version: 2.7.0
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
@@ -225,11 +225,11 @@ Check that `state` came back as you sent it before you exchange `code`, which wo
225
225
 
226
226
  ## Other Libraries
227
227
 
228
- There are official InternetData client libraries available for many languages including PHP, Python, Go, Java, Ruby, and many popular frameworks such as Django, Rails, and Laravel. See our GitHub at https://github.com/internetdata for more.
228
+ There are official InternetData client libraries available for many languages including PHP, Python, Go, Java, and Ruby. See our GitHub at https://github.com/internetdata for more.
229
229
 
230
230
  ## About InternetData
231
231
 
232
- InternetData: IP and network intelligence databases, published as CSV and MMDB, licensed by contract and downloadable on demand.
232
+ Geolocation, anonymity, ownership and network databases for IP addresses and AS numbers, licensed as files you download and query yourself.
233
233
 
234
234
  [<img src="https://s3.internetdata.io/internetdata-public/brand/mark.svg" alt="InternetData" height="64"/>](https://internetdata.io/)
235
235
 
@@ -191,11 +191,11 @@ Check that `state` came back as you sent it before you exchange `code`, which wo
191
191
 
192
192
  ## Other Libraries
193
193
 
194
- There are official InternetData client libraries available for many languages including PHP, Python, Go, Java, Ruby, and many popular frameworks such as Django, Rails, and Laravel. See our GitHub at https://github.com/internetdata for more.
194
+ There are official InternetData client libraries available for many languages including PHP, Python, Go, Java, and Ruby. See our GitHub at https://github.com/internetdata for more.
195
195
 
196
196
  ## About InternetData
197
197
 
198
- InternetData: IP and network intelligence databases, published as CSV and MMDB, licensed by contract and downloadable on demand.
198
+ Geolocation, anonymity, ownership and network databases for IP addresses and AS numbers, licensed as files you download and query yourself.
199
199
 
200
200
  [<img src="https://s3.internetdata.io/internetdata-public/brand/mark.svg" alt="InternetData" height="64"/>](https://internetdata.io/)
201
201
 
@@ -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.09.26
15
+ version: 2026.10.08
16
16
  servers:
17
17
  - url: https://internetdata.io
18
18
  tags:
@@ -28,7 +28,9 @@ tags:
28
28
  database is usually fetched.
29
29
 
30
30
  Access is granted by CONTRACT, one dataset family at a time: there is no
31
- self-service purchase and no subscription behind it.
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
34
 
33
35
  A successful download answers `302` with a time-limited URL pointing
34
36
  straight at object storage. Follow the redirect (`curl -L`); the link
@@ -58,6 +60,7 @@ paths:
58
60
  The whole published catalog, with your organization's license beside
59
61
  each entry, so `standing` says whether a database is yours today
60
62
  (`licensed`), was (`expired`), or has never been bought (`unlicensed`).
63
+ An `open` database downloads whatever its `standing`.
61
64
  security:
62
65
  - ApiKeyQueryAuth: []
63
66
  - ApiKeyHeaderAuth: []
@@ -126,7 +129,8 @@ paths:
126
129
  `updated` and `entries` without downloading anything.
127
130
 
128
131
  No `format` parameter - one document describes every format the database
129
- is built in.
132
+ is built in. Needs no license: it answers for every database `list`
133
+ returns, whatever its `standing`.
130
134
  security:
131
135
  - ApiKeyQueryAuth: []
132
136
  - ApiKeyHeaderAuth: []
@@ -142,8 +146,6 @@ paths:
142
146
  $ref: '#/components/schemas/DatabaseMetadata'
143
147
  '401':
144
148
  $ref: '#/components/responses/V2Unauthorized'
145
- '403':
146
- $ref: '#/components/responses/V2NotLicensed'
147
149
  '404':
148
150
  $ref: '#/components/responses/V2UnknownDatabase'
149
151
  '503':
@@ -897,7 +899,8 @@ components:
897
899
  description: |
898
900
  Where your license for a database family stands today. `licensed` is a
899
901
  live grant, `expired` one whose term has ended, and `unlicensed` a
900
- database published but never bought.
902
+ database published but never bought. An Open database (`open`)
903
+ downloads whatever this says.
901
904
  enum:
902
905
  - licensed
903
906
  - expired
@@ -913,6 +916,7 @@ components:
913
916
  - name
914
917
  - summary
915
918
  - standing
919
+ - open
916
920
  - license_type
917
921
  - starts
918
922
  - expires
@@ -932,6 +936,14 @@ components:
932
936
  description: One line on what the newest version contains.
933
937
  standing:
934
938
  $ref: '#/components/schemas/Standing'
939
+ open:
940
+ type: boolean
941
+ description: |
942
+ An Open database: any organization downloads it, and fetches its
943
+ checksums, with no license, under CC BY-SA 4.0 (credit
944
+ InternetData, https://internetdata.io, and share what you build
945
+ from it under the same license). `standing` still reports your
946
+ own license, which grants more where you hold one.
935
947
  license_type:
936
948
  type: string
937
949
  nullable: true
@@ -1014,6 +1026,7 @@ components:
1014
1026
  - format
1015
1027
  - outcome
1016
1028
  - sample
1029
+ - open
1017
1030
  - bytes
1018
1031
  - http_status
1019
1032
  - apikey_id
@@ -1037,6 +1050,12 @@ components:
1037
1050
  sample:
1038
1051
  type: boolean
1039
1052
  description: The evaluation sample rather than the database itself.
1053
+ open:
1054
+ type: boolean
1055
+ description: |
1056
+ Taken under the Open license rather than one of your licenses: an
1057
+ Open database downloaded while your organization held no license
1058
+ in term for it.
1040
1059
  bytes:
1041
1060
  type: integer
1042
1061
  format: int64
@@ -1625,7 +1644,7 @@ components:
1625
1644
  V2NotLicensed:
1626
1645
  description: |
1627
1646
  Your organization holds no license for this database, or its term has
1628
- ended.
1647
+ ended. Never for an Open database.
1629
1648
  `rc`: `NOT_LICENSED`, `LICENSE_EXPIRED`.
1630
1649
  content:
1631
1650
  application/json:
@@ -1641,7 +1660,7 @@ components:
1641
1660
  $ref: '#/components/schemas/Error'
1642
1661
  V2NotAvailable:
1643
1662
  description: |
1644
- Licensed, but the current build has not been published yet.
1663
+ The current build has not been published yet.
1645
1664
  `rc`: `NOT_AVAILABLE`.
1646
1665
  content:
1647
1666
  application/json:
@@ -39,7 +39,7 @@ from .models import (
39
39
  TokenResponse,
40
40
  )
41
41
 
42
- __version__ = "2.6.1"
42
+ __version__ = "2.7.0"
43
43
 
44
44
  __all__ = [
45
45
  "DATABASE_FORMATS",
@@ -43,11 +43,6 @@ def _parse_response(
43
43
 
44
44
  return response_401
45
45
 
46
- if response.status_code == 403:
47
- response_403 = Error.from_dict(response.json())
48
-
49
- return response_403
50
-
51
46
  if response.status_code == 404:
52
47
  response_404 = Error.from_dict(response.json())
53
48
 
@@ -86,7 +81,8 @@ def sync_detailed(
86
81
  `updated` and `entries` without downloading anything.
87
82
 
88
83
  No `format` parameter - one document describes every format the database
89
- is built in.
84
+ is built in. Needs no license: it answers for every database `list`
85
+ returns, whatever its `standing`.
90
86
 
91
87
  Args:
92
88
  id (str): Example: vpn_ip_v1.
@@ -121,7 +117,8 @@ def sync(
121
117
  `updated` and `entries` without downloading anything.
122
118
 
123
119
  No `format` parameter - one document describes every format the database
124
- is built in.
120
+ is built in. Needs no license: it answers for every database `list`
121
+ returns, whatever its `standing`.
125
122
 
126
123
  Args:
127
124
  id (str): Example: vpn_ip_v1.
@@ -151,7 +148,8 @@ async def asyncio_detailed(
151
148
  `updated` and `entries` without downloading anything.
152
149
 
153
150
  No `format` parameter - one document describes every format the database
154
- is built in.
151
+ is built in. Needs no license: it answers for every database `list`
152
+ returns, whatever its `standing`.
155
153
 
156
154
  Args:
157
155
  id (str): Example: vpn_ip_v1.
@@ -184,7 +182,8 @@ async def asyncio(
184
182
  `updated` and `entries` without downloading anything.
185
183
 
186
184
  No `format` parameter - one document describes every format the database
187
- is built in.
185
+ is built in. Needs no license: it answers for every database `list`
186
+ returns, whatever its `standing`.
188
187
 
189
188
  Args:
190
189
  id (str): Example: vpn_ip_v1.
@@ -59,6 +59,7 @@ 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`.
62
63
 
63
64
  Raises:
64
65
  errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
@@ -86,6 +87,7 @@ def sync(
86
87
  The whole published catalog, with your organization's license beside
87
88
  each entry, so `standing` says whether a database is yours today
88
89
  (`licensed`), was (`expired`), or has never been bought (`unlicensed`).
90
+ An `open` database downloads whatever its `standing`.
89
91
 
90
92
  Raises:
91
93
  errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
@@ -109,6 +111,7 @@ async def asyncio_detailed(
109
111
  The whole published catalog, with your organization's license beside
110
112
  each entry, so `standing` says whether a database is yours today
111
113
  (`licensed`), was (`expired`), or has never been bought (`unlicensed`).
114
+ An `open` database downloads whatever its `standing`.
112
115
 
113
116
  Raises:
114
117
  errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
@@ -134,6 +137,7 @@ async def asyncio(
134
137
  The whole published catalog, with your organization's license beside
135
138
  each entry, so `standing` says whether a database is yours today
136
139
  (`licensed`), was (`expired`), or has never been bought (`unlicensed`).
140
+ An `open` database downloads whatever its `standing`.
137
141
 
138
142
  Raises:
139
143
  errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
@@ -31,7 +31,13 @@ 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.
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.
35
41
  license_type (DatabaseLicenseTypeType1 | DatabaseLicenseTypeType2Type1 | DatabaseLicenseTypeType3Type1 | None):
36
42
  What your license permits you to do with the data. Null when there
37
43
  is no license, which is every family with standing `unlicensed`.
@@ -51,6 +57,7 @@ class Database:
51
57
  name: str
52
58
  summary: str
53
59
  standing: Standing
60
+ open_: bool
54
61
  license_type: (
55
62
  DatabaseLicenseTypeType1
56
63
  | DatabaseLicenseTypeType2Type1
@@ -73,6 +80,8 @@ class Database:
73
80
 
74
81
  standing = self.standing.value
75
82
 
83
+ open_ = self.open_
84
+
76
85
  license_type: None | str
77
86
  if (
78
87
  isinstance(self.license_type, DatabaseLicenseTypeType1)
@@ -120,6 +129,7 @@ class Database:
120
129
  "name": name,
121
130
  "summary": summary,
122
131
  "standing": standing,
132
+ "open": open_,
123
133
  "license_type": license_type,
124
134
  "starts": starts,
125
135
  "expires": expires,
@@ -144,6 +154,8 @@ class Database:
144
154
 
145
155
  standing = Standing(d.pop("standing"))
146
156
 
157
+ open_ = d.pop("open")
158
+
147
159
  def _parse_license_type(
148
160
  data: object,
149
161
  ) -> (
@@ -260,6 +272,7 @@ class Database:
260
272
  name=name,
261
273
  summary=summary,
262
274
  standing=standing,
275
+ open_=open_,
263
276
  license_type=license_type,
264
277
  starts=starts,
265
278
  expires=expires,
@@ -21,6 +21,9 @@ 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
27
  bytes_ (int | None): Object size at redirect time, NOT bytes delivered: the transfer is a
25
28
  presigned redirect straight to object storage, so we never observe
26
29
  how much of it was taken.
@@ -35,6 +38,7 @@ class Download:
35
38
  format_: str
36
39
  outcome: DownloadOutcome
37
40
  sample: bool
41
+ open_: bool
38
42
  bytes_: int | None
39
43
  http_status: int | None
40
44
  apikey_id: None | str
@@ -52,6 +56,8 @@ class Download:
52
56
 
53
57
  sample = self.sample
54
58
 
59
+ open_ = self.open_
60
+
55
61
  bytes_: int | None
56
62
  bytes_ = self.bytes_
57
63
 
@@ -77,6 +83,7 @@ class Download:
77
83
  "format": format_,
78
84
  "outcome": outcome,
79
85
  "sample": sample,
86
+ "open": open_,
80
87
  "bytes": bytes_,
81
88
  "http_status": http_status,
82
89
  "apikey_id": apikey_id,
@@ -99,6 +106,8 @@ class Download:
99
106
 
100
107
  sample = d.pop("sample")
101
108
 
109
+ open_ = d.pop("open")
110
+
102
111
  def _parse_bytes_(data: object) -> int | None:
103
112
  if data is None:
104
113
  return data
@@ -141,6 +150,7 @@ class Download:
141
150
  format_=format_,
142
151
  outcome=outcome,
143
152
  sample=sample,
153
+ open_=open_,
144
154
  bytes_=bytes_,
145
155
  http_status=http_status,
146
156
  apikey_id=apikey_id,
@@ -86,7 +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.
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.
90
92
 
91
93
  A rolling license carries `renews_at`, when it next renews, and `notice_due_at`, the
92
94
  last day notice of non-renewal can be given for the term ending then. Both are None
@@ -107,6 +109,9 @@ class Database:
107
109
  notice_due_at: datetime.datetime | None = field(default=None, kw_only=True)
108
110
  versions: tuple[DatabaseVersion, ...]
109
111
  raw: dict[str, Any] = field(default_factory=dict)
112
+ # Keyword-only with a default, so a Database built by hand before 2.7.0 still builds;
113
+ # the parser always sets it.
114
+ open: bool = field(default=False, kw_only=True)
110
115
 
111
116
 
112
117
  @dataclass(frozen=True, slots=True)
@@ -162,6 +167,10 @@ class Download:
162
167
  # with a default, so a Download built by hand before 2.5.0 still builds; the parser
163
168
  # always sets it.
164
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.
173
+ open: bool = field(default=False, kw_only=True)
165
174
 
166
175
 
167
176
  def to_database(body: dict[str, Any]) -> Database:
@@ -177,6 +186,7 @@ def to_database(body: dict[str, Any]) -> Database:
177
186
  notice_due_at=_datetime(body["notice_due_at"]),
178
187
  versions=tuple(to_version(v) for v in body["versions"]),
179
188
  raw=body,
189
+ open=body["open"],
180
190
  )
181
191
 
182
192
 
@@ -223,6 +233,7 @@ def to_download(body: dict[str, Any]) -> Download:
223
233
  client_ip=body["client_ip"],
224
234
  user_agent=body["user_agent"],
225
235
  sample=body["sample"],
236
+ open=body["open"],
226
237
  )
227
238
 
228
239
 
@@ -441,6 +441,7 @@ def database(base: str, **overrides: Any) -> dict[str, Any]:
441
441
  "name": base.replace("_", " ").title(),
442
442
  "summary": f"{base} rows",
443
443
  "standing": "licensed",
444
+ "open": False,
444
445
  "license_type": "standard",
445
446
  "starts": "2026-01-01T00:00:00.000Z",
446
447
  "expires": None,
@@ -88,6 +88,7 @@ DOWNLOADS = {
88
88
  "client_ip": "203.0.113.4",
89
89
  "user_agent": "curl/8.5.0",
90
90
  "sample": False,
91
+ "open": True,
91
92
  "created": "2026-09-04T10:00:00.000Z",
92
93
  },
93
94
  {
@@ -100,6 +101,7 @@ DOWNLOADS = {
100
101
  "client_ip": None,
101
102
  "user_agent": None,
102
103
  "sample": True,
104
+ "open": False,
103
105
  "created": "2026-09-04T09:00:00.000Z",
104
106
  },
105
107
  ]
@@ -202,6 +204,7 @@ def test_a_refusal_is_listed_beside_a_success(make_client: ClientFactory) -> Non
202
204
  assert denied.http_status == 403
203
205
  assert denied.apikey_id is None and denied.client_ip is None
204
206
  assert [a.sample for a in attempts] == [False, True]
207
+ assert [a.open for a in attempts] == [True, False]
205
208
 
206
209
 
207
210
  def test_the_downloads_limit_reaches_the_wire(make_client: ClientFactory) -> None:
@@ -225,6 +228,19 @@ def test_a_licence_with_no_end_date_reads_as_none(make_client: ClientFactory) ->
225
228
  assert family.notice_due_at is None
226
229
 
227
230
 
231
+ def test_an_open_family_is_read_beside_its_standing(make_client: ClientFactory) -> None:
232
+ served = [
233
+ database("asn", standing="unlicensed", open=True, license_type=None, starts=None),
234
+ database("vpn_ip"),
235
+ ]
236
+ stub = Stub({LIST_PATH: {"body": {"databases": served}}})
237
+ client = make_client(transport=stub.transport)
238
+
239
+ families = client.database.list()
240
+
241
+ assert [(f.open, f.standing) for f in families] == [(True, "unlicensed"), (False, "licensed")]
242
+
243
+
228
244
  def test_a_rolling_license_carries_its_renewal_dates(make_client: ClientFactory) -> None:
229
245
  served = database(
230
246
  "bogon_ip",
File without changes
File without changes