summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpmoody@google.com <pmoody@google.com@09200d28-7f98-11dd-ad27-0f66e57d2035>2011-11-24 20:59:46 +0000
committerpmoody@google.com <pmoody@google.com@09200d28-7f98-11dd-ad27-0f66e57d2035>2011-11-24 20:59:46 +0000
commitbcbd71a53623d3980c6c54daa3e0ecd99d6160fc (patch)
treee2f88ef1142456d48496e0e82507ae8a0c4b4739
parentc3c978c963d0e367d65a1d668a87acb35afad3d8 (diff)
downloadipaddr-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.py100
-rwxr-xr-xtrunk/ipaddr_test.py47
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)