maxminddb-rust 0.9.0__cp315-cp315-win_amd64.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.
- maxminddb_rust/__init__.py +5 -0
- maxminddb_rust/__init__.pyi +323 -0
- maxminddb_rust/maxminddb_rust.cp315-win_amd64.pyd +0 -0
- maxminddb_rust/py.typed +0 -0
- maxminddb_rust-0.9.0.dist-info/METADATA +334 -0
- maxminddb_rust-0.9.0.dist-info/RECORD +11 -0
- maxminddb_rust-0.9.0.dist-info/WHEEL +4 -0
- maxminddb_rust-0.9.0.dist-info/licenses/LICENSE +15 -0
- maxminddb_rust-0.9.0.dist-info/sboms/maxminddb-rust.cyclonedx.json +1032 -0
- maxminddb_rust.pyi +323 -0
- py.typed +0 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"""Type stubs for maxminddb_rust.
|
|
2
|
+
|
|
3
|
+
This module provides a high-performance alternative to the maxminddb Python package,
|
|
4
|
+
implemented in Rust using PyO3 with 100% API compatibility.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from collections.abc import Iterable, Iterator, Sequence
|
|
8
|
+
from ipaddress import IPv4Address, IPv4Network, IPv6Address, IPv6Network
|
|
9
|
+
from os import PathLike
|
|
10
|
+
from types import TracebackType
|
|
11
|
+
from typing import Any, BinaryIO, Literal
|
|
12
|
+
|
|
13
|
+
__all__ = [ # noqa: RUF022 - Match the extension export order.
|
|
14
|
+
"Reader",
|
|
15
|
+
"Metadata",
|
|
16
|
+
"InvalidDatabaseError",
|
|
17
|
+
"open_database",
|
|
18
|
+
"MODE_AUTO",
|
|
19
|
+
"MODE_MMAP_EXT",
|
|
20
|
+
"MODE_MMAP",
|
|
21
|
+
"MODE_FILE",
|
|
22
|
+
"MODE_MEMORY",
|
|
23
|
+
"MODE_FD",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
# Mode constants
|
|
27
|
+
MODE_AUTO: Literal[0]
|
|
28
|
+
MODE_MMAP_EXT: Literal[1]
|
|
29
|
+
MODE_MMAP: Literal[2]
|
|
30
|
+
MODE_FILE: Literal[4]
|
|
31
|
+
MODE_MEMORY: Literal[8]
|
|
32
|
+
MODE_FD: Literal[16]
|
|
33
|
+
|
|
34
|
+
class InvalidDatabaseError(RuntimeError):
|
|
35
|
+
"""Exception raised when the MaxMind DB file is invalid or corrupt."""
|
|
36
|
+
|
|
37
|
+
class Metadata:
|
|
38
|
+
"""Metadata about a MaxMind DB database."""
|
|
39
|
+
|
|
40
|
+
binary_format_major_version: int
|
|
41
|
+
"""The major version number of the binary format used when creating the database."""
|
|
42
|
+
|
|
43
|
+
binary_format_minor_version: int
|
|
44
|
+
"""The minor version number of the binary format used when creating the database."""
|
|
45
|
+
|
|
46
|
+
build_epoch: int
|
|
47
|
+
"""The Unix epoch timestamp for when the database was built."""
|
|
48
|
+
|
|
49
|
+
database_type: str
|
|
50
|
+
"""A string identifying the database type (e.g., 'GeoIP2-City',
|
|
51
|
+
'GeoLite2-Country')."""
|
|
52
|
+
|
|
53
|
+
ip_version: int
|
|
54
|
+
"""The IP version of the data in a database. A value of 4 means IPv4 only; 6
|
|
55
|
+
supports both IPv4 and IPv6."""
|
|
56
|
+
|
|
57
|
+
node_count: int
|
|
58
|
+
"""The number of nodes in the search tree."""
|
|
59
|
+
|
|
60
|
+
record_size: int
|
|
61
|
+
"""The record size in bits (24, 28, or 32)."""
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def description(self) -> dict[str, str]:
|
|
65
|
+
"""A dictionary from locale codes to the database description in that locale."""
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def languages(self) -> list[str]:
|
|
69
|
+
"""List the locale codes supported by the database."""
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def node_byte_size(self) -> int:
|
|
73
|
+
"""The size of a node in bytes."""
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def search_tree_size(self) -> int:
|
|
77
|
+
"""The size of the search tree in bytes."""
|
|
78
|
+
|
|
79
|
+
class Reader:
|
|
80
|
+
"""A MaxMind DB database reader.
|
|
81
|
+
|
|
82
|
+
Provides methods to query IP address information from MaxMind DB files.
|
|
83
|
+
Supports memory-mapped files (MODE_AUTO/MODE_MMAP/MODE_MMAP_EXT) and
|
|
84
|
+
read-file modes (MODE_FILE/MODE_MEMORY/MODE_FD). MODE_AUTO currently
|
|
85
|
+
resolves to MODE_MMAP, and MODE_MMAP_EXT uses the same Rust memory-mapped
|
|
86
|
+
reader as MODE_MMAP.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def __init__(
|
|
90
|
+
self, database: str | PathLike[str] | BinaryIO, mode: int = ...
|
|
91
|
+
) -> None:
|
|
92
|
+
"""Initialize a Reader for a MaxMind DB file.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
database: Path to the MaxMind DB file, or a readable binary object
|
|
96
|
+
for MODE_FD. Raw integer OS file descriptors are not accepted.
|
|
97
|
+
mode: The mode to use when opening the database. Defaults to MODE_AUTO.
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
FileNotFoundError: If the database file does not exist.
|
|
101
|
+
IOError: If the database file cannot be read or memory-mapped.
|
|
102
|
+
InvalidDatabaseError: If the file is not a valid MaxMind DB file.
|
|
103
|
+
ValueError: If an unsupported mode is specified.
|
|
104
|
+
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def closed(self) -> bool:
|
|
109
|
+
"""True if the database has been closed, False otherwise."""
|
|
110
|
+
|
|
111
|
+
def get(self, ip_address: str | IPv4Address | IPv6Address) -> Any | None: # noqa: ANN401 - Records have database-defined shapes.
|
|
112
|
+
"""Query the database for information about an IP address.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
ip_address: The IP address to look up. May be a string (e.g., '1.2.3.4')
|
|
116
|
+
or an ipaddress.IPv4Address or ipaddress.IPv6Address object.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
The database record value for the IP address, or None if the address
|
|
120
|
+
is not in the database.
|
|
121
|
+
|
|
122
|
+
Raises:
|
|
123
|
+
ValueError: If the database has been closed or the IP address is invalid.
|
|
124
|
+
InvalidDatabaseError: If the database data is corrupt or invalid.
|
|
125
|
+
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def get_with_prefix_len(
|
|
129
|
+
self, ip_address: str | IPv4Address | IPv6Address
|
|
130
|
+
) -> tuple[Any | None, int]:
|
|
131
|
+
"""Look up an IP address and its network prefix length.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
ip_address: The IP address to look up. May be a string (e.g., '1.2.3.4')
|
|
135
|
+
or an ipaddress.IPv4Address or ipaddress.IPv6Address object.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
A tuple of (record, prefix_length) where record is the database
|
|
139
|
+
record value (or None if not found), and prefix_length is an integer
|
|
140
|
+
representing the network prefix length associated with the record.
|
|
141
|
+
|
|
142
|
+
Raises:
|
|
143
|
+
ValueError: If the database has been closed or the IP address is invalid.
|
|
144
|
+
InvalidDatabaseError: If the database data is corrupt or invalid.
|
|
145
|
+
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
def get_path(
|
|
149
|
+
self,
|
|
150
|
+
ip_address: str | IPv4Address | IPv6Address,
|
|
151
|
+
path: Sequence[str | int],
|
|
152
|
+
) -> Any | None: # noqa: ANN401 - Records have database-defined shapes.
|
|
153
|
+
"""Query the database for a specific path within the record.
|
|
154
|
+
|
|
155
|
+
This method is more efficient than get() when you only need a specific field
|
|
156
|
+
(e.g., country code) from the record, as it avoids decoding the entire record.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
ip_address: The IP address to look up.
|
|
160
|
+
path: A sequence (tuple or list) of strings or integers
|
|
161
|
+
representing the path to the data.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
The value at the specified path, or None if the IP address or
|
|
165
|
+
path is not found.
|
|
166
|
+
|
|
167
|
+
Example:
|
|
168
|
+
>>> reader.get_path('8.8.8.8', ('country', 'iso_code'))
|
|
169
|
+
'US'
|
|
170
|
+
>>> reader.get_path('8.8.8.8', ('subdivisions', 0, 'iso_code'))
|
|
171
|
+
'CA'
|
|
172
|
+
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
def get_many(
|
|
176
|
+
self, ips: Iterable[str | IPv4Address | IPv6Address]
|
|
177
|
+
) -> list[Any | None]:
|
|
178
|
+
"""Query the database for multiple IP addresses in a single batch operation.
|
|
179
|
+
|
|
180
|
+
This is an extension method not available in the original maxminddb module.
|
|
181
|
+
It provides better performance than calling get() repeatedly by reducing
|
|
182
|
+
call overhead. This method keeps the GIL for the duration of the batch.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
ips: An iterable of IP address strings or ipaddress objects to look up
|
|
186
|
+
(e.g., ['1.2.3.4', '8.8.8.8']).
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
A list of database record values for each IP address.
|
|
190
|
+
Elements will be None for IP addresses not found in the database.
|
|
191
|
+
The order of results matches the order of input IPs.
|
|
192
|
+
|
|
193
|
+
Raises:
|
|
194
|
+
ValueError: If the database has been closed or any IP address is invalid.
|
|
195
|
+
InvalidDatabaseError: If the database data is corrupt or invalid.
|
|
196
|
+
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
def get_many_path(
|
|
200
|
+
self,
|
|
201
|
+
ips: Iterable[str | IPv4Address | IPv6Address],
|
|
202
|
+
path: Sequence[str | int],
|
|
203
|
+
) -> list[Any | None]:
|
|
204
|
+
"""Query the database for a specific path for multiple IP addresses.
|
|
205
|
+
|
|
206
|
+
This extension combines get_many() batching with get_path() selective
|
|
207
|
+
decoding. It parses the path once and avoids decoding full records when
|
|
208
|
+
only one field is needed.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
ips: An iterable of IP address strings or ipaddress objects to look up
|
|
212
|
+
(e.g., ['1.2.3.4', '8.8.8.8']).
|
|
213
|
+
path: A sequence of strings or integers representing the path to the data.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
A list of values at the specified path. Elements will be None for IP
|
|
217
|
+
addresses or paths not found in the database.
|
|
218
|
+
|
|
219
|
+
Raises:
|
|
220
|
+
ValueError: If the database has been closed or any IP address is invalid.
|
|
221
|
+
InvalidDatabaseError: If the database data is corrupt or invalid.
|
|
222
|
+
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
def metadata(self) -> Metadata:
|
|
226
|
+
"""Get metadata about the MaxMind DB database.
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
A Metadata object containing information about the database.
|
|
230
|
+
|
|
231
|
+
Raises:
|
|
232
|
+
OSError: If the database has been closed.
|
|
233
|
+
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
def close(self) -> None:
|
|
237
|
+
"""Close the database and release resources.
|
|
238
|
+
|
|
239
|
+
Closes the MaxMind DB file handle and releases associated resources.
|
|
240
|
+
After calling this method, attempting to call get() or other query
|
|
241
|
+
methods will raise a ValueError.
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
def __enter__(self) -> Reader: # noqa: PYI034 - Reader cannot be subclassed.
|
|
245
|
+
"""Enter the context manager (for use with 'with' statement).
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
The Reader object itself.
|
|
249
|
+
|
|
250
|
+
Raises:
|
|
251
|
+
ValueError: If attempting to reopen a closed database.
|
|
252
|
+
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
def __exit__(
|
|
256
|
+
self,
|
|
257
|
+
exc_type: type[BaseException] | None,
|
|
258
|
+
exc_val: BaseException | None,
|
|
259
|
+
exc_tb: TracebackType | None,
|
|
260
|
+
) -> None:
|
|
261
|
+
"""Exit the context manager (for use with 'with' statement).
|
|
262
|
+
|
|
263
|
+
Automatically closes the database when exiting the 'with' block.
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
def __iter__(
|
|
267
|
+
self,
|
|
268
|
+
) -> Iterator[tuple[IPv4Network | IPv6Network, Any]]:
|
|
269
|
+
"""Iterate over all networks in the database.
|
|
270
|
+
|
|
271
|
+
Returns an iterator that yields (network, data) tuples for all networks
|
|
272
|
+
in the database. Networks are represented as ipaddress.IPv4Network or
|
|
273
|
+
ipaddress.IPv6Network objects.
|
|
274
|
+
|
|
275
|
+
Yields:
|
|
276
|
+
Tuples of (network, record) for each entry in the database.
|
|
277
|
+
|
|
278
|
+
Raises:
|
|
279
|
+
ValueError: If the database has been closed.
|
|
280
|
+
|
|
281
|
+
"""
|
|
282
|
+
|
|
283
|
+
def open_database(database: str | PathLike[str] | BinaryIO, mode: int = ...) -> Reader:
|
|
284
|
+
"""Open a MaxMind DB database file.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
database: Path to the MaxMind DB file, or a readable binary object for
|
|
288
|
+
MODE_FD. Raw integer OS file descriptors are not accepted.
|
|
289
|
+
mode: The mode to use when opening the database. Defaults to MODE_AUTO.
|
|
290
|
+
Available modes:
|
|
291
|
+
- MODE_AUTO (0): Currently resolves to MODE_MMAP
|
|
292
|
+
- MODE_MMAP (2): Use memory-mapped file I/O (default, best performance)
|
|
293
|
+
- MODE_MMAP_EXT (1): Compatibility alias for the same Rust mmap
|
|
294
|
+
reader as MODE_MMAP
|
|
295
|
+
- MODE_FILE (4): Read the database file into memory
|
|
296
|
+
- MODE_MEMORY (8): Load entire database into memory
|
|
297
|
+
- MODE_FD (16): Read bytes from a file-like object into memory
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
A Reader object that can be used to query the database.
|
|
301
|
+
|
|
302
|
+
Raises:
|
|
303
|
+
FileNotFoundError: If the database file does not exist.
|
|
304
|
+
IOError: If the database file cannot be read or memory-mapped.
|
|
305
|
+
InvalidDatabaseError: If the file is not a valid MaxMind DB file.
|
|
306
|
+
ValueError: If an unsupported mode is specified.
|
|
307
|
+
|
|
308
|
+
Example:
|
|
309
|
+
>>> import maxminddb_rust
|
|
310
|
+
>>> reader = maxminddb_rust.open_database('/path/to/GeoIP2-City.mmdb')
|
|
311
|
+
>>> reader.get('8.8.8.8')
|
|
312
|
+
{'city': {'names': {'en': 'Mountain View'}}, ...}
|
|
313
|
+
>>> reader.close()
|
|
314
|
+
|
|
315
|
+
>>> # Using context manager
|
|
316
|
+
>>> with maxminddb_rust.open_database('/path/to/GeoIP2-City.mmdb') as reader:
|
|
317
|
+
... result = reader.get('8.8.8.8')
|
|
318
|
+
|
|
319
|
+
>>> # Specify mode explicitly
|
|
320
|
+
>>> reader = maxminddb_rust.open_database('/path/to/GeoIP2-City.mmdb',
|
|
321
|
+
... mode=maxminddb_rust.MODE_MEMORY)
|
|
322
|
+
|
|
323
|
+
"""
|
|
Binary file
|
maxminddb_rust/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: maxminddb-rust
|
|
3
|
+
Version: 0.9.0
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Environment :: WebAssembly :: Emscripten
|
|
6
|
+
Classifier: Intended Audience :: Developers
|
|
7
|
+
Classifier: License :: OSI Approved :: ISC License (ISCL)
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
16
|
+
Classifier: Programming Language :: Rust
|
|
17
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Topic :: Database
|
|
20
|
+
Classifier: Topic :: Internet
|
|
21
|
+
Requires-Dist: pytest>=8.4.2,<9.0.0 ; python_full_version < '3.10' and extra == 'test'
|
|
22
|
+
Requires-Dist: pytest>=9.0.2 ; python_full_version >= '3.10' and extra == 'test'
|
|
23
|
+
Provides-Extra: test
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Summary: Rust-based Python binding for MaxMind DB - high-performance alternative to maxminddb
|
|
26
|
+
Keywords: maxmind,geoip,geolocation,ip-address,mmdb
|
|
27
|
+
Author-email: Gregory Oschwald <oschwald@gmail.com>
|
|
28
|
+
License: ISC
|
|
29
|
+
Requires-Python: >=3.9
|
|
30
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
31
|
+
Project-URL: Bug Tracker, https://github.com/oschwald/maxminddb-rust-python/issues
|
|
32
|
+
Project-URL: Changelog, https://github.com/oschwald/maxminddb-rust-python/blob/main/CHANGELOG.md
|
|
33
|
+
Project-URL: Contributing, https://github.com/oschwald/maxminddb-rust-python/blob/main/CONTRIBUTING.md
|
|
34
|
+
Project-URL: Homepage, https://github.com/oschwald/maxminddb-rust-python
|
|
35
|
+
Project-URL: Migration Guide, https://github.com/oschwald/maxminddb-rust-python/blob/main/MIGRATION.md
|
|
36
|
+
Project-URL: Repository, https://github.com/oschwald/maxminddb-rust-python
|
|
37
|
+
|
|
38
|
+
# maxminddb-rust
|
|
39
|
+
|
|
40
|
+
A Rust-backed Python module for reading MaxMind DB files.
|
|
41
|
+
|
|
42
|
+
It mirrors the public API of the official
|
|
43
|
+
[`maxminddb`](https://github.com/maxmind/MaxMind-DB-Reader-python) package and
|
|
44
|
+
keeps the same programming model with the `maxminddb_rust` import name.
|
|
45
|
+
|
|
46
|
+
## Performance
|
|
47
|
+
|
|
48
|
+
This project is intended to provide performance comparable to the `maxminddb` C
|
|
49
|
+
extension while keeping API compatibility.
|
|
50
|
+
|
|
51
|
+
Performance depends on the database, lookup pattern, and hardware. Run the
|
|
52
|
+
benchmark scripts in `benchmarks/` against your own databases to measure
|
|
53
|
+
expected throughput in your environment.
|
|
54
|
+
|
|
55
|
+
The reader is thread-safe and can be shared across threads. On standard CPython
|
|
56
|
+
builds, CPU-bound lookups from Python threads remain constrained by the GIL.
|
|
57
|
+
Free-threaded CPython 3.14 and 3.15 wheels allow lookup threads to run concurrently;
|
|
58
|
+
scaling depends on the database and workload.
|
|
59
|
+
|
|
60
|
+
## Features
|
|
61
|
+
|
|
62
|
+
### API Compatibility
|
|
63
|
+
|
|
64
|
+
This package provides API compatibility with the official
|
|
65
|
+
[`maxminddb`](https://github.com/maxmind/MaxMind-DB-Reader-python) Python
|
|
66
|
+
module.
|
|
67
|
+
|
|
68
|
+
Supported:
|
|
69
|
+
|
|
70
|
+
- `Reader` class with `get()`, `get_with_prefix_len()`, `metadata()`, and
|
|
71
|
+
`close()` methods
|
|
72
|
+
- `open_database()` function
|
|
73
|
+
- Context manager support (`with` statement)
|
|
74
|
+
- MODE\_\* constants (`MODE_AUTO`, `MODE_MMAP`, `MODE_MMAP_EXT`, `MODE_FILE`,
|
|
75
|
+
`MODE_MEMORY`, and `MODE_FD`)
|
|
76
|
+
- `InvalidDatabaseError` exception
|
|
77
|
+
- `Metadata` class with all attributes and computed properties
|
|
78
|
+
- Support for string IP addresses and `ipaddress.IPv4Address`/`IPv6Address`
|
|
79
|
+
objects
|
|
80
|
+
- `closed` attribute
|
|
81
|
+
- Iterator support (`__iter__`) for iterating all database records
|
|
82
|
+
|
|
83
|
+
Extensions (not in the original package):
|
|
84
|
+
|
|
85
|
+
- `get_many()` for batch lookups
|
|
86
|
+
- `get_path()` for retrieving a specific field from a record (for example,
|
|
87
|
+
`('country', 'iso_code')`) without decoding the entire record
|
|
88
|
+
- `get_many_path()` for retrieving a specific field for many IP addresses
|
|
89
|
+
|
|
90
|
+
## Installation
|
|
91
|
+
|
|
92
|
+
### From PyPI
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pip install maxminddb-rust
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### From Source
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
git clone https://github.com/oschwald/maxminddb-rust-python.git
|
|
102
|
+
cd maxminddb-rust-python
|
|
103
|
+
uv run --with maturin maturin develop --release
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Usage
|
|
107
|
+
|
|
108
|
+
This module follows the same API as `maxminddb`, with a different import name:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
import ipaddress
|
|
112
|
+
|
|
113
|
+
import maxminddb_rust
|
|
114
|
+
|
|
115
|
+
# Open the database and release resources automatically when done.
|
|
116
|
+
with maxminddb_rust.open_database("/var/lib/GeoIP/GeoIP2-City.mmdb") as reader:
|
|
117
|
+
result = reader.get("8.8.8.8")
|
|
118
|
+
print(result)
|
|
119
|
+
|
|
120
|
+
result, prefix_len = reader.get_with_prefix_len("8.8.8.8")
|
|
121
|
+
print(f"Result: {result}, Prefix: {prefix_len}")
|
|
122
|
+
|
|
123
|
+
ip = ipaddress.IPv4Address("8.8.8.8")
|
|
124
|
+
result = reader.get(ip)
|
|
125
|
+
print(result)
|
|
126
|
+
|
|
127
|
+
metadata = reader.metadata()
|
|
128
|
+
print(f"Database type: {metadata.database_type}")
|
|
129
|
+
print(f"Node count: {metadata.node_count}")
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Batch Lookup (Extension)
|
|
133
|
+
|
|
134
|
+
`get_many()` is an extension that is not available in the original `maxminddb`
|
|
135
|
+
module:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
import maxminddb_rust
|
|
139
|
+
|
|
140
|
+
with maxminddb_rust.open_database("/var/lib/GeoIP/GeoIP2-City.mmdb") as reader:
|
|
141
|
+
ips = ["8.8.8.8", "1.1.1.1", "208.67.222.222"]
|
|
142
|
+
results = reader.get_many(ips)
|
|
143
|
+
|
|
144
|
+
for ip, result in zip(ips, results):
|
|
145
|
+
print(f"{ip}: {result}")
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Selective Field Lookup (Extension)
|
|
149
|
+
|
|
150
|
+
`get_path()` retrieves a specific field from a record:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
import maxminddb_rust
|
|
154
|
+
|
|
155
|
+
with maxminddb_rust.open_database("/var/lib/GeoIP/GeoIP2-City.mmdb") as reader:
|
|
156
|
+
# Retrieve a specific field without decoding the entire record.
|
|
157
|
+
# Path elements can be strings (map keys) or integers (array indices).
|
|
158
|
+
iso_code = reader.get_path("8.8.8.8", ("country", "iso_code"))
|
|
159
|
+
print(f"ISO Code: {iso_code}")
|
|
160
|
+
|
|
161
|
+
subdivision = reader.get_path("8.8.8.8", ("subdivisions", 0, "iso_code"))
|
|
162
|
+
print(f"Subdivision: {subdivision}")
|
|
163
|
+
|
|
164
|
+
ips = ["8.8.8.8", "1.1.1.1", "208.67.222.222"]
|
|
165
|
+
iso_codes = reader.get_many_path(ips, ("country", "iso_code"))
|
|
166
|
+
print(iso_codes)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Iterator Support
|
|
170
|
+
|
|
171
|
+
Iterate over all networks in the database:
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
import maxminddb_rust
|
|
175
|
+
|
|
176
|
+
with maxminddb_rust.open_database("/var/lib/GeoIP/GeoLite2-Country.mmdb") as reader:
|
|
177
|
+
for network, data in reader:
|
|
178
|
+
print(f"{network}: {data['country']['iso_code']}")
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Database Modes
|
|
182
|
+
|
|
183
|
+
Choose between memory-mapped files (default) and read-file modes:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
import maxminddb_rust
|
|
187
|
+
|
|
188
|
+
# MODE_AUTO: currently resolves to MODE_MMAP.
|
|
189
|
+
reader = maxminddb_rust.open_database(
|
|
190
|
+
"/var/lib/GeoIP/GeoIP2-City.mmdb", mode=maxminddb_rust.MODE_AUTO
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# MODE_MMAP: explicitly use memory-mapped files.
|
|
194
|
+
reader = maxminddb_rust.open_database(
|
|
195
|
+
"/var/lib/GeoIP/GeoIP2-City.mmdb", mode=maxminddb_rust.MODE_MMAP
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# MODE_MMAP_EXT: accepted for compatibility; same Rust mmap reader as MODE_MMAP.
|
|
199
|
+
reader = maxminddb_rust.open_database(
|
|
200
|
+
"/var/lib/GeoIP/GeoIP2-City.mmdb", mode=maxminddb_rust.MODE_MMAP_EXT
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# MODE_MEMORY: load the database file into memory.
|
|
204
|
+
reader = maxminddb_rust.open_database(
|
|
205
|
+
"/var/lib/GeoIP/GeoIP2-City.mmdb", mode=maxminddb_rust.MODE_MEMORY
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# MODE_FILE: compatibility mode that also reads the file into memory.
|
|
209
|
+
reader = maxminddb_rust.open_database(
|
|
210
|
+
"/var/lib/GeoIP/GeoIP2-City.mmdb", mode=maxminddb_rust.MODE_FILE
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# MODE_FD: read from a file-like object into memory.
|
|
214
|
+
with open("/var/lib/GeoIP/GeoIP2-City.mmdb", "rb") as database:
|
|
215
|
+
reader = maxminddb_rust.open_database(database, mode=maxminddb_rust.MODE_FD)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`MODE_FD` follows the official package's pure Python behavior: pass a readable
|
|
219
|
+
binary object and the reader calls `read()` from its current position. Raw
|
|
220
|
+
integer OS file descriptors are not accepted directly.
|
|
221
|
+
|
|
222
|
+
`MODE_MMAP_EXT` is accepted for official-package compatibility. This package
|
|
223
|
+
does not have a separate C extension backend, so it uses the same Rust
|
|
224
|
+
memory-mapped reader as `MODE_MMAP`.
|
|
225
|
+
|
|
226
|
+
## Examples
|
|
227
|
+
|
|
228
|
+
The `examples/` directory contains complete working examples:
|
|
229
|
+
|
|
230
|
+
- **[basic_usage.py](examples/basic_usage.py)**: simple IP lookups, metadata
|
|
231
|
+
access, and database lifecycle
|
|
232
|
+
- **[context_manager.py](examples/context_manager.py)**:
|
|
233
|
+
using `with` for automatic cleanup
|
|
234
|
+
- **[iterator_demo.py](examples/iterator_demo.py)**:
|
|
235
|
+
iterating over all networks in the database
|
|
236
|
+
- **[batch_processing.py](examples/batch_processing.py)**: batch lookups with
|
|
237
|
+
`get_many()`
|
|
238
|
+
|
|
239
|
+
Run any example:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
uv run python examples/basic_usage.py
|
|
243
|
+
uv run python examples/batch_processing.py
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Documentation
|
|
247
|
+
|
|
248
|
+
- **API documentation**: classes and methods include docstrings. Use `help()`:
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
import maxminddb_rust
|
|
252
|
+
|
|
253
|
+
help(maxminddb_rust.open_database)
|
|
254
|
+
help(maxminddb_rust.Reader.get)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
- **Type hints**: full type stub file (`maxminddb_rust.pyi`) is included for
|
|
258
|
+
IDE autocomplete and type checking
|
|
259
|
+
- **Changelog**: see [CHANGELOG.md](CHANGELOG.md) for version history and
|
|
260
|
+
release notes
|
|
261
|
+
- **Migration Guide**: see [MIGRATION.md](MIGRATION.md) for migrating from the
|
|
262
|
+
official `maxminddb` package
|
|
263
|
+
|
|
264
|
+
## Benchmarking
|
|
265
|
+
|
|
266
|
+
Benchmark scripts are consolidated in the `benchmarks/` directory.
|
|
267
|
+
They default to commonly installed databases under `/var/lib/GeoIP`, preferring
|
|
268
|
+
GeoLite2 City when available. Pass `--file /path/to/database.mmdb` to benchmark
|
|
269
|
+
a different database. The comprehensive benchmark accepts `--file` more than
|
|
270
|
+
once.
|
|
271
|
+
|
|
272
|
+
Run the included benchmarks after building from source:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
# Single lookup benchmark
|
|
276
|
+
uv run python benchmarks/benchmark.py --count 250000
|
|
277
|
+
|
|
278
|
+
# Comprehensive benchmark across multiple databases
|
|
279
|
+
uv run python benchmarks/benchmark_comprehensive.py --count 250000
|
|
280
|
+
|
|
281
|
+
# Batch lookup benchmark
|
|
282
|
+
uv run python benchmarks/benchmark_batch.py --batch-size 100
|
|
283
|
+
|
|
284
|
+
# Threaded lookup benchmark (shared Reader across Python threads, default DB set)
|
|
285
|
+
uv run python benchmarks/benchmark_parallel.py --count 500000 --workers 1,2,4,8
|
|
286
|
+
|
|
287
|
+
# get() vs get_path() benchmark
|
|
288
|
+
uv run python benchmarks/benchmark_path.py --count 250000
|
|
289
|
+
|
|
290
|
+
# Compare benchmark throughput between two git refs
|
|
291
|
+
uv run python benchmarks/compare_refs.py --baseline-ref origin/main --candidate-ref HEAD
|
|
292
|
+
|
|
293
|
+
# CI-friendly comparison with JSON output and a 5% regression threshold
|
|
294
|
+
uv run python benchmarks/compare_refs.py --json-output bench.json --max-regression-pct 5
|
|
295
|
+
|
|
296
|
+
# Force successful lookups, including when using a sparse test database
|
|
297
|
+
uv run python benchmarks/compare_refs.py --workload database-hits
|
|
298
|
+
|
|
299
|
+
# Path cache profiling: cached tuple, new tuple per call, list path per call
|
|
300
|
+
uv run python benchmarks/compare_refs.py --case get_path --case get_path_new_tuple --case get_path_list
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Testing
|
|
304
|
+
|
|
305
|
+
This project includes Python tests, Rust unit tests, and adapted upstream
|
|
306
|
+
compatibility tests from
|
|
307
|
+
[MaxMind-DB-Reader-python](https://github.com/maxmind/MaxMind-DB-Reader-python).
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
# Initialize test data submodule (first time only)
|
|
311
|
+
git submodule update --init --recursive
|
|
312
|
+
|
|
313
|
+
# Run Python tests.
|
|
314
|
+
uv run pytest
|
|
315
|
+
|
|
316
|
+
# Run Rust unit tests.
|
|
317
|
+
cargo test --all-targets --all-features
|
|
318
|
+
|
|
319
|
+
# Run configured linters and formatters.
|
|
320
|
+
uv run precious lint .
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
For upstream test compatibility and syncing instructions, see
|
|
324
|
+
[tests/maxmind/README.md](tests/maxmind/README.md).
|
|
325
|
+
|
|
326
|
+
## License
|
|
327
|
+
|
|
328
|
+
ISC License. See [LICENSE](LICENSE) for details.
|
|
329
|
+
|
|
330
|
+
## Contributing
|
|
331
|
+
|
|
332
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, code quality
|
|
333
|
+
guidelines, and pull request procedures.
|
|
334
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
maxminddb_rust/__init__.py,sha256=CdGbXQ9SetxY7Dc9As9djUQOGHvkWXUQ3rVc2FdG3XY,139
|
|
2
|
+
maxminddb_rust/__init__.pyi,sha256=IqJ4Qtf-bi9HUxoss-H4sF9R9AS4IvIa8Qopmy-VFic,11800
|
|
3
|
+
maxminddb_rust/maxminddb_rust.cp315-win_amd64.pyd,sha256=uL0cP598uVfRCwH_kldfFZri1yFSquoLVjnfvLxBVIs,674816
|
|
4
|
+
maxminddb_rust/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
maxminddb_rust-0.9.0.dist-info/METADATA,sha256=S89D-owe959YZA9lQQiX1ElhRf1i4XDOCDtrPj16hDs,11519
|
|
6
|
+
maxminddb_rust-0.9.0.dist-info/WHEEL,sha256=Q5QdGvjBfn8x6ihg-B4VQuuGKk7DT67L9DVrwEH_qj8,97
|
|
7
|
+
maxminddb_rust-0.9.0.dist-info/licenses/LICENSE,sha256=UXMzEE2L7LGBOeGjwe5NcFoX7waI76pJwJY0rohW-k4,787
|
|
8
|
+
maxminddb_rust-0.9.0.dist-info/sboms/maxminddb-rust.cyclonedx.json,sha256=lwFmgOpOy8Yfv-AOfyhYtzsWt9TeT1yS-tbjkmt0NBg,31681
|
|
9
|
+
maxminddb_rust.pyi,sha256=IqJ4Qtf-bi9HUxoss-H4sF9R9AS4IvIa8Qopmy-VFic,11800
|
|
10
|
+
py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
maxminddb_rust-0.9.0.dist-info/RECORD,,
|