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.
@@ -0,0 +1,5 @@
1
+ from .maxminddb_rust import *
2
+
3
+ __doc__ = maxminddb_rust.__doc__
4
+ if hasattr(maxminddb_rust, "__all__"):
5
+ __all__ = maxminddb_rust.__all__
@@ -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
+ """
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,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.16.0)
3
+ Root-Is-Purelib: false
4
+ Tag: cp315-cp315-win_amd64