[Python-checkins] cpython: Issue 14814: Further error case testing coverage and cleanups

nick.coghlan python-checkins at python.org
Sun Jul 8 09:36:35 CEST 2012


http://hg.python.org/cpython/rev/18c1d4d60bdf
changeset:   77987:18c1d4d60bdf
parent:      77985:e6bb919b2623
user:        Nick Coghlan <ncoghlan at gmail.com>
date:        Sun Jul 08 17:11:04 2012 +1000
summary:
  Issue 14814: Further error case testing coverage and cleanups

files:
  Lib/ipaddress.py           |   51 ++++----
  Lib/test/test_ipaddress.py |  140 +++++++++++-------------
  2 files changed, 93 insertions(+), 98 deletions(-)


diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py
--- a/Lib/ipaddress.py
+++ b/Lib/ipaddress.py
@@ -163,6 +163,7 @@
         raise AddressValueError("Only one '/' permitted in %r" % address)
     return addr
 
+
 def _find_address_range(addresses):
     """Find a sequence of IPv#Address.
 
@@ -408,6 +409,8 @@
 class _TotalOrderingMixin:
     # Helper that derives the other comparison operations from
     # __lt__ and __eq__
+    # We avoid functools.total_ordering because it doesn't handle
+    # NotImplemented correctly yet (http://bugs.python.org/issue10042)
     def __eq__(self, other):
         raise NotImplementedError
     def __ne__(self, other):
@@ -455,6 +458,22 @@
         msg = '%200s has no version specified' % (type(self),)
         raise NotImplementedError(msg)
 
+    def _check_int_address(self, address):
+        if address < 0:
+            msg = "%d (< 0) is not permitted as an IPv%d address"
+            raise AddressValueError(msg % (address, self._version))
+        if address > self._ALL_ONES:
+            msg = "%d (>= 2**%d) is not permitted as an IPv%d address"
+            raise AddressValueError(msg % (address, self._max_prefixlen,
+                                           self._version))
+
+    def _check_packed_address(self, address, expected_len):
+        address_len = len(address)
+        if address_len != expected_len:
+            msg = "%r (len %d != %d) is not permitted as an IPv%d address"
+            raise AddressValueError(msg % (address, address_len,
+                                           expected_len, self._version))
+
     def _ip_int_from_prefix(self, prefixlen=None):
         """Turn the prefix length netmask into a int for comparison.
 
@@ -1215,16 +1234,13 @@
 
         # Efficient constructor from integer.
         if isinstance(address, int):
+            self._check_int_address(address)
             self._ip = address
-            if address < 0 or address > self._ALL_ONES:
-                raise AddressValueError(address)
             return
 
         # Constructing from a packed address
         if isinstance(address, bytes):
-            if len(address) != 4:
-                msg = "Packed address %r must be exactly 4 bytes"
-                raise AddressValueError(msg % address)
+            self._check_packed_address(address, 4)
             self._ip = struct.unpack('!I', address)[0]
             return
 
@@ -1368,11 +1384,7 @@
 
         # Constructing from a packed address
         if isinstance(address, bytes):
-            if len(address) != 4:
-                msg = "Packed address %r must be exactly 4 bytes"
-                raise AddressValueError(msg % address)
-            self.network_address = IPv4Address(
-                struct.unpack('!I', address)[0])
+            self.network_address = IPv4Address(address)
             self._prefixlen = self._max_prefixlen
             self.netmask = IPv4Address(self._ALL_ONES)
             #fixme: address/network test here
@@ -1380,11 +1392,9 @@
 
         # Efficient constructor from integer.
         if isinstance(address, int):
+            self.network_address = IPv4Address(address)
             self._prefixlen = self._max_prefixlen
             self.netmask = IPv4Address(self._ALL_ONES)
-            if address < 0 or address > self._ALL_ONES:
-                raise AddressValueError(address)
-            self.network_address = IPv4Address(address)
             #fixme: address/network test here.
             return
 
@@ -1868,16 +1878,13 @@
 
         # Efficient constructor from integer.
         if isinstance(address, int):
+            self._check_int_address(address)
             self._ip = address
-            if address < 0 or address > self._ALL_ONES:
-                raise AddressValueError(address)
             return
 
         # Constructing from a packed address
         if isinstance(address, bytes):
-            if len(address) != 16:
-                msg = "Packed address %r must be exactly 16 bytes"
-                raise AddressValueError(msg % address)
+            self._check_packed_address(address, 16)
             tmp = struct.unpack('!QQ', address)
             self._ip = (tmp[0] << 64) | tmp[1]
             return
@@ -2014,8 +2021,6 @@
 
         # Efficient constructor from integer.
         if isinstance(address, int):
