[Python-checkins] bpo-27860: ipaddress: fix Interface constructor (GH-14200)

Inada Naoki webhook-mailer at python.org
Wed Jun 19 07:47:46 EDT 2019


https://github.com/python/cpython/commit/f532fe5583d29d21e12aa22d8fca13e3bca94fb3
commit: f532fe5583d29d21e12aa22d8fca13e3bca94fb3
branch: 3.7
author: Inada Naoki <songofacandy at gmail.com>
committer: GitHub <noreply at github.com>
date: 2019-06-19T20:47:39+09:00
summary:

bpo-27860: ipaddress: fix Interface constructor (GH-14200)

Interface constructor is documented as accepts address same to Network,
but it didn't accept some form of the address.

This commit is backport of GH-12836 (commit 6fa84bd)

files:
A Misc/NEWS.d/next/Library/2019-06-18-21-25-23.bpo-27860.Mc4wtK.rst
M Lib/ipaddress.py
M Lib/test/test_ipaddress.py

diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py
index 4eec1f337c13..80249288d73a 100644
--- a/Lib/ipaddress.py
+++ b/Lib/ipaddress.py
@@ -532,6 +532,30 @@ def _prefix_from_ip_string(cls, ip_str):
         except ValueError:
             cls._report_invalid_netmask(ip_str)
 
+    @classmethod
+    def _split_addr_prefix(cls, address):
+        """Helper function to parse address of Network/Interface.
+
+        Arg:
+            address: Argument of Network/Interface.
+
+        Returns:
+            (addr, prefix) tuple.
+        """
+        # a packed address or integer
+        if isinstance(address, (bytes, int)):
+            return address, cls._max_prefixlen
+
+        if not isinstance(address, tuple):
+            # Assume input argument to be string or any object representation
+            # which converts into a formatted IP prefix string.
+            address = _split_optional_netmask(address)
+
+        # Constructing from a tuple (addr, [mask])
+        if len(address) > 1:
+            return address
+        return address[0], cls._max_prefixlen
+
     def __reduce__(self):
         return self.__class__, (str(self),)
 
@@ -1381,32 +1405,13 @@ def is_link_local(self):
 class IPv4Interface(IPv4Address):
 
     def __init__(self, address):
-        if isinstance(address, (bytes, int)):
-            IPv4Address.__init__(self, address)
-            self.network = IPv4Network(self._ip)
-            self._prefixlen = self._max_prefixlen
-            return
-
-        if isinstance(address, tuple):
-            IPv4Address.__init__(self, address[0])
-            if len(address) > 1:
-                self._prefixlen = int(address[1])
-            else:
-                self._prefixlen = self._max_prefixlen
-
-            self.network = IPv4Network(address, strict=False)
-            self.netmask = self.network.netmask
-            self.hostmask = self.network.hostmask
-            return
-
-        addr = _split_optional_netmask(address)
-        IPv4Address.__init__(self, addr[0])
-
-        self.network = IPv4Network(address, strict=False)
-        self._prefixlen = self.network._prefixlen
+        addr, mask = self._split_addr_prefix(address)
 
+        IPv4Address.__init__(self, addr)
+        self.network = IPv4Network((addr, mask), strict=False)
         self.netmask = self.network.netmask
         self.hostmask = self.network.hostmask
