Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion examples/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,3 @@ def lookup_ip_address() -> None:
number=args.count,
)

print(f"{int(args.count / elapsed):,}", "lookups per second")
6 changes: 4 additions & 2 deletions maxminddb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ def open_database(
MODE_MMAP,
MODE_MMAP_EXT,
):
raise ValueError(f"Unsupported open mode: {mode}")
msg = f"Unsupported open mode: {mode}"
raise ValueError(msg)

has_extension = _extension and hasattr(_extension, "Reader")
use_extension = has_extension if mode == MODE_AUTO else mode == MODE_MMAP_EXT
Expand All @@ -72,8 +73,9 @@ def open_database(
return Reader(database, mode)

if not has_extension:
msg = "MODE_MMAP_EXT requires the maxminddb.extension module to be available"
raise ValueError(
"MODE_MMAP_EXT requires the maxminddb.extension module to be available",
msg,
)

# The C type exposes the same API as the Python Reader, so for type
Expand Down
17 changes: 12 additions & 5 deletions maxminddb/decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,9 @@ def decode(self, offset: int) -> tuple[Record, int]:
try:
decoder = self._type_decoder[type_num]
except KeyError as ex:
msg = f"Unexpected type number ({type_num}) encountered"
raise InvalidDatabaseError(
f"Unexpected type number ({type_num}) encountered",
msg,
) from ex

(size, new_offset) = self._size_from_ctrl_byte(ctrl_byte, new_offset, type_num)
Expand All @@ -160,18 +161,24 @@ def _read_extended(self, offset: int) -> tuple[int, int]:
next_byte = self._buffer[offset]
type_num = next_byte + 7
if type_num < 7:
raise InvalidDatabaseError(
msg = (
"Something went horribly wrong in the decoder. An "
f"extended type resolved to a type number < 8 ({type_num})",
f"extended type resolved to a type number < 8 ({type_num})"
)
raise InvalidDatabaseError(
msg,
)
return type_num, offset + 1

@staticmethod
def _verify_size(expected: int, actual: int) -> None:
if expected != actual:
raise InvalidDatabaseError(
msg = (
"The MaxMind DB file's data section contains bad data "
"(unknown data type or corrupt data)",
"(unknown data type or corrupt data)"
)
raise InvalidDatabaseError(
msg,
)

def _size_from_ctrl_byte(
Expand Down
52 changes: 17 additions & 35 deletions maxminddb/extension.pyi
Original file line number Diff line number Diff line change
@@ -1,65 +1,47 @@
"""C extension database reader and related classes."""


# pylint: disable=E0601,E0602
from ipaddress import IPv4Address, IPv6Address
from os import PathLike
from typing import IO, Any, AnyStr, Optional, Union
from typing import IO, Any, AnyStr

from typing_extensions import Self

from maxminddb.types import Record

class Reader:
"""A C extension implementation of a reader for the MaxMind DB format. IP
addresses can be looked up using the ``get`` method.
"""


closed: bool = ...

def __init__(
self,
database: Union[AnyStr, int, PathLike, IO],
database: AnyStr | int | PathLike | IO,
mode: int = ...,
) -> None:
"""Reader for the MaxMind DB file format

Arguments:
database -- A path to a valid MaxMind DB file such as a GeoIP2 database
file, or a file descriptor in the case of MODE_FD.
mode -- mode to open the database with. The only supported modes are
MODE_AUTO and MODE_MMAP_EXT.

"""
...

def close(self) -> None:
"""Closes the MaxMind DB file and returns the resources to the system"""
...

def get(self, ip_address: Union[str, IPv6Address, IPv4Address]) -> Optional[Record]:
"""Return the record for the ip_address in the MaxMind DB

Arguments:
ip_address -- an IP address in the standard string notation

"""
def get(self, ip_address: str | IPv6Address | IPv4Address) -> Record | None:
...

def get_with_prefix_len(
self,
ip_address: Union[str, IPv6Address, IPv4Address],
) -> tuple[Optional[Record], int]:
"""Return a tuple with the record and the associated prefix length

Arguments:
ip_address -- an IP address in the standard string notation

"""
ip_address: str | IPv6Address | IPv4Address,
) -> tuple[Record | None, int]:
...

def metadata(self) -> Metadata:
"""Return the metadata associated with the MaxMind DB file"""
...

def __enter__(self) -> Reader: ...
def __enter__(self) -> Self: ...
def __exit__(self, *args) -> None: ...

# pylint: disable=too-few-public-methods
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we want to delete this pylint comment?

class Metadata:
"""Metadata for the MaxMind DB reader"""


binary_format_major_version: int
"""
Expand Down Expand Up @@ -111,4 +93,4 @@ class Metadata:
"""

def __init__(self, **kwargs: Any) -> None:
"""Creates new Metadata object. kwargs are key/value pairs from spec"""
...
3 changes: 2 additions & 1 deletion maxminddb/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ def __getitem__(self, key: Union[slice, int]):
return self._read(key.stop - key.start, key.start)
if isinstance(key, int):
return self._read(1, key)[0]
raise TypeError("Invalid argument type.")
msg = "Invalid argument type."
raise TypeError(msg)

def rfind(self, needle: bytes, start: int) -> int:
"""Reverse find needle from start."""
Expand Down
30 changes: 21 additions & 9 deletions maxminddb/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,13 @@ def __init__(
self._buffer_size = len(self._buffer) # type: ignore
filename = database.name # type: ignore
else:
raise ValueError(
msg = (
f"Unsupported open mode ({mode}). Only MODE_AUTO, MODE_FILE, "
"MODE_MEMORY and MODE_FD are supported by the pure Python "
"Reader",
"Reader"
)
raise ValueError(
msg,
)

metadata_start = self._buffer.rfind(
Expand All @@ -90,18 +93,22 @@ def __init__(

if metadata_start == -1:
self.close()
raise InvalidDatabaseError(
msg = (
f"Error opening database file ({filename}). "
"Is this a valid MaxMind DB file?",
"Is this a valid MaxMind DB file?"
)
raise InvalidDatabaseError(
msg,
)

metadata_start += len(self._METADATA_START_MARKER)
metadata_decoder = Decoder(self._buffer, metadata_start)
(metadata, _) = metadata_decoder.decode(metadata_start)

if not isinstance(metadata, dict):
msg = f"Error reading metadata in database file ({filename})."
raise InvalidDatabaseError(
f"Error reading metadata in database file ({filename}).",
msg,
)

self._metadata = Metadata(**metadata) # pylint: disable=bad-option-value
Expand Down Expand Up @@ -157,12 +164,16 @@ def get_with_prefix_len(
try:
packed_address = bytearray(address.packed)
except AttributeError as ex:
raise TypeError("argument 1 must be a string or ipaddress object") from ex
msg = "argument 1 must be a string or ipaddress object"
raise TypeError(msg) from ex

if address.version == 6 and self._metadata.ip_version == 4:
raise ValueError(
msg = (
f"Error looking up {ip_address}. You attempted to look up "
"an IPv6 address in an IPv4-only database.",
"an IPv6 address in an IPv4-only database."
)
raise ValueError(
msg,
)

(pointer, prefix_len) = self._find_address_in_tree(packed_address)
Expand Down Expand Up @@ -216,7 +227,8 @@ def _find_address_in_tree(self, packed: bytearray) -> tuple[int, int]:
if node > node_count:
return node, i

raise InvalidDatabaseError("Invalid node in search tree")
msg = "Invalid node in search tree"
raise InvalidDatabaseError(msg)

def _start_node(self, length: int) -> int:
if self._metadata.ip_version == 6 and length == 32:
Expand Down