maxminddb 2.6.3__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 (80) hide show
  1. {maxminddb-2.6.3 → maxminddb-2.7.0}/HISTORY.rst +11 -0
  2. {maxminddb-2.6.3/maxminddb.egg-info → maxminddb-2.7.0}/PKG-INFO +9 -10
  3. {maxminddb-2.6.3 → maxminddb-2.7.0}/README.rst +4 -4
  4. {maxminddb-2.6.3 → maxminddb-2.7.0}/docs/conf.py +1 -2
  5. {maxminddb-2.6.3 → maxminddb-2.7.0}/docs/index.rst +11 -3
  6. {maxminddb-2.6.3 → maxminddb-2.7.0}/examples/benchmark.py +5 -5
  7. {maxminddb-2.6.3 → maxminddb-2.7.0}/extension/libmaxminddb/src/data-pool.c +6 -2
  8. {maxminddb-2.6.3 → maxminddb-2.7.0}/extension/libmaxminddb/src/maxminddb.c +2 -0
  9. {maxminddb-2.6.3 → maxminddb-2.7.0}/extension/maxminddb.c +10 -9
  10. {maxminddb-2.6.3 → maxminddb-2.7.0}/maxminddb/__init__.py +7 -4
  11. {maxminddb-2.6.3 → maxminddb-2.7.0}/maxminddb/const.py +1 -1
  12. {maxminddb-2.6.3 → maxminddb-2.7.0}/maxminddb/decoder.py +29 -30
  13. maxminddb-2.7.0/maxminddb/errors.py +5 -0
  14. {maxminddb-2.6.3 → maxminddb-2.7.0}/maxminddb/extension.pyi +19 -20
  15. {maxminddb-2.6.3 → maxminddb-2.7.0}/maxminddb/file.py +6 -6
  16. {maxminddb-2.6.3 → maxminddb-2.7.0}/maxminddb/reader.py +43 -41
  17. maxminddb-2.7.0/maxminddb/types.py +14 -0
  18. {maxminddb-2.6.3 → maxminddb-2.7.0/maxminddb.egg-info}/PKG-INFO +9 -10
  19. {maxminddb-2.6.3 → maxminddb-2.7.0}/maxminddb.egg-info/SOURCES.txt +1 -0
  20. {maxminddb-2.6.3 → maxminddb-2.7.0}/pyproject.toml +48 -9
  21. {maxminddb-2.6.3 → maxminddb-2.7.0}/setup.cfg +3 -4
  22. {maxminddb-2.6.3 → maxminddb-2.7.0}/setup.py +26 -22
  23. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/decoder_test.py +34 -36
  24. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/reader_test.py +153 -114
  25. maxminddb-2.7.0/uv.lock +359 -0
  26. maxminddb-2.6.3/maxminddb/errors.py +0 -10
  27. maxminddb-2.6.3/maxminddb/types.py +0 -23
  28. {maxminddb-2.6.3 → maxminddb-2.7.0}/LICENSE +0 -0
  29. {maxminddb-2.6.3 → maxminddb-2.7.0}/MANIFEST.in +0 -0
  30. {maxminddb-2.6.3 → maxminddb-2.7.0}/docs/Makefile +0 -0
  31. {maxminddb-2.6.3 → maxminddb-2.7.0}/extension/libmaxminddb/include/maxminddb.h +0 -0
  32. {maxminddb-2.6.3 → maxminddb-2.7.0}/extension/libmaxminddb/src/data-pool.h +0 -0
  33. {maxminddb-2.6.3 → maxminddb-2.7.0}/extension/libmaxminddb/src/maxminddb-compat-util.h +0 -0
  34. {maxminddb-2.6.3 → maxminddb-2.7.0}/extension/maxminddb_config.h +0 -0
  35. {maxminddb-2.6.3 → maxminddb-2.7.0}/maxminddb/py.typed +0 -0
  36. {maxminddb-2.6.3 → maxminddb-2.7.0}/maxminddb.egg-info/dependency_links.txt +0 -0
  37. {maxminddb-2.6.3 → maxminddb-2.7.0}/maxminddb.egg-info/top_level.txt +0 -0
  38. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/bad-data/libmaxminddb/libmaxminddb-offset-integer-overflow.mmdb +0 -0
  39. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/bad-data/maxminddb-golang/cyclic-data-structure.mmdb +0 -0
  40. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/bad-data/maxminddb-golang/invalid-bytes-length.mmdb +0 -0
  41. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/bad-data/maxminddb-golang/invalid-data-record-offset.mmdb +0 -0
  42. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/bad-data/maxminddb-golang/invalid-map-key-length.mmdb +0 -0
  43. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/bad-data/maxminddb-golang/invalid-string-length.mmdb +0 -0
  44. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/bad-data/maxminddb-golang/metadata-is-an-uint128.mmdb +0 -0
  45. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/bad-data/maxminddb-golang/unexpected-bytes.mmdb +0 -0
  46. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/bad-data/maxminddb-python/bad-unicode-in-map-key.mmdb +0 -0
  47. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb +0 -0
  48. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb +0 -0
  49. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb +0 -0
  50. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoIP2-City-Test.mmdb +0 -0
  51. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoIP2-Connection-Type-Test.mmdb +0 -0
  52. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoIP2-Country-Test.mmdb +0 -0
  53. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoIP2-DensityIncome-Test.mmdb +0 -0
  54. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoIP2-Domain-Test.mmdb +0 -0
  55. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoIP2-Enterprise-Test.mmdb +0 -0
  56. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoIP2-ISP-Test.mmdb +0 -0
  57. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb +0 -0
  58. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb +0 -0
  59. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoIP2-User-Count-Test.mmdb +0 -0
  60. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoLite2-ASN-Test.mmdb +0 -0
  61. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoLite2-City-Test.mmdb +0 -0
  62. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/GeoLite2-Country-Test.mmdb +0 -0
  63. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb +0 -0
  64. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-string-value-entries.mmdb +0 -0
  65. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-test-broken-pointers-24.mmdb +0 -0
  66. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-test-broken-search-tree-24.mmdb +0 -0
  67. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-test-decoder.mmdb +0 -0
  68. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-test-ipv4-24.mmdb +0 -0
  69. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-test-ipv4-28.mmdb +0 -0
  70. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-test-ipv4-32.mmdb +0 -0
  71. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-test-ipv6-24.mmdb +0 -0
  72. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-test-ipv6-28.mmdb +0 -0
  73. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-test-ipv6-32.mmdb +0 -0
  74. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb +0 -0
  75. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-test-mixed-24.mmdb +0 -0
  76. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-test-mixed-28.mmdb +0 -0
  77. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-test-mixed-32.mmdb +0 -0
  78. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-test-nested.mmdb +0 -0
  79. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/MaxMind-DB-test-pointer-decoder.mmdb +0 -0
  80. {maxminddb-2.6.3 → maxminddb-2.7.0}/tests/data/test-data/maps-with-pointers.raw +0 -0
