From 58b0fb45d539d9fb4f76b3028b646897f424f2c9 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 2 Oct 2026 22:30:28 +0000 Subject: [PATCH 01/12] Import InvalidDatabaseError from maxminddb.errors maxminddb.decoder does not export InvalidDatabaseError. It only imports it. mypy --strict reports the import as an implicit re-export. Co-Authored-By: Claude Opus 5.5 --- maxminddb/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maxminddb/__init__.py b/maxminddb/__init__.py index b3ef0ef6..576dc1d0 100644 --- a/maxminddb/__init__.py +++ b/maxminddb/__init__.py @@ -13,7 +13,7 @@ MODE_MMAP, MODE_MMAP_EXT, ) -from .decoder import InvalidDatabaseError +from .errors import InvalidDatabaseError from .reader import Reader if TYPE_CHECKING: From 2f405b6ffdd5b2ef7136b7d6457fcb65a033a26c Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 2 Oct 2026 22:38:44 +0000 Subject: [PATCH 02/12] Export Mode from maxminddb Since 3.0.0, the README has said that the modes are available from maxminddb.Mode. maxminddb did not import Mode, so that attribute raised AttributeError. Co-Authored-By: Claude Opus 5.5 --- HISTORY.rst | 2 ++ maxminddb/__init__.py | 2 ++ tests/reader_test.py | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index 82a93809..fe48b3a8 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -56,6 +56,8 @@ History * The C extension ignores unknown keys. Previously, ``Reader.metadata()`` crashed on them. +* Added ``maxminddb.Mode``, which the README already described. + 3.2.0 (2026-09-10) ++++++++++++++++++ diff --git a/maxminddb/__init__.py b/maxminddb/__init__.py index 576dc1d0..c17b230a 100644 --- a/maxminddb/__init__.py +++ b/maxminddb/__init__.py @@ -12,6 +12,7 @@ MODE_MEMORY, MODE_MMAP, MODE_MMAP_EXT, + Mode, ) from .errors import InvalidDatabaseError from .reader import Reader @@ -33,6 +34,7 @@ "MODE_MMAP", "MODE_MMAP_EXT", "InvalidDatabaseError", + "Mode", "Reader", "open_database", ] diff --git a/tests/reader_test.py b/tests/reader_test.py index c0615662..0529f1fc 100644 --- a/tests/reader_test.py +++ b/tests/reader_test.py @@ -1374,6 +1374,11 @@ class TestExtensionReaderWithIPObjects(BaseTestReader): reader_class = maxminddb.extension.Reader +class TestModule(unittest.TestCase): + def test_mode_is_exported(self) -> None: + self.assertIs(maxminddb.Mode, maxminddb.const.Mode) + + @unittest.skipIf( not has_maxminddb_extension() and not os.environ.get("MM_FORCE_EXT_TESTS"), "No C extension module found. Skipping tests", From d357584a248ff5b33deb2f2917681389af45f636 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 2 Oct 2026 22:55:06 +0000 Subject: [PATCH 03/12] Add a release note for the concrete Record types GitHub #464 replaced the AnyStr TypeVar in Primitive with str | bytes. Primitive and Record are no longer generic aliases, so a subscript such as Record[str] now raises TypeError. HISTORY.rst did not mention the change. Co-Authored-By: Claude Opus 5.5 --- HISTORY.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index fe48b3a8..b10300a4 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -57,6 +57,9 @@ History crashed on them. * Added ``maxminddb.Mode``, which the README already described. +* ``maxminddb.types.Record`` and ``Primitive`` are no longer generic type + aliases. Remove any subscript, such as ``Record[str]``. Pull request by Adam + Hitchcock. GitHub #464. 3.2.0 (2026-09-10) ++++++++++++++++++ From 6f27df60f4be45c58c6f2ba29f372d7944162bfd Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 2 Oct 2026 22:36:18 +0000 Subject: [PATCH 04/12] Add bytearray to Primitive The C extension decodes the MaxMind DB bytes type to bytearray. The pure Python reader decodes it to bytes. MODE_AUTO uses the extension when it is available, so most callers get bytearray. mypy does not treat bytearray as bytes, so it reported an isinstance(value, bytearray) check as unreachable. Co-Authored-By: Claude Opus 5.5 --- HISTORY.rst | 5 +++++ maxminddb/types.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index b10300a4..67e20f6c 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -61,6 +61,11 @@ History aliases. Remove any subscript, such as ``Record[str]``. Pull request by Adam Hitchcock. GitHub #464. +* Type hints: + + * ``Primitive`` includes ``bytearray``, which the C extension returns for + the ``bytes`` type. + 3.2.0 (2026-09-10) ++++++++++++++++++ diff --git a/maxminddb/types.py b/maxminddb/types.py index dcb7b61e..eafe1655 100644 --- a/maxminddb/types.py +++ b/maxminddb/types.py @@ -4,7 +4,7 @@ from typing import TypeAlias -Primitive: TypeAlias = str | bytes | bool | float | int +Primitive: TypeAlias = str | bytes | bytearray | bool | float | int RecordList: TypeAlias = list["Record"] """RecordList is a type for lists in a database record.""" From 3961a4263ab4e74b21ee144ec13bda1c9da80144 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 2 Oct 2026 22:36:32 +0000 Subject: [PATCH 05/12] Add type arguments to Reader.__iter__ __iter__ and _generate_children returned a bare Iterator, so strict type checkers inferred Unknown for the network and record of each item. The extension stub already declares the item type. Use the same type. Co-Authored-By: Claude Opus 5.5 --- HISTORY.rst | 1 + maxminddb/reader.py | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 67e20f6c..9bdb45a5 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -65,6 +65,7 @@ History * ``Primitive`` includes ``bytearray``, which the C extension returns for the ``bytes`` type. + * ``Reader.__iter__`` declares its item type. 3.2.0 (2026-09-10) ++++++++++++++++++ diff --git a/maxminddb/reader.py b/maxminddb/reader.py index 46486a9f..fc6b1327 100644 --- a/maxminddb/reader.py +++ b/maxminddb/reader.py @@ -216,10 +216,12 @@ def get_with_prefix_len( return self._resolve_data_pointer(pointer), prefix_len return None, prefix_len - def __iter__(self) -> Iterator: + def __iter__(self) -> Iterator[tuple[IPv4Network | IPv6Network, Record]]: return self._iterate(self._generation) - def _iterate(self, generation: int) -> Iterator: + def _iterate( + self, generation: int + ) -> Iterator[tuple[IPv4Network | IPv6Network, Record]]: children = self._generate_children(0, 0, 0) while True: # Check before the walk resumes and reads more nodes, as the C @@ -234,7 +236,12 @@ def _iterate(self, generation: int) -> Iterator: return yield record - def _generate_children(self, node: int, depth: int, ip_acc: int) -> Iterator: + def _generate_children( + self, + node: int, + depth: int, + ip_acc: int, + ) -> Iterator[tuple[IPv4Network | IPv6Network, Record]]: node_count = self._metadata.node_count bits = 128 if self._metadata.ip_version == 6 else 32 # Skip the IPv4 subtree when an address with a set bit in its first 96 From 616bacdf0b03f66d299ee5a6ddc3536209435089 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 2 Oct 2026 22:36:34 +0000 Subject: [PATCH 06/12] Annotate the __exit__ arguments Both __exit__ methods left their arguments untyped and suppressed the ruff warning. Strict type checkers report the missing annotation. Co-Authored-By: Claude Opus 5.5 --- maxminddb/extension.pyi | 2 +- maxminddb/reader.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/maxminddb/extension.pyi b/maxminddb/extension.pyi index 39ae512f..8d1e5d47 100644 --- a/maxminddb/extension.pyi +++ b/maxminddb/extension.pyi @@ -59,7 +59,7 @@ class Reader: def __iter__(self) -> Iterator[tuple[IPv4Network | IPv6Network, Record]]: ... def __enter__(self) -> Self: ... - def __exit__(self, *args) -> None: ... # noqa: ANN002 + def __exit__(self, *args: object) -> None: ... class Metadata: """Metadata for the MaxMind DB reader.""" diff --git a/maxminddb/reader.py b/maxminddb/reader.py index fc6b1327..45541c51 100644 --- a/maxminddb/reader.py +++ b/maxminddb/reader.py @@ -390,7 +390,7 @@ def close(self) -> None: self.closed = True - def __exit__(self, *_) -> None: # noqa: ANN002 + def __exit__(self, *_: object) -> None: self.close() def __enter__(self) -> Self: From 446e0d7050ccd191351d937befae2186fd613ade Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 2 Oct 2026 22:36:42 +0000 Subject: [PATCH 07/12] Remove an unneeded type: ignore hasattr() never causes an attr-defined error. mypy --strict reports the ignore as unused. The ignore on the os.pread() call stays because Windows has no os.pread. Co-Authored-By: Claude Opus 5.5 --- maxminddb/file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maxminddb/file.py b/maxminddb/file.py index 1901d9ba..1d7945ea 100644 --- a/maxminddb/file.py +++ b/maxminddb/file.py @@ -51,7 +51,7 @@ def close(self) -> None: """Close file.""" self._handle.close() - if hasattr(os, "pread"): # type: ignore[attr-defined] + if hasattr(os, "pread"): def _read(self, buffersize: int, offset: int) -> bytes: """Read that uses pread.""" From 6dbe298ba4b56d7f7e3d9780c1c582a8f2b71b73 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 2 Oct 2026 22:37:12 +0000 Subject: [PATCH 08/12] Add type aliases for the database argument open_database(), Reader.__init__, Reader._load and Reader._load_buffer each spelled out the database argument union. A change to the accepted types had to edit every copy, and nothing caught a missed one. Define StrOrBytesPath and DatabaseSource in maxminddb.types and use them. The extension stub accepts different types, so it keeps its own annotation. Co-Authored-By: Claude Opus 5.5 --- HISTORY.rst | 2 ++ maxminddb/__init__.py | 6 +++--- maxminddb/reader.py | 11 +++++------ maxminddb/types.py | 11 +++++++++-- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 9bdb45a5..10543363 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -66,6 +66,8 @@ History * ``Primitive`` includes ``bytearray``, which the C extension returns for the ``bytes`` type. * ``Reader.__iter__`` declares its item type. + * Added the ``StrOrBytesPath`` and ``DatabaseSource`` aliases to + ``maxminddb.types``. 3.2.0 (2026-09-10) ++++++++++++++++++ diff --git a/maxminddb/__init__.py b/maxminddb/__init__.py index c17b230a..2d96923e 100644 --- a/maxminddb/__init__.py +++ b/maxminddb/__init__.py @@ -3,7 +3,7 @@ from __future__ import annotations from importlib.metadata import version -from typing import IO, TYPE_CHECKING, cast +from typing import TYPE_CHECKING, cast from .const import ( MODE_AUTO, @@ -18,7 +18,7 @@ from .reader import Reader if TYPE_CHECKING: - import os + from .types import DatabaseSource try: from . import extension as _extension @@ -41,7 +41,7 @@ def open_database( - database: str | bytes | int | os.PathLike[str] | os.PathLike[bytes] | IO[bytes], + database: DatabaseSource, mode: int = MODE_AUTO, ) -> Reader: """Open a MaxMind DB database. diff --git a/maxminddb/reader.py b/maxminddb/reader.py index 45541c51..ce1d2b05 100644 --- a/maxminddb/reader.py +++ b/maxminddb/reader.py @@ -11,7 +11,7 @@ import ipaddress from dataclasses import dataclass from ipaddress import IPv4Address, IPv4Network, IPv6Address, IPv6Network -from typing import IO, TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any from maxminddb.const import MODE_AUTO, MODE_FD, MODE_FILE, MODE_MEMORY, MODE_MMAP from maxminddb.decoder import Decoder @@ -20,11 +20,10 @@ if TYPE_CHECKING: from collections.abc import Iterator - from os import PathLike from typing_extensions import Self - from maxminddb.types import Record, RecordDict + from maxminddb.types import DatabaseSource, Record, RecordDict _IPV4_MAX_NUM = 2**32 _REOPENED = "Attempt to iterate over a reopened MaxMind DB. Create a new iterator." @@ -55,7 +54,7 @@ class Reader: def __init__( self, - database: str | bytes | int | PathLike[str] | PathLike[bytes] | IO[bytes], + database: DatabaseSource, mode: int = MODE_AUTO, ) -> None: """Reader for the MaxMind DB file format. @@ -92,7 +91,7 @@ def __init__( def _load( self, - database: str | bytes | int | PathLike[str] | PathLike[bytes] | IO[bytes], + database: DatabaseSource, mode: int, ) -> None: # TRY301 is suppressed because the handler only closes the buffer and @@ -341,7 +340,7 @@ def _resolve_data_pointer(self, pointer: int) -> Record: def _load_buffer( self, - database: str | bytes | int | PathLike[str] | PathLike[bytes] | IO[bytes], + database: DatabaseSource, mode: int = MODE_AUTO, ) -> str: filename: Any diff --git a/maxminddb/types.py b/maxminddb/types.py index eafe1655..bd731da3 100644 --- a/maxminddb/types.py +++ b/maxminddb/types.py @@ -1,8 +1,9 @@ -"""Types representing database records.""" +"""Types for database records and database arguments.""" from __future__ import annotations -from typing import TypeAlias +import os +from typing import IO, TypeAlias Primitive: TypeAlias = str | bytes | bytearray | bool | float | int @@ -13,3 +14,9 @@ """RecordDict is a type for dicts in a database record.""" Record: TypeAlias = Primitive | RecordList | RecordDict + +StrOrBytesPath: TypeAlias = str | bytes | os.PathLike[str] | os.PathLike[bytes] +"""StrOrBytesPath is a type for a path to a database file.""" + +DatabaseSource: TypeAlias = StrOrBytesPath | int | IO[bytes] +"""DatabaseSource is a type for the database argument of a reader.""" From a8dd6d435f196c44de645e139ebfe839bc5bba21 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 2 Oct 2026 22:59:42 +0000 Subject: [PATCH 09/12] Accept only paths in the extension Reader stub The extension parses the database argument with PyUnicode_FSConverter, so it accepts only str, bytes and os.PathLike. The stub also accepted int and IO[bytes], which raise TypeError. The extension does not support MODE_FD, so the docstring was wrong too. open_database() passes any database argument to the extension in MODE_AUTO and MODE_MMAP_EXT. The narrower stub shows that mismatch. Keep the runtime behavior and explain the type: ignore. Co-Authored-By: Claude Opus 5.5 --- HISTORY.rst | 2 ++ maxminddb/__init__.py | 5 +++-- maxminddb/extension.pyi | 8 +++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 10543363..b2521fb8 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -68,6 +68,8 @@ History * ``Reader.__iter__`` declares its item type. * Added the ``StrOrBytesPath`` and ``DatabaseSource`` aliases to ``maxminddb.types``. + * The ``maxminddb.extension.Reader`` stub accepts only a path, as the + extension does. 3.2.0 (2026-09-10) ++++++++++++++++++ diff --git a/maxminddb/__init__.py b/maxminddb/__init__.py index 2d96923e..49b8576c 100644 --- a/maxminddb/__init__.py +++ b/maxminddb/__init__.py @@ -86,8 +86,9 @@ def open_database( # The C type exposes the same API as the Python Reader, so for type # checking purposes, pretend it is one. (Ideally this would be a subclass # of, or share a common parent class with, the Python Reader - # implementation.) - return cast("Reader", _extension.Reader(database, mode)) + # implementation.) The extension accepts only a path. It raises TypeError + # for a file descriptor or a file object. + return cast("Reader", _extension.Reader(database, mode)) # type: ignore[arg-type] __version__ = version("maxminddb") diff --git a/maxminddb/extension.pyi b/maxminddb/extension.pyi index 8d1e5d47..19c8d4fe 100644 --- a/maxminddb/extension.pyi +++ b/maxminddb/extension.pyi @@ -2,12 +2,10 @@ from collections.abc import Iterator from ipaddress import IPv4Address, IPv4Network, IPv6Address, IPv6Network -from os import PathLike -from typing import IO from typing_extensions import Self -from maxminddb.types import Record +from maxminddb.types import Record, StrOrBytesPath class Reader: """A C extension implementation of a reader for the MaxMind DB format. @@ -19,14 +17,14 @@ class Reader: def __init__( self, - database: str | bytes | int | PathLike[str] | PathLike[bytes] | IO[bytes], + database: StrOrBytesPath, mode: int = ..., ) -> None: """Reader for the MaxMind DB file format. Arguments: database: A path to a valid MaxMind DB file such as a GeoIP database - file, or a file descriptor in the case of MODE_FD. + file. mode: mode to open the database with. The only supported modes are MODE_AUTO and MODE_MMAP_EXT. From 06f9e5cd226064ec11a6d14cc9cf9c3b3b7567de Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 2 Oct 2026 22:37:57 +0000 Subject: [PATCH 10/12] Accept any binary reader with MODE_FD MODE_FD calls only database.read() and reads database.name when it exists. IO[bytes] requires much more, so a GzipFile and other binary readers did not type-check, although they work at runtime. Accept any object with a read() method that returns bytes. Co-Authored-By: Claude Opus 5.5 --- HISTORY.rst | 3 +++ maxminddb/types.py | 12 ++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index b2521fb8..5d49a971 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -70,6 +70,9 @@ History ``maxminddb.types``. * The ``maxminddb.extension.Reader`` stub accepts only a path, as the extension does. + * ``MODE_FD`` accepts any object whose ``read()`` method returns ``bytes``, + such as a ``gzip.GzipFile``. ``maxminddb.types.SupportsRead`` describes + this type. 3.2.0 (2026-09-10) ++++++++++++++++++ diff --git a/maxminddb/types.py b/maxminddb/types.py index bd731da3..b14eabb3 100644 --- a/maxminddb/types.py +++ b/maxminddb/types.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import IO, TypeAlias +from typing import Protocol, TypeAlias Primitive: TypeAlias = str | bytes | bytearray | bool | float | int @@ -18,5 +18,13 @@ StrOrBytesPath: TypeAlias = str | bytes | os.PathLike[str] | os.PathLike[bytes] """StrOrBytesPath is a type for a path to a database file.""" -DatabaseSource: TypeAlias = StrOrBytesPath | int | IO[bytes] + +class SupportsRead(Protocol): + """SupportsRead is a type for a binary file object for MODE_FD.""" + + def read(self) -> bytes: + """Return the remaining bytes.""" + + +DatabaseSource: TypeAlias = StrOrBytesPath | int | SupportsRead """DatabaseSource is a type for the database argument of a reader.""" From 1bd4629b323809ec6d18f000a23b9f0a6558e7bd Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Sat, 3 Oct 2026 04:30:57 +0000 Subject: [PATCH 11/12] Document maxminddb.types The release notes point users to the type aliases in maxminddb.types, such as Record, DatabaseSource and SupportsRead, but the API docs did not include that module. Add it. Co-Authored-By: Claude Opus 5.5 --- docs/index.rst | 8 ++++++++ maxminddb/types.py | 2 ++ 2 files changed, 10 insertions(+) diff --git a/docs/index.rst b/docs/index.rst index b062a0d9..5eca7cb3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -35,6 +35,14 @@ Database Reader :undoc-members: :show-inheritance: +===== +Types +===== + +.. automodule:: maxminddb.types + :members: + :undoc-members: + ================== Indices and tables ================== diff --git a/maxminddb/types.py b/maxminddb/types.py index b14eabb3..eee642e1 100644 --- a/maxminddb/types.py +++ b/maxminddb/types.py @@ -6,6 +6,7 @@ from typing import Protocol, TypeAlias Primitive: TypeAlias = str | bytes | bytearray | bool | float | int +"""Primitive is a type for a scalar value in a database record.""" RecordList: TypeAlias = list["Record"] """RecordList is a type for lists in a database record.""" @@ -14,6 +15,7 @@ """RecordDict is a type for dicts in a database record.""" Record: TypeAlias = Primitive | RecordList | RecordDict +"""Record is a type for a value in a database record.""" StrOrBytesPath: TypeAlias = str | bytes | os.PathLike[str] | os.PathLike[bytes] """StrOrBytesPath is a type for a path to a database file.""" From 8ff0a38020a503142c7ceb9c79894657e765267a Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 2 Oct 2026 22:42:11 +0000 Subject: [PATCH 12/12] Add static type checks for the public API No test checked the public types, and non-strict mypy accepts a bare generic alias with no error. A TypeVar back in Primitive, a bare Iterator, or a wider stub would pass CI. mypy already checks tests/ in the lint environment. Add assert_type checks under TYPE_CHECKING, so the file costs nothing at runtime. The file enables warn-unused-ignores, so each type: ignore asserts that a call fails the type check. Co-Authored-By: Claude Opus 5.5 --- tests/typing_test.py | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 tests/typing_test.py diff --git a/tests/typing_test.py b/tests/typing_test.py new file mode 100644 index 00000000..204ba660 --- /dev/null +++ b/tests/typing_test.py @@ -0,0 +1,54 @@ +# mypy: warn-unused-ignores +"""Static type checks for the public API. + +mypy checks this file in the lint environment. The code does not run. Each +type: ignore marks a line that must fail the type check. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + import gzip + import io + from ipaddress import IPv4Network, IPv6Network + from pathlib import Path + + from typing_extensions import assert_type + + import maxminddb + import maxminddb.extension + from maxminddb.types import Primitive, Record + + reader = maxminddb.open_database("GeoIP2-City.mmdb") + assert_type(reader.get("1.1.1.1"), Record | None) + assert_type(reader.get_with_prefix_len("1.1.1.1"), tuple[Record | None, int]) + for network, record in reader: + assert_type(network, IPv4Network | IPv6Network) + assert_type(record, Record) + assert_type(reader.metadata().search_tree_size, int) + with maxminddb.open_database(Path("GeoIP2-City.mmdb")) as path_reader: + assert_type(path_reader, maxminddb.Reader) + + # Record includes the bytearray that the C extension returns for the + # bytes type. + value: Record = bytearray(b"\x00") + + # A TypeVar in either alias would give Record members of type Any. + def check_not_generic( + primitive: Primitive[str], # type: ignore[type-arg] + record: Record[str], # type: ignore[type-arg] + ) -> None: + pass + + def check_mode_fd(gzip_file: gzip.GzipFile, text_file: io.TextIOWrapper) -> None: + maxminddb.open_database(gzip_file, maxminddb.Mode.FD) + maxminddb.open_database(text_file, maxminddb.Mode.FD) # type: ignore[arg-type] + + extension_reader = maxminddb.extension.Reader("GeoIP2-City.mmdb") + for network, record in extension_reader: + assert_type(network, IPv4Network | IPv6Network) + assert_type(record, Record) + assert_type(extension_reader.metadata().node_byte_size, int) + maxminddb.extension.Reader(3) # type: ignore[arg-type]