-            if address < 0 or address > self._ALL_ONES:
-                raise AddressValueError(address)
             self.network_address = IPv6Address(address)
             self._prefixlen = self._max_prefixlen
             self.netmask = IPv6Address(self._ALL_ONES)
@@ -2023,11 +2028,7 @@
 
         # Constructing from a packed address
         if isinstance(address, bytes):
-            if len(address) != 16:
-                msg = "Packed address %r must be exactly 16 bytes"
-                raise AddressValueError(msg % address)
-            tmp = struct.unpack('!QQ', address)
-            self.network_address = IPv6Address((tmp[0] << 64) | tmp[1])
+            self.network_address = IPv6Address(address)
             self._prefixlen = self._max_prefixlen
             self.netmask = IPv6Address(self._ALL_ONES)
             return
diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py
--- a/Lib/test/test_ipaddress.py
+++ b/Lib/test/test_ipaddress.py
@@ -23,6 +23,10 @@
     # address parts) that don't have an obvious home in the main test
     # suite
 
+    @property
+    def factory(self):
+        raise NotImplementedError
+
     @contextlib.contextmanager
     def assertCleanError(self, exc_type, details, *args):
         """
@@ -49,11 +53,64 @@
         return self.assertCleanError(ipaddress.NetmaskValueError,
                                 details, *args)
 
-class AddressErrors_v4(ErrorReporting):
+class CommonErrorsMixin:
 
     def test_empty_address(self):
         with self.assertAddressError("Address cannot be empty"):
-            ipaddress.IPv4Address("")
+            self.factory("")
+
+    def test_floats_rejected(self):
+        with self.assertAddressError(re.escape(repr("1.0"))):
+            self.factory(1.0)
+
+class CommonErrorsMixin_v4(CommonErrorsMixin):
+
+    def test_negative_ints_rejected(self):
+        msg = "-1 (< 0) is not permitted as an IPv4 address"
+        with self.assertAddressError(re.escape(msg)):
+            self.factory(-1)
+
+    def test_large_ints_rejected(self):
+        msg = "%d (>= 2**32) is not permitted as an IPv4 address"
+        with self.assertAddressError(re.escape(msg % 2**32)):
+            self.factory(2**32)
+
+    def test_bad_packed_length(self):
+        def assertBadLength(length):
+            addr = b'\x00' * length
+            msg = "%r (len %d != 4) is not permitted as an IPv4 address"
+            with self.assertAddressError(re.escape(msg % (addr, length))):
+                self.factory(addr)
+
+        assertBadLength(3)
+        assertBadLength(5)
+
+class CommonErrorsMixin_v6(CommonErrorsMixin):
+
+    def test_negative_ints_rejected(self):
+        msg = "-1 (< 0) is not permitted as an IPv6 address"
+        with self.assertAddressError(re.escape(msg)):
+            self.factory(-1)
+
+    def test_large_ints_rejected(self):
+        msg = "%d (>= 2**128) is not permitted as an IPv6 address"
+        with self.assertAddressError(re.escape(msg % 2**128)):
+            self.factory(2**128)
+
+    def test_bad_packed_length(self):
+        def assertBadLength(length):
+            addr = b'\x00' * length
+            msg = "%r (len %d != 16) is not permitted as an IPv6 address"
+            with self.assertAddressError(re.escape(msg % (addr, length))):
+                self.factory(addr)
+                self.factory(addr)
+
+        assertBadLength(15)
+        assertBadLength(17)
+
+
+class AddressErrors_v4(ErrorReporting, CommonErrorsMixin_v4):
+    factory = ipaddress.IPv4Address
 
     def test_network_passed_as_address(self):
         addr = "127.0.0.1/24"
@@ -133,22 +190,9 @@
         assertBadOctet("12345.67899.-54321.-98765", 12345)
         assertBadOctet("257.0.0.0", 257)
 
-    def test_bad_packed_length(self):
-        def assertBadLength(length):
-            addr = b'\x00' * length
-            msg = "Packed address %r must be exactly 4 bytes" % addr
-            with self.assertAddressError(re.escape(msg)):
-                ipaddress.IPv4Address(addr)
 
-        assertBadLength(3)
-        assertBadLength(5)
-
-
-class AddressErrors_v6(ErrorReporting):
-
-    def test_empty_address(self):
-        with self.assertAddressError("Address cannot be empty"):
-            ipaddress.IPv6Address("")
+class AddressErrors_v6(ErrorReporting, CommonErrorsMixin_v6):
+    factory = ipaddress.IPv6Address
 
     def test_network_passed_as_address(self):
         addr = "::1/24"
@@ -277,24 +321,10 @@
         assertBadPart("02001:db8::", "02001")
         assertBadPart('2001:888888::1', "888888")
 
-    def test_bad_packed_length(self):
-        def assertBadLength(length):
-            addr = b'\x00' * length
-            msg = "Packed address %r must be exactly 16 bytes" % addr
-            with self.assertAddressError(re.escape(msg)):
-                ipaddress.IPv6Address(addr)
 
-        assertBadLength(15)
-        assertBadLength(17)
-
-
-class NetmaskErrorsMixin_v4:
+class NetmaskErrorsMixin_v4(CommonErrorsMixin_v4):
     """Input validation on interfaces and networks is very similar"""
 
-    @property
-    def factory(self):
-        raise NotImplementedError
-
     def test_split_netmask(self):
         addr = "1.2.3.4/32/24"
         with self.assertAddressError("Only one '/' permitted in %r" % addr):
@@ -305,7 +335,8 @@
             with self.assertAddressError(details):
                 self.factory(addr)
 
-        assertBadAddress("", "Address cannot be empty")
+        assertBadAddress("/", "Address cannot be empty")
+        assertBadAddress("/8", "Address cannot be empty")
         assertBadAddress("bogus", "Expected 4 octets")
         assertBadAddress("google.com", "Expected 4 octets")
         assertBadAddress("10/8", "Expected 4 octets")
@@ -325,17 +356,6 @@
         assertBadNetmask("1.1.1.1", "1.a.2.3")
         assertBadNetmask("1.1.1.1", "pudding")
 
-    def test_bad_packed_length(self):
-        def assertBadLength(length):
-            addr = b'\x00' * length
-            msg = "Packed address %r must be exactly 4 bytes" % addr
-            with self.assertAddressError(re.escape(msg)):
-                self.factory(addr)
-
-        assertBadLength(3)
-        assertBadLength(5)
-
-
 class InterfaceErrors_v4(ErrorReporting, NetmaskErrorsMixin_v4):
     factory = ipaddress.IPv4Interface
 
@@ -343,13 +363,9 @@
     factory = ipaddress.IPv4Network
 
 
-class NetmaskErrorsMixin_v6:
+class NetmaskErrorsMixin_v6(CommonErrorsMixin_v6):
     """Input validation on interfaces and networks is very similar"""
 
-    @property
-    def factory(self):
-        raise NotImplementedError
-
     def test_split_netmask(self):
         addr = "cafe:cafe::/128/190"
         with self.assertAddressError("Only one '/' permitted in %r" % addr):
@@ -360,7 +376,8 @@
             with self.assertAddressError(details):
                 self.factory(addr)
 
-        assertBadAddress("", "Address cannot be empty")
+        assertBadAddress("/", "Address cannot be empty")
+        assertBadAddress("/8", "Address cannot be empty")
         assertBadAddress("google.com", "At least 3 parts")
         assertBadAddress("1.2.3.4", "At least 3 parts")
         assertBadAddress("10/8", "At least 3 parts")
@@ -378,17 +395,6 @@
         assertBadNetmask("::1", "129")
         assertBadNetmask("::1", "pudding")
 
-    def test_bad_packed_length(self):
-        def assertBadLength(length):
-            addr = b'\x00' * length
-            msg = "Packed address %r must be exactly 16 bytes" % addr
-            with self.assertAddressError(re.escape(msg)):
-                self.factory(addr)
-
-        assertBadLength(15)
-        assertBadLength(17)
-
-
 class InterfaceErrors_v6(ErrorReporting, NetmaskErrorsMixin_v6):
     factory = ipaddress.IPv6Interface
 
@@ -585,10 +591,6 @@
     def testIpFromInt(self):
         self.assertEqual(self.ipv4_interface._ip,
                          ipaddress.IPv4Interface(16909060)._ip)
-        self.assertRaises(ipaddress.AddressValueError,
-                          ipaddress.IPv4Interface, 2**32)
-        self.assertRaises(ipaddress.AddressValueError,
-                          ipaddress.IPv4Interface, -1)
 
         ipv4 = ipaddress.ip_network('1.2.3.4')
         ipv6 = ipaddress.ip_network('2001:658:22a:cafe:200:0:0:1')
@@ -598,14 +600,6 @@
         v6_int = 42540616829182469433547762482097946625
         self.assertEqual(self.ipv6_interface._ip,
                          ipaddress.IPv6Interface(v6_int)._ip)
-        self.assertRaises(ipaddress.AddressValueError,
-                          ipaddress.IPv6Interface, 2**128)
-        self.assertRaises(ipaddress.AddressValueError,
-                          ipaddress.IPv6Interface, -1)
-        self.assertRaises(ipaddress.AddressValueError,
-                          ipaddress.IPv6Network, 2**128)
-        self.assertRaises(ipaddress.AddressValueError,
-                          ipaddress.IPv6Network, -1)
 
         self.assertEqual(ipaddress.ip_network(self.ipv4_address._ip).version,
                          4)

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list