@@ -3,6 +3,17 @@
3
3
  History
4
4
  -------
5
5
 
6
+ 2.7.0 (2025-05-05)
7
+ ++++++++++++++++++
8
+
9
+ * IMPORTANT: Python 3.9 or greater is required. If you are using an older
10
+ version, please use an earlier release.
11
+ * The vendored ``libmaxminddb`` has been updated to 1.12.2.
12
+ * The C extension now checks that the database metadata lookup was
13
+ successful.
14
+ * A theoretical segmentation fault with the C extension when doing lookups
15
+ on a corrupt or invalid database was fixed.
16
+
6
17
  2.6.3 (2025-01-09)
7
18
  ++++++++++++++++++
8
19
 
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: maxminddb
3
- Version: 2.6.3
3
+ Version: 2.7.0
4
4
  Summary: Reader for the MaxMind DB format
5
5
  Author-email: Gregory Oschwald <goschwald@maxmind.com>
6
- License: Apache License, Version 2.0
6
+ License-Expression: Apache-2.0
7
7
  Project-URL: Homepage, https://www.maxmind.com/
8
8
  Project-URL: Documentation, https://maxminddb.readthedocs.org/
9
9
  Project-URL: Source Code, https://github.com/maxmind/MaxMind-DB-Reader-python
@@ -12,10 +12,8 @@ Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Environment :: Web Environment
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: Intended Audience :: System Administrators
15
- Classifier: License :: OSI Approved :: Apache Software License
16
15
  Classifier: Programming Language :: Python
17
16
  Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.8
19
17
  Classifier: Programming Language :: Python :: 3.9
