diff options
author | pmoody@google.com <pmoody@google.com@09200d28-7f98-11dd-ad27-0f66e57d2035> | 2011-11-24 20:59:46 +0000 |
---|---|---|
committer | pmoody@google.com <pmoody@google.com@09200d28-7f98-11dd-ad27-0f66e57d2035> | 2011-11-24 20:59:46 +0000 |
commit | bcbd71a53623d3980c6c54daa3e0ecd99d6160fc (patch) | |
tree | e2f88ef1142456d48496e0e82507ae8a0c4b4739 | |
parent | c3c978c963d0e367d65a1d668a87acb35afad3d8 (diff) | |
download | ipaddr-py-bcbd71a53623d3980c6c54daa3e0ecd99d6160fc.tar.gz |
patch from pmarks
git-svn-id: https://ipaddr-py.googlecode.com/svn@235 09200d28-7f98-11dd-ad27-0f66e57d2035
-rw-r--r-- | trunk/ipaddr.py | 100 | ||||
-rwxr-xr-x | trunk/ipaddr_test.py | 47 |
2 files changed, 67 insertions, 80 deletions
diff --git a/trunk/ipaddr.py b/trunk/ipaddr.py index a89298a..09e4ebd 100644 --- a/trunk/ipaddr.py +++ b/trunk/ipaddr.py @@ -134,7 +134,7 @@ def v4_int_to_packed(address): """ if address > _BaseV4._ALL_ONES: raise ValueError('Address too large for IPv4') - return struct.pack('!I', address) + return Bytes(struct.pack('!I', address)) def v6_int_to_packed(address): @@ -146,7 +146,7 @@ def v6_int_to_packed(address): Returns: The binary representation of this address. """ - return struct.pack('!QQ', address >> 64, address & (2**64 - 1)) + return Bytes(struct.pack('!QQ', address >> 64, address & (2**64 - 1))) def _find_address_range(addresses): @@ -368,15 +368,27 @@ def collapse_address_list(addresses): # backwards compatibility CollapseAddrList = collapse_address_list -# Test whether this Python implementation supports byte objects that -# are not identical to str ones. -# We need to exclude platforms where bytes == str so that we can -# distinguish between packed representations and strings, for example -# b'12::' (the IPv4 address 49.50.58.58) and '12::' (an IPv6 address). +# We need to distinguish between the string and packed-bytes representations +# of an IP address. For example, b'0::1' is the IPv4 address 48.58.58.49, +# while '0::1' is an IPv6 address. +# +# In Python 3, the native 'bytes' type already provides this functionality, +# so we use it directly. For earlier implementations where bytes is not a +# distinct type, we create a subclass of str to serve as a tag. +# +# Usage example (Python 2): +# ip = ipaddr.IPAddress(ipaddr.Bytes('xxxx')) +# +# Usage example (Python 3): +# ip = ipaddr.IPAddress(b'xxxx') try: - _compat_has_real_bytes = bytes is not str -except NameError: # <Python2.6 - _compat_has_real_bytes = False + if bytes is str: + raise TypeError("bytes is not a distinct type") + Bytes = bytes +except (NameError, TypeError): + class Bytes(str): + def __repr__(self): + return 'Bytes(%s)' % str.__repr__(self) def get_mixed_type_key(obj): """Return a key suitable for sorting between networks and addresses. @@ -435,11 +447,6 @@ class _BaseIP(_IPAddrBase): """ - def __init__(self, address): - if (not (_compat_has_real_bytes and isinstance(address, bytes)) - and '/' in str(address)): - raise AddressValueError(address) - def __eq__(self, other): try: return (self._ip == other._ip @@ -1184,7 +1191,6 @@ class IPv4Address(_BaseV4, _BaseIP): AddressValueError: If ipaddr isn't a valid IPv4 address. """ - _BaseIP.__init__(self, address) _BaseV4.__init__(self, address) # Efficient constructor from integer. @@ -1195,10 +1201,12 @@ class IPv4Address(_BaseV4, _BaseIP): return # Constructing from a packed address - if _compat_has_real_bytes: - if isinstance(address, bytes) and len(address) == 4: - self._ip = struct.unpack('!I', address)[0] - return + if isinstance(address, Bytes): + try: + self._ip, = struct.unpack('!I', address) + except struct.error: + raise AddressValueError(address) # Wrong length. + return # Assume input argument to be string or any object representation # which converts into a formatted IP string. @@ -1267,25 +1275,14 @@ class IPv4Network(_BaseV4, _BaseNet): _BaseNet.__init__(self, address) _BaseV4.__init__(self, address) - # Efficient constructor from integer. - if isinstance(address, (int, long)): - self._ip = address - self.ip = IPv4Address(self._ip) + # Constructing from an integer or packed bytes. + if isinstance(address, (int, long, Bytes)): + self.ip = IPv4Address(address) + self._ip = self.ip._ip self._prefixlen = self._max_prefixlen self.netmask = IPv4Address(self._ALL_ONES) - if address < 0 or address > self._ALL_ONES: - raise AddressValueError(address) return - # Constructing from a packed address - if _compat_has_real_bytes: - if isinstance(address, bytes) and len(address) == 4: - self._ip = struct.unpack('!I', address)[0] - self.ip = IPv4Address(self._ip) - self._prefixlen = self._max_prefixlen - self.netmask = IPv4Address(self._ALL_ONES) - return - # Assume input argument to be string or any object representation # which converts into a formatted IP prefix string. addr = str(address).split('/') @@ -1764,7 +1761,6 @@ class IPv6Address(_BaseV6, _BaseIP): AddressValueError: If address isn't a valid IPv6 address. """ - _BaseIP.__init__(self, address) _BaseV6.__init__(self, address) # Efficient constructor from integer. @@ -1775,11 +1771,13 @@ class IPv6Address(_BaseV6, _BaseIP): return # Constructing from a packed address - if _compat_has_real_bytes: - if isinstance(address, bytes) and len(address) == 16: - tmp = struct.unpack('!QQ', address) - self._ip = (tmp[0] << 64) | tmp[1] - return + if isinstance(address, Bytes): + try: + hi, lo = struct.unpack('!QQ', address) + except struct.error: + raise AddressValueError(address) # Wrong length. + self._ip = (hi << 64) | lo + return # Assume input argument to be string or any object representation # which converts into a formatted IP string. @@ -1840,26 +1838,14 @@ class IPv6Network(_BaseV6, _BaseNet): _BaseNet.__init__(self, address) _BaseV6.__init__(self, address) - # Efficient constructor from integer. - if isinstance(address, (int, long)): - self._ip = address - self.ip = IPv6Address(self._ip) + # Constructing from an integer or packed bytes. + if isinstance(address, (int, long, Bytes)): + self.ip = IPv6Address(address) + self._ip = self.ip._ip self._prefixlen = self._max_prefixlen self.netmask = IPv6Address(self._ALL_ONES) - if address < 0 or address > self._ALL_ONES: - raise AddressValueError(address) return - # Constructing from a packed address - if _compat_has_real_bytes: - if isinstance(address, bytes) and len(address) == 16: - tmp = struct.unpack('!QQ', address) - self._ip = (tmp[0] << 64) | tmp[1] - self.ip = IPv6Address(self._ip) - self._prefixlen = self._max_prefixlen - self.netmask = IPv6Address(self._ALL_ONES) - return - # Assume input argument to be string or any object representation # which converts into a formatted IP prefix string. addr = str(address).split('/') diff --git a/trunk/ipaddr_test.py b/trunk/ipaddr_test.py index 09bece0..24f1fae 100755 --- a/trunk/ipaddr_test.py +++ b/trunk/ipaddr_test.py @@ -23,10 +23,10 @@ import time import ipaddr # Compatibility function to cast str to bytes objects -if ipaddr._compat_has_real_bytes: - _cb = lambda bytestr: bytes(bytestr, 'charmap') +if issubclass(ipaddr.Bytes, str): + _cb = ipaddr.Bytes else: - _cb = str + _cb = lambda bytestr: bytes(bytestr, 'charmap') class IpaddrUnitTest(unittest.TestCase): @@ -131,6 +131,8 @@ class IpaddrUnitTest(unittest.TestCase): AssertInvalidIP(":1:2:3:4:5:6:7") AssertInvalidIP("1:2:3:4:5:6:7:") AssertInvalidIP(":1:2:3:4:5:6:") + AssertInvalidIP("192.0.2.1/32") + AssertInvalidIP("2001:db8::1/128") self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv4Network, '') self.assertRaises(ipaddr.AddressValueError, ipaddr.IPv4Network, @@ -233,26 +235,25 @@ class IpaddrUnitTest(unittest.TestCase): self.assertEqual(ipaddr.IPNetwork(self.ipv4.ip).version, 4) self.assertEqual(ipaddr.IPNetwork(self.ipv6.ip).version, 6) - if ipaddr._compat_has_real_bytes: # on python3+ - def testIpFromPacked(self): - ip = ipaddr.IPNetwork - - self.assertEqual(self.ipv4.ip, - ip(_cb('\x01\x02\x03\x04')).ip) - self.assertEqual(ip('255.254.253.252'), - ip(_cb('\xff\xfe\xfd\xfc'))) - self.assertRaises(ValueError, ipaddr.IPNetwork, _cb('\x00' * 3)) - self.assertRaises(ValueError, ipaddr.IPNetwork, _cb('\x00' * 5)) - self.assertEqual(self.ipv6.ip, - ip(_cb('\x20\x01\x06\x58\x02\x2a\xca\xfe' - '\x02\x00\x00\x00\x00\x00\x00\x01')).ip) - self.assertEqual(ip('ffff:2:3:4:ffff::'), - ip(_cb('\xff\xff\x00\x02\x00\x03\x00\x04' + - '\xff\xff' + '\x00' * 6))) - self.assertEqual(ip('::'), - ip(_cb('\x00' * 16))) - self.assertRaises(ValueError, ip, _cb('\x00' * 15)) - self.assertRaises(ValueError, ip, _cb('\x00' * 17)) + def testIpFromPacked(self): + ip = ipaddr.IPNetwork + + self.assertEqual(self.ipv4.ip, + ip(_cb('\x01\x02\x03\x04')).ip) + self.assertEqual(ip('255.254.253.252'), + ip(_cb('\xff\xfe\xfd\xfc'))) + self.assertRaises(ValueError, ipaddr.IPNetwork, _cb('\x00' * 3)) + self.assertRaises(ValueError, ipaddr.IPNetwork, _cb('\x00' * 5)) + self.assertEqual(self.ipv6.ip, + ip(_cb('\x20\x01\x06\x58\x02\x2a\xca\xfe' + '\x02\x00\x00\x00\x00\x00\x00\x01')).ip) + self.assertEqual(ip('ffff:2:3:4:ffff::'), + ip(_cb('\xff\xff\x00\x02\x00\x03\x00\x04' + + '\xff\xff' + '\x00' * 6))) + self.assertEqual(ip('::'), + ip(_cb('\x00' * 16))) + self.assertRaises(ValueError, ip, _cb('\x00' * 15)) + self.assertRaises(ValueError, ip, _cb('\x00' * 17)) def testGetIp(self): self.assertEqual(int(self.ipv4.ip), 16909060) |