Merge ~ahasenack/ubuntu/+source/python-maxminddb:groovy-python-maxminddb-1.5.4 into ubuntu/+source/python-maxminddb:ubuntu/devel

Proposed by Andreas Hasenack
Status: Merged
Approved by: Andreas Hasenack
Approved revision: b084998c9d7d6f2acf9b4c3fc8eb5f5d46b33a24
Merged at revision: b084998c9d7d6f2acf9b4c3fc8eb5f5d46b33a24
Proposed branch: ~ahasenack/ubuntu/+source/python-maxminddb:groovy-python-maxminddb-1.5.4
Merge into: ubuntu/+source/python-maxminddb:ubuntu/devel
Diff against target: 2949 lines (+787/-667)
31 files modified
HISTORY.rst (+12/-0)
MANIFEST.in (+2/-1)
PKG-INFO (+1/-1)
debian/changelog (+7/-0)
debian/patches/rebuild-sphinx-docs.patch (+67/-65)
docs/html/.buildinfo (+1/-1)
docs/html/_static/basic.css (+1/-1)
docs/html/_static/doctools.js (+4/-3)
docs/html/_static/documentation_options.js (+2/-1)
docs/html/_static/language_data.js (+1/-1)
docs/html/_static/searchtools.js (+17/-8)
docs/html/_static/sphinxdoc.css (+1/-1)
docs/html/genindex.html (+40/-14)
docs/html/index.html (+36/-36)
docs/html/py-modindex.html (+10/-10)
docs/html/search.html (+14/-16)
docs/html/searchindex.js (+1/-1)
extension/maxminddb.c (+10/-0)
maxminddb.egg-info/PKG-INFO (+1/-1)
maxminddb.egg-info/SOURCES.txt (+11/-0)
maxminddb/__init__.py (+15/-10)
maxminddb/compat.py (+1/-1)
maxminddb/decoder.py (+26/-24)
maxminddb/file.py (+4/-3)
maxminddb/reader.py (+43/-46)
setup.py (+80/-73)
tests/data/bad-data/maxminddb-golang/invalid-bytes-length.mmdb (+1/-0)
tests/data/bad-data/maxminddb-golang/invalid-string-length.mmdb (+1/-0)
tests/data/bad-data/maxminddb-golang/metadata-is-an-uint128.mmdb (+1/-0)
tests/decoder_test.py (+89/-110)
tests/reader_test.py (+287/-239)
Reviewer Review Type Date Requested Status
Christian Ehrhardt  (community) Approve
Canonical Server Pending
Review via email: mp+385185@code.launchpad.net

Description of the change

Version update to 1.5.4. Debian is still behind. The sphinx patch update is just cosmetic changes.

To post a comment you must log in.
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Many cosmetic changes indeed - Hiding the actual changes :-)
But the upstream update as well as the merge of that LGTM.

Did you build it already anywhere together with bind maybe - just to ensure things work?
Ah yeah found it at https://launchpad.net/~ci-train-ppa-service/+archive/ubuntu/4087/+packages

+1

review: Approve
Revision history for this message
Andreas Hasenack (ahasenack) wrote :

Thanks, tagging and uploading b084998c9d7d6f2acf9b4c3fc8eb5f5d46b33a24

$ git push pkg upload/1.5.4-0ubuntu1
Enumerating objects: 158, done.
Counting objects: 100% (158/158), done.
Delta compression using up to 4 threads
Compressing objects: 100% (84/84), done.
Writing objects: 100% (91/91), 36.44 KiB | 5.21 MiB/s, done.
Total 91 (delta 62), reused 3 (delta 3)
To ssh://git.launchpad.net/~usd-import-team/ubuntu/+source/python-maxminddb
 * [new tag] upload/1.5.4-0ubuntu1 -> upload/1.5.4-0ubuntu1

$ dput ubuntu ../python-maxminddb_1.5.4-0ubuntu1_source.changes
Checking signature on .changes
gpg: ../python-maxminddb_1.5.4-0ubuntu1_source.changes: Valid signature from AC983EB5BF6BCBA9
Checking signature on .dsc
gpg: ../python-maxminddb_1.5.4-0ubuntu1.dsc: Valid signature from AC983EB5BF6BCBA9
Uploading to ubuntu (via ftp to upload.ubuntu.com):
  Uploading python-maxminddb_1.5.4-0ubuntu1.dsc: done.
  Uploading python-maxminddb_1.5.4.orig.tar.gz: done.
  Uploading python-maxminddb_1.5.4-0ubuntu1.debian.tar.xz: done.
  Uploading python-maxminddb_1.5.4-0ubuntu1_source.buildinfo: done.
  Uploading python-maxminddb_1.5.4-0ubuntu1_source.changes: done.
Successfully uploaded packages.

Revision history for this message
Andreas Hasenack (ahasenack) wrote :