20
18
  Classifier: Programming Language :: Python :: 3.10
21
19
  Classifier: Programming Language :: Python :: 3.11
@@ -23,9 +21,10 @@ Classifier: Programming Language :: Python :: 3.12
23
21
  Classifier: Programming Language :: Python :: 3.13
24
22
  Classifier: Topic :: Internet
25
23
  Classifier: Topic :: Internet :: Proxy Servers
26
- Requires-Python: >=3.8
24
+ Requires-Python: >=3.9
27
25
  Description-Content-Type: text/x-rst
28
26
  License-File: LICENSE
27
+ Dynamic: license-file
29
28
 
30
29
  ========================
31
30
  MaxMind DB Python Module
@@ -43,18 +42,18 @@ subnets (IPv4 or IPv6).
43
42
  Installation
44
43
  ------------
45
44
 
46
- To install maxminddb, type:
45
+ To install ``maxminddb```, type:
47
46
 
48
47
  .. code-block:: bash
49
48
 
50
49
  $ pip install maxminddb
51
50
 
52
- If you are not able to use pip, you may also use easy_install from the
51
+ If you are not able to install from PyPI, you may also use ``pip`` from the
53
52
  source directory:
54
53
 
55
54
  .. code-block:: bash
56
55
 
57
- $ easy_install .
56
+ $ python -m pip install .
58
57
 
59
58
  The installer will attempt to build the C extension. If this fails, the
60
59
  module will fall-back to the pure Python implementation.
@@ -126,7 +125,7 @@ invalid IP address or an IPv6 address in an IPv4 database.
126
125
  Requirements
127
126
  ------------
128
127
 
129
- This code requires Python 3.8+. Older versions are not supported. The C
128
+ This code requires Python 3.9+. Older versions are not supported. The C
130
129
  extension requires CPython.
131
130
 
132
131
  Versioning
@@ -14,18 +14,18 @@ subnets (IPv4 or IPv6).
14
14
  Installation
15
15
  ------------
16
16
 
17
- To install maxminddb, type:
17
+ To install ``maxminddb```, type:
18
18
 
19
19
  .. code-block:: bash
20
20
 
21
21
  $ pip install maxminddb
22
22
 
23
- If you are not able to use pip, you may also use easy_install from the
23
+ If you are not able to install from PyPI, you may also use ``pip`` from the
24
24
  source directory:
25
25
 
26
26
  .. code-block:: bash
27
27
 
28
- $ easy_install .
28
+ $ python -m pip install .
29
29
 
30
30
  The installer will attempt to build the C extension. If this fails, the
31
31
  module will fall-back to the pure Python implementation.
@@ -97,7 +97,7 @@ invalid IP address or an IPv6 address in an IPv4 database.
97
97
  Requirements
98
98
  ------------
99
99
 
100
- This code requires Python 3.8+. Older versions are not supported. The C
100
+ This code requires Python 3.9+. Older versions are not supported. The C
101
101
  extension requires CPython.
102
102
 
103
103
  Versioning
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
2
  #
4
3
  # maxminddb documentation build configuration file, created by
5
4
  # sphinx-quickstart on Tue Apr 9 13:34:57 2013.
@@ -12,8 +11,8 @@
12
11
  # All configuration values have a default; values that are commented out
13
12
  # serve to show the default.
14
13
 
15
- import sys
16
14
  import os
15
+ import sys
17
16
 
18
17
  sys.path.insert(0, os.path.abspath(".."))
19
18
  import maxminddb
@@ -8,20 +8,28 @@
8
8
 
9
9
  .. include:: ../README.rst
10
10
 
11
- =======
12
- Modules
13
- =======
11
+ ======
12
+ Module
13
+ ======
14
14
 
15
15
  .. automodule:: maxminddb
16
16
  :members:
17
17
  :undoc-members:
18
18
  :show-inheritance:
19
19
 
20
+ ======
21
+ Errors
22
+ ======
23
+
20
24
  .. automodule:: maxminddb.errors
21
25
  :members:
22
26
  :undoc-members:
23
27
  :show-inheritance:
24
28
 
29
+ ===============
30
+ Database Reader
31
+ ===============
32
+
25
33
  .. automodule:: maxminddb.reader
26
34
  :members:
27
35
  :undoc-members:
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/python
2
- # -*- coding: utf-8 -*-
3
2
 
