Skip to content

Commit c50b86b

Browse files
authored
Merge pull request #26 from maxmind/greg/packed-bytearray-fix
Fix incompatibility with py2-ipaddress 
2 parents 6b95603 + b6289b9 commit c50b86b

File tree

9 files changed

+170
-177
lines changed

9 files changed

+170
-177
lines changed

HISTORY.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
History
44
-------
55

6+
1.2.3
7+
++++++++++++++++++
8+
9+
* Improve compatibility with other Python 2 ``ipaddress`` backports. Although
10+
```ipaddress`` <https://pypi.python.org/pypi/ipaddress/1.0.18>`_ is highly
11+
recommended, ``py2-ipaddress`` should now work. Incompatibility reported by
12+
John Zadroga. ``geoip2`` GitHub issue #41.
13+
614
1.2.2 (2016-11-21)
715
++++++++++++++++++
816

maxminddb/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def Reader(database): # pylint: disable=invalid-name
3939
"""This exists for backwards compatibility. Use open_database instead"""
4040
return open_database(database)
4141

42+
4243
__title__ = 'maxminddb'
4344
__version__ = '1.2.2'
4445
__author__ = 'Gregory Oschwald'

maxminddb/compat.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# pylint: skip-file
66

77
if sys.version_info[0] == 2:
8+
89
def compat_ip_address(address):
910
if isinstance(address, bytes):
1011
address = address.decode()
@@ -21,6 +22,7 @@ def int_from_bytes(b):
2122

2223
byte_from_int = chr
2324
else:
25+
2426
def compat_ip_address(address):
2527
return ipaddress.ip_address(address)
2628

maxminddb/decoder.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515

1616
class Decoder(object): # pylint: disable=too-few-public-methods
17-
1817
"""Decoder for the data section of the MaxMind DB"""
1918

2019
def __init__(self, database_buffer, pointer_base=0, pointer_test=False):
@@ -55,8 +54,9 @@ def unpack_type(self, size, offset):
5554
packed_bytes = self._buffer[offset:new_offset]
5655
if pad:
5756
packed_bytes = packed_bytes.rjust(type_size, b'\x00')
58-
(value,) = struct.unpack(type_code, packed_bytes)
57+
(value, ) = struct.unpack(type_code, packed_bytes)
5958
return value, new_offset
59+
6060
return unpack_type
6161

6262
def _decode_map(self, size, offset):
@@ -105,7 +105,8 @@ def _decode_utf8_string(self, size, offset):
105105
5: _decode_uint, # uint16
106106
6: _decode_uint, # uint32
107107
7: _decode_map,
108-
8: _decode_packed_type(b'!i', 4, pad=True), # int32
108+
8: _decode_packed_type(
109+
b'!i', 4, pad=True), # int32
109110
9: _decode_uint, # uint64
110111
10: _decode_uint, # uint128
111112
11: _decode_array,
@@ -120,7 +121,7 @@ def decode(self, offset):
120121
offset -- the location of the data structure to decode
121122
"""
122123
new_offset = offset + 1
123-
(ctrl_byte,) = struct.unpack(b'!B', self._buffer[offset:new_offset])
124+
(ctrl_byte, ) = struct.unpack(b'!B', self._buffer[offset:new_offset])
124125
type_num = ctrl_byte >> 5
125126
# Extended type
126127
if not type_num:
@@ -130,12 +131,12 @@ def decode(self, offset):
130131
raise InvalidDatabaseError('Unexpected type number ({type}) '
131132
'encountered'.format(type=type_num))
132133

133-
(size, new_offset) = self._size_from_ctrl_byte(
134-
ctrl_byte, new_offset, type_num)
134+
(size, new_offset) = self._size_from_ctrl_byte(ctrl_byte, new_offset,
135+
type_num)
135136
return self._type_decoder[type_num](self, size, new_offset)
136137

137138
def _read_extended(self, offset):
138-
(next_byte,) = struct.unpack(b'!B', self._buffer[offset:offset + 1])
139+
(next_byte, ) = struct.unpack(b'!B', self._buffer[offset:offset + 1])
139140
type_num = next_byte + 7
140141
if type_num < 7:
141142
raise InvalidDatabaseError(
@@ -148,8 +149,7 @@ def _verify_size(self, expected, actual):
148149
if expected != actual:
149150
raise InvalidDatabaseError(
150151
'The MaxMind DB file\'s data section contains bad data '
151-
'(unknown data type or corrupt data)'
152-
)
152+
'(unknown data type or corrupt data)')
153153

154154
def _size_from_ctrl_byte(self, ctrl_byte, offset, type_num):
155155
size = ctrl_byte & 0x1f
@@ -167,7 +167,7 @@ def _size_from_ctrl_byte(self, ctrl_byte, offset, type_num):
167167
elif size == 30:
168168
size = 285 + struct.unpack(b'!H', size_bytes)[0]
169169
elif size > 30:
170-
size = struct.unpack(
171-
b'!I', size_bytes.rjust(4, b'\x00'))[0] + 65821
170+
size = struct.unpack(b'!I', size_bytes.rjust(4,
171+
b'\x00'))[0] + 65821
172172

173173
return size, new_offset

maxminddb/errors.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,4 @@
77

88

99
class InvalidDatabaseError(RuntimeError):
10-
1110
"""This error is thrown when unexpected data is found in the database."""

maxminddb/file.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111

1212
class FileBuffer(object):
13-
1413
"""A slice-able file reader"""
1514

1615
def __init__(self, database):

maxminddb/reader.py

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,14 @@
1515

1616
import struct
1717

18-
from maxminddb.compat import byte_from_int, int_from_byte, compat_ip_address
18+
from maxminddb.compat import byte_from_int, compat_ip_address
1919
from maxminddb.const import MODE_AUTO, MODE_MMAP, MODE_FILE, MODE_MEMORY
2020
from maxminddb.decoder import Decoder
2121
from maxminddb.errors import InvalidDatabaseError
2222
from maxminddb.file import FileBuffer
2323

2424

2525
class Reader(object):
26-
2726
"""
2827
Instances of this class provide a reader for the MaxMind DB format. IP
2928
addresses can be looked up using the ``get`` method.
@@ -60,13 +59,14 @@ def __init__(self, database, mode=MODE_AUTO):
6059
self._buffer = db_file.read()
6160
self._buffer_size = len(self._buffer)
6261
else:
63-
raise ValueError('Unsupported open mode ({0}). Only MODE_AUTO, '
64-
' MODE_FILE, and MODE_MEMORY are support by the pure Python '
65-
'Reader'.format(mode))
62+
raise ValueError(
63+
'Unsupported open mode ({0}). Only MODE_AUTO, '
64+
' MODE_FILE, and MODE_MEMORY are support by the pure Python '
65+
'Reader'.format(mode))
6666