+        self._prefixlen = self.network._prefixlen
 
     def __str__(self):
         return '%s/%d' % (self._string_from_ip_int(self._ip),
@@ -1511,24 +1516,9 @@ def __init__(self, address, strict=True):
               an IPv4 address.
             ValueError: If strict is True and a network address is not
               supplied.
-
         """
         _BaseNetwork.__init__(self, address)
-
-        # Constructing from a packed address or integer
-        if isinstance(address, (int, bytes)):
-            addr = address
-            mask = self._max_prefixlen
-        # Constructing from a tuple (addr, [mask])
-        elif isinstance(address, tuple):
-            addr = address[0]
-            mask = address[1] if len(address) > 1 else self._max_prefixlen
-        # Assume input argument to be string or any object representation
-        # which converts into a formatted IP prefix string.
-        else:
-            args = _split_optional_netmask(address)
-            addr = self._ip_int_from_string(args[0])
-            mask = args[1] if len(args) == 2 else self._max_prefixlen
+        addr, mask = self._split_addr_prefix(address)
 
         self.network_address = IPv4Address(addr)
         self.netmask, self._prefixlen = self._make_netmask(mask)
@@ -2061,28 +2051,13 @@ def sixtofour(self):
 class IPv6Interface(IPv6Address):
 
     def __init__(self, address):
-        if isinstance(address, (bytes, int)):
-            IPv6Address.__init__(self, address)
-            self.network = IPv6Network(self._ip)
-            self._prefixlen = self._max_prefixlen
-            return
-        if isinstance(address, tuple):
-            IPv6Address.__init__(self, address[0])
-            if len(address) > 1:
-                self._prefixlen = int(address[1])
-            else:
-                self._prefixlen = self._max_prefixlen
-            self.network = IPv6Network(address, strict=False)
-            self.netmask = self.network.netmask
-            self.hostmask = self.network.hostmask
-            return
+        addr, mask = self._split_addr_prefix(address)
 
-        addr = _split_optional_netmask(address)
-        IPv6Address.__init__(self, addr[0])
-        self.network = IPv6Network(address, strict=False)
+        IPv6Address.__init__(self, addr)
+        self.network = IPv6Network((addr, mask), strict=False)
         self.netmask = self.network.netmask
-        self._prefixlen = self.network._prefixlen
         self.hostmask = self.network.hostmask
+        self._prefixlen = self.network._prefixlen
 
     def __str__(self):
         return '%s/%d' % (self._string_from_ip_int(self._ip),
@@ -2191,24 +2166,9 @@ def __init__(self, address, strict=True):
               an IPv6 address.
             ValueError: If strict was True and a network address was not
               supplied.
-
         """
         _BaseNetwork.__init__(self, address)
-
-        # Constructing from a packed address or integer
-        if isinstance(address, (int, bytes)):
-            addr = address
-            mask = self._max_prefixlen
-        # Constructing from a tuple (addr, [mask])
-        elif isinstance(address, tuple):
-            addr = address[0]
-            mask = address[1] if len(address) > 1 else self._max_prefixlen
-        # Assume input argument to be string or any object representation
-        # which converts into a formatted IP prefix string.
-        else:
-            args = _split_optional_netmask(address)
-            addr = self._ip_int_from_string(args[0])
-            mask = args[1] if len(args) == 2 else self._max_prefixlen
+        addr, mask = self._split_addr_prefix(address)
 
         self.network_address = IPv6Address(addr)
         self.netmask, self._prefixlen = self._make_netmask(mask)
diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py
index 3c50eec456ab..a2326db0af86 100644
--- a/Lib/test/test_ipaddress.py
+++ b/Lib/test/test_ipaddress.py
@@ -405,7 +405,13 @@ class NetmaskTestMixin_v4(CommonTestMixin_v4):
     """Input validation on interfaces and networks is very similar"""
 
     def test_no_mask(self):
-        self.assertEqual(str(self.factory('1.2.3.4')), '1.2.3.4/32')
+        for address in ('1.2.3.4', 0x01020304, b'\x01\x02\x03\x04'):
+            net = self.factory(address)
+            self.assertEqual(str(net), '1.2.3.4/32')
+            self.assertEqual(str(net.netmask), '255.255.255.255')
+            self.assertEqual(str(net.hostmask), '0.0.0.0')
+            # IPv4Network has prefixlen, but IPv4Interface doesn't.
+            # Should we add it to IPv4Interface too? (bpo-36392)
 
     def test_split_netmask(self):
         addr = "1.2.3.4/32/24"
@@ -541,6 +547,15 @@ def test_subnet_of_mixed_types(self):
 class NetmaskTestMixin_v6(CommonTestMixin_v6):
     """Input validation on interfaces and networks is very similar"""
 
+    def test_no_mask(self):
+        for address in ('::1', 1, b'\x00'*15 + b'\x01'):
+            net = self.factory(address)
+            self.assertEqual(str(net), '::1/128')
+            self.assertEqual(str(net.netmask), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')
+            self.assertEqual(str(net.hostmask), '::')
+            # IPv6Network has prefixlen, but IPv6Interface doesn't.
+            # Should we add it to IPv4Interface too? (bpo-36392)
+
     def test_split_netmask(self):
         addr = "cafe:cafe::/128/190"
         with self.assertAddressError("Only one '/' permitted in %r" % addr):
diff --git a/Misc/NEWS.d/next/Library/2019-06-18-21-25-23.bpo-27860.Mc4wtK.rst b/Misc/NEWS.d/next/Library/2019-06-18-21-25-23.bpo-27860.Mc4wtK.rst
new file mode 100644
index 000000000000..e07541c12454
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-06-18-21-25-23.bpo-27860.Mc4wtK.rst
@@ -0,0 +1,2 @@
+Fix ``IPv4Interface`` and ``IPv6Interface`` didn't accept string mask when
+the argument is tuple.



More information about the Python-checkins mailing list