4
3
  import argparse
5
- import maxminddb
6
4
  import random
7
5
  import socket
8
6
  import struct
9
7
  import timeit
10
8
 
9
+ import maxminddb
10
+
11
11
  parser = argparse.ArgumentParser(description="Benchmark maxminddb.")
12
12
  parser.add_argument("--count", default=250000, type=int, help="number of lookups")
13
13
  parser.add_argument("--mode", default=0, type=int, help="reader mode to use")
@@ -19,9 +19,9 @@ random.seed(0)
19
19
  reader = maxminddb.open_database(args.file, args.mode)
20
20
 
21
21
 
22
- def lookup_ip_address():
22
+ def lookup_ip_address() -> None:
23
23
  ip = socket.inet_ntoa(struct.pack("!L", random.getrandbits(32)))
24
- record = reader.get(str(ip))
24
+ reader.get(str(ip))
25
25
 
26
26
 
27
27
  elapsed = timeit.timeit(
@@ -30,4 +30,4 @@ elapsed = timeit.timeit(
30
30
  number=args.count,
31
31
  )
32
32
 
33
- print("{:,}".format(int(args.count / elapsed)), "lookups per second")
33
+ print(f"{int(args.count / elapsed):,}", "lookups per second")
@@ -158,9 +158,13 @@ int main(void) {
158
158
  }
159
159
 
160
160
  static void test_can_multiply(void) {
161
- { ok(can_multiply(SIZE_MAX, 1, SIZE_MAX), "1*SIZE_MAX is ok"); }
161
+ {
162
+ ok(can_multiply(SIZE_MAX, 1, SIZE_MAX), "1*SIZE_MAX is ok");
163
+ }
162
164
 
163
- { ok(!can_multiply(SIZE_MAX, 2, SIZE_MAX), "2*SIZE_MAX is not ok"); }
165
+ {
166
+ ok(!can_multiply(SIZE_MAX, 2, SIZE_MAX), "2*SIZE_MAX is not ok");
167
+ }
164
168
 
165
169
  {
166
170
  ok(can_multiply(SIZE_MAX, 10240, sizeof(MMDB_entry_data_list_s)),
@@ -1636,6 +1636,8 @@ int MMDB_get_metadata_as_entry_data_list(
1636
1636
 
1637
1637
  int MMDB_get_entry_data_list(MMDB_entry_s *start,
1638
1638
  MMDB_entry_data_list_s **const entry_data_list) {
1639
+ *entry_data_list = NULL;
1640
+
1639
1641
  MMDB_data_pool_s *const pool = data_pool_new(MMDB_POOL_INIT_SIZE);
1640
1642
  if (!pool) {
1641
1643
  return MMDB_OUT_OF_MEMORY_ERROR;
@@ -351,7 +351,14 @@ static PyObject *Reader_metadata(PyObject *self, PyObject *UNUSED(args)) {
351
351
  }
352
352
 
353
353
  MMDB_entry_data_list_s *entry_data_list;
354
- MMDB_get_metadata_as_entry_data_list(mmdb_obj->mmdb, &entry_data_list);
354
+ int status =
355
+ MMDB_get_metadata_as_entry_data_list(mmdb_obj->mmdb, &entry_data_list);
356
+ if (status != MMDB_SUCCESS) {
357
+ PyErr_Format(MaxMindDB_error,
358
+ "Error decoding metadata. %s",
359
+ MMDB_strerror(status));
360
+ return NULL;
361
+ }
355
362
  MMDB_entry_data_list_s *original_entry_data_list = entry_data_list;
356
363
 
357
364
  PyObject *metadata_dict = from_entry_data_list(&entry_data_list);
@@ -744,10 +751,7 @@ static PyObject *from_map(MMDB_entry_data_list_s **entry_data_list) {
744
751
  const uint32_t map_size = (*entry_data_list)->entry_data.data_size;
745
752
 
746
753
  uint32_t i;
747
- // entry_data_list cannot start out NULL (see from_entry_data_list). We
748
- // check it in the loop because it may become NULL.
749
- // coverity[check_after_deref]
750
- for (i = 0; i < map_size && entry_data_list; i++) {
754
+ for (i = 0; i < map_size && *entry_data_list; i++) {
751
755
  *entry_data_list = (*entry_data_list)->next;
752
756
 
753
757
  PyObject *key = PyUnicode_FromStringAndSize(
@@ -785,10 +789,7 @@ static PyObject *from_array(MMDB_entry_data_list_s **entry_data_list) {
785
789
  }
786
790
 
787
791
  uint32_t i;
788
- // entry_data_list cannot start out NULL (see from_entry_data_list). We
789
- // check it in the loop because it may become NULL.
790
- // coverity[check_after_deref]
791
- for (i = 0; i < size && entry_data_list; i++) {
792
+ for (i = 0; i < size && *entry_data_list; i++) {
792
793
  *entry_data_list = (*entry_data_list)->next;
793
794
  PyObject *value = from_entry_data_list(entry_data_list);
794
795
  if (value == NULL) {
@@ -1,3 +1,5 @@
1
+ """Module for reading MaxMind DB files."""
2
+
1
3
  # pylint:disable=C0111
2
4
  import os
3
5
  from typing import IO, AnyStr, Union, cast
@@ -21,13 +23,13 @@ except ImportError:
21
23
 
22
24
 
23
25
  __all__ = [
24
- "InvalidDatabaseError",
25
26
  "MODE_AUTO",
26
27
  "MODE_FD",
27
28
  "MODE_FILE",
28
29
  "MODE_MEMORY",
29
30
  "MODE_MMAP",
30
31
  "MODE_MMAP_EXT",
32
+ "InvalidDatabaseError",
31
33
  "Reader",
32
34
  "open_database",
33
35
  ]
@@ -37,7 +39,7 @@ def open_database(
37
39
  database: Union[AnyStr, int, os.PathLike, IO],
38
40
  mode: int = MODE_AUTO,
39
41
  ) -> Reader:
40
- """Open a MaxMind DB database
42
+ """Open a MaxMind DB database.
41
43
 
42
44
  Arguments:
43
45
  database -- A path to a valid MaxMind DB file such as a GeoIP2 database
@@ -51,6 +53,7 @@ def open_database(
51
53
  a path. This mode implies MODE_MEMORY.
52
54
  * MODE_AUTO - tries MODE_MMAP_EXT, MODE_MMAP, MODE_FILE in that
53
55
  order. Default mode.
56
+
54
57
  """
55
58
  if mode not in (
56
59
  MODE_AUTO,
@@ -70,7 +73,7 @@ def open_database(
70
73
 
71
74
  if not has_extension:
72
75
  raise ValueError(
73
- "MODE_MMAP_EXT requires the maxminddb.extension module to be available"
76
+ "MODE_MMAP_EXT requires the maxminddb.extension module to be available",
74
77
  )
75
78
 
76
79
  # The C type exposes the same API as the Python Reader, so for type
@@ -81,7 +84,7 @@ def open_database(
81
84
 
82
85
 
83
86
  __title__ = "maxminddb"
84
- __version__ = "2.6.3"
87
+ __version__ = "2.7.0"
85
88
  __author__ = "Gregory Oschwald"
86
89
  __license__ = "Apache License, Version 2.0"
87
90
  __copyright__ = "Copyright 2013-2025 MaxMind, Inc."
@@ -1,4 +1,4 @@
1
- """Constants used in the API"""
1
+ """Constants used in the API."""
2
2
 
3
3
  MODE_AUTO = 0
4
4
  MODE_MMAP_EXT = 1
@@ -1,13 +1,7 @@
1
- """
2
- maxminddb.decoder
3
- ~~~~~~~~~~~~~~~~~
4
-
5
- This package contains code for decoding the MaxMind DB data section.
6
-
7
- """
1
+ """Decoder for the MaxMind DB data section."""
8
2
 
9
3
  import struct
10
- from typing import cast, Dict, List, Tuple, Union
4
+ from typing import Union, cast
11
5
 
12
6
  try:
13
7
  # pylint: disable=unused-import
@@ -23,7 +17,7 @@ from maxminddb.types import Record
23
17
 
24
18
 
25
19
  class Decoder: # pylint: disable=too-few-public-methods
26
- """Decoder for the data section of the MaxMind DB"""
20
+ """Decoder for the data section of the MaxMind DB."""
27
21
 
28
22
  def __init__(
29
23
  self,
@@ -31,46 +25,47 @@ class Decoder: # pylint: disable=too-few-public-methods
31
25
  pointer_base: int = 0,
32
26
  pointer_test: bool = False,
33
27
  ) -> None:
34
- """Created a Decoder for a MaxMind DB
28
+ """Created a Decoder for a MaxMind DB.
35
29
 
36
30
  Arguments:
37
31
  database_buffer -- an mmap'd MaxMind DB file.
38
32
  pointer_base -- the base number to use when decoding a pointer
39
33
  pointer_test -- used for internal unit testing of pointer code
34
+
40
35
  """
41
36
  self._pointer_test = pointer_test
42
37
  self._buffer = database_buffer
43
38
  self._pointer_base = pointer_base
44
39
 
45
- def _decode_array(self, size: int, offset: int) -> Tuple[List[Record], int]:
40
+ def _decode_array(self, size: int, offset: int) -> tuple[list[Record], int]:
46
41
  array = []
47
42
  for _ in range(size):
48
43
  (value, offset) = self.decode(offset)
49
44
  array.append(value)
50
45
  return array, offset
51
46
 
52
- def _decode_boolean(self, size: int, offset: int) -> Tuple[bool, int]:
47
+ def _decode_boolean(self, size: int, offset: int) -> tuple[bool, int]:
53
48
  return size != 0, offset
54
49
 
55
- def _decode_bytes(self, size: int, offset: int) -> Tuple[bytes, int]:
50
+ def _decode_bytes(self, size: int, offset: int) -> tuple[bytes, int]:
56
51
  new_offset = offset + size
57
52
  return self._buffer[offset:new_offset], new_offset
58
53
 
59
- def _decode_double(self, size: int, offset: int) -> Tuple[float, int]:
54
+ def _decode_double(self, size: int, offset: int) -> tuple[float, int]:
60
55
  self._verify_size(size, 8)
61
56
  new_offset = offset + size
62
57
  packed_bytes = self._buffer[offset:new_offset]
63
58
  (value,) = struct.unpack(b"!d", packed_bytes)
64
59
  return value, new_offset
65
60
 
66
- def _decode_float(self, size: int, offset: int) -> Tuple[float, int]:
61
+ def _decode_float(self, size: int, offset: int) -> tuple[float, int]:
67
62
  self._verify_size(size, 4)
68
63
  new_offset = offset + size
69
64
  packed_bytes = self._buffer[offset:new_offset]
70
65
  (value,) = struct.unpack(b"!f", packed_bytes)
71
66
  return value, new_offset
72
67
 
73
- def _decode_int32(self, size: int, offset: int) -> Tuple[int, int]:
68
+ def _decode_int32(self, size: int, offset: int) -> tuple[int, int]:
74
69
  if size == 0:
75
70
  return 0, offset
76
71
  new_offset = offset + size
@@ -81,15 +76,15 @@ class Decoder: # pylint: disable=too-few-public-methods
81
76
  (value,) = struct.unpack(b"!i", packed_bytes)
82
77
  return value, new_offset
83
78
 
84
- def _decode_map(self, size: int, offset: int) -> Tuple[Dict[str, Record], int]:
85
- container: Dict[str, Record] = {}
79
+ def _decode_map(self, size: int, offset: int) -> tuple[dict[str, Record], int]:
80
+ container: dict[str, Record] = {}
86
81
  for _ in range(size):
87
82
  (key, offset) = self.decode(offset)
88
83
  (value, offset) = self.decode(offset)
89
- container[cast(str, key)] = value
84
+ container[cast("str", key)] = value
90
85
  return container, offset
91
86
 
92
- def _decode_pointer(self, size: int, offset: int) -> Tuple[Record, int]:
87
+ def _decode_pointer(self, size: int, offset: int) -> tuple[Record, int]:
93
88
  pointer_size = (size >> 3) + 1
94
89
 
95
90
  buf = self._buffer[offset : offset + pointer_size]
@@ -112,12 +107,12 @@ class Decoder: # pylint: disable=too-few-public-methods
112
107
  (value, _) = self.decode(pointer)
113
108
  return value, new_offset
114
109
 
115
- def _decode_uint(self, size: int, offset: int) -> Tuple[int, int]:
110
+ def _decode_uint(self, size: int, offset: int) -> tuple[int, int]:
116
111
  new_offset = offset + size
117
112
  uint_bytes = self._buffer[offset:new_offset]
118
113
  return int.from_bytes(uint_bytes, "big"), new_offset
119
114
 
120
- def _decode_utf8_string(self, size: int, offset: int) -> Tuple[str, int]:
115
+ def _decode_utf8_string(self, size: int, offset: int) -> tuple[str, int]:
121
116
  new_offset = offset + size
122
117
  return self._buffer[offset:new_offset].decode("utf-8"), new_offset
123
118
 
@@ -137,11 +132,12 @@ class Decoder: # pylint: disable=too-few-public-methods
137
132
  15: _decode_float,
138
133
  }
139
134
 
140
- def decode(self, offset: int) -> Tuple[Record, int]:
141
- """Decode a section of the data section starting at offset
135
+ def decode(self, offset: int) -> tuple[Record, int]:
136
+ """Decode a section of the data section starting at offset.
142
137
 
143
138
  Arguments:
144
139
  offset -- the location of the data structure to decode
140
+
145
141
  """
146
142
  new_offset = offset + 1
147
143
  ctrl_byte = self._buffer[offset]
@@ -154,19 +150,19 @@ class Decoder: # pylint: disable=too-few-public-methods
154
150
  decoder = self._type_decoder[type_num]
155
151
  except KeyError as ex:
156
152
  raise InvalidDatabaseError(
157
- f"Unexpected type number ({type_num}) encountered"
153
+ f"Unexpected type number ({type_num}) encountered",
158
154
  ) from ex
159
155
 
160
156
  (size, new_offset) = self._size_from_ctrl_byte(ctrl_byte, new_offset, type_num)
161
157
  return decoder(self, size, new_offset)
162
158
 
163
- def _read_extended(self, offset: int) -> Tuple[int, int]:
159
+ def _read_extended(self, offset: int) -> tuple[int, int]:
164
160
  next_byte = self._buffer[offset]
165
161
  type_num = next_byte + 7
166
162
  if type_num < 7:
167
163
  raise InvalidDatabaseError(
168
164
  "Something went horribly wrong in the decoder. An "
169
- f"extended type resolved to a type number < 8 ({type_num})"
165
+ f"extended type resolved to a type number < 8 ({type_num})",
170
166
  )
171
167
  return type_num, offset + 1
172
168
 
@@ -175,12 +171,15 @@ class Decoder: # pylint: disable=too-few-public-methods
175
171
  if expected != actual:
176
172
  raise InvalidDatabaseError(
177
173
  "The MaxMind DB file's data section contains bad data "
178
- "(unknown data type or corrupt data)"
174
+ "(unknown data type or corrupt data)",
179
175
  )
180
176
 
181
177
  def _size_from_ctrl_byte(
182
- self, ctrl_byte: int, offset: int, type_num: int
183
- ) -> Tuple[int, int]:
178
+ self,
179
+ ctrl_byte: int,
180
+ offset: int,
181
+ type_num: int,
182
+ ) -> tuple[int, int]:
184
183
  size = ctrl_byte & 0x1F
185
184
  if type_num == 1 or size < 29:
186
185
  return size, offset
@@ -0,0 +1,5 @@
1
+ """Typed errors thrown by this library."""
2
+
3
+
4
+ class InvalidDatabaseError(RuntimeError):
5
+ """An error thrown when unexpected data is found in the database."""
@@ -1,27 +1,24 @@
1
- """
2
- maxminddb.extension
3
- ~~~~~~~~~~~~~~~~
4
-
5
- This module contains the C extension database reader and related classes.
6
-
7
- """
1
+ """C extension database reader and related classes."""
8
2
 
3
+ # pylint: disable=E0601,E0602
9
4
  from ipaddress import IPv4Address, IPv6Address
10
5
  from os import PathLike
11
- from typing import Any, AnyStr, Dict, IO, List, Optional, Tuple, Union
12
- from maxminddb import MODE_AUTO
6
+ from typing import IO, Any, AnyStr, Optional, Union
7
+
8
+ from maxminddb.const import MODE_AUTO
13
9
  from maxminddb.types import Record
14
10
 
15
11
  class Reader:
16
- """
17
- A C extension implementation of a reader for the MaxMind DB format. IP
12
+ """A C extension implementation of a reader for the MaxMind DB format. IP
18
13
  addresses can be looked up using the ``get`` method.
19
14
  """
20
15
 
21
16
  closed: bool = ...
22
17
 
23
18
  def __init__(
24
- self, database: Union[AnyStr, int, PathLike, IO], mode: int = MODE_AUTO
19
+ self,
20
+ database: Union[AnyStr, int, PathLike, IO],
21
+ mode: int = MODE_AUTO,
25
22
  ) -> None:
26
23
  """Reader for the MaxMind DB file format
27
24
 
@@ -30,6 +27,7 @@ class Reader:
30
27
  file, or a file descriptor in the case of MODE_FD.
31
28
  mode -- mode to open the database with. The only supported modes are
32
29
  MODE_AUTO and MODE_MMAP_EXT.
30
+
33
31
  """
34
32
 
35
33
  def close(self) -> None:
@@ -38,25 +36,26 @@ class Reader:
38
36
  def get(self, ip_address: Union[str, IPv6Address, IPv4Address]) -> Optional[Record]:
39
37
  """Return the record for the ip_address in the MaxMind DB
40
38
 
41
-
42
39
  Arguments:
43
40
  ip_address -- an IP address in the standard string notation
41
+
44
42
  """
45
43
 
46
44
  def get_with_prefix_len(
47
- self, ip_address: Union[str, IPv6Address, IPv4Address]
48
- ) -> Tuple[Optional[Record], int]:
45
+ self,
46
+ ip_address: Union[str, IPv6Address, IPv4Address],
47
+ ) -> tuple[Optional[Record], int]:
49
48
  """Return a tuple with the record and the associated prefix length
50
49
 
51
-
52
50
  Arguments:
53
51
  ip_address -- an IP address in the standard string notation
52
+
54
53
  """
55
54
 
56
- def metadata(self) -> "Metadata":
55
+ def metadata(self) -> Metadata:
57
56
  """Return the metadata associated with the MaxMind DB file"""
58
57
 
59
- def __enter__(self) -> "Reader": ...
58
+ def __enter__(self) -> Reader: ...
60
59
  def __exit__(self, *args) -> None: ...
61
60
 
62
61
  # pylint: disable=too-few-public-methods
@@ -85,7 +84,7 @@ class Metadata:
85
84
  A string identifying the database type, e.g., "GeoIP2-City".
86
85
  """
87
86
 
88
- description: Dict[str, str]
87
+ description: dict[str, str]
89
88
  """
90
89
  A map from locales to text descriptions of the database.
91
90
  """
@@ -97,7 +96,7 @@ class Metadata:
97
96
  both IPv4 and IPv6 lookups.
98
97
  """
99
98
 
100
- languages: List[str]
99
+ languages: list[str]
101
100
  """
102
101
  A list of locale codes supported by the databse.
103
102
  """
@@ -11,7 +11,7 @@ except ImportError:
11
11
 
12
12
 
13
13
  class FileBuffer:
14
- """A slice-able file reader"""
14
+ """A slice-able file reader."""
15
15
 
16
16
  def __init__(self, database: str) -> None:
17
17
  # pylint: disable=consider-using-with
@@ -28,31 +28,31 @@ class FileBuffer:
28
28
  raise TypeError("Invalid argument type.")
29
29
 
30
30
  def rfind(self, needle: bytes, start: int) -> int:
31
- """Reverse find needle from start"""
31
+ """Reverse find needle from start."""
32
32
  pos = self._read(self._size - start - 1, start).rfind(needle)
33
33
  if pos == -1:
34
34
  return pos
35
35
  return start + pos
36
36
 
37
37
  def size(self) -> int:
38
- """Size of file"""
38
+ """Size of file."""
39
39
  return self._size
40
40
 
41
41
  def close(self) -> None:
42
- """Close file"""
42
+ """Close file."""
43
43
  self._handle.close()
44
44
 
45
45
  if hasattr(os, "pread"):
46
46
 
47
47
  def _read(self, buffersize: int, offset: int) -> bytes:
48
- """read that uses pread"""
48
+ """Read that uses pread."""
49
49
  # pylint: disable=no-member
50
50
  return os.pread(self._handle.fileno(), buffersize, offset) # type: ignore
51
51
 
52
52
  else:
53
53
 
54
54
  def _read(self, buffersize: int, offset: int) -> bytes:
55
- """read with a lock
55
+ """Read with a lock.
56
56
 
57
57
  This lock is necessary as after a fork, the different processes
58
58
  will share the same file table entry, even if we dup the fd, and