67-
metadata_start = self._buffer.rfind(self._METADATA_START_MARKER,
68-
max(0, self._buffer_size
69-
- 128 * 1024))
67+
metadata_start = self._buffer.rfind(
68+
self._METADATA_START_MARKER, max(0,
69+
self._buffer_size - 128 * 1024))
7070

7171
if metadata_start == -1:
7272
self.close()
@@ -77,11 +77,10 @@ def __init__(self, database, mode=MODE_AUTO):
7777
metadata_start += len(self._METADATA_START_MARKER)
7878
metadata_decoder = Decoder(self._buffer, metadata_start)
7979
(metadata, _) = metadata_decoder.decode(metadata_start)
80-
self._metadata = Metadata(
81-
**metadata) # pylint: disable=bad-option-value
80+
self._metadata = Metadata(**metadata) # pylint: disable=bad-option-value
8281

83-
self._decoder = Decoder(self._buffer, self._metadata.search_tree_size
84-
+ self._DATA_SECTION_SEPARATOR_SIZE)
82+
self._decoder = Decoder(self._buffer, self._metadata.search_tree_size +
83+
self._DATA_SECTION_SEPARATOR_SIZE)
8584

8685
def metadata(self):
8786
"""Return the metadata associated with the MaxMind DB file"""
@@ -98,23 +97,23 @@ def get(self, ip_address):
9897
address = compat_ip_address(ip_address)
9998

10099
if address.version == 6 and self._metadata.ip_version == 4:
101-
raise ValueError('Error looking up {0}. You attempted to look up '
102-
'an IPv6 address in an IPv4-only database.'.format(
103-
ip_address))
100+
raise ValueError(
101+
'Error looking up {0}. You attempted to look up '
102+
'an IPv6 address in an IPv4-only database.'.format(ip_address))
104103
pointer = self._find_address_in_tree(address)
105104

106105
return self._resolve_data_pointer(pointer) if pointer else None
107106

108107
def _find_address_in_tree(self, ip_address):
109-
packed = ip_address.packed
108+
packed = bytearray(ip_address.packed)
110109

111110
bit_count = len(packed) * 8
112111
node = self._start_node(bit_count)
113112

114113
for i in range(bit_count):
115114
if node >= self._metadata.node_count:
116115
break
117-
bit = 1 & (int_from_byte(packed[i >> 3]) >> 7 - (i % 8))
116+
bit = 1 & (packed[i >> 3] >> 7 - (i % 8))
118117
node = self._read_node(node, bit)
119118
if node == self._metadata.node_count:
120119
# Record is empty
@@ -149,21 +148,21 @@ def _read_node(self, node_number, index):
149148
offset = base_offset + index * 3
150149
node_bytes = b'\x00' + self._buffer[offset:offset + 3]
151150
elif record_size == 28:
152-
(middle,) = struct.unpack(
151+
(middle, ) = struct.unpack(
153152
b'!B', self._buffer[base_offset + 3:base_offset + 4])
154153
if index:
155154
middle &= 0x0F
156155
else:
157156
middle = (0xF0 & middle) >> 4
158157
offset = base_offset + index * 4
159-
node_bytes = byte_from_int(
160-
middle) + self._buffer[offset:offset + 3]
158+
node_bytes = byte_from_int(middle) + self._buffer[offset:offset +
159+
3]
161160
elif record_size == 32:
162161
offset = base_offset + index * 4
163162
node_bytes = self._buffer[offset:offset + 4]
164163
else:
165-
raise InvalidDatabaseError(
166-
'Unknown record size: {0}'.format(record_size))
164+
raise InvalidDatabaseError('Unknown record size: {0}'.format(
165+
record_size))
167166
return struct.unpack(b'!I', node_bytes)[0]
168167

169168
def _resolve_data_pointer(self, pointer):
@@ -185,7 +184,6 @@ def close(self):
185184

186185

187186
class Metadata(object):
188-
189187
"""Metadata for the MaxMind DB reader"""
190188

191189
# pylint: disable=too-many-instance-attributes

0 commit comments

Comments
 (0)