I'll wait for this to build in proposed and then upload bind9

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/HISTORY.rst b/HISTORY.rst
2index 5784604..389aa54 100644
3--- a/HISTORY.rst
4+++ b/HISTORY.rst
5@@ -3,6 +3,18 @@
6 History
7 -------
8
9+1.5.4 (2020-05-05)
10+++++++++++++++++++
11+
12+* 1.5.3 was missing a test database. This release adds the test file.
13+ There are no other changes. Reported by Lumír 'Frenzy' Balhar. GitHub #60.
14+
15+1.5.3 (2020-05-04)
16+++++++++++++++++++
17+
18+* Fix a segfault when decoding a database with a corrupt data section.
19+ Reported by Robert Scott. GitHub #58.
20+
21 1.5.2 (2019-12-20)
22 ++++++++++++++++++
23
24diff --git a/MANIFEST.in b/MANIFEST.in
25index 3658f4c..06b2c18 100644
26--- a/MANIFEST.in
27+++ b/MANIFEST.in
28@@ -1,2 +1,3 @@
29-include HISTORY.rst README.rst LICENSE tests/*.py tests/data/test-data/*.mmdb tests/data/test-data/*.raw
30+include HISTORY.rst README.rst LICENSE
31+recursive-include tests/ *.mmdb *.py *.raw
32 graft docs/html
33diff --git a/PKG-INFO b/PKG-INFO
34index 1a961e9..e8709ed 100644
35--- a/PKG-INFO
36+++ b/PKG-INFO
37@@ -1,6 +1,6 @@
38 Metadata-Version: 1.2
39 Name: maxminddb
40-Version: 1.5.2
41+Version: 1.5.4
42 Summary: Reader for the MaxMind DB format
43 Home-page: http://www.maxmind.com/
44 Author: Gregory Oschwald
45diff --git a/debian/changelog b/debian/changelog
46index d333eda..6b22a46 100644
47--- a/debian/changelog
48+++ b/debian/changelog
49@@ -1,3 +1,10 @@
50+python-maxminddb (1.5.4-0ubuntu1) groovy; urgency=medium
51+
52+ * New upstream release: 1.5.4
53+ * d/p/rebuild-sphinx-docs.patch: update from upstream's 1.5.4
54+
55+ -- Andreas Hasenack <andreas@canonical.com> Tue, 02 Jun 2020 17:00:28 -0300
56+
57 python-maxminddb (1.5.2-0ubuntu1) focal; urgency=medium
58
59 * New upstream release: 1.5.2 (LP: #1867919)
60diff --git a/debian/patches/rebuild-sphinx-docs.patch b/debian/patches/rebuild-sphinx-docs.patch
61index bbce8e6..3fe26a2 100644
62--- a/debian/patches/rebuild-sphinx-docs.patch
63+++ b/debian/patches/rebuild-sphinx-docs.patch
64@@ -27,12 +27,9 @@ Forwarded: not-needed
65 Last-Update: 2020-03-18
66 ---
67 This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
68-diff --git a/docs-source/conf.py b/docs-source/conf.py
69-new file mode 100644
70-index 0000000..f0ae2c9
71 --- /dev/null
72 +++ b/docs-source/conf.py
73-@@ -0,0 +1,248 @@
74+@@ -0,0 +1,253 @@
75 +#!/usr/bin/env python3
76 +# -*- coding: utf-8 -*-
77 +#
78@@ -50,7 +47,7 @@ index 0000000..f0ae2c9
79 +import sys
80 +import os
81 +
82-+sys.path.insert(0, os.path.abspath('..'))
83++sys.path.insert(0, os.path.abspath(".."))
84 +import maxminddb
85 +
86 +__version__ = maxminddb.__version__
87@@ -58,35 +55,37 @@ index 0000000..f0ae2c9
88 +# If extensions (or modules to document with autodoc) are in another directory,
89 +# add these directories to sys.path here. If the directory is relative to the
90 +# documentation root, use os.path.abspath to make it absolute, like shown here.
91-+sys.path.insert(0, os.path.abspath('..'))
92++sys.path.insert(0, os.path.abspath(".."))
93 +
94 +# -- General configuration -----------------------------------------------
95 +
96 +# If your documentation needs a minimal Sphinx version, state it here.
97-+#needs_sphinx = '1.0'
98++# needs_sphinx = '1.0'
99 +
100 +# Add any Sphinx extension module names here, as strings. They can be extensions
101 +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
102 +extensions = [
103-+ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx',
104-+ 'sphinx.ext.coverage'
105++ "sphinx.ext.autodoc",
106++ "sphinx.ext.doctest",
107++ "sphinx.ext.intersphinx",
108++ "sphinx.ext.coverage",
109 +]
110 +
111 +# Add any paths that contain templates here, relative to this directory.
112-+templates_path = ['_templates']
113++templates_path = ["_templates"]
114 +
115 +# The suffix of source filenames.
116-+source_suffix = '.rst'
117++source_suffix = ".rst"
118 +
119 +# The encoding of source files.
120-+#source_encoding = 'utf-8-sig'
121++# source_encoding = 'utf-8-sig'
122 +
123 +# The master toctree document.
124-+master_doc = 'index'
125++master_doc = "index"
126 +
127 +# General information about the project.
128-+project = 'maxminddb'
129-+copyright = '2013-2019, MaxMind, Inc.'
130++project = "maxminddb"
131++copyright = "2013-2019, MaxMind, Inc."
132 +
133 +# The version info for the project you're documenting, acts as replacement for
134 +# |version| and |release|, also used in various other places throughout the
135@@ -99,126 +98,124 @@ index 0000000..f0ae2c9
136 +
137 +# The language for content autogenerated by Sphinx. Refer to documentation
138 +# for a list of supported languages.
139-+#language = None
140++# language = None
141 +
142 +# There are two options for replacing |today|: either, you set today to some
143 +# non-false value, then it is used:
144-+#today = ''
145++# today = ''
146 +# Else, today_fmt is used as the format for a strftime call.
147-+#today_fmt = '%B %d, %Y'
148++# today_fmt = '%B %d, %Y'
149 +
150 +# List of patterns, relative to source directory, that match files and
151 +# directories to ignore when looking for source files.
152-+exclude_patterns = ['_build']
153++exclude_patterns = ["_build"]
154 +
155 +# The reST default role (used for this markup: `text`) to use for all documents.
156-+#default_role = None
157++# default_role = None
158 +
159 +# If true, '()' will be appended to :func: etc. cross-reference text.
160-+#add_function_parentheses = True
161++# add_function_parentheses = True
162 +
163 +# If true, the current module name will be prepended to all description
164 +# unit titles (such as .. function::).
165-+#add_module_names = True
166++# add_module_names = True
167 +
168 +# If true, sectionauthor and moduleauthor directives will be shown in the
169 +# output. They are ignored by default.
170-+#show_authors = False
171++# show_authors = False
172 +
173 +# The name of the Pygments (syntax highlighting) style to use.
174-+pygments_style = 'sphinx'
175++pygments_style = "sphinx"
176 +
177 +# A list of ignored prefixes for module index sorting.
178-+#modindex_common_prefix = []
179++# modindex_common_prefix = []
180 +
181 +# -- Options for HTML output ---------------------------------------------
182 +
183 +# The theme to use for HTML and HTML Help pages. See the documentation for
184 +# a list of builtin themes.
185-+html_theme = 'sphinxdoc'
186++html_theme = "sphinxdoc"
187 +
188 +# Theme options are theme-specific and customize the look and feel of a theme
189 +# further. For a list of options available for each theme, see the
190 +# documentation.
191-+#html_theme_options = {}
192++# html_theme_options = {}
193 +
194 +# Add any paths that contain custom themes here, relative to this directory.
195-+#html_theme_path = []
196++# html_theme_path = []
197 +
198 +# The name for this set of Sphinx documents. If None, it defaults to
199 +# "<project> v<release> documentation".
200-+#html_title = None
201++# html_title = None
202 +
203 +# A shorter title for the navigation bar. Default is the same as html_title.
204-+#html_short_title = None
205++# html_short_title = None
206 +
207 +# The name of an image file (relative to this directory) to place at the top
208 +# of the sidebar.
209-+#html_logo = None
210++# html_logo = None
211 +
212 +# The name of an image file (within the static path) to use as favicon of the
213 +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
214 +# pixels large.
215-+#html_favicon = None
216++# html_favicon = None
217 +
218 +# Add any paths that contain custom static files (such as style sheets) here,
219 +# relative to this directory. They are copied after the builtin static files,
220 +# so a file named "default.css" will overwrite the builtin "default.css".
221-+html_static_path = ['_static']
222++html_static_path = ["_static"]
223 +
224 +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
225 +# using the given strftime format.
226-+#html_last_updated_fmt = '%b %d, %Y'
227++# html_last_updated_fmt = '%b %d, %Y'
228 +
229 +# If true, SmartyPants will be used to convert quotes and dashes to
230 +# typographically correct entities.
231-+#html_use_smartypants = True
232++# html_use_smartypants = True
233 +
234 +# Custom sidebar templates, maps document names to template names.
235-+#html_sidebars = {}
236++# html_sidebars = {}
237 +
238 +# Additional templates that should be rendered to pages, maps page names to
239 +# template names.
240-+#html_additional_pages = {}
241++# html_additional_pages = {}
242 +
243 +# If false, no module index is generated.
244-+#html_domain_indices = True
245++# html_domain_indices = True
246 +
247 +# If false, no index is generated.
248-+#html_use_index = True
249++# html_use_index = True
250 +
251 +# If true, the index is split into individual pages for each letter.
252-+#html_split_index = False
253++# html_split_index = False
254 +
255 +# If true, links to the reST sources are added to the pages.
256-+#html_show_sourcelink = True
257++# html_show_sourcelink = True
258 +
259 +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
260-+#html_show_sphinx = True
261++# html_show_sphinx = True
262 +
263 +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
264-+#html_show_copyright = True
265++# html_show_copyright = True
266 +
267 +# If true, an OpenSearch description file will be output, and all pages will
268 +# contain a <link> tag referring to it. The value of this option must be the
269 +# base URL from which the finished HTML is served.
270-+#html_use_opensearch = ''
271++# html_use_opensearch = ''
272 +
273 +# This is the file name suffix for HTML files (e.g. ".xhtml").
274-+#html_file_suffix = None
275++# html_file_suffix = None
276 +
277 +# Output file base name for HTML help builder.
278-+htmlhelp_basename = 'maxminddbdoc'
279++htmlhelp_basename = "maxminddbdoc"
280 +
281 +# -- Options for LaTeX output --------------------------------------------
282 +
283 +latex_elements = {
284 + # The paper size ('letterpaper' or 'a4paper').
285 + #'papersize': 'letterpaper',
286-+
287 + # The font size ('10pt', '11pt' or '12pt').
288 + #'pointsize': '10pt',
289-+
290 + # Additional stuff for the LaTeX preamble.
291 + #'preamble': '',
292 +}
293@@ -226,39 +223,37 @@ index 0000000..f0ae2c9
294 +# Grouping the document tree into LaTeX files. List of tuples
295 +# (source start file, target name, title, author, documentclass [howto/manual]).
296 +latex_documents = [
297-+ ('index', 'maxminddb.tex', 'maxminddb Documentation', 'Gregory Oschwald',
298-+ 'manual'),
299++ ("index", "maxminddb.tex", "maxminddb Documentation", "Gregory Oschwald", "manual"),
300 +]
301 +
302 +# The name of an image file (relative to this directory) to place at the top of
303 +# the title page.
304-+#latex_logo = None
305++# latex_logo = None
306 +
307 +# For "manual" documents, if this is true, then toplevel headings are parts,
308 +# not chapters.
309-+#latex_use_parts = False
310++# latex_use_parts = False
311 +
312 +# If true, show page references after internal links.
313-+#latex_show_pagerefs = False
314++# latex_show_pagerefs = False
315 +
316 +# If true, show URL addresses after external links.
317-+#latex_show_urls = False
318++# latex_show_urls = False
319 +
320 +# Documents to append as an appendix to all manuals.
321-+#latex_appendices = []
322++# latex_appendices = []
323 +
324 +# If false, no module index is generated.
325-+#latex_domain_indices = True
326++# latex_domain_indices = True
327 +
328 +# -- Options for manual page output --------------------------------------
329 +
330 +# One entry per manual page. List of tuples
331 +# (source start file, name, description, authors, manual section).
332-+man_pages = [('index', 'maxminddb', 'maxminddb Documentation',
333-+ ['Gregory Oschwald'], 1)]
334++man_pages = [("index", "maxminddb", "maxminddb Documentation", ["Gregory Oschwald"], 1)]
335 +
336 +# If true, show URL addresses after external links.
337-+#man_show_urls = False
338++# man_show_urls = False
339 +
340 +# -- Options for Texinfo output ------------------------------------------
341 +
342@@ -266,18 +261,25 @@ index 0000000..f0ae2c9
343 +# (source start file, target name, title, author,
344 +# dir menu entry, description, category)
345 +texinfo_documents = [
346-+ ('index', 'maxminddb', 'maxminddb Documentation', 'Gregory Oschwald',
347-+ 'maxminddb', 'MaxMind DB Reader', 'Miscellaneous'),
348++ (
349++ "index",
350++ "maxminddb",
351++ "maxminddb Documentation",
352++ "Gregory Oschwald",
353++ "maxminddb",
354++ "MaxMind DB Reader",
355++ "Miscellaneous",
356++ ),
357 +]
358 +
359 +# Documents to append as an appendix to all manuals.
360-+#texinfo_appendices = []
361++# texinfo_appendices = []
362 +
363 +# If false, no module index is generated.
364-+#texinfo_domain_indices = True
365++# texinfo_domain_indices = True
366 +
367 +# How to display URL addresses: 'footnote', 'no', or 'inline'.
368-+#texinfo_show_urls = 'footnote'
369++# texinfo_show_urls = 'footnote'
370 +
371 +# Example configuration for intersphinx: refer to the Python standard library.
372-+intersphinx_mapping = {'http://docs.python.org/': None}
373++intersphinx_mapping = {"http://docs.python.org/": None}
374diff --git a/docs/html/.buildinfo b/docs/html/.buildinfo
375index 5e0172d..f487e9a 100644
376--- a/docs/html/.buildinfo
377+++ b/docs/html/.buildinfo
378@@ -1,4 +1,4 @@
379 # Sphinx build info version 1
380 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
381-config: f4b35ba4b456e07a1fe93fc0f298d57b
382+config: d833e7ff76f25fea37f13f0af124f5fd
383 tags: 645f666f9bcd5a90fca523b33c5a78b7
384diff --git a/docs/html/_static/basic.css b/docs/html/_static/basic.css
385index b04360d..0119285 100644
386--- a/docs/html/_static/basic.css
387+++ b/docs/html/_static/basic.css
388@@ -4,7 +4,7 @@
389 *
390 * Sphinx stylesheet -- basic theme.
391 *
392- * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
393+ * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
394 * :license: BSD, see LICENSE for details.
395 *
396 */
397diff --git a/docs/html/_static/doctools.js b/docs/html/_static/doctools.js
398index b33f87f..daccd20 100644
399--- a/docs/html/_static/doctools.js
400+++ b/docs/html/_static/doctools.js
401@@ -4,7 +4,7 @@
402 *
403 * Sphinx JavaScript utilities for all documentation.
404 *
405- * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
406+ * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
407 * :license: BSD, see LICENSE for details.
408 *
409 */
410@@ -283,10 +283,11 @@ var Documentation = {
411 },
412
413 initOnKeyListeners: function() {
414- $(document).keyup(function(event) {
415+ $(document).keydown(function(event) {
416 var activeElementType = document.activeElement.tagName;
417 // don't navigate when in search box or textarea
418- if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') {
419+ if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT'
420+ && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) {
421 switch (event.keyCode) {
422 case 37: // left
423 var prevHref = $('link[rel="prev"]').prop('href');
424diff --git a/docs/html/_static/documentation_options.js b/docs/html/_static/documentation_options.js
425index 375482b..a815b52 100644
426--- a/docs/html/_static/documentation_options.js
427+++ b/docs/html/_static/documentation_options.js
428@@ -1,10 +1,11 @@
429 var DOCUMENTATION_OPTIONS = {
430 URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
431- VERSION: '1.5.2',
432+ VERSION: '1.5.4',
433 LANGUAGE: 'None',
434 COLLAPSE_INDEX: false,
435 BUILDER: 'html',
436 FILE_SUFFIX: '.html',
437+ LINK_SUFFIX: '.html',
438 HAS_SOURCE: true,
439 SOURCELINK_SUFFIX: '.txt',
440 NAVIGATION_WITH_KEYS: false
441diff --git a/docs/html/_static/language_data.js b/docs/html/_static/language_data.js
442index 5266fb1..d2b4ee9 100644
443--- a/docs/html/_static/language_data.js
444+++ b/docs/html/_static/language_data.js
445@@ -5,7 +5,7 @@
446 * This script contains the language-specific data used by searchtools.js,
447 * namely the list of stopwords, stemmer, scorer and splitter.
448 *
449- * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
450+ * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
451 * :license: BSD, see LICENSE for details.
452 *
453 */
454diff --git a/docs/html/_static/searchtools.js b/docs/html/_static/searchtools.js
455index ad84587..ab56499 100644
456--- a/docs/html/_static/searchtools.js
457+++ b/docs/html/_static/searchtools.js
458@@ -4,7 +4,7 @@
459 *
460 * Sphinx JavaScript utilities for the full-text search.
461 *
462- * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
463+ * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
464 * :license: BSD, see LICENSE for details.
465 *
466 */
467@@ -63,6 +63,11 @@ var Search = {
468 htmlElement.innerHTML = htmlString;
469 $(htmlElement).find('.headerlink').remove();
470 docContent = $(htmlElement).find('[role=main]')[0];
471+ if(docContent === undefined) {
472+ console.warn("Content block not found. Sphinx search tries to obtain it " +
473+ "via '[role=main]'. Could you check your theme or template.");
474+ return "";
475+ }
476 return docContent.textContent || docContent.innerText;
477 },
478
479@@ -245,6 +250,8 @@ var Search = {
480 if (results.length) {
481 var item = results.pop();
482 var listItem = $('<li style="display:none"></li>');
483+ var requestUrl = "";
484+ var linkUrl = "";
485 if (DOCUMENTATION_OPTIONS.BUILDER === 'dirhtml') {
486 // dirhtml builder
487 var dirname = item[0] + '/';
488@@ -253,15 +260,17 @@ var Search = {
489 } else if (dirname == 'index/') {
490 dirname = '';
491 }
492- listItem.append($('<a/>').attr('href',
493- DOCUMENTATION_OPTIONS.URL_ROOT + dirname +
494- highlightstring + item[2]).html(item[1]));
495+ requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + dirname;
496+ linkUrl = requestUrl;
497+
498 } else {
499 // normal html builders
500- listItem.append($('<a/>').attr('href',
501- item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
502- highlightstring + item[2]).html(item[1]));
503+ requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX;
504+ linkUrl = item[0] + DOCUMENTATION_OPTIONS.LINK_SUFFIX;
505 }
506+ listItem.append($('<a/>').attr('href',
507+ linkUrl +
508+ highlightstring + item[2]).html(item[1]));
509 if (item[3]) {
510 listItem.append($('<span> (' + item[3] + ')</span>'));
511 Search.output.append(listItem);
512@@ -269,7 +278,7 @@ var Search = {
513 displayNextItem();
514 });
515 } else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
516- $.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX,
517+ $.ajax({url: requestUrl,
518 dataType: "text",
519 complete: function(jqxhr, textstatus) {
520 var data = jqxhr.responseText;
521diff --git a/docs/html/_static/sphinxdoc.css b/docs/html/_static/sphinxdoc.css
522index 1224733..8c762aa 100644
523--- a/docs/html/_static/sphinxdoc.css
524+++ b/docs/html/_static/sphinxdoc.css
525@@ -5,7 +5,7 @@
526 * Sphinx stylesheet -- sphinxdoc theme. Originally created by
527 * Armin Ronacher for Werkzeug.
528 *
529- * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
530+ * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
531 * :license: BSD, see LICENSE for details.
532 *
533 */
534diff --git a/docs/html/genindex.html b/docs/html/genindex.html
535index 8e58ab8..2b9c62e 100644
536--- a/docs/html/genindex.html
537+++ b/docs/html/genindex.html
538@@ -5,14 +5,14 @@
539 <html xmlns="http://www.w3.org/1999/xhtml">
540 <head>
541 <meta charset="utf-8" />
542- <title>Index &#8212; maxminddb 1.5.2 documentation</title>
543+ <title>Index &#8212; maxminddb 1.5.4 documentation</title>
544 <link rel="stylesheet" href="_static/sphinxdoc.css" type="text/css" />
545 <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
546- <script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
547- <script type="text/javascript" src="_static/jquery.js"></script>
548- <script type="text/javascript" src="_static/underscore.js"></script>
549- <script type="text/javascript" src="_static/doctools.js"></script>
550- <script type="text/javascript" src="_static/language_data.js"></script>
551+ <script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
552+ <script src="_static/jquery.js"></script>
553+ <script src="_static/underscore.js"></script>
554+ <script src="_static/doctools.js"></script>
555+ <script src="_static/language_data.js"></script>
556 <link rel="index" title="Index" href="#" />
557 <link rel="search" title="Search" href="search.html" />
558 </head><body>
559@@ -25,7 +25,7 @@
560 <li class="right" >
561 <a href="py-modindex.html" title="Python Module Index"
562 >modules</a> |</li>
563- <li class="nav-item nav-item-0"><a href="index.html">maxminddb 1.5.2 documentation</a> &#187;</li>
564+ <li class="nav-item nav-item-0"><a href="index.html">maxminddb 1.5.4 documentation</a> &#187;</li>
565 </ul>
566 </div>
567 <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
568@@ -39,7 +39,7 @@
569 </form>
570 </div>
571 </div>
572-<script type="text/javascript">$('#searchbox').show(0);</script>
573+<script>$('#searchbox').show(0);</script>
574 </div>
575 </div>
576
577@@ -134,18 +134,44 @@
578 <h2 id="M">M</h2>
579 <table style="width: 100%" class="indextable genindextable"><tr>
580 <td style="width: 33%; vertical-align: top;"><ul>
581- <li><a href="index.html#module-maxminddb">maxminddb (module)</a>
582+ <li>
583+ maxminddb
584+
585+ <ul>
586+ <li><a href="index.html#module-maxminddb">module</a>
587 </li>
588- <li><a href="index.html#module-maxminddb.errors">maxminddb.errors (module)</a>
589+ </ul></li>
590+ <li>
591+ maxminddb.errors
592+
593+ <ul>
594+ <li><a href="index.html#module-maxminddb.errors">module</a>
595+</li>
596+ </ul></li>
597+ <li>
598+ maxminddb.reader
599+
600+ <ul>
601+ <li><a href="index.html#module-maxminddb.reader">module</a>
602 </li>
603+ </ul></li>
604 </ul></td>
605 <td style="width: 33%; vertical-align: top;"><ul>
606- <li><a href="index.html#module-maxminddb.reader">maxminddb.reader (module)</a>
607-</li>
608 <li><a href="index.html#maxminddb.reader.Metadata">Metadata (class in maxminddb.reader)</a>
609 </li>
610 <li><a href="index.html#maxminddb.reader.Reader.metadata">metadata() (maxminddb.reader.Reader method)</a>
611 </li>
612+ <li>
613+ module
614+
615+ <ul>
616+ <li><a href="index.html#module-maxminddb">maxminddb</a>
617+</li>
618+ <li><a href="index.html#module-maxminddb.errors">maxminddb.errors</a>
619+</li>
620+ <li><a href="index.html#module-maxminddb.reader">maxminddb.reader</a>
621+</li>
622+ </ul></li>
623 </ul></td>
624 </tr></table>
625
626@@ -207,12 +233,12 @@
627 <li class="right" >
628 <a href="py-modindex.html" title="Python Module Index"
629 >modules</a> |</li>
630- <li class="nav-item nav-item-0"><a href="index.html">maxminddb 1.5.2 documentation</a> &#187;</li>
631+ <li class="nav-item nav-item-0"><a href="index.html">maxminddb 1.5.4 documentation</a> &#187;</li>
632 </ul>
633 </div>
634 <div class="footer" role="contentinfo">
635 &#169; Copyright 2013-2019, MaxMind, Inc..
636- Created using <a href="http://sphinx-doc.org/">Sphinx</a> 2.3.0.
637+ Created using <a href="http://sphinx-doc.org/">Sphinx</a> 3.0.0.
638 </div>
639 </body>
640 </html>
641\ No newline at end of file
642diff --git a/docs/html/index.html b/docs/html/index.html
643index b9b2187..967c00d 100644
644--- a/docs/html/index.html
645+++ b/docs/html/index.html
646@@ -4,14 +4,14 @@
647 <html xmlns="http://www.w3.org/1999/xhtml">
648 <head>
649 <meta charset="utf-8" />
650- <title>MaxMind DB Python Module &#8212; maxminddb 1.5.2 documentation</title>
651+ <title>MaxMind DB Python Module &#8212; maxminddb 1.5.4 documentation</title>
652 <link rel="stylesheet" href="_static/sphinxdoc.css" type="text/css" />
653 <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
654- <script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
655- <script type="text/javascript" src="_static/jquery.js"></script>
656- <script type="text/javascript" src="_static/underscore.js"></script>
657- <script type="text/javascript" src="_static/doctools.js"></script>
658- <script type="text/javascript" src="_static/language_data.js"></script>
659+ <script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
660+ <script src="_static/jquery.js"></script>
661+ <script src="_static/underscore.js"></script>
662+ <script src="_static/doctools.js"></script>
663+ <script src="_static/language_data.js"></script>
664 <link rel="index" title="Index" href="genindex.html" />
665 <link rel="search" title="Search" href="search.html" />
666 </head><body>
667@@ -24,7 +24,7 @@
668 <li class="right" >
669 <a href="py-modindex.html" title="Python Module Index"
670 >modules</a> |</li>
671- <li class="nav-item nav-item-0"><a href="#">maxminddb 1.5.2 documentation</a> &#187;</li>
672+ <li class="nav-item nav-item-0"><a href="#">maxminddb 1.5.4 documentation</a> &#187;</li>
673 </ul>
674 </div>
675 <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
676@@ -66,7 +66,7 @@
677 </form>
678 </div>
679 </div>
680-<script type="text/javascript">$('#searchbox').show(0);</script>
681+<script>$('#searchbox').show(0);</script>
682 </div>
683 </div>
684
685@@ -174,15 +174,15 @@ assistance.</p>
686 </div>
687 <div class="section" id="module-maxminddb">
688 <span id="modules"></span><h1>Modules<a class="headerlink" href="#module-maxminddb" title="Permalink to this headline">¶</a></h1>
689-<dl class="function">
690+<dl class="py function">
691 <dt id="maxminddb.Reader">
692-<code class="sig-prename descclassname">maxminddb.</code><code class="sig-name descname">Reader</code><span class="sig-paren">(</span><em class="sig-param">database</em><span class="sig-paren">)</span><a class="headerlink" href="#maxminddb.Reader" title="Permalink to this definition">¶</a></dt>
693+<code class="sig-prename descclassname">maxminddb.</code><code class="sig-name descname">Reader</code><span class="sig-paren">(</span><em class="sig-param"><span class="n">database</span></em><span class="sig-paren">)</span><a class="headerlink" href="#maxminddb.Reader" title="Permalink to this definition">¶</a></dt>
694 <dd><p>This exists for backwards compatibility. Use open_database instead</p>
695 </dd></dl>
696
697-<dl class="function">
698+<dl class="py function">
699 <dt id="maxminddb.open_database">
700-<code class="sig-prename descclassname">maxminddb.</code><code class="sig-name descname">open_database</code><span class="sig-paren">(</span><em class="sig-param">database</em>, <em class="sig-param">mode=0</em><span class="sig-paren">)</span><a class="headerlink" href="#maxminddb.open_database" title="Permalink to this definition">¶</a></dt>
701+<code class="sig-prename descclassname">maxminddb.</code><code class="sig-name descname">open_database</code><span class="sig-paren">(</span><em class="sig-param"><span class="n">database</span></em>, <em class="sig-param"><span class="n">mode</span><span class="o">=</span><span class="default_value">0</span></em><span class="sig-paren">)</span><a class="headerlink" href="#maxminddb.open_database" title="Permalink to this definition">¶</a></dt>
702 <dd><p>Open a Maxmind DB database</p>
703 <dl class="simple">
704 <dt>Arguments:</dt><dd><dl class="simple">
705@@ -213,7 +213,7 @@ assistance.</p>
706 <span class="target" id="module-maxminddb.errors"></span><div class="section" id="maxminddb-errors">
707 <h2>maxminddb.errors<a class="headerlink" href="#maxminddb-errors" title="Permalink to this headline">¶</a></h2>
708 <p>This module contains custom errors for the MaxMind DB reader</p>
709-<dl class="exception">
710+<dl class="py exception">
711 <dt id="maxminddb.errors.InvalidDatabaseError">
712 <em class="property">exception </em><code class="sig-prename descclassname">maxminddb.errors.</code><code class="sig-name descname">InvalidDatabaseError</code><a class="headerlink" href="#maxminddb.errors.InvalidDatabaseError" title="Permalink to this definition">¶</a></dt>
713 <dd><p>Bases: <a class="reference external" href="https://docs.python.org/3/library/exceptions.html#RuntimeError" title="(in Python v3.8)"><code class="xref py py-class docutils literal notranslate"><span class="pre">RuntimeError</span></code></a></p>
714@@ -224,12 +224,12 @@ assistance.</p>
715 <span class="target" id="module-maxminddb.reader"></span><div class="section" id="maxminddb-reader">
716 <h2>maxminddb.reader<a class="headerlink" href="#maxminddb-reader" title="Permalink to this headline">¶</a></h2>
717 <p>This module contains the pure Python database reader and related classes.</p>
718-<dl class="class">
719+<dl class="py class">
720 <dt id="maxminddb.reader.Metadata">
721-<em class="property">class </em><code class="sig-prename descclassname">maxminddb.reader.</code><code class="sig-name descname">Metadata</code><span class="sig-paren">(</span><em class="sig-param">**kwargs</em><span class="sig-paren">)</span><a class="headerlink" href="#maxminddb.reader.Metadata" title="Permalink to this definition">¶</a></dt>
722+<em class="property">class </em><code class="sig-prename descclassname">maxminddb.reader.</code><code class="sig-name descname">Metadata</code><span class="sig-paren">(</span><em class="sig-param"><span class="o">**</span><span class="n">kwargs</span></em><span class="sig-paren">)</span><a class="headerlink" href="#maxminddb.reader.Metadata" title="Permalink to this definition">¶</a></dt>
723 <dd><p>Bases: <a class="reference external" href="https://docs.python.org/3/library/functions.html#object" title="(in Python v3.8)"><code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></a></p>
724 <p>Metadata for the MaxMind DB reader</p>
725-<dl class="attribute">
726+<dl class="py attribute">
727 <dt id="maxminddb.reader.Metadata.binary_format_major_version">
728 <code class="sig-name descname">binary_format_major_version</code><a class="headerlink" href="#maxminddb.reader.Metadata.binary_format_major_version" title="Permalink to this definition">¶</a></dt>
729 <dd><p>The major version number of the binary format used when creating the
730@@ -241,7 +241,7 @@ database.</p>
731 </dl>
732 </dd></dl>
733
734-<dl class="attribute">
735+<dl class="py attribute">
736 <dt id="maxminddb.reader.Metadata.binary_format_minor_version">
737 <code class="sig-name descname">binary_format_minor_version</code><a class="headerlink" href="#maxminddb.reader.Metadata.binary_format_minor_version" title="Permalink to this definition">¶</a></dt>
738 <dd><p>The minor version number of the binary format used when creating the
739@@ -253,7 +253,7 @@ database.</p>
740 </dl>
741 </dd></dl>
742
743-<dl class="attribute">
744+<dl class="py attribute">
745 <dt id="maxminddb.reader.Metadata.build_epoch">
746 <code class="sig-name descname">build_epoch</code><a class="headerlink" href="#maxminddb.reader.Metadata.build_epoch" title="Permalink to this definition">¶</a></dt>
747 <dd><p>The Unix epoch for the build time of the database.</p>
748@@ -264,7 +264,7 @@ database.</p>
749 </dl>
750 </dd></dl>
751
752-<dl class="attribute">
753+<dl class="py attribute">
754 <dt id="maxminddb.reader.Metadata.database_type">
755 <code class="sig-name descname">database_type</code><a class="headerlink" href="#maxminddb.reader.Metadata.database_type" title="Permalink to this definition">¶</a></dt>
756 <dd><p>A string identifying the database type, e.g., “GeoIP2-City”.</p>
757@@ -275,7 +275,7 @@ database.</p>
758 </dl>
759 </dd></dl>
760
761-<dl class="attribute">
762+<dl class="py attribute">
763 <dt id="maxminddb.reader.Metadata.description">
764 <code class="sig-name descname">description</code><a class="headerlink" href="#maxminddb.reader.Metadata.description" title="Permalink to this definition">¶</a></dt>
765 <dd><p>A map from locales to text descriptions of the database.</p>
766@@ -286,7 +286,7 @@ database.</p>
767 </dl>
768 </dd></dl>
769
770-<dl class="attribute">
771+<dl class="py attribute">
772 <dt id="maxminddb.reader.Metadata.ip_version">
773 <code class="sig-name descname">ip_version</code><a class="headerlink" href="#maxminddb.reader.Metadata.ip_version" title="Permalink to this definition">¶</a></dt>
774 <dd><p>The IP version of the data in a database. A value of “4” means the
775@@ -299,7 +299,7 @@ both IPv4 and IPv6 lookups.</p>
776 </dl>
777 </dd></dl>
778
779-<dl class="attribute">
780+<dl class="py attribute">
781 <dt id="maxminddb.reader.Metadata.languages">
782 <code class="sig-name descname">languages</code><a class="headerlink" href="#maxminddb.reader.Metadata.languages" title="Permalink to this definition">¶</a></dt>
783 <dd><p>A list of locale codes supported by the databse.</p>
784@@ -310,7 +310,7 @@ both IPv4 and IPv6 lookups.</p>
785 </dl>
786 </dd></dl>
787
788-<dl class="attribute">
789+<dl class="py attribute">
790 <dt id="maxminddb.reader.Metadata.node_count">
791 <code class="sig-name descname">node_count</code><a class="headerlink" href="#maxminddb.reader.Metadata.node_count" title="Permalink to this definition">¶</a></dt>
792 <dd><p>The number of nodes in the database.</p>
793@@ -321,7 +321,7 @@ both IPv4 and IPv6 lookups.</p>
794 </dl>
795 </dd></dl>
796
797-<dl class="attribute">
798+<dl class="py attribute">
799 <dt id="maxminddb.reader.Metadata.record_size">
800 <code class="sig-name descname">record_size</code><a class="headerlink" href="#maxminddb.reader.Metadata.record_size" title="Permalink to this definition">¶</a></dt>
801 <dd><p>The bit size of a record in the search tree.</p>
802@@ -332,7 +332,7 @@ both IPv4 and IPv6 lookups.</p>
803 </dl>
804 </dd></dl>
805
806-<dl class="method">
807+<dl class="py method">
808 <dt id="maxminddb.reader.Metadata.node_byte_size">
809 <em class="property">property </em><code class="sig-name descname">node_byte_size</code><a class="headerlink" href="#maxminddb.reader.Metadata.node_byte_size" title="Permalink to this definition">¶</a></dt>
810 <dd><p>The size of a node in bytes</p>
811@@ -343,7 +343,7 @@ both IPv4 and IPv6 lookups.</p>
812 </dl>
813 </dd></dl>
814
815-<dl class="method">
816+<dl class="py method">
817 <dt id="maxminddb.reader.Metadata.search_tree_size">
818 <em class="property">property </em><code class="sig-name descname">search_tree_size</code><a class="headerlink" href="#maxminddb.reader.Metadata.search_tree_size" title="Permalink to this definition">¶</a></dt>
819 <dd><p>The size of the search tree</p>
820@@ -356,35 +356,35 @@ both IPv4 and IPv6 lookups.</p>
821
822 </dd></dl>
823
824-<dl class="class">
825+<dl class="py class">
826 <dt id="maxminddb.reader.Reader">
827-<em class="property">class </em><code class="sig-prename descclassname">maxminddb.reader.</code><code class="sig-name descname">Reader</code><span class="sig-paren">(</span><em class="sig-param">database</em>, <em class="sig-param">mode=0</em><span class="sig-paren">)</span><a class="headerlink" href="#maxminddb.reader.Reader" title="Permalink to this definition">¶</a></dt>
828+<em class="property">class </em><code class="sig-prename descclassname">maxminddb.reader.</code><code class="sig-name descname">Reader</code><span class="sig-paren">(</span><em class="sig-param"><span class="n">database</span></em>, <em class="sig-param"><span class="n">mode</span><span class="o">=</span><span class="default_value">0</span></em><span class="sig-paren">)</span><a class="headerlink" href="#maxminddb.reader.Reader" title="Permalink to this definition">¶</a></dt>
829 <dd><p>Bases: <a class="reference external" href="https://docs.python.org/3/library/functions.html#object" title="(in Python v3.8)"><code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></a></p>
830 <p>Instances of this class provide a reader for the MaxMind DB format. IP
831 addresses can be looked up using the <code class="docutils literal notranslate"><span class="pre">get</span></code> method.</p>
832-<dl class="method">
833+<dl class="py method">
834 <dt id="maxminddb.reader.Reader.close">
835 <code class="sig-name descname">close</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#maxminddb.reader.Reader.close" title="Permalink to this definition">¶</a></dt>
836 <dd><p>Closes the MaxMind DB file and returns the resources to the system</p>
837 </dd></dl>
838
839-<dl class="method">
840+<dl class="py method">
841 <dt id="maxminddb.reader.Reader.get">
842-<code class="sig-name descname">get</code><span class="sig-paren">(</span><em class="sig-param">ip_address</em><span class="sig-paren">)</span><a class="headerlink" href="#maxminddb.reader.Reader.get" title="Permalink to this definition">¶</a></dt>
843+<code class="sig-name descname">get</code><span class="sig-paren">(</span><em class="sig-param"><span class="n">ip_address</span></em><span class="sig-paren">)</span><a class="headerlink" href="#maxminddb.reader.Reader.get" title="Permalink to this definition">¶</a></dt>
844 <dd><p>Return the record for the ip_address in the MaxMind DB</p>
845 <p>Arguments:
846 ip_address – an IP address in the standard string notation</p>
847 </dd></dl>
848
849-<dl class="method">
850+<dl class="py method">
851 <dt id="maxminddb.reader.Reader.get_with_prefix_len">
852-<code class="sig-name descname">get_with_prefix_len</code><span class="sig-paren">(</span><em class="sig-param">ip_address</em><span class="sig-paren">)</span><a class="headerlink" href="#maxminddb.reader.Reader.get_with_prefix_len" title="Permalink to this definition">¶</a></dt>
853+<code class="sig-name descname">get_with_prefix_len</code><span class="sig-paren">(</span><em class="sig-param"><span class="n">ip_address</span></em><span class="sig-paren">)</span><a class="headerlink" href="#maxminddb.reader.Reader.get_with_prefix_len" title="Permalink to this definition">¶</a></dt>
854 <dd><p>Return a tuple with the record and the associated prefix length</p>
855 <p>Arguments:
856 ip_address – an IP address in the standard string notation</p>
857 </dd></dl>
858
859-<dl class="method">
860+<dl class="py method">
861 <dt id="maxminddb.reader.Reader.metadata">
862 <code class="sig-name descname">metadata</code><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#maxminddb.reader.Reader.metadata" title="Permalink to this definition">¶</a></dt>
863 <dd><p>Return the metadata associated with the MaxMind DB file</p>
864@@ -428,12 +428,12 @@ ip_address – an IP address in the standard string notation</p>
865 <li class="right" >
866 <a href="py-modindex.html" title="Python Module Index"
867 >modules</a> |</li>
868- <li class="nav-item nav-item-0"><a href="#">maxminddb 1.5.2 documentation</a> &#187;</li>
869+ <li class="nav-item nav-item-0"><a href="#">maxminddb 1.5.4 documentation</a> &#187;</li>
870 </ul>
871 </div>
872 <div class="footer" role="contentinfo">
873 &#169; Copyright 2013-2019, MaxMind, Inc..
874- Created using <a href="http://sphinx-doc.org/">Sphinx</a> 2.3.0.
875+ Created using <a href="http://sphinx-doc.org/">Sphinx</a> 3.0.0.
876 </div>
877 </body>
878 </html>
879\ No newline at end of file
880diff --git a/docs/html/objects.inv b/docs/html/objects.inv
881index 9a9ceeb..8874aae 100644
882Binary files a/docs/html/objects.inv and b/docs/html/objects.inv differ
883diff --git a/docs/html/py-modindex.html b/docs/html/py-modindex.html
884index a8537d3..f8ad535 100644
885--- a/docs/html/py-modindex.html
886+++ b/docs/html/py-modindex.html
887@@ -4,14 +4,14 @@
888 <html xmlns="http://www.w3.org/1999/xhtml">
889 <head>
890 <meta charset="utf-8" />
891- <title>Python Module Index &#8212; maxminddb 1.5.2 documentation</title>
892+ <title>Python Module Index &#8212; maxminddb 1.5.4 documentation</title>
893 <link rel="stylesheet" href="_static/sphinxdoc.css" type="text/css" />
894 <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
895- <script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
896- <script type="text/javascript" src="_static/jquery.js"></script>
897- <script type="text/javascript" src="_static/underscore.js"></script>
898- <script type="text/javascript" src="_static/doctools.js"></script>
899- <script type="text/javascript" src="_static/language_data.js"></script>
900+ <script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
901+ <script src="_static/jquery.js"></script>
902+ <script src="_static/underscore.js"></script>
903+ <script src="_static/doctools.js"></script>
904+ <script src="_static/language_data.js"></script>
905 <link rel="index" title="Index" href="genindex.html" />
906 <link rel="search" title="Search" href="search.html" />
907
908@@ -27,7 +27,7 @@
909 <li class="right" >
910 <a href="#" title="Python Module Index"
911 >modules</a> |</li>
912- <li class="nav-item nav-item-0"><a href="index.html">maxminddb 1.5.2 documentation</a> &#187;</li>
913+ <li class="nav-item nav-item-0"><a href="index.html">maxminddb 1.5.4 documentation</a> &#187;</li>
914 </ul>
915 </div>
916 <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
917@@ -41,7 +41,7 @@
918 </form>
919 </div>
920 </div>
921-<script type="text/javascript">$('#searchbox').show(0);</script>
922+<script>$('#searchbox').show(0);</script>
923 </div>
924 </div>
925
926@@ -94,12 +94,12 @@
927 <li class="right" >
928 <a href="#" title="Python Module Index"
929 >modules</a> |</li>
930- <li class="nav-item nav-item-0"><a href="index.html">maxminddb 1.5.2 documentation</a> &#187;</li>
931+ <li class="nav-item nav-item-0"><a href="index.html">maxminddb 1.5.4 documentation</a> &#187;</li>
932 </ul>
933 </div>
934 <div class="footer" role="contentinfo">
935 &#169; Copyright 2013-2019, MaxMind, Inc..
936- Created using <a href="http://sphinx-doc.org/">Sphinx</a> 2.3.0.
937+ Created using <a href="http://sphinx-doc.org/">Sphinx</a> 3.0.0.
938 </div>
939 </body>
940 </html>
941\ No newline at end of file
942diff --git a/docs/html/search.html b/docs/html/search.html
943index ddd6a1a..c29f498 100644
944--- a/docs/html/search.html
945+++ b/docs/html/search.html
946@@ -4,19 +4,19 @@
947 <html xmlns="http://www.w3.org/1999/xhtml">
948 <head>
949 <meta charset="utf-8" />
950- <title>Search &#8212; maxminddb 1.5.2 documentation</title>
951+ <title>Search &#8212; maxminddb 1.5.4 documentation</title>
952 <link rel="stylesheet" href="_static/sphinxdoc.css" type="text/css" />
953 <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
954
955- <script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
956- <script type="text/javascript" src="_static/jquery.js"></script>
957- <script type="text/javascript" src="_static/underscore.js"></script>
958- <script type="text/javascript" src="_static/doctools.js"></script>
959- <script type="text/javascript" src="_static/language_data.js"></script>
960- <script type="text/javascript" src="_static/searchtools.js"></script>
961+ <script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
962+ <script src="_static/jquery.js"></script>
963+ <script src="_static/underscore.js"></script>
964+ <script src="_static/doctools.js"></script>
965+ <script src="_static/language_data.js"></script>
966+ <script src="_static/searchtools.js"></script>
967 <link rel="index" title="Index" href="genindex.html" />
968 <link rel="search" title="Search" href="#" />
969- <script type="text/javascript" src="searchindex.js" defer></script>
970+ <script src="searchindex.js" defer></script>
971
972
973 </head><body>
974@@ -29,7 +29,7 @@
975 <li class="right" >
976 <a href="py-modindex.html" title="Python Module Index"
977 >modules</a> |</li>
978- <li class="nav-item nav-item-0"><a href="index.html">maxminddb 1.5.2 documentation</a> &#187;</li>
979+ <li class="nav-item nav-item-0"><a href="index.html">maxminddb 1.5.4 documentation</a> &#187;</li>
980 </ul>
981 </div>
982 <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
983@@ -44,17 +44,15 @@
984
985 <h1 id="search-documentation">Search</h1>
986 <div id="fallback" class="admonition warning">
987- <script type="text/javascript">$('#fallback').hide();</script>
988+ <script>$('#fallback').hide();</script>
989 <p>
990 Please activate JavaScript to enable the search
991 functionality.
992 </p>
993 </div>
994 <p>
995- From here you can search these documents. Enter your search
996- words into the box below and click "search". Note that the search
997- function will automatically search for all of the words. Pages
998- containing fewer words won't appear in the result list.
999+ Searching for multiple words only shows matches that contain
1000+ all words.
1001 </p>
1002 <form action="" method="get">
1003 <input type="text" name="q" aria-labelledby="search-documentation" value="" />
1004@@ -80,12 +78,12 @@
1005 <li class="right" >
1006 <a href="py-modindex.html" title="Python Module Index"
1007 >modules</a> |</li>
1008- <li class="nav-item nav-item-0"><a href="index.html">maxminddb 1.5.2 documentation</a> &#187;</li>
1009+ <li class="nav-item nav-item-0"><a href="index.html">maxminddb 1.5.4 documentation</a> &#187;</li>
1010 </ul>
1011 </div>
1012 <div class="footer" role="contentinfo">
1013 &#169; Copyright 2013-2019, MaxMind, Inc..
1014- Created using <a href="http://sphinx-doc.org/">Sphinx</a> 2.3.0.
1015+ Created using <a href="http://sphinx-doc.org/">Sphinx</a> 3.0.0.
1016 </div>
1017 </body>
1018 </html>
1019\ No newline at end of file
1020diff --git a/docs/html/searchindex.js b/docs/html/searchindex.js
1021index bcebefe..999a308 100644
1022--- a/docs/html/searchindex.js
1023+++ b/docs/html/searchindex.js
1024@@ -1 +1 @@
1025-Search.setIndex({docnames:["index"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.intersphinx":1,sphinx:56},filenames:["index.rst"],objects:{"":{maxminddb:[0,0,0,"-"]},"maxminddb.errors":{InvalidDatabaseError:[0,2,1,""]},"maxminddb.reader":{Metadata:[0,3,1,""],Reader:[0,3,1,""]},"maxminddb.reader.Metadata":{binary_format_major_version:[0,4,1,""],binary_format_minor_version:[0,4,1,""],build_epoch:[0,4,1,""],database_type:[0,4,1,""],description:[0,4,1,""],ip_version:[0,4,1,""],languages:[0,4,1,""],node_byte_size:[0,5,1,""],node_count:[0,4,1,""],record_size:[0,4,1,""],search_tree_size:[0,5,1,""]},"maxminddb.reader.Reader":{close:[0,5,1,""],get:[0,5,1,""],get_with_prefix_len:[0,5,1,""],metadata:[0,5,1,""]},maxminddb:{Reader:[0,1,1,""],errors:[0,0,0,"-"],open_database:[0,1,1,""],reader:[0,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","function","Python function"],"2":["py","exception","Python exception"],"3":["py","class","Python class"],"4":["py","attribute","Python attribute"],"5":["py","method","Python method"]},objtypes:{"0":"py:module","1":"py:function","2":"py:exception","3":"py:class","4":"py:attribute","5":"py:method"},terms:{"byte":0,"case":0,"class":0,"default":0,"export":0,"function":0,"import":0,"int":0,"return":0,"try":0,The:0,These:0,Use:0,abl:0,address:0,after:0,all:0,also:0,apach:0,api:0,argument:0,assist:0,associ:0,avail:0,back:0,backward:0,base:0,been:0,befor:0,binari:0,binary_format_major_vers:0,binary_format_minor_vers:0,bit:0,both:0,build:0,build_epoch:0,call:0,caller:0,can:0,citi:0,close:0,code:0,compat:0,contact:0,contain:0,copyright:0,correspond:0,corrupt:0,countri:0,cpython:0,creat:0,custom:0,data:0,databas:0,database_typ:0,databs:0,decompress:0,descriptor:0,dict:0,dictionari:0,directori:0,doe:0,download:0,easy_instal:0,epoch:0,exist:0,extens:0,fall:0,file:0,first:0,follow:0,format:0,found:0,free:0,from:0,geoip2:0,geolite2:0,get:0,get_with_prefix_len:0,github:0,gunzip:0,has:0,have:0,identifi:0,immedi:0,implement:0,impli:0,inc:0,includ:0,index:0,instanc:0,instead:0,invalid:0,invaliddatabaseerror:0,ip_address:0,ip_vers:0,ipaddress:0,ipv4:0,ipv6:0,issu:0,kwarg:0,languag:0,length:0,libmaxminddb:0,librari:0,licens:0,list:0,load:0,local:0,look:0,lookup:0,mai:0,major:0,map:0,mean:0,memori:0,metadata:0,method:0,minor:0,mmdb:0,mode:0,mode_auto:0,mode_fd:0,mode_fil:0,mode_memori:0,mode_mmap:0,mode_mmap_ext:0,must:0,network:0,node:0,node_byte_s:0,node_count:0,none:0,notat:0,note:0,number:0,object:0,obtain:0,older:0,onli:0,open:0,open_databas:0,option:0,order:0,otherwis:0,page:0,param:0,pass:0,path:0,pip:0,pleas:0,prefix:0,properli:0,properti:0,provid:0,pure:0,pypi:0,read:0,record:0,record_s:0,relat:0,report:0,resourc:0,respons:0,retriev:0,runtimeerror:0,search:0,search_tree_s:0,second:0,semant:0,servic:0,size:0,sourc:0,specif:0,standard:0,store:0,str:0,string:0,subnet:0,sure:0,system:0,test:0,text:0,thi:0,thrown:0,time:0,tracker:0,tree:0,tri:0,tupl:0,type:0,unexpect:0,unix:0,use:0,used:0,uses:0,using:0,valid:0,valu:0,valueerror:0,via:0,want:0,when:0,wish:0,you:0},titles:["MaxMind DB Python Module"],titleterms:{descript:0,error:0,exampl:0,except:0,indic:0,instal:0,maxmind:0,maxminddb:0,modul:0,python:0,reader:0,requir:0,support:0,tabl:0,usag:0,version:0}})
1026\ No newline at end of file
1027+Search.setIndex({docnames:["index"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":2,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":2,"sphinx.domains.rst":2,"sphinx.domains.std":1,"sphinx.ext.intersphinx":1,sphinx:56},filenames:["index.rst"],objects:{"":{maxminddb:[0,0,0,"-"]},"maxminddb.errors":{InvalidDatabaseError:[0,2,1,""]},"maxminddb.reader":{Metadata:[0,3,1,""],Reader:[0,3,1,""]},"maxminddb.reader.Metadata":{binary_format_major_version:[0,4,1,""],binary_format_minor_version:[0,4,1,""],build_epoch:[0,4,1,""],database_type:[0,4,1,""],description:[0,4,1,""],ip_version:[0,4,1,""],languages:[0,4,1,""],node_byte_size:[0,5,1,""],node_count:[0,4,1,""],record_size:[0,4,1,""],search_tree_size:[0,5,1,""]},"maxminddb.reader.Reader":{close:[0,5,1,""],get:[0,5,1,""],get_with_prefix_len:[0,5,1,""],metadata:[0,5,1,""]},maxminddb:{Reader:[0,1,1,""],errors:[0,0,0,"-"],open_database:[0,1,1,""],reader:[0,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","function","Python function"],"2":["py","exception","Python exception"],"3":["py","class","Python class"],"4":["py","attribute","Python attribute"],"5":["py","method","Python method"]},objtypes:{"0":"py:module","1":"py:function","2":"py:exception","3":"py:class","4":"py:attribute","5":"py:method"},terms:{"byte":0,"case":0,"class":0,"default":0,"export":0,"function":0,"import":0,"int":0,"return":0,"try":0,The:0,These:0,Use:0,abl:0,address:0,after:0,all:0,also:0,apach:0,api:0,argument:0,assist:0,associ:0,avail:0,back:0,backward:0,base:0,been:0,befor:0,binari:0,binary_format_major_vers:0,binary_format_minor_vers:0,bit:0,both:0,build:0,build_epoch:0,call:0,caller:0,can:0,citi:0,close:0,code:0,compat:0,contact:0,contain:0,copyright:0,correspond:0,corrupt:0,countri:0,cpython:0,creat:0,custom:0,data:0,databas:0,database_typ:0,databs:0,decompress:0,descriptor:0,dict:0,dictionari:0,directori:0,doe:0,download:0,easy_instal:0,epoch:0,exist:0,extens:0,fall:0,file:0,first:0,follow:0,format:0,found:0,free:0,from:0,geoip2:0,geolite2:0,get:0,get_with_prefix_len:0,github:0,gunzip:0,has:0,have:0,identifi:0,immedi:0,implement:0,impli:0,inc:0,includ:0,index:0,instanc:0,instead:0,invalid:0,invaliddatabaseerror:0,ip_address:0,ip_vers:0,ipaddress:0,ipv4:0,ipv6:0,issu:0,kwarg:0,languag:0,length:0,libmaxminddb:0,librari:0,licens:0,list:0,load:0,local:0,look:0,lookup:0,mai:0,major:0,map:0,mean:0,memori:0,metadata:0,method:0,minor:0,mmdb:0,mode:0,mode_auto:0,mode_fd:0,mode_fil:0,mode_memori:0,mode_mmap:0,mode_mmap_ext:0,must:0,network:0,node:0,node_byte_s:0,node_count:0,none:0,notat:0,note:0,number:0,object:0,obtain:0,older:0,onli:0,open:0,open_databas:0,option:0,order:0,otherwis:0,page:0,param:0,pass:0,path:0,pip:0,pleas:0,prefix:0,properli:0,properti:0,provid:0,pure:0,pypi:0,read:0,record:0,record_s:0,relat:0,report:0,resourc:0,respons:0,retriev:0,runtimeerror:0,search:0,search_tree_s:0,second:0,semant:0,servic:0,size:0,sourc:0,specif:0,standard:0,store:0,str:0,string:0,subnet:0,sure:0,system:0,test:0,text:0,thi:0,thrown:0,time:0,tracker:0,tree:0,tri:0,tupl:0,type:0,unexpect:0,unix:0,use:0,used:0,uses:0,using:0,valid:0,valu:0,valueerror:0,via:0,want:0,when:0,wish:0,you:0},titles:["MaxMind DB Python Module"],titleterms:{descript:0,error:0,exampl:0,except:0,indic:0,instal:0,maxmind:0,maxminddb:0,modul:0,python:0,reader:0,requir:0,support:0,tabl:0,usag:0,version:0}})
1028\ No newline at end of file
1029diff --git a/extension/maxminddb.c b/extension/maxminddb.c
1030index 69f18c5..a464200 100644
1031--- a/extension/maxminddb.c
1032+++ b/extension/maxminddb.c
1033@@ -204,6 +204,11 @@ static int get_record(PyObject *self, PyObject *args, PyObject **record) {
1034 *record = from_entry_data_list(&entry_data_list);
1035 MMDB_free_entry_data_list(original_entry_data_list);
1036
1037+ // from_entry_data_list will return NULL on errors.
1038+ if (*record == NULL) {
1039+ return -1;
1040+ }
1041+
1042 return prefix_len;
1043 }
1044
1045@@ -526,6 +531,11 @@ static PyObject *from_map(MMDB_entry_data_list_s **entry_data_list) {
1046 PyObject *key = PyUnicode_FromStringAndSize(
1047 (char *)(*entry_data_list)->entry_data.utf8_string,
1048 (*entry_data_list)->entry_data.data_size);
1049+ if (!key) {
1050+ // PyUnicode_FromStringAndSize will set an appropriate exception
1051+ // in this case.
1052+ return NULL;
1053+ }
1054
1055 *entry_data_list = (*entry_data_list)->next;
1056
1057diff --git a/maxminddb.egg-info/PKG-INFO b/maxminddb.egg-info/PKG-INFO
1058index 1a961e9..e8709ed 100644
1059--- a/maxminddb.egg-info/PKG-INFO
1060+++ b/maxminddb.egg-info/PKG-INFO
1061@@ -1,6 +1,6 @@
1062 Metadata-Version: 1.2
1063 Name: maxminddb
1064-Version: 1.5.2
1065+Version: 1.5.4
1066 Summary: Reader for the MaxMind DB format
1067 Home-page: http://www.maxmind.com/
1068 Author: Gregory Oschwald
1069diff --git a/maxminddb.egg-info/SOURCES.txt b/maxminddb.egg-info/SOURCES.txt
1070index e1b82e1..6bd54af 100644
1071--- a/maxminddb.egg-info/SOURCES.txt
1072+++ b/maxminddb.egg-info/SOURCES.txt
1073@@ -42,6 +42,16 @@ maxminddb.egg-info/dependency_links.txt
1074 maxminddb.egg-info/top_level.txt
1075 tests/decoder_test.py
1076 tests/reader_test.py
1077+tests/data/MaxMind-DB-test-metadata-pointers.mmdb
1078+tests/data/bad-data/libmaxminddb/libmaxminddb-offset-integer-overflow.mmdb
1079+tests/data/bad-data/maxminddb-golang/cyclic-data-structure.mmdb
1080+tests/data/bad-data/maxminddb-golang/invalid-bytes-length.mmdb
1081+tests/data/bad-data/maxminddb-golang/invalid-data-record-offset.mmdb
1082+tests/data/bad-data/maxminddb-golang/invalid-map-key-length.mmdb
1083+tests/data/bad-data/maxminddb-golang/invalid-string-length.mmdb
1084+tests/data/bad-data/maxminddb-golang/metadata-is-an-uint128.mmdb
1085+tests/data/bad-data/maxminddb-golang/unexpected-bytes.mmdb
1086+tests/data/bad-data/maxminddb-python/bad-unicode-in-map-key.mmdb
1087 tests/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb
1088 tests/data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb
1089 tests/data/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb
1090@@ -53,6 +63,7 @@ tests/data/test-data/GeoIP2-Domain-Test.mmdb
1091 tests/data/test-data/GeoIP2-Enterprise-Test.mmdb
1092 tests/data/test-data/GeoIP2-ISP-Test.mmdb
1093 tests/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb
1094+tests/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb
1095 tests/data/test-data/GeoIP2-User-Count-Test.mmdb
1096 tests/data/test-data/GeoLite2-ASN-Test.mmdb
1097 tests/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb
1098diff --git a/maxminddb/__init__.py b/maxminddb/__init__.py
1099index dadd6fe..7ad56be 100644
1100--- a/maxminddb/__init__.py
1101+++ b/maxminddb/__init__.py
1102@@ -8,8 +8,14 @@ try:
1103 except ImportError:
1104 maxminddb.extension = None
1105
1106-from maxminddb.const import (MODE_AUTO, MODE_MMAP, MODE_MMAP_EXT, MODE_FILE,
1107- MODE_MEMORY, MODE_FD)
1108+from maxminddb.const import (
1109+ MODE_AUTO,
1110+ MODE_MMAP,
1111+ MODE_MMAP_EXT,
1112+ MODE_FILE,
1113+ MODE_MEMORY,
1114+ MODE_FD,
1115+)
1116 from maxminddb.decoder import InvalidDatabaseError
1117
1118
1119@@ -29,8 +35,7 @@ def open_database(database, mode=MODE_AUTO):
1120 * MODE_AUTO - tries MODE_MMAP_EXT, MODE_MMAP, MODE_FILE in that
1121 order. Default mode.
1122 """
1123- has_extension = maxminddb.extension and hasattr(maxminddb.extension,
1124- 'Reader')
1125+ has_extension = maxminddb.extension and hasattr(maxminddb.extension, "Reader")
1126 if (mode == MODE_AUTO and has_extension) or mode == MODE_MMAP_EXT:
1127 if not has_extension:
1128 raise ValueError(
1129@@ -39,7 +44,7 @@ def open_database(database, mode=MODE_AUTO):
1130 return maxminddb.extension.Reader(database)
1131 if mode in (MODE_AUTO, MODE_MMAP, MODE_FILE, MODE_MEMORY, MODE_FD):
1132 return maxminddb.reader.Reader(database, mode)
1133- raise ValueError('Unsupported open mode: {0}'.format(mode))
1134+ raise ValueError("Unsupported open mode: {0}".format(mode))
1135
1136
1137 def Reader(database): # pylint: disable=invalid-name
1138@@ -47,8 +52,8 @@ def Reader(database): # pylint: disable=invalid-name
1139 return open_database(database)
1140
1141
1142-__title__ = 'maxminddb'
1143-__version__ = '1.5.2'
1144-__author__ = 'Gregory Oschwald'
1145-__license__ = 'Apache License, Version 2.0'
1146-__copyright__ = 'Copyright 2013-2019 Maxmind, Inc.'
1147+__title__ = "maxminddb"
1148+__version__ = "1.5.4"
1149+__author__ = "Gregory Oschwald"
1150+__license__ = "Apache License, Version 2.0"
1151+__copyright__ = "Copyright 2013-2019 Maxmind, Inc."
1152diff --git a/maxminddb/compat.py b/maxminddb/compat.py
1153index 118a2a1..5935d99 100644
1154--- a/maxminddb/compat.py
1155+++ b/maxminddb/compat.py
1156@@ -33,7 +33,7 @@ else:
1157
1158 FileNotFoundError = FileNotFoundError
1159
1160- int_from_bytes = lambda x: int.from_bytes(x, 'big')
1161+ int_from_bytes = lambda x: int.from_bytes(x, "big")
1162
1163 byte_from_int = lambda x: bytes([x])
1164
1165diff --git a/maxminddb/decoder.py b/maxminddb/decoder.py
1166index 3d51e25..71dc4a0 100644
1167--- a/maxminddb/decoder.py
1168+++ b/maxminddb/decoder.py
1169@@ -15,6 +15,7 @@ from maxminddb.errors import InvalidDatabaseError
1170
1171 class Decoder(object): # pylint: disable=too-few-public-methods
1172 """Decoder for the data section of the MaxMind DB"""
1173+
1174 def __init__(self, database_buffer, pointer_base=0, pointer_test=False):
1175 """Created a Decoder for a MaxMind DB
1176
1177@@ -45,14 +46,14 @@ class Decoder(object): # pylint: disable=too-few-public-methods
1178 self._verify_size(size, 8)
1179 new_offset = offset + size
1180 packed_bytes = self._buffer[offset:new_offset]
1181- (value, ) = struct.unpack(b'!d', packed_bytes)
1182+ (value,) = struct.unpack(b"!d", packed_bytes)
1183 return value, new_offset
1184
1185 def _decode_float(self, size, offset):
1186 self._verify_size(size, 4)
1187 new_offset = offset + size
1188 packed_bytes = self._buffer[offset:new_offset]
1189- (value, ) = struct.unpack(b'!f', packed_bytes)
1190+ (value,) = struct.unpack(b"!f", packed_bytes)
1191 return value, new_offset
1192
1193 def _decode_int32(self, size, offset):
1194@@ -62,8 +63,8 @@ class Decoder(object): # pylint: disable=too-few-public-methods
1195 packed_bytes = self._buffer[offset:new_offset]
1196
1197 if size != 4:
1198- packed_bytes = packed_bytes.rjust(4, b'\x00')
1199- (value, ) = struct.unpack(b'!i', packed_bytes)
1200+ packed_bytes = packed_bytes.rjust(4, b"\x00")
1201+ (value,) = struct.unpack(b"!i", packed_bytes)
1202 return value, new_offset
1203
1204 def _decode_map(self, size, offset):
1205@@ -77,21 +78,20 @@ class Decoder(object): # pylint: disable=too-few-public-methods
1206 def _decode_pointer(self, size, offset):
1207 pointer_size = (size >> 3) + 1
1208
1209- buf = self._buffer[offset:offset + pointer_size]
1210+ buf = self._buffer[offset : offset + pointer_size]
1211 new_offset = offset + pointer_size
1212
1213 if pointer_size == 1:
1214 buf = byte_from_int(size & 0x7) + buf
1215- pointer = struct.unpack(b'!H', buf)[0] + self._pointer_base
1216+ pointer = struct.unpack(b"!H", buf)[0] + self._pointer_base
1217 elif pointer_size == 2:
1218- buf = b'\x00' + byte_from_int(size & 0x7) + buf
1219- pointer = struct.unpack(b'!I', buf)[0] + 2048 + self._pointer_base
1220+ buf = b"\x00" + byte_from_int(size & 0x7) + buf
1221+ pointer = struct.unpack(b"!I", buf)[0] + 2048 + self._pointer_base
1222 elif pointer_size == 3:
1223 buf = byte_from_int(size & 0x7) + buf
1224- pointer = struct.unpack(b'!I',
1225- buf)[0] + 526336 + self._pointer_base
1226+ pointer = struct.unpack(b"!I", buf)[0] + 526336 + self._pointer_base
1227 else:
1228- pointer = struct.unpack(b'!I', buf)[0] + self._pointer_base
1229+ pointer = struct.unpack(b"!I", buf)[0] + self._pointer_base
1230
1231 if self._pointer_test:
1232 return pointer, new_offset
1233@@ -105,7 +105,7 @@ class Decoder(object): # pylint: disable=too-few-public-methods
1234
1235 def _decode_utf8_string(self, size, offset):
1236 new_offset = offset + size
1237- return self._buffer[offset:new_offset].decode('utf-8'), new_offset
1238+ return self._buffer[offset:new_offset].decode("utf-8"), new_offset
1239
1240 _type_decoder = {
1241 1: _decode_pointer,
1242@@ -139,11 +139,11 @@ class Decoder(object): # pylint: disable=too-few-public-methods
1243 try:
1244 decoder = self._type_decoder[type_num]
1245 except KeyError:
1246- raise InvalidDatabaseError('Unexpected type number ({type}) '
1247- 'encountered'.format(type=type_num))
1248+ raise InvalidDatabaseError(
1249+ "Unexpected type number ({type}) " "encountered".format(type=type_num)
1250+ )
1251
1252- (size, new_offset) = self._size_from_ctrl_byte(ctrl_byte, new_offset,
1253- type_num)
1254+ (size, new_offset) = self._size_from_ctrl_byte(ctrl_byte, new_offset, type_num)
1255 return decoder(self, size, new_offset)
1256
1257 def _read_extended(self, offset):
1258@@ -151,19 +151,21 @@ class Decoder(object): # pylint: disable=too-few-public-methods
1259 type_num = next_byte + 7
1260 if type_num < 7:
1261 raise InvalidDatabaseError(
1262- 'Something went horribly wrong in the decoder. An '
1263- 'extended type resolved to a type number < 8 '
1264- '({type})'.format(type=type_num))
1265+ "Something went horribly wrong in the decoder. An "
1266+ "extended type resolved to a type number < 8 "
1267+ "({type})".format(type=type_num)
1268+ )
1269 return type_num, offset + 1
1270
1271 def _verify_size(self, expected, actual):
1272 if expected != actual:
1273 raise InvalidDatabaseError(
1274- 'The MaxMind DB file\'s data section contains bad data '
1275- '(unknown data type or corrupt data)')
1276+ "The MaxMind DB file's data section contains bad data "
1277+ "(unknown data type or corrupt data)"
1278+ )
1279
1280 def _size_from_ctrl_byte(self, ctrl_byte, offset, type_num):
1281- size = ctrl_byte & 0x1f
1282+ size = ctrl_byte & 0x1F
1283 if type_num == 1 or size < 29:
1284 return size, offset
1285
1286@@ -176,10 +178,10 @@ class Decoder(object): # pylint: disable=too-few-public-methods
1287 if size == 30:
1288 new_offset = offset + 2
1289 size_bytes = self._buffer[offset:new_offset]
1290- size = 285 + struct.unpack(b'!H', size_bytes)[0]
1291+ size = 285 + struct.unpack(b"!H", size_bytes)[0]
1292 return size, new_offset
1293
1294 new_offset = offset + 3
1295 size_bytes = self._buffer[offset:new_offset]
1296- size = struct.unpack(b'!I', b'\x00' + size_bytes)[0] + 65821
1297+ size = struct.unpack(b"!I", b"\x00" + size_bytes)[0] + 65821
1298 return size, new_offset
1299diff --git a/maxminddb/file.py b/maxminddb/file.py
1300index 0019e4f..736d99d 100644
1301--- a/maxminddb/file.py
1302+++ b/maxminddb/file.py
1303@@ -11,10 +11,11 @@ except ImportError:
1304
1305 class FileBuffer(object):
1306 """A slice-able file reader"""
1307+
1308 def __init__(self, database):
1309- self._handle = open(database, 'rb')
1310+ self._handle = open(database, "rb")
1311 self._size = os.fstat(self._handle.fileno()).st_size
1312- if not hasattr(os, 'pread'):
1313+ if not hasattr(os, "pread"):
1314 self._lock = Lock()
1315
1316 def __getitem__(self, key):
1317@@ -39,7 +40,7 @@ class FileBuffer(object):
1318 """Close file"""
1319 self._handle.close()
1320
1321- if hasattr(os, 'pread'):
1322+ if hasattr(os, "pread"):
1323
1324 def _read(self, buffersize, offset):
1325 """read that uses pread"""
1326diff --git a/maxminddb/reader.py b/maxminddb/reader.py
1327index 9d924f3..e3cd32f 100644
1328--- a/maxminddb/reader.py
1329+++ b/maxminddb/reader.py
1330@@ -48,10 +48,8 @@ class Reader(object):
1331 a path. This mode implies MODE_MEMORY.
1332 """
1333 if (mode == MODE_AUTO and mmap) or mode == MODE_MMAP:
1334- with open(database, 'rb') as db_file:
1335- self._buffer = mmap.mmap(db_file.fileno(),
1336- 0,
1337- access=mmap.ACCESS_READ)
1338+ with open(database, "rb") as db_file:
1339+ self._buffer = mmap.mmap(db_file.fileno(), 0, access=mmap.ACCESS_READ)
1340 self._buffer_size = self._buffer.size()
1341 filename = database
1342 elif mode in (MODE_AUTO, MODE_FILE):
1343@@ -59,7 +57,7 @@ class Reader(object):
1344 self._buffer_size = self._buffer.size()
1345 filename = database
1346 elif mode == MODE_MEMORY:
1347- with open(database, 'rb') as db_file:
1348+ with open(database, "rb") as db_file:
1349 self._buffer = db_file.read()
1350 self._buffer_size = len(self._buffer)
1351 filename = database
1352@@ -69,19 +67,22 @@ class Reader(object):
1353 filename = database.name
1354 else:
1355 raise ValueError(
1356- 'Unsupported open mode ({0}). Only MODE_AUTO, MODE_FILE, '
1357- 'MODE_MEMORY and MODE_FD are supported by the pure Python '
1358- 'Reader'.format(mode))
1359+ "Unsupported open mode ({0}). Only MODE_AUTO, MODE_FILE, "
1360+ "MODE_MEMORY and MODE_FD are supported by the pure Python "
1361+ "Reader".format(mode)
1362+ )
1363
1364 metadata_start = self._buffer.rfind(
1365- self._METADATA_START_MARKER, max(0,
1366- self._buffer_size - 128 * 1024))
1367+ self._METADATA_START_MARKER, max(0, self._buffer_size - 128 * 1024)
1368+ )
1369
1370 if metadata_start == -1:
1371 self.close()
1372- raise InvalidDatabaseError('Error opening database file ({0}). '
1373- 'Is this a valid MaxMind DB file?'
1374- ''.format(filename))
1375+ raise InvalidDatabaseError(
1376+ "Error opening database file ({0}). "
1377+ "Is this a valid MaxMind DB file?"
1378+ "".format(filename)
1379+ )
1380
1381 metadata_start += len(self._METADATA_START_MARKER)
1382 metadata_decoder = Decoder(self._buffer, metadata_start)
1383@@ -89,8 +90,9 @@ class Reader(object):
1384 self._metadata = Metadata(**metadata) # pylint: disable=bad-option-value
1385
1386 self._decoder = Decoder(
1387- self._buffer, self._metadata.search_tree_size +
1388- self._DATA_SECTION_SEPARATOR_SIZE)
1389+ self._buffer,
1390+ self._metadata.search_tree_size + self._DATA_SECTION_SEPARATOR_SIZE,
1391+ )
1392 self.closed = False
1393
1394 def metadata(self):
1395@@ -122,12 +124,13 @@ class Reader(object):
1396 try:
1397 packed_address = bytearray(address.packed)
1398 except AttributeError:
1399- raise TypeError('argument 1 must be a string or ipaddress object')
1400+ raise TypeError("argument 1 must be a string or ipaddress object")
1401
1402 if address.version == 6 and self._metadata.ip_version == 4:
1403 raise ValueError(
1404- 'Error looking up {0}. You attempted to look up '
1405- 'an IPv6 address in an IPv4-only database.'.format(ip_address))
1406+ "Error looking up {0}. You attempted to look up "
1407+ "an IPv6 address in an IPv4-only database.".format(ip_address)
1408+ )
1409
1410 (pointer, prefix_len) = self._find_address_in_tree(packed_address)
1411
1412@@ -152,7 +155,7 @@ class Reader(object):
1413 if node > node_count:
1414 return node, i
1415
1416- raise InvalidDatabaseError('Invalid node in search tree')
1417+ raise InvalidDatabaseError("Invalid node in search tree")
1418
1419 def _start_node(self, length):
1420 if self._metadata.ip_version != 6 or length == 128:
1421@@ -177,10 +180,10 @@ class Reader(object):
1422 record_size = self._metadata.record_size
1423 if record_size == 24:
1424 offset = base_offset + index * 3
1425- node_bytes = b'\x00' + self._buffer[offset:offset + 3]
1426+ node_bytes = b"\x00" + self._buffer[offset : offset + 3]
1427 elif record_size == 28:
1428 offset = base_offset + 3 * index
1429- node_bytes = bytearray(self._buffer[offset:offset + 4])
1430+ node_bytes = bytearray(self._buffer[offset : offset + 4])
1431 if index:
1432 node_bytes[0] = 0x0F & node_bytes[0]
1433 else:
1434@@ -188,19 +191,16 @@ class Reader(object):
1435 node_bytes.insert(0, middle)
1436 elif record_size == 32:
1437 offset = base_offset + index * 4
1438- node_bytes = self._buffer[offset:offset + 4]
1439+ node_bytes = self._buffer[offset : offset + 4]
1440 else:
1441- raise InvalidDatabaseError(
1442- 'Unknown record size: {0}'.format(record_size))
1443- return struct.unpack(b'!I', node_bytes)[0]
1444+ raise InvalidDatabaseError("Unknown record size: {0}".format(record_size))
1445+ return struct.unpack(b"!I", node_bytes)[0]
1446
1447 def _resolve_data_pointer(self, pointer):
1448- resolved = pointer - self._metadata.node_count + \
1449- self._metadata.search_tree_size
1450+ resolved = pointer - self._metadata.node_count + self._metadata.search_tree_size
1451
1452 if resolved >= self._buffer_size:
1453- raise InvalidDatabaseError(
1454- "The MaxMind DB file's search tree is corrupt")
1455+ raise InvalidDatabaseError("The MaxMind DB file's search tree is corrupt")
1456
1457 (data, _) = self._decoder.decode(resolved)
1458 return data
1459@@ -217,7 +217,7 @@ class Reader(object):
1460
1461 def __enter__(self):
1462 if self.closed:
1463- raise ValueError('Attempt to reopen a closed MaxMind DB')
1464+ raise ValueError("Attempt to reopen a closed MaxMind DB")
1465 return self
1466
1467
1468@@ -290,17 +290,15 @@ class Metadata(object):
1469 """Creates new Metadata object. kwargs are key/value pairs from spec"""
1470 # Although I could just update __dict__, that is less obvious and it
1471 # doesn't work well with static analysis tools and some IDEs
1472- self.node_count = kwargs['node_count']
1473- self.record_size = kwargs['record_size']
1474- self.ip_version = kwargs['ip_version']
1475- self.database_type = kwargs['database_type']
1476- self.languages = kwargs['languages']
1477- self.binary_format_major_version = kwargs[
1478- 'binary_format_major_version']
1479- self.binary_format_minor_version = kwargs[
1480- 'binary_format_minor_version']
1481- self.build_epoch = kwargs['build_epoch']
1482- self.description = kwargs['description']
1483+ self.node_count = kwargs["node_count"]
1484+ self.record_size = kwargs["record_size"]
1485+ self.ip_version = kwargs["ip_version"]
1486+ self.database_type = kwargs["database_type"]
1487+ self.languages = kwargs["languages"]
1488+ self.binary_format_major_version = kwargs["binary_format_major_version"]
1489+ self.binary_format_minor_version = kwargs["binary_format_minor_version"]
1490+ self.build_epoch = kwargs["build_epoch"]
1491+ self.description = kwargs["description"]
1492
1493 @property
1494 def node_byte_size(self):
1495@@ -319,8 +317,7 @@ class Metadata(object):
1496 return self.node_count * self.node_byte_size
1497
1498 def __repr__(self):
1499- args = ', '.join('%s=%r' % x for x in self.__dict__.items())
1500- return '{module}.{class_name}({data})'.format(
1501- module=self.__module__,
1502- class_name=self.__class__.__name__,
1503- data=args)
1504+ args = ", ".join("%s=%r" % x for x in self.__dict__.items())
1505+ return "{module}.{class_name}({data})".format(
1506+ module=self.__module__, class_name=self.__class__.__name__, data=args
1507+ )
1508diff --git a/setup.py b/setup.py
1509index 73f74ba..ca9838f 100644
1510--- a/setup.py
1511+++ b/setup.py
1512@@ -6,34 +6,32 @@ import sys
1513 import multiprocessing
1514
1515 from distutils.command.build_ext import build_ext
1516-from distutils.errors import (CCompilerError, DistutilsExecError,
1517- DistutilsPlatformError)
1518+from distutils.errors import CCompilerError, DistutilsExecError, DistutilsPlatformError
1519
1520 from setuptools import setup, Extension
1521
1522 cmdclass = {}
1523-PYPY = hasattr(sys, 'pypy_version_info')
1524-JYTHON = sys.platform.startswith('java')
1525+PYPY = hasattr(sys, "pypy_version_info")
1526+JYTHON = sys.platform.startswith("java")
1527 requirements = []
1528
1529-if sys.version_info[0] == 2 or (sys.version_info[0] == 3
1530- and sys.version_info[1] < 3):
1531- requirements.append('ipaddress')
1532- if os.environ.get('SNYK_TOKEN'):
1533- with open('requirements.txt', 'w') as f:
1534+if sys.version_info[0] == 2 or (sys.version_info[0] == 3 and sys.version_info[1] < 3):
1535+ requirements.append("ipaddress")
1536+ if os.environ.get("SNYK_TOKEN"):
1537+ with open("requirements.txt", "w") as f:
1538 for r in requirements:
1539- f.write(r + '\n')
1540+ f.write(r + "\n")
1541
1542-compile_args = ['-Wall', '-Wextra']
1543+compile_args = ["-Wall", "-Wextra"]
1544
1545 if sys.version_info[0] == 2:
1546- compile_args.append('-fno-strict-aliasing')
1547+ compile_args.append("-fno-strict-aliasing")
1548
1549 ext_module = [
1550 Extension(
1551- 'maxminddb.extension',
1552- libraries=['maxminddb'],
1553- sources=['extension/maxminddb.c'],
1554+ "maxminddb.extension",
1555+ libraries=["maxminddb"],
1556+ sources=["extension/maxminddb.c"],
1557 extra_compile_args=compile_args,
1558 )
1559 ]
1560@@ -69,37 +67,38 @@ class ve_build_ext(build_ext):
1561 raise
1562
1563
1564-cmdclass['build_ext'] = ve_build_ext
1565+cmdclass["build_ext"] = ve_build_ext
1566
1567 #
1568
1569 ROOT = os.path.dirname(__file__)
1570
1571-with open(os.path.join(ROOT, 'README.rst'), 'rb') as fd:
1572- README = fd.read().decode('utf8')
1573+with open(os.path.join(ROOT, "README.rst"), "rb") as fd:
1574+ README = fd.read().decode("utf8")
1575
1576-with open(os.path.join(ROOT, 'maxminddb', '__init__.py'), 'rb') as fd:
1577- maxminddb_text = fd.read().decode('utf8')
1578- LICENSE = re.compile(r".*__license__ = '(.*?)'",
1579- re.S).match(maxminddb_text).group(1)
1580- VERSION = re.compile(r".*__version__ = '(.*?)'",
1581- re.S).match(maxminddb_text).group(1)
1582+with open(os.path.join(ROOT, "maxminddb", "__init__.py"), "rb") as fd:
1583+ maxminddb_text = fd.read().decode("utf8")
1584+ LICENSE = (
1585+ re.compile(r".*__license__ = \"(.*?)\"", re.S).match(maxminddb_text).group(1)
1586+ )
1587+ VERSION = (
1588+ re.compile(r".*__version__ = \"(.*?)\"", re.S).match(maxminddb_text).group(1)
1589+ )
1590
1591
1592 def status_msgs(*msgs):
1593- print('*' * 75)
1594+ print("*" * 75)
1595 for msg in msgs:
1596 print(msg)
1597- print('*' * 75)
1598+ print("*" * 75)
1599
1600
1601 def find_packages(location):
1602 packages = []
1603- for pkg in ['maxminddb']:
1604- for _dir, subdirectories, files in (os.walk(os.path.join(
1605- location, pkg))):
1606- if '__init__.py' in files:
1607- tokens = _dir.split(os.sep)[len(location.split(os.sep)):]
1608+ for pkg in ["maxminddb"]:
1609+ for _dir, subdirectories, files in os.walk(os.path.join(location, pkg)):
1610+ if "__init__.py" in files:
1611+ tokens = _dir.split(os.sep)[len(location.split(os.sep)) :]
1612 packages.append(".".join(tokens))
1613 return packages
1614
1615@@ -107,59 +106,67 @@ def find_packages(location):
1616 def run_setup(with_cext):
1617 kwargs = {}
1618 if with_cext:
1619- kwargs['ext_modules'] = ext_module
1620-
1621- setup(name='maxminddb',
1622- version=VERSION,
1623- author='Gregory Oschwald',
1624- author_email='goschwald@maxmind.com',
1625- description='Reader for the MaxMind DB format',
1626- long_description=README,
1627- url='http://www.maxmind.com/',
1628- packages=find_packages('.'),
1629- package_data={'': ['LICENSE']},
1630- package_dir={'maxminddb': 'maxminddb'},
1631- python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*',
1632- include_package_data=True,
1633- install_requires=requirements,
1634- tests_require=['nose'],
1635- test_suite='nose.collector',
1636- license=LICENSE,
1637- cmdclass=cmdclass,
1638- classifiers=[
1639- 'Development Status :: 5 - Production/Stable',
1640- 'Environment :: Web Environment',
1641- 'Intended Audience :: Developers',
1642- 'Intended Audience :: System Administrators',
1643- 'License :: OSI Approved :: Apache Software License',
1644- 'Programming Language :: Python :: 2.7',
1645- 'Programming Language :: Python :: 3',
1646- 'Programming Language :: Python :: 3.5',
1647- 'Programming Language :: Python :: 3.6',
1648- 'Programming Language :: Python :: 3.7',
1649- 'Programming Language :: Python',
1650- 'Topic :: Internet :: Proxy Servers',
1651- 'Topic :: Internet',
1652- ],
1653- **kwargs)
1654+ kwargs["ext_modules"] = ext_module
1655+
1656+ setup(
1657+ name="maxminddb",
1658+ version=VERSION,
1659+ author="Gregory Oschwald",
1660+ author_email="goschwald@maxmind.com",
1661+ description="Reader for the MaxMind DB format",
1662+ long_description=README,
1663+ url="http://www.maxmind.com/",
1664+ packages=find_packages("."),
1665+ package_data={"": ["LICENSE"]},
1666+ package_dir={"maxminddb": "maxminddb"},
1667+ python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
1668+ include_package_data=True,
1669+ install_requires=requirements,
1670+ tests_require=["nose"],
1671+ test_suite="nose.collector",
1672+ license=LICENSE,
1673+ cmdclass=cmdclass,
1674+ classifiers=[
1675+ "Development Status :: 5 - Production/Stable",
1676+ "Environment :: Web Environment",
1677+ "Intended Audience :: Developers",
1678+ "Intended Audience :: System Administrators",
1679+ "License :: OSI Approved :: Apache Software License",
1680+ "Programming Language :: Python :: 2.7",
1681+ "Programming Language :: Python :: 3",
1682+ "Programming Language :: Python :: 3.5",
1683+ "Programming Language :: Python :: 3.6",
1684+ "Programming Language :: Python :: 3.7",
1685+ "Programming Language :: Python",
1686+ "Topic :: Internet :: Proxy Servers",
1687+ "Topic :: Internet",
1688+ ],
1689+ **kwargs
1690+ )
1691
1692
1693 if PYPY or JYTHON:
1694 run_setup(False)
1695- status_msgs("WARNING: Disabling C extension due to Python platform.",
1696- "Plain-Python build succeeded.")
1697+ status_msgs(
1698+ "WARNING: Disabling C extension due to Python platform.",
1699+ "Plain-Python build succeeded.",
1700+ )
1701 else:
1702 try:
1703 run_setup(True)
1704 except BuildFailed as exc:
1705 status_msgs(
1706- exc.cause, "WARNING: The C extension could not be compiled, " +
1707- "speedups are not enabled.",
1708+ exc.cause,
1709+ "WARNING: The C extension could not be compiled, "
1710+ + "speedups are not enabled.",
1711 "Failure information, if any, is above.",
1712- "Retrying the build without the C extension now.")
1713+ "Retrying the build without the C extension now.",
1714+ )
1715
1716 run_setup(False)
1717
1718 status_msgs(
1719- "WARNING: The C extension could not be compiled, " +
1720- "speedups are not enabled.", "Plain-Python build succeeded.")
1721+ "WARNING: The C extension could not be compiled, "
1722+ + "speedups are not enabled.",
1723+ "Plain-Python build succeeded.",
1724+ )
1725diff --git a/tests/data/MaxMind-DB-test-metadata-pointers.mmdb b/tests/data/MaxMind-DB-test-metadata-pointers.mmdb
1726new file mode 100644
1727index 0000000..9a9f625
1728Binary files /dev/null and b/tests/data/MaxMind-DB-test-metadata-pointers.mmdb differ
1729diff --git a/tests/data/bad-data/libmaxminddb/libmaxminddb-offset-integer-overflow.mmdb b/tests/data/bad-data/libmaxminddb/libmaxminddb-offset-integer-overflow.mmdb
1730new file mode 100644
1731index 0000000..b76f354
1732Binary files /dev/null and b/tests/data/bad-data/libmaxminddb/libmaxminddb-offset-integer-overflow.mmdb differ
1733diff --git a/tests/data/bad-data/maxminddb-golang/cyclic-data-structure.mmdb b/tests/data/bad-data/maxminddb-golang/cyclic-data-structure.mmdb
1734new file mode 100644
1735index 0000000..1015cbe
1736Binary files /dev/null and b/tests/data/bad-data/maxminddb-golang/cyclic-data-structure.mmdb differ
1737diff --git a/tests/data/bad-data/maxminddb-golang/invalid-bytes-length.mmdb b/tests/data/bad-data/maxminddb-golang/invalid-bytes-length.mmdb
1738new file mode 100644
1739index 0000000..8c13018
1740--- /dev/null
1741+++ b/tests/data/bad-data/maxminddb-golang/invalid-bytes-length.mmdb
1742@@ -0,0 +1 @@
1743+���MaxMind.com�Kdescription�Ben�
1744\ No newline at end of file
1745diff --git a/tests/data/bad-data/maxminddb-golang/invalid-data-record-offset.mmdb b/tests/data/bad-data/maxminddb-golang/invalid-data-record-offset.mmdb
1746new file mode 100644
1747index 0000000..228294e
1748Binary files /dev/null and b/tests/data/bad-data/maxminddb-golang/invalid-data-record-offset.mmdb differ
1749diff --git a/tests/data/bad-data/maxminddb-golang/invalid-map-key-length.mmdb b/tests/data/bad-data/maxminddb-golang/invalid-map-key-length.mmdb
1750new file mode 100644
1751index 0000000..23e5906
1752Binary files /dev/null and b/tests/data/bad-data/maxminddb-golang/invalid-map-key-length.mmdb differ
1753diff --git a/tests/data/bad-data/maxminddb-golang/invalid-string-length.mmdb b/tests/data/bad-data/maxminddb-golang/invalid-string-length.mmdb
1754new file mode 100644
1755index 0000000..c073c77
1756--- /dev/null
1757+++ b/tests/data/bad-data/maxminddb-golang/invalid-string-length.mmdb
1758@@ -0,0 +1 @@
1759+Dmap2�Earray�Dmap3�Aa�Ab�Ac����MaxMind.com�[binary_format_major_version�[binary_format_minor_version�Kbuild_epochX�2|Mdatabase_type]MaxMind DB Nested Data StructuresKdescription
1760\ No newline at end of file
1761diff --git a/tests/data/bad-data/maxminddb-golang/metadata-is-an-uint128.mmdb b/tests/data/bad-data/maxminddb-golang/metadata-is-an-uint128.mmdb
1762new file mode 100644
1763index 0000000..4000d97
1764--- /dev/null
1765+++ b/tests/data/bad-data/maxminddb-golang/metadata-is-an-uint128.mmdb
1766@@ -0,0 +1 @@
1767+���MaxMind.com
1768\ No newline at end of file
1769diff --git a/tests/data/bad-data/maxminddb-golang/unexpected-bytes.mmdb b/tests/data/bad-data/maxminddb-golang/unexpected-bytes.mmdb
1770new file mode 100644
1771index 0000000..29b3bc3
1772Binary files /dev/null and b/tests/data/bad-data/maxminddb-golang/unexpected-bytes.mmdb differ
1773diff --git a/tests/data/bad-data/maxminddb-python/bad-unicode-in-map-key.mmdb b/tests/data/bad-data/maxminddb-python/bad-unicode-in-map-key.mmdb
1774new file mode 100644
1775index 0000000..37cec83
1776Binary files /dev/null and b/tests/data/bad-data/maxminddb-python/bad-unicode-in-map-key.mmdb differ
1777diff --git a/tests/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb b/tests/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb
1778index f1e7e6c..e051118 100644
1779Binary files a/tests/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb and b/tests/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb differ
1780diff --git a/tests/data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb b/tests/data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb
1781index 317bb43..a6519b8 100644
1782Binary files a/tests/data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb and b/tests/data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb differ
1783diff --git a/tests/data/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb b/tests/data/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb
1784index 09973d3..e245431 100644
1785Binary files a/tests/data/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb and b/tests/data/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb differ
1786diff --git a/tests/data/test-data/GeoIP2-City-Test.mmdb b/tests/data/test-data/GeoIP2-City-Test.mmdb
1787index 85f4540..993d89c 100644
1788Binary files a/tests/data/test-data/GeoIP2-City-Test.mmdb and b/tests/data/test-data/GeoIP2-City-Test.mmdb differ
1789diff --git a/tests/data/test-data/GeoIP2-Connection-Type-Test.mmdb b/tests/data/test-data/GeoIP2-Connection-Type-Test.mmdb
1790index 8b22aef..7601a6f 100644
1791Binary files a/tests/data/test-data/GeoIP2-Connection-Type-Test.mmdb and b/tests/data/test-data/GeoIP2-Connection-Type-Test.mmdb differ
1792diff --git a/tests/data/test-data/GeoIP2-Country-Test.mmdb b/tests/data/test-data/GeoIP2-Country-Test.mmdb
1793index 2b3bdbf..5624cb1 100644
1794Binary files a/tests/data/test-data/GeoIP2-Country-Test.mmdb and b/tests/data/test-data/GeoIP2-Country-Test.mmdb differ
1795diff --git a/tests/data/test-data/GeoIP2-DensityIncome-Test.mmdb b/tests/data/test-data/GeoIP2-DensityIncome-Test.mmdb
1796index f474846..caf2996 100644
1797Binary files a/tests/data/test-data/GeoIP2-DensityIncome-Test.mmdb and b/tests/data/test-data/GeoIP2-DensityIncome-Test.mmdb differ
1798diff --git a/tests/data/test-data/GeoIP2-Domain-Test.mmdb b/tests/data/test-data/GeoIP2-Domain-Test.mmdb
1799index 9504984..89532cb 100644
1800Binary files a/tests/data/test-data/GeoIP2-Domain-Test.mmdb and b/tests/data/test-data/GeoIP2-Domain-Test.mmdb differ
1801diff --git a/tests/data/test-data/GeoIP2-Enterprise-Test.mmdb b/tests/data/test-data/GeoIP2-Enterprise-Test.mmdb
1802index 71f28a1..e8ea1e1 100644
1803Binary files a/tests/data/test-data/GeoIP2-Enterprise-Test.mmdb and b/tests/data/test-data/GeoIP2-Enterprise-Test.mmdb differ
1804diff --git a/tests/data/test-data/GeoIP2-ISP-Test.mmdb b/tests/data/test-data/GeoIP2-ISP-Test.mmdb
1805index 1ec5070..6f2d714 100644
1806Binary files a/tests/data/test-data/GeoIP2-ISP-Test.mmdb and b/tests/data/test-data/GeoIP2-ISP-Test.mmdb differ
1807diff --git a/tests/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb b/tests/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb
1808index 6fd5391..a7b8da7 100644
1809Binary files a/tests/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb and b/tests/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb differ
1810diff --git a/tests/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb b/tests/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb
1811new file mode 100644
1812index 0000000..cf7d26f
1813Binary files /dev/null and b/tests/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb differ
1814diff --git a/tests/data/test-data/GeoIP2-User-Count-Test.mmdb b/tests/data/test-data/GeoIP2-User-Count-Test.mmdb
1815index fe751d2..0b19ae8 100644
1816Binary files a/tests/data/test-data/GeoIP2-User-Count-Test.mmdb and b/tests/data/test-data/GeoIP2-User-Count-Test.mmdb differ
1817diff --git a/tests/data/test-data/GeoLite2-ASN-Test.mmdb b/tests/data/test-data/GeoLite2-ASN-Test.mmdb
1818index 6867459..bbf6bf2 100644
1819Binary files a/tests/data/test-data/GeoLite2-ASN-Test.mmdb and b/tests/data/test-data/GeoLite2-ASN-Test.mmdb differ
1820diff --git a/tests/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb b/tests/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb
1821index 5b38bfa..7310a54 100644
1822Binary files a/tests/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb and b/tests/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb differ
1823diff --git a/tests/data/test-data/MaxMind-DB-string-value-entries.mmdb b/tests/data/test-data/MaxMind-DB-string-value-entries.mmdb
1824index 725ed14..6454f17 100644
1825Binary files a/tests/data/test-data/MaxMind-DB-string-value-entries.mmdb and b/tests/data/test-data/MaxMind-DB-string-value-entries.mmdb differ
1826diff --git a/tests/data/test-data/MaxMind-DB-test-broken-pointers-24.mmdb b/tests/data/test-data/MaxMind-DB-test-broken-pointers-24.mmdb
1827index 45124c3..7c36926 100644
1828Binary files a/tests/data/test-data/MaxMind-DB-test-broken-pointers-24.mmdb and b/tests/data/test-data/MaxMind-DB-test-broken-pointers-24.mmdb differ
1829diff --git a/tests/data/test-data/MaxMind-DB-test-broken-search-tree-24.mmdb b/tests/data/test-data/MaxMind-DB-test-broken-search-tree-24.mmdb
1830index df69bcb..b35e91b 100644
1831Binary files a/tests/data/test-data/MaxMind-DB-test-broken-search-tree-24.mmdb and b/tests/data/test-data/MaxMind-DB-test-broken-search-tree-24.mmdb differ
1832diff --git a/tests/data/test-data/MaxMind-DB-test-decoder.mmdb b/tests/data/test-data/MaxMind-DB-test-decoder.mmdb
1833index 5a4a155..64a147f 100644
1834Binary files a/tests/data/test-data/MaxMind-DB-test-decoder.mmdb and b/tests/data/test-data/MaxMind-DB-test-decoder.mmdb differ
1835diff --git a/tests/data/test-data/MaxMind-DB-test-ipv4-24.mmdb b/tests/data/test-data/MaxMind-DB-test-ipv4-24.mmdb
1836index 2decb0d..d33ba90 100644
1837Binary files a/tests/data/test-data/MaxMind-DB-test-ipv4-24.mmdb and b/tests/data/test-data/MaxMind-DB-test-ipv4-24.mmdb differ
1838diff --git a/tests/data/test-data/MaxMind-DB-test-ipv4-28.mmdb b/tests/data/test-data/MaxMind-DB-test-ipv4-28.mmdb
1839index ac9870b..0a3c8b9 100644
1840Binary files a/tests/data/test-data/MaxMind-DB-test-ipv4-28.mmdb and b/tests/data/test-data/MaxMind-DB-test-ipv4-28.mmdb differ
1841diff --git a/tests/data/test-data/MaxMind-DB-test-ipv4-32.mmdb b/tests/data/test-data/MaxMind-DB-test-ipv4-32.mmdb
1842index 1352aec..1c328ba 100644
1843Binary files a/tests/data/test-data/MaxMind-DB-test-ipv4-32.mmdb and b/tests/data/test-data/MaxMind-DB-test-ipv4-32.mmdb differ
1844diff --git a/tests/data/test-data/MaxMind-DB-test-ipv6-24.mmdb b/tests/data/test-data/MaxMind-DB-test-ipv6-24.mmdb
1845index 73cec25..6530c41 100644
1846Binary files a/tests/data/test-data/MaxMind-DB-test-ipv6-24.mmdb and b/tests/data/test-data/MaxMind-DB-test-ipv6-24.mmdb differ
1847diff --git a/tests/data/test-data/MaxMind-DB-test-ipv6-28.mmdb b/tests/data/test-data/MaxMind-DB-test-ipv6-28.mmdb
1848index 5a9d19e..1ea130b 100644
1849Binary files a/tests/data/test-data/MaxMind-DB-test-ipv6-28.mmdb and b/tests/data/test-data/MaxMind-DB-test-ipv6-28.mmdb differ
1850diff --git a/tests/data/test-data/MaxMind-DB-test-ipv6-32.mmdb b/tests/data/test-data/MaxMind-DB-test-ipv6-32.mmdb
1851index 86ce484..2da6950 100644
1852Binary files a/tests/data/test-data/MaxMind-DB-test-ipv6-32.mmdb and b/tests/data/test-data/MaxMind-DB-test-ipv6-32.mmdb differ
1853diff --git a/tests/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb b/tests/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb
1854index f38e14a..9602af8 100644
1855Binary files a/tests/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb and b/tests/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb differ
1856diff --git a/tests/data/test-data/MaxMind-DB-test-mixed-24.mmdb b/tests/data/test-data/MaxMind-DB-test-mixed-24.mmdb
1857index 17a8f25..868fcd2 100644
1858Binary files a/tests/data/test-data/MaxMind-DB-test-mixed-24.mmdb and b/tests/data/test-data/MaxMind-DB-test-mixed-24.mmdb differ
1859diff --git a/tests/data/test-data/MaxMind-DB-test-mixed-28.mmdb b/tests/data/test-data/MaxMind-DB-test-mixed-28.mmdb
1860index 0ab56de..3901eec 100644
1861Binary files a/tests/data/test-data/MaxMind-DB-test-mixed-28.mmdb and b/tests/data/test-data/MaxMind-DB-test-mixed-28.mmdb differ
1862diff --git a/tests/data/test-data/MaxMind-DB-test-mixed-32.mmdb b/tests/data/test-data/MaxMind-DB-test-mixed-32.mmdb
1863index b4a43e6..5cc031a 100644
1864Binary files a/tests/data/test-data/MaxMind-DB-test-mixed-32.mmdb and b/tests/data/test-data/MaxMind-DB-test-mixed-32.mmdb differ
1865diff --git a/tests/data/test-data/MaxMind-DB-test-nested.mmdb b/tests/data/test-data/MaxMind-DB-test-nested.mmdb
1866index 7fc3d6d..629b2e5 100644
1867Binary files a/tests/data/test-data/MaxMind-DB-test-nested.mmdb and b/tests/data/test-data/MaxMind-DB-test-nested.mmdb differ
1868diff --git a/tests/decoder_test.py b/tests/decoder_test.py
1869index 3c9dbe3..6cb695d 100644
1870--- a/tests/decoder_test.py
1871+++ b/tests/decoder_test.py
1872@@ -19,18 +19,18 @@ if sys.version_info[0] == 2:
1873 class TestDecoder(unittest.TestCase):
1874 def test_arrays(self):
1875 arrays = {
1876- b'\x00\x04': [],
1877- b'\x01\x04\x43\x46\x6f\x6f': ['Foo'],
1878- b'\x02\x04\x43\x46\x6f\x6f\x43\xe4\xba\xba': ['Foo', '人'],
1879+ b"\x00\x04": [],
1880+ b"\x01\x04\x43\x46\x6f\x6f": ["Foo"],
1881+ b"\x02\x04\x43\x46\x6f\x6f\x43\xe4\xba\xba": ["Foo", "人"],
1882 }
1883- self.validate_type_decoding('arrays', arrays)
1884+ self.validate_type_decoding("arrays", arrays)
1885
1886 def test_boolean(self):
1887 booleans = {
1888 b"\x00\x07": False,
1889 b"\x01\x07": True,
1890 }
1891- self.validate_type_decoding('booleans', booleans)
1892+ self.validate_type_decoding("booleans", booleans)
1893
1894 def test_double(self):
1895 doubles = {
1896@@ -43,7 +43,7 @@ class TestDecoder(unittest.TestCase):
1897 b"\x68\xC0\x09\x21\xFB\x54\x44\x2E\xEA": -3.14159265359,
1898 b"\x68\xC1\xD0\x00\x00\x00\x07\xF8\xF4": -1073741824.12457,
1899 }
1900- self.validate_type_decoding('double', doubles)
1901+ self.validate_type_decoding("double", doubles)
1902
1903 def test_float(self):
1904 floats = {
1905@@ -55,9 +55,9 @@ class TestDecoder(unittest.TestCase):
1906 b"\x04\x08\xBF\x80\x00\x00": -1.0,
1907 b"\x04\x08\xBF\x8C\xCC\xCD": -1.1,
1908 b"\x04\x08\xC0\x48\xF5\xC3": -3.14,
1909- b"\x04\x08\xC6\x1C\x3F\xF6": -9999.99
1910+ b"\x04\x08\xC6\x1C\x3F\xF6": -9999.99,
1911 }
1912- self.validate_type_decoding('float', floats)
1913+ self.validate_type_decoding("float", floats)
1914
1915 def test_int32(self):
1916 int32 = {
1917@@ -74,90 +74,85 @@ class TestDecoder(unittest.TestCase):
1918 b"\x04\x01\x7f\xff\xff\xff": 2147483647,
1919 b"\x04\x01\x80\x00\x00\x01": -2147483647,
1920 }
1921- self.validate_type_decoding('int32', int32)
1922+ self.validate_type_decoding("int32", int32)
1923
1924 def test_map(self):
1925 maps = {
1926- b'\xe0': {},
1927- b'\xe1\x42\x65\x6e\x43\x46\x6f\x6f': {
1928- 'en': 'Foo'
1929- },
1930- b'\xe2\x42\x65\x6e\x43\x46\x6f\x6f\x42\x7a\x68\x43\xe4\xba\xba': {
1931- 'en': 'Foo',
1932- 'zh': '人'
1933- },
1934- (b'\xe1\x44\x6e\x61\x6d\x65\xe2\x42\x65\x6e'
1935- b'\x43\x46\x6f\x6f\x42\x7a\x68\x43\xe4\xba\xba'): {
1936- 'name': {
1937- 'en': 'Foo',
1938- 'zh': '人'
1939- }
1940- },
1941- (b'\xe1\x49\x6c\x61\x6e\x67\x75\x61\x67\x65\x73'
1942- b'\x02\x04\x42\x65\x6e\x42\x7a\x68'): {
1943- 'languages': ['en', 'zh']
1944+ b"\xe0": {},
1945+ b"\xe1\x42\x65\x6e\x43\x46\x6f\x6f": {"en": "Foo"},
1946+ b"\xe2\x42\x65\x6e\x43\x46\x6f\x6f\x42\x7a\x68\x43\xe4\xba\xba": {
1947+ "en": "Foo",
1948+ "zh": "人",
1949 },
1950+ (
1951+ b"\xe1\x44\x6e\x61\x6d\x65\xe2\x42\x65\x6e"
1952+ b"\x43\x46\x6f\x6f\x42\x7a\x68\x43\xe4\xba\xba"
1953+ ): {"name": {"en": "Foo", "zh": "人"}},
1954+ (
1955+ b"\xe1\x49\x6c\x61\x6e\x67\x75\x61\x67\x65\x73"
1956+ b"\x02\x04\x42\x65\x6e\x42\x7a\x68"
1957+ ): {"languages": ["en", "zh"]},
1958 }
1959- self.validate_type_decoding('maps', maps)
1960+ self.validate_type_decoding("maps", maps)
1961
1962 def test_pointer(self):
1963 pointers = {
1964- b'\x20\x00': 0,
1965- b'\x20\x05': 5,
1966- b'\x20\x0a': 10,
1967- b'\x23\xff': 1023,
1968- b'\x28\x03\xc9': 3017,
1969- b'\x2f\xf7\xfb': 524283,
1970- b'\x2f\xff\xff': 526335,
1971- b'\x37\xf7\xf7\xfe': 134217726,
1972- b'\x37\xff\xff\xff': 134744063,
1973- b'\x38\x7f\xff\xff\xff': 2147483647,
1974- b'\x38\xff\xff\xff\xff': 4294967295,
1975+ b"\x20\x00": 0,
1976+ b"\x20\x05": 5,
1977+ b"\x20\x0a": 10,
1978+ b"\x23\xff": 1023,
1979+ b"\x28\x03\xc9": 3017,
1980+ b"\x2f\xf7\xfb": 524283,
1981+ b"\x2f\xff\xff": 526335,
1982+ b"\x37\xf7\xf7\xfe": 134217726,
1983+ b"\x37\xff\xff\xff": 134744063,
1984+ b"\x38\x7f\xff\xff\xff": 2147483647,
1985+ b"\x38\xff\xff\xff\xff": 4294967295,
1986 }
1987- self.validate_type_decoding('pointers', pointers)
1988+ self.validate_type_decoding("pointers", pointers)
1989
1990 strings = {
1991- b"\x40":
1992- '',
1993- b"\x41\x31":
1994- '1',
1995- b"\x43\xE4\xBA\xBA":
1996- '人',
1997- (b"\x5b\x31\x32\x33\x34"
1998- b"\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35"
1999- b"\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37"):
2000- '123456789012345678901234567',
2001- (b"\x5c\x31\x32\x33\x34"
2002- b"\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35"
2003- b"\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36"
2004- b"\x37\x38"):
2005- '1234567890123456789012345678',
2006- (b"\x5d\x00\x31\x32\x33"
2007- b"\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34"
2008- b"\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35"
2009- b"\x36\x37\x38\x39"):
2010- '12345678901234567890123456789',
2011- (b"\x5d\x01\x31\x32\x33"
2012- b"\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34"
2013- b"\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35"
2014- b"\x36\x37\x38\x39\x30"):
2015- '123456789012345678901234567890',
2016- b'\x5e\x00\xd7' + 500 * b'\x78':
2017- 'x' * 500,
2018- b'\x5e\x06\xb3' + 2000 * b'\x78':
2019- 'x' * 2000,
2020- b'\x5f\x00\x10\x53' + 70000 * b'\x78':
2021- 'x' * 70000,
2022+ b"\x40": "",
2023+ b"\x41\x31": "1",
2024+ b"\x43\xE4\xBA\xBA": "人",
2025+ (
2026+ b"\x5b\x31\x32\x33\x34"
2027+ b"\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35"
2028+ b"\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37"
2029+ ): "123456789012345678901234567",
2030+ (
2031+ b"\x5c\x31\x32\x33\x34"
2032+ b"\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35"
2033+ b"\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36"
2034+ b"\x37\x38"
2035+ ): "1234567890123456789012345678",
2036+ (
2037+ b"\x5d\x00\x31\x32\x33"
2038+ b"\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34"
2039+ b"\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35"
2040+ b"\x36\x37\x38\x39"
2041+ ): "12345678901234567890123456789",
2042+ (
2043+ b"\x5d\x01\x31\x32\x33"
2044+ b"\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34"
2045+ b"\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35"
2046+ b"\x36\x37\x38\x39\x30"
2047+ ): "123456789012345678901234567890",
2048+ b"\x5e\x00\xd7" + 500 * b"\x78": "x" * 500,
2049+ b"\x5e\x06\xb3" + 2000 * b"\x78": "x" * 2000,
2050+ b"\x5f\x00\x10\x53" + 70000 * b"\x78": "x" * 70000,
2051 }
2052
2053 def test_string(self):
2054- self.validate_type_decoding('string', self.strings)
2055+ self.validate_type_decoding("string", self.strings)
2056
2057 def test_byte(self):
2058 # Python 2.6 doesn't support dictionary comprehension
2059- b = dict((byte_from_int(0xc0 ^ int_from_byte(k[0])) + k[1:],
2060- v.encode('utf-8')) for k, v in self.strings.items())
2061- self.validate_type_decoding('byte', b)
2062+ b = dict(
2063+ (byte_from_int(0xC0 ^ int_from_byte(k[0])) + k[1:], v.encode("utf-8"))
2064+ for k, v in self.strings.items()
2065+ )
2066+ self.validate_type_decoding("byte", b)
2067
2068 def test_uint16(self):
2069 uint16 = {
2070@@ -167,7 +162,7 @@ class TestDecoder(unittest.TestCase):
2071 b"\xa2\x2a\x78": 10872,
2072 b"\xa2\xff\xff": 65535,
2073 }
2074- self.validate_type_decoding('uint16', uint16)
2075+ self.validate_type_decoding("uint16", uint16)
2076
2077 def test_uint32(self):
2078 uint32 = {
2079@@ -179,26 +174,26 @@ class TestDecoder(unittest.TestCase):
2080 b"\xc3\xff\xff\xff": 16777215,
2081 b"\xc4\xff\xff\xff\xff": 4294967295,
2082 }
2083- self.validate_type_decoding('uint32', uint32)
2084+ self.validate_type_decoding("uint32", uint32)
2085
2086 def generate_large_uint(self, bits):
2087- ctrl_byte = b'\x02' if bits == 64 else b'\x03'
2088+ ctrl_byte = b"\x02" if bits == 64 else b"\x03"
2089 uints = {
2090- b'\x00' + ctrl_byte: 0,
2091- b'\x02' + ctrl_byte + b'\x01\xf4': 500,
2092- b'\x02' + ctrl_byte + b'\x2a\x78': 10872,
2093+ b"\x00" + ctrl_byte: 0,
2094+ b"\x02" + ctrl_byte + b"\x01\xf4": 500,
2095+ b"\x02" + ctrl_byte + b"\x2a\x78": 10872,
2096 }
2097 for power in range(bits // 8 + 1):
2098- expected = 2**(8 * power) - 1
2099- input = byte_from_int(power) + ctrl_byte + (b'\xff' * power)
2100+ expected = 2 ** (8 * power) - 1
2101+ input = byte_from_int(power) + ctrl_byte + (b"\xff" * power)
2102 uints[input] = expected
2103 return uints
2104
2105 def test_uint64(self):
2106- self.validate_type_decoding('uint64', self.generate_large_uint(64))
2107+ self.validate_type_decoding("uint64", self.generate_large_uint(64))
2108
2109 def test_uint128(self):
2110- self.validate_type_decoding('uint128', self.generate_large_uint(128))
2111+ self.validate_type_decoding("uint128", self.generate_large_uint(128))
2112
2113 def validate_type_decoding(self, type, tests):
2114 for input, expected in tests.items():
2115@@ -211,44 +206,28 @@ class TestDecoder(unittest.TestCase):
2116 db.write(input)
2117
2118 decoder = Decoder(db, pointer_test=True)
2119- (
2120- actual,
2121- _,
2122- ) = decoder.decode(0)
2123+ (actual, _,) = decoder.decode(0)
2124
2125- if type in ('float', 'double'):
2126+ if type in ("float", "double"):
2127 self.assertAlmostEqual(expected, actual, places=3, msg=type)
2128 else:
2129 self.assertEqual(expected, actual, type)
2130
2131 def test_real_pointers(self):
2132- with open('tests/data/test-data/maps-with-pointers.raw',
2133- 'r+b') as db_file:
2134+ with open("tests/data/test-data/maps-with-pointers.raw", "r+b") as db_file:
2135 mm = mmap.mmap(db_file.fileno(), 0)
2136 decoder = Decoder(mm, 0)
2137
2138- self.assertEqual(({
2139- 'long_key': 'long_value1'
2140- }, 22), decoder.decode(0))
2141+ self.assertEqual(({"long_key": "long_value1"}, 22), decoder.decode(0))
2142
2143- self.assertEqual(({
2144- 'long_key': 'long_value2'
2145- }, 37), decoder.decode(22))
2146+ self.assertEqual(({"long_key": "long_value2"}, 37), decoder.decode(22))
2147
2148- self.assertEqual(({
2149- 'long_key2': 'long_value1'
2150- }, 50), decoder.decode(37))
2151+ self.assertEqual(({"long_key2": "long_value1"}, 50), decoder.decode(37))
2152
2153- self.assertEqual(({
2154- 'long_key2': 'long_value2'
2155- }, 55), decoder.decode(50))
2156+ self.assertEqual(({"long_key2": "long_value2"}, 55), decoder.decode(50))
2157
2158- self.assertEqual(({
2159- 'long_key': 'long_value1'
2160- }, 57), decoder.decode(55))
2161+ self.assertEqual(({"long_key": "long_value1"}, 57), decoder.decode(55))
2162
2163- self.assertEqual(({
2164- 'long_key2': 'long_value2'
2165- }, 59), decoder.decode(57))
2166+ self.assertEqual(({"long_key2": "long_value2"}, 59), decoder.decode(57))
2167
2168 mm.close()
2169diff --git a/tests/reader_test.py b/tests/reader_test.py
2170index e2b4fc7..8510bd0 100644
2171--- a/tests/reader_test.py
2172+++ b/tests/reader_test.py
2173@@ -20,8 +20,14 @@ except ImportError:
2174
2175 from maxminddb import open_database, InvalidDatabaseError
2176 from maxminddb.compat import FileNotFoundError
2177-from maxminddb.const import (MODE_AUTO, MODE_MMAP_EXT, MODE_MMAP, MODE_FILE,
2178- MODE_MEMORY, MODE_FD)
2179+from maxminddb.const import (
2180+ MODE_AUTO,
2181+ MODE_MMAP_EXT,
2182+ MODE_MMAP,
2183+ MODE_FILE,
2184+ MODE_MEMORY,
2185+ MODE_FD,
2186+)
2187
2188 if sys.version_info[:2] == (2, 6):
2189 import unittest2 as unittest
2190@@ -36,7 +42,7 @@ if sys.version_info[0] == 2:
2191 def get_reader_from_file_descriptor(filepath, mode):
2192 """Patches open_database() for class TestFDReader()."""
2193 if mode == MODE_FD:
2194- with open(filepath, 'rb') as mmdb_fh:
2195+ with open(filepath, "rb") as mmdb_fh:
2196 return maxminddb.open_database(mmdb_fh, mode)
2197 else:
2198 # There are a few cases where mode is statically defined in
2199@@ -56,9 +62,13 @@ class BaseTestReader(object):
2200 def test_reader(self):
2201 for record_size in [24, 28, 32]:
2202 for ip_version in [4, 6]:
2203- file_name = ('tests/data/test-data/MaxMind-DB-test-ipv' +
2204- str(ip_version) + '-' + str(record_size) +
2205- '.mmdb')
2206+ file_name = (
2207+ "tests/data/test-data/MaxMind-DB-test-ipv"
2208+ + str(ip_version)
2209+ + "-"
2210+ + str(record_size)
2211+ + ".mmdb"
2212+ )
2213 reader = open_database(file_name, self.mode)
2214
2215 self._check_metadata(reader, ip_version, record_size)
2216@@ -73,16 +83,11 @@ class BaseTestReader(object):
2217 decoder_record = {
2218 "array": [1, 2, 3],
2219 "boolean": True,
2220- "bytes": b'\x00\x00\x00*',
2221+ "bytes": b"\x00\x00\x00*",
2222 "double": 42.123456,
2223 "float": 1.100000023841858,
2224 "int32": -268435456,
2225- "map": {
2226- "mapX": {
2227- "arrayX": [7, 8, 9],
2228- "utf8_stringX": "hello",
2229- },
2230- },
2231+ "map": {"mapX": {"arrayX": [7, 8, 9], "utf8_stringX": "hello",},},
2232 "uint128": 1329227995784915872903807060280344576,
2233 "uint16": 0x64,
2234 "uint32": 0x10000000,
2235@@ -90,278 +95,313 @@ class BaseTestReader(object):
2236 "utf8_string": "unicode! ☯ - ♫",
2237 }
2238
2239- tests = [{
2240- 'ip': '1.1.1.1',
2241- 'file_name': 'MaxMind-DB-test-ipv6-32.mmdb',
2242- 'expected_prefix_len': 8,
2243- 'expected_record': None,
2244- }, {
2245- 'ip': '::1:ffff:ffff',
2246- 'file_name': 'MaxMind-DB-test-ipv6-24.mmdb',
2247- 'expected_prefix_len': 128,
2248- 'expected_record': {
2249- "ip": "::1:ffff:ffff"
2250+ tests = [
2251+ {
2252+ "ip": "1.1.1.1",
2253+ "file_name": "MaxMind-DB-test-ipv6-32.mmdb",
2254+ "expected_prefix_len": 8,
2255+ "expected_record": None,
2256+ },
2257+ {
2258+ "ip": "::1:ffff:ffff",
2259+ "file_name": "MaxMind-DB-test-ipv6-24.mmdb",
2260+ "expected_prefix_len": 128,
2261+ "expected_record": {"ip": "::1:ffff:ffff"},
2262+ },
2263+ {
2264+ "ip": "::2:0:1",
2265+ "file_name": "MaxMind-DB-test-ipv6-24.mmdb",
2266+ "expected_prefix_len": 122,
2267+ "expected_record": {"ip": "::2:0:0"},
2268+ },
2269+ {
2270+ "ip": "1.1.1.1",
2271+ "file_name": "MaxMind-DB-test-ipv4-24.mmdb",
2272+ "expected_prefix_len": 32,
2273+ "expected_record": {"ip": "1.1.1.1"},
2274+ },
2275+ {
2276+ "ip": "1.1.1.3",
2277+ "file_name": "MaxMind-DB-test-ipv4-24.mmdb",
2278+ "expected_prefix_len": 31,
2279+ "expected_record": {"ip": "1.1.1.2"},
2280+ },
2281+ {
2282+ "ip": "1.1.1.3",
2283+ "file_name": "MaxMind-DB-test-decoder.mmdb",
2284+ "expected_prefix_len": 24,
2285+ "expected_record": decoder_record,
2286+ },
2287+ {
2288+ "ip": "::ffff:1.1.1.128",
2289+ "file_name": "MaxMind-DB-test-decoder.mmdb",
2290+ "expected_prefix_len": 120,
2291+ "expected_record": decoder_record,
2292 },
2293- }, {
2294- 'ip': '::2:0:1',
2295- 'file_name': 'MaxMind-DB-test-ipv6-24.mmdb',
2296- 'expected_prefix_len': 122,
2297- 'expected_record': {
2298- "ip": "::2:0:0"
2299+ {
2300+ "ip": "::1.1.1.128",
2301+ "file_name": "MaxMind-DB-test-decoder.mmdb",
2302+ "expected_prefix_len": 120,
2303+ "expected_record": decoder_record,
2304 },
2305- }, {
2306- 'ip': '1.1.1.1',
2307- 'file_name': 'MaxMind-DB-test-ipv4-24.mmdb',
2308- 'expected_prefix_len': 32,
2309- 'expected_record': {
2310- "ip": "1.1.1.1"
2311+ {
2312+ "ip": "200.0.2.1",
2313+ "file_name": "MaxMind-DB-no-ipv4-search-tree.mmdb",
2314+ "expected_prefix_len": 0,
2315+ "expected_record": "::0/64",
2316 },
2317- }, {
2318- 'ip': '1.1.1.3',
2319- 'file_name': 'MaxMind-DB-test-ipv4-24.mmdb',
2320- 'expected_prefix_len': 31,
2321- 'expected_record': {
2322- "ip": "1.1.1.2"
2323+ {
2324+ "ip": "::200.0.2.1",
2325+ "file_name": "MaxMind-DB-no-ipv4-search-tree.mmdb",
2326+ "expected_prefix_len": 64,
2327+ "expected_record": "::0/64",
2328 },
2329- }, {
2330- 'ip': '1.1.1.3',
2331- 'file_name': 'MaxMind-DB-test-decoder.mmdb',
2332- 'expected_prefix_len': 24,
2333- 'expected_record': decoder_record,
2334- }, {
2335- 'ip': '::ffff:1.1.1.128',
2336- 'file_name': 'MaxMind-DB-test-decoder.mmdb',
2337- 'expected_prefix_len': 120,
2338- 'expected_record': decoder_record,
2339- }, {
2340- 'ip': '::1.1.1.128',
2341- 'file_name': 'MaxMind-DB-test-decoder.mmdb',
2342- 'expected_prefix_len': 120,
2343- 'expected_record': decoder_record,
2344- }, {
2345- 'ip': '200.0.2.1',
2346- 'file_name': 'MaxMind-DB-no-ipv4-search-tree.mmdb',
2347- 'expected_prefix_len': 0,
2348- 'expected_record': "::0/64",
2349- }, {
2350- 'ip': '::200.0.2.1',
2351- 'file_name': 'MaxMind-DB-no-ipv4-search-tree.mmdb',
2352- 'expected_prefix_len': 64,
2353- 'expected_record': "::0/64",
2354- }, {
2355- 'ip': '0:0:0:0:ffff:ffff:ffff:ffff',
2356- 'file_name': 'MaxMind-DB-no-ipv4-search-tree.mmdb',
2357- 'expected_prefix_len': 64,
2358- 'expected_record': "::0/64",
2359- }, {
2360- 'ip': 'ef00::',
2361- 'file_name': 'MaxMind-DB-no-ipv4-search-tree.mmdb',
2362- 'expected_prefix_len': 1,
2363- 'expected_record': None,
2364- }]
2365+ {
2366+ "ip": "0:0:0:0:ffff:ffff:ffff:ffff",
2367+ "file_name": "MaxMind-DB-no-ipv4-search-tree.mmdb",
2368+ "expected_prefix_len": 64,
2369+ "expected_record": "::0/64",
2370+ },
2371+ {
2372+ "ip": "ef00::",
2373+ "file_name": "MaxMind-DB-no-ipv4-search-tree.mmdb",
2374+ "expected_prefix_len": 1,
2375+ "expected_record": None,
2376+ },
2377+ ]
2378
2379 for test in tests:
2380- with open_database('tests/data/test-data/' + test['file_name'],
2381- self.mode) as reader:
2382- (record, prefix_len) = reader.get_with_prefix_len(test['ip'])
2383+ with open_database(
2384+ "tests/data/test-data/" + test["file_name"], self.mode
2385+ ) as reader:
2386+ (record, prefix_len) = reader.get_with_prefix_len(test["ip"])
2387
2388 self.assertEqual(
2389- prefix_len, test['expected_prefix_len'],
2390- 'expected prefix_len of {} for {} in {} but got {}'.format(
2391- test['expected_prefix_len'], test['ip'],
2392- test['file_name'], prefix_len))
2393+ prefix_len,
2394+ test["expected_prefix_len"],
2395+ "expected prefix_len of {} for {} in {} but got {}".format(
2396+ test["expected_prefix_len"],
2397+ test["ip"],
2398+ test["file_name"],
2399+ prefix_len,
2400+ ),
2401+ )
2402 self.assertEqual(
2403- record, test['expected_record'], 'expected_record for ' +
2404- test['ip'] + ' in ' + test['file_name'])
2405+ record,
2406+ test["expected_record"],
2407+ "expected_record for " + test["ip"] + " in " + test["file_name"],
2408+ )
2409
2410 def test_decoder(self):
2411 reader = open_database(
2412- 'tests/data/test-data/MaxMind-DB-test-decoder.mmdb', self.mode)
2413- record = reader.get(self.ipf('::1.1.1.0'))
2414-
2415- self.assertEqual(record['array'], [1, 2, 3])
2416- self.assertEqual(record['boolean'], True)
2417- self.assertEqual(record['bytes'], bytearray(b'\x00\x00\x00*'))
2418- self.assertEqual(record['double'], 42.123456)
2419- self.assertAlmostEqual(record['float'], 1.1)
2420- self.assertEqual(record['int32'], -268435456)
2421+ "tests/data/test-data/MaxMind-DB-test-decoder.mmdb", self.mode
2422+ )
2423+ record = reader.get(self.ipf("::1.1.1.0"))
2424+
2425+ self.assertEqual(record["array"], [1, 2, 3])
2426+ self.assertEqual(record["boolean"], True)
2427+ self.assertEqual(record["bytes"], bytearray(b"\x00\x00\x00*"))
2428+ self.assertEqual(record["double"], 42.123456)
2429+ self.assertAlmostEqual(record["float"], 1.1)
2430+ self.assertEqual(record["int32"], -268435456)
2431 self.assertEqual(
2432- {
2433- 'mapX': {
2434- 'arrayX': [7, 8, 9],
2435- 'utf8_stringX': 'hello'
2436- },
2437- }, record['map'])
2438-
2439- self.assertEqual(record['uint16'], 100)
2440- self.assertEqual(record['uint32'], 268435456)
2441- self.assertEqual(record['uint64'], 1152921504606846976)
2442- self.assertEqual(record['utf8_string'], 'unicode! ☯ - ♫')
2443-
2444- self.assertEqual(1329227995784915872903807060280344576,
2445- record['uint128'])
2446+ {"mapX": {"arrayX": [7, 8, 9], "utf8_stringX": "hello"},}, record["map"]
2447+ )
2448+
2449+ self.assertEqual(record["uint16"], 100)
2450+ self.assertEqual(record["uint32"], 268435456)
2451+ self.assertEqual(record["uint64"], 1152921504606846976)
2452+ self.assertEqual(record["utf8_string"], "unicode! ☯ - ♫")
2453+
2454+ self.assertEqual(1329227995784915872903807060280344576, record["uint128"])
2455 reader.close()
2456
2457 def test_no_ipv4_search_tree(self):
2458 reader = open_database(
2459- 'tests/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb',
2460- self.mode)
2461+ "tests/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb", self.mode
2462+ )
2463
2464- self.assertEqual(reader.get(self.ipf('1.1.1.1')), '::0/64')
2465- self.assertEqual(reader.get(self.ipf('192.1.1.1')), '::0/64')
2466+ self.assertEqual(reader.get(self.ipf("1.1.1.1")), "::0/64")
2467+ self.assertEqual(reader.get(self.ipf("192.1.1.1")), "::0/64")
2468 reader.close()
2469
2470 def test_ipv6_address_in_ipv4_database(self):
2471 reader = open_database(
2472- 'tests/data/test-data/MaxMind-DB-test-ipv4-24.mmdb', self.mode)
2473+ "tests/data/test-data/MaxMind-DB-test-ipv4-24.mmdb", self.mode
2474+ )
2475 with self.assertRaisesRegex(
2476- ValueError, 'Error looking up 2001::. '
2477- 'You attempted to look up an IPv6 address '
2478- 'in an IPv4-only database'):
2479- reader.get(self.ipf('2001::'))
2480+ ValueError,
2481+ "Error looking up 2001::. "
2482+ "You attempted to look up an IPv6 address "
2483+ "in an IPv4-only database",
2484+ ):
2485+ reader.get(self.ipf("2001::"))
2486 reader.close()
2487
2488 def test_no_extension_exception(self):
2489 real_extension = maxminddb.extension
2490 maxminddb.extension = None
2491 with self.assertRaisesRegex(
2492- ValueError,
2493- 'MODE_MMAP_EXT requires the maxminddb.extension module to be available'
2494+ ValueError,
2495+ "MODE_MMAP_EXT requires the maxminddb.extension module to be available",
2496 ):
2497- open_database('tests/data/test-data/MaxMind-DB-test-decoder.mmdb',
2498- MODE_MMAP_EXT)
2499+ open_database(
2500+ "tests/data/test-data/MaxMind-DB-test-decoder.mmdb", MODE_MMAP_EXT
2501+ )
2502 maxminddb.extension = real_extension
2503
2504 def test_broken_database(self):
2505 reader = open_database(
2506- 'tests/data/test-data/'
2507- 'GeoIP2-City-Test-Broken-Double-Format.mmdb', self.mode)
2508+ "tests/data/test-data/" "GeoIP2-City-Test-Broken-Double-Format.mmdb",
2509+ self.mode,
2510+ )
2511 with self.assertRaisesRegex(
2512- InvalidDatabaseError, r"The MaxMind DB file's data "
2513- r"section contains bad data \(unknown data "
2514- r"type or corrupt data\)"):
2515- reader.get(self.ipf('2001:220::'))
2516+ InvalidDatabaseError,
2517+ r"The MaxMind DB file's data "
2518+ r"section contains bad data \(unknown data "
2519+ r"type or corrupt data\)",
2520+ ):
2521+ reader.get(self.ipf("2001:220::"))
2522 reader.close()
2523
2524 def test_ip_validation(self):
2525 reader = open_database(
2526- 'tests/data/test-data/MaxMind-DB-test-decoder.mmdb', self.mode)
2527+ "tests/data/test-data/MaxMind-DB-test-decoder.mmdb", self.mode
2528+ )
2529 with self.assertRaisesRegex(
2530- ValueError, "'not_ip' does not appear to be an IPv4 or "
2531- "IPv6 address"):
2532- reader.get('not_ip')
2533+ ValueError, "'not_ip' does not appear to be an IPv4 or " "IPv6 address"
2534+ ):
2535+ reader.get("not_ip")
2536 reader.close()
2537
2538 def test_missing_database(self):
2539- with self.assertRaisesRegex(FileNotFoundError,
2540- "No such file or directory"):
2541- open_database('file-does-not-exist.mmdb', self.mode)
2542+ with self.assertRaisesRegex(FileNotFoundError, "No such file or directory"):
2543+ open_database("file-does-not-exist.mmdb", self.mode)
2544
2545 def test_nondatabase(self):
2546 with self.assertRaisesRegex(
2547- InvalidDatabaseError,
2548- r'Error opening database file \(README.rst\). '
2549- r'Is this a valid MaxMind DB file\?'):
2550- open_database('README.rst', self.mode)
2551+ InvalidDatabaseError,
2552+ r"Error opening database file \(README.rst\). "
2553+ r"Is this a valid MaxMind DB file\?",
2554+ ):
2555+ open_database("README.rst", self.mode)
2556+
2557+ # This is from https://github.com/maxmind/MaxMind-DB-Reader-python/issues/58
2558+ def test_database_with_invalid_utf8_key(self):
2559+ reader = open_database(
2560+ "tests/data/bad-data/maxminddb-python/bad-unicode-in-map-key.mmdb",
2561+ self.mode,
2562+ )
2563+ with self.assertRaises(UnicodeDecodeError):
2564+ reader.get_with_prefix_len("163.254.149.39")
2565
2566 def test_too_many_constructor_args(self):
2567 with self.assertRaises(TypeError):
2568- self.readerClass[0]('README.md', self.mode, 1)
2569+ self.readerClass[0]("README.md", self.mode, 1)
2570
2571 def test_bad_constructor_mode(self):
2572- with self.assertRaisesRegex(ValueError,
2573- r'Unsupported open mode \(100\)'):
2574- self.readerClass[0]('README.md', mode=100)
2575+ with self.assertRaisesRegex(ValueError, r"Unsupported open mode \(100\)"):
2576+ self.readerClass[0]("README.md", mode=100)
2577
2578 def test_no_constructor_args(self):
2579 with self.assertRaisesRegex(
2580- TypeError, r' 1 required positional argument|'
2581- r'\(pos 1\) not found|'
2582- r'takes at least 2 arguments|'
2583- r'function missing required argument \'database\' \(pos 1\)'):
2584+ TypeError,
2585+ r" 1 required positional argument|"
2586+ r"\(pos 1\) not found|"
2587+ r"takes at least 2 arguments|"
2588+ r"function missing required argument \'database\' \(pos 1\)",
2589+ ):
2590 self.readerClass[0]()
2591
2592 def test_too_many_get_args(self):
2593 reader = open_database(
2594- 'tests/data/test-data/MaxMind-DB-test-decoder.mmdb', self.mode)
2595+ "tests/data/test-data/MaxMind-DB-test-decoder.mmdb", self.mode
2596+ )
2597 with self.assertRaises(TypeError):
2598- reader.get(self.ipf('1.1.1.1'), 'blah')
2599+ reader.get(self.ipf("1.1.1.1"), "blah")
2600 reader.close()
2601
2602 def test_no_get_args(self):
2603 reader = open_database(
2604- 'tests/data/test-data/MaxMind-DB-test-decoder.mmdb', self.mode)
2605+ "tests/data/test-data/MaxMind-DB-test-decoder.mmdb", self.mode
2606+ )
2607 with self.assertRaises(TypeError):
2608 reader.get()
2609 reader.close()
2610
2611 def test_incorrect_get_arg_type(self):
2612- reader = open_database('tests/data/test-data/GeoIP2-City-Test.mmdb',
2613- self.mode)
2614+ reader = open_database("tests/data/test-data/GeoIP2-City-Test.mmdb", self.mode)
2615 with self.assertRaisesRegex(
2616- TypeError, "argument 1 must be a string or ipaddress object"):
2617+ TypeError, "argument 1 must be a string or ipaddress object"
2618+ ):
2619 reader.get(1)
2620 reader.close()
2621
2622 def test_metadata_args(self):
2623 reader = open_database(
2624- 'tests/data/test-data/MaxMind-DB-test-decoder.mmdb', self.mode)
2625+ "tests/data/test-data/MaxMind-DB-test-decoder.mmdb", self.mode
2626+ )
2627 with self.assertRaises(TypeError):
2628- reader.metadata('blah')
2629+ reader.metadata("blah")
2630 reader.close()
2631
2632 def test_metadata_unknown_attribute(self):
2633 reader = open_database(
2634- 'tests/data/test-data/MaxMind-DB-test-decoder.mmdb', self.mode)
2635+ "tests/data/test-data/MaxMind-DB-test-decoder.mmdb", self.mode
2636+ )
2637 metadata = reader.metadata()
2638 with self.assertRaisesRegex(
2639- AttributeError, "'Metadata' object has no "
2640- "attribute 'blah'"):
2641+ AttributeError, "'Metadata' object has no " "attribute 'blah'"
2642+ ):
2643 metadata.blah
2644 reader.close()
2645
2646 def test_close(self):
2647 reader = open_database(
2648- 'tests/data/test-data/MaxMind-DB-test-decoder.mmdb', self.mode)
2649+ "tests/data/test-data/MaxMind-DB-test-decoder.mmdb", self.mode
2650+ )
2651 reader.close()
2652
2653 def test_double_close(self):
2654 reader = open_database(
2655- 'tests/data/test-data/MaxMind-DB-test-decoder.mmdb', self.mode)
2656+ "tests/data/test-data/MaxMind-DB-test-decoder.mmdb", self.mode
2657+ )
2658 reader.close()
2659- self.assertIsNone(reader.close(),
2660- 'Double close does not throw an exception')
2661+ self.assertIsNone(reader.close(), "Double close does not throw an exception")
2662
2663 def test_closed_get(self):
2664 if self.mode in [MODE_MEMORY, MODE_FD]:
2665 return
2666 reader = open_database(
2667- 'tests/data/test-data/MaxMind-DB-test-decoder.mmdb', self.mode)
2668+ "tests/data/test-data/MaxMind-DB-test-decoder.mmdb", self.mode
2669+ )
2670 reader.close()
2671 with self.assertRaisesRegex(
2672- ValueError,
2673- 'Attempt to read from a closed MaxMind DB.|closed'):
2674- reader.get(self.ipf('1.1.1.1'))
2675+ ValueError, "Attempt to read from a closed MaxMind DB.|closed"
2676+ ):
2677+ reader.get(self.ipf("1.1.1.1"))
2678
2679 def test_with_statement(self):
2680- filename = 'tests/data/test-data/MaxMind-DB-test-ipv4-24.mmdb'
2681+ filename = "tests/data/test-data/MaxMind-DB-test-ipv4-24.mmdb"
2682 with open_database(filename, self.mode) as reader:
2683 self._check_ip_v4(reader, filename)
2684 self.assertEqual(reader.closed, True)
2685
2686 def test_with_statement_close(self):
2687- filename = 'tests/data/test-data/MaxMind-DB-test-ipv4-24.mmdb'
2688+ filename = "tests/data/test-data/MaxMind-DB-test-ipv4-24.mmdb"
2689 reader = open_database(filename, self.mode)
2690 reader.close()
2691
2692- with self.assertRaisesRegex(ValueError,
2693- 'Attempt to reopen a closed MaxMind DB'):
2694+ with self.assertRaisesRegex(
2695+ ValueError, "Attempt to reopen a closed MaxMind DB"
2696+ ):
2697 with reader:
2698 pass
2699
2700 def test_closed(self):
2701 reader = open_database(
2702- 'tests/data/test-data/MaxMind-DB-test-decoder.mmdb', self.mode)
2703+ "tests/data/test-data/MaxMind-DB-test-decoder.mmdb", self.mode
2704+ )
2705 self.assertEqual(reader.closed, False)
2706 reader.close()
2707 self.assertEqual(reader.closed, True)
2708@@ -372,7 +412,8 @@ class BaseTestReader(object):
2709 # to keep the metadata in memory.
2710 def test_closed_metadata(self):
2711 reader = open_database(
2712- 'tests/data/test-data/MaxMind-DB-test-decoder.mmdb', self.mode)
2713+ "tests/data/test-data/MaxMind-DB-test-decoder.mmdb", self.mode
2714+ )
2715 reader.close()
2716
2717 # The primary purpose of this is to ensure the extension doesn't
2718@@ -380,11 +421,13 @@ class BaseTestReader(object):
2719 try:
2720 metadata = reader.metadata()
2721 except IOError as ex:
2722- self.assertEqual('Attempt to read from a closed MaxMind DB.',
2723- str(ex), 'extension throws exception')
2724+ self.assertEqual(
2725+ "Attempt to read from a closed MaxMind DB.",
2726+ str(ex),
2727+ "extension throws exception",
2728+ )
2729 else:
2730- self.assertIsNotNone(metadata,
2731- 'pure Python implementation returns value')
2732+ self.assertIsNotNone(metadata, "pure Python implementation returns value")
2733
2734 def test_multiprocessing(self):
2735 self._check_concurrency(Process)
2736@@ -396,17 +439,19 @@ class BaseTestReader(object):
2737
2738 def test_byte_ip_on_python2(self):
2739 reader = open_database(
2740- 'tests/data/test-data/MaxMind-DB-test-decoder.mmdb', self.mode)
2741- record = reader.get(b'::1.1.1.0')
2742+ "tests/data/test-data/MaxMind-DB-test-decoder.mmdb", self.mode
2743+ )
2744+ record = reader.get(b"::1.1.1.0")
2745
2746 def _check_concurrency(self, worker_class):
2747- reader = open_database('tests/data/test-data/GeoIP2-Domain-Test.mmdb',
2748- self.mode)
2749+ reader = open_database(
2750+ "tests/data/test-data/GeoIP2-Domain-Test.mmdb", self.mode
2751+ )
2752
2753 def lookup(pipe):
2754 try:
2755 for i in range(32):
2756- reader.get(self.ipf('65.115.240.{i}'.format(i=i)))
2757+ reader.get(self.ipf("65.115.240.{i}".format(i=i)))
2758 pipe.send(1)
2759 except:
2760 pipe.send(0)
2761@@ -416,7 +461,7 @@ class BaseTestReader(object):
2762 pipe.close()
2763
2764 pipes = [Pipe() for _ in range(32)]
2765- procs = [worker_class(target=lookup, args=(c, )) for (p, c) in pipes]
2766+ procs = [worker_class(target=lookup, args=(c,)) for (p, c) in pipes]
2767 for proc in procs:
2768 proc.start()
2769 for proc in procs:
2770@@ -426,92 +471,95 @@ class BaseTestReader(object):
2771
2772 count = sum([p.recv() for (p, c) in pipes])
2773
2774- self.assertEqual(count, 32, 'expected number of successful lookups')
2775+ self.assertEqual(count, 32, "expected number of successful lookups")
2776
2777 def _check_metadata(self, reader, ip_version, record_size):
2778 metadata = reader.metadata()
2779
2780- self.assertEqual(2, metadata.binary_format_major_version,
2781- 'major version')
2782+ self.assertEqual(2, metadata.binary_format_major_version, "major version")
2783 self.assertEqual(metadata.binary_format_minor_version, 0)
2784 self.assertGreater(metadata.build_epoch, 1373571901)
2785- self.assertEqual(metadata.database_type, 'Test')
2786+ self.assertEqual(metadata.database_type, "Test")
2787
2788- self.assertEqual({
2789- 'en': 'Test Database',
2790- 'zh': 'Test Database Chinese'
2791- }, metadata.description)
2792+ self.assertEqual(
2793+ {"en": "Test Database", "zh": "Test Database Chinese"}, metadata.description
2794+ )
2795 self.assertEqual(metadata.ip_version, ip_version)
2796- self.assertEqual(metadata.languages, ['en', 'zh'])
2797+ self.assertEqual(metadata.languages, ["en", "zh"])
2798 self.assertGreater(metadata.node_count, 36)
2799
2800 self.assertEqual(metadata.record_size, record_size)
2801
2802 def _check_ip_v4(self, reader, file_name):
2803 for i in range(6):
2804- address = '1.1.1.' + str(pow(2, i))
2805- self.assertEqual({'ip': address}, reader.get(self.ipf(address)),
2806- 'found expected data record for ' + address +
2807- ' in ' + file_name)
2808+ address = "1.1.1." + str(pow(2, i))
2809+ self.assertEqual(
2810+ {"ip": address},
2811+ reader.get(self.ipf(address)),
2812+ "found expected data record for " + address + " in " + file_name,
2813+ )
2814
2815 pairs = {
2816- '1.1.1.3': '1.1.1.2',
2817- '1.1.1.5': '1.1.1.4',
2818- '1.1.1.7': '1.1.1.4',
2819- '1.1.1.9': '1.1.1.8',
2820- '1.1.1.15': '1.1.1.8',
2821- '1.1.1.17': '1.1.1.16',
2822- '1.1.1.31': '1.1.1.16'
2823+ "1.1.1.3": "1.1.1.2",
2824+ "1.1.1.5": "1.1.1.4",
2825+ "1.1.1.7": "1.1.1.4",
2826+ "1.1.1.9": "1.1.1.8",
2827+ "1.1.1.15": "1.1.1.8",
2828+ "1.1.1.17": "1.1.1.16",
2829+ "1.1.1.31": "1.1.1.16",
2830 }
2831 for key_address, value_address in pairs.items():
2832- data = {'ip': value_address}
2833+ data = {"ip": value_address}
2834
2835 self.assertEqual(
2836- data, reader.get(self.ipf(key_address)),
2837- 'found expected data record for ' + key_address + ' in ' +
2838- file_name)
2839+ data,
2840+ reader.get(self.ipf(key_address)),
2841+ "found expected data record for " + key_address + " in " + file_name,
2842+ )
2843
2844- for ip in ['1.1.1.33', '255.254.253.123']:
2845+ for ip in ["1.1.1.33", "255.254.253.123"]:
2846 self.assertIsNone(reader.get(self.ipf(ip)))
2847
2848 def _check_ip_v6(self, reader, file_name):
2849- subnets = [
2850- '::1:ffff:ffff', '::2:0:0', '::2:0:40', '::2:0:50', '::2:0:58'
2851- ]
2852+ subnets = ["::1:ffff:ffff", "::2:0:0", "::2:0:40", "::2:0:50", "::2:0:58"]
2853
2854 for address in subnets:
2855- self.assertEqual({'ip': address}, reader.get(self.ipf(address)),
2856- 'found expected data record for ' + address +
2857- ' in ' + file_name)
2858+ self.assertEqual(
2859+ {"ip": address},
2860+ reader.get(self.ipf(address)),
2861+ "found expected data record for " + address + " in " + file_name,
2862+ )
2863
2864 pairs = {
2865- '::2:0:1': '::2:0:0',
2866- '::2:0:33': '::2:0:0',
2867- '::2:0:39': '::2:0:0',
2868- '::2:0:41': '::2:0:40',
2869- '::2:0:49': '::2:0:40',
2870- '::2:0:52': '::2:0:50',
2871- '::2:0:57': '::2:0:50',
2872- '::2:0:59': '::2:0:58'
2873+ "::2:0:1": "::2:0:0",
2874+ "::2:0:33": "::2:0:0",
2875+ "::2:0:39": "::2:0:0",
2876+ "::2:0:41": "::2:0:40",
2877+ "::2:0:49": "::2:0:40",
2878+ "::2:0:52": "::2:0:50",
2879+ "::2:0:57": "::2:0:50",
2880+ "::2:0:59": "::2:0:58",
2881 }
2882
2883 for key_address, value_address in pairs.items():
2884- self.assertEqual({'ip': value_address},
2885- reader.get(self.ipf(key_address)),
2886- 'found expected data record for ' + key_address +
2887- ' in ' + file_name)
2888+ self.assertEqual(
2889+ {"ip": value_address},
2890+ reader.get(self.ipf(key_address)),
2891+ "found expected data record for " + key_address + " in " + file_name,
2892+ )
2893
2894- for ip in ['1.1.1.33', '255.254.253.123', '89fa::']:
2895+ for ip in ["1.1.1.33", "255.254.253.123", "89fa::"]:
2896 self.assertIsNone(reader.get(self.ipf(ip)))
2897
2898
2899 def has_maxminddb_extension():
2900- return maxminddb.extension and hasattr(maxminddb.extension, 'Reader')
2901+ return maxminddb.extension and hasattr(maxminddb.extension, "Reader")
2902
2903
2904-@unittest.skipIf(not has_maxminddb_extension()
2905- and not os.environ.get('MM_FORCE_EXT_TESTS'),
2906- 'No C extension module found. Skipping tests')
2907+@unittest.skipIf(
2908+ not has_maxminddb_extension() and not os.environ.get("MM_FORCE_EXT_TESTS"),
2909+ "No C extension module found. Skipping tests",
2910+)
2911 class TestExtensionReader(BaseTestReader, unittest.TestCase):
2912 mode = MODE_MMAP_EXT
2913
2914@@ -519,9 +567,10 @@ class TestExtensionReader(BaseTestReader, unittest.TestCase):
2915 readerClass = [maxminddb.extension.Reader]
2916
2917
2918-@unittest.skipIf(not has_maxminddb_extension()
2919- and not os.environ.get('MM_FORCE_EXT_TESTS'),
2920- 'No C extension module found. Skipping tests')
2921+@unittest.skipIf(
2922+ not has_maxminddb_extension() and not os.environ.get("MM_FORCE_EXT_TESTS"),
2923+ "No C extension module found. Skipping tests",
2924+)
2925 class TestExtensionReaderWithIPObjects(BaseTestReader, unittest.TestCase):
2926 mode = MODE_MMAP_EXT
2927 use_ip_objects = True
2928@@ -564,7 +613,7 @@ class TestMemoryReader(BaseTestReader, unittest.TestCase):
2929
2930 class TestFDReader(BaseTestReader, unittest.TestCase):
2931 def setUp(self):
2932- self.open_database_patcher = mock.patch('reader_test.open_database')
2933+ self.open_database_patcher = mock.patch("reader_test.open_database")
2934 self.addCleanup(self.open_database_patcher.stop)
2935 self.open_database = self.open_database_patcher.start()
2936 self.open_database.side_effect = get_reader_from_file_descriptor
2937@@ -575,9 +624,8 @@ class TestFDReader(BaseTestReader, unittest.TestCase):
2938
2939 class TestOldReader(unittest.TestCase):
2940 def test_old_reader(self):
2941- reader = maxminddb.Reader(
2942- 'tests/data/test-data/MaxMind-DB-test-decoder.mmdb')
2943- record = reader.get('::1.1.1.0')
2944+ reader = maxminddb.Reader("tests/data/test-data/MaxMind-DB-test-decoder.mmdb")
2945+ record = reader.get("::1.1.1.0")
2946
2947- self.assertEqual(record['array'], [1, 2, 3])
2948+ self.assertEqual(record["array"], [1, 2, 3])
2949 reader.close()

Subscribers

People subscribed via source and target branches