]> granicus.if.org Git - python/commitdiff
bpo-27683: Fix a regression for host() of ipaddress network objects (GH-6016)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Wed, 21 Mar 2018 01:49:41 +0000 (18:49 -0700)
committerGitHub <noreply@github.com>
Wed, 21 Mar 2018 01:49:41 +0000 (18:49 -0700)
The result of host() was not empty when the network is constructed by a tuple containing an
integer mask and only 1 bit left for addresses.
(cherry picked from commit 10b134a07c898c2fbc5fd3582503680a54ed80a2)

Co-authored-by: Xiang Zhang <angwerzx@126.com>
Doc/library/ipaddress.rst
Lib/ipaddress.py
Lib/test/test_ipaddress.py
Misc/NEWS.d/next/Library/2018-03-07-22-28-17.bpo-27683.572Rv4.rst [new file with mode: 0644]

index 66c1aaa6a1811772896737bdba0afc4147e780fe..4ce1ed1cedb6872ec56ca11db46a62a593ac3328 100644 (file)
@@ -483,12 +483,16 @@ dictionaries.
 
       Returns an iterator over the usable hosts in the network.  The usable
       hosts are all the IP addresses that belong to the network, except the
-      network address itself and the network broadcast address.
+      network address itself and the network broadcast address.  For networks
+      with a mask length of 31, the network address and network broadcast
+      address are also included in the result.
 
          >>> list(ip_network('192.0.2.0/29').hosts())  #doctest: +NORMALIZE_WHITESPACE
          [IPv4Address('192.0.2.1'), IPv4Address('192.0.2.2'),
           IPv4Address('192.0.2.3'), IPv4Address('192.0.2.4'),
           IPv4Address('192.0.2.5'), IPv4Address('192.0.2.6')]
+         >>> list(ip_network('192.0.2.0/31').hosts())
+         [IPv4Address('192.0.2.0'), IPv4Address('192.0.2.1')]
 
    .. method:: overlaps(other)
 
@@ -620,6 +624,12 @@ dictionaries.
    .. attribute:: num_addresses
    .. attribute:: prefixlen
    .. method:: hosts()
+
+      Returns an iterator over the usable hosts in the network.  The usable
+      hosts are all the IP addresses that belong to the network, except the
+      Subnet-Router anycast address.  For networks with a mask length of 127,
+      the Subnet-Router anycast address is also included in the result.
+
    .. method:: overlaps(other)
    .. method:: address_exclude(network)
    .. method:: subnets(prefixlen_diff=1, new_prefix=None)
index 70746f8de85c092368f163caf97071439fc2465f..583f02ad5427529a998d2deba8f69dce9212bcb0 100644 (file)
@@ -1498,45 +1498,28 @@ class IPv4Network(_BaseV4, _BaseNetwork):
 
         # Constructing from a packed address or integer
         if isinstance(address, (int, bytes)):
-            self.network_address = IPv4Address(address)
-            self.netmask, self._prefixlen = self._make_netmask(self._max_prefixlen)
-            #fixme: address/network test here.
-            return
-
-        if isinstance(address, tuple):
-            if len(address) > 1:
-                arg = address[1]
-            else:
-                # We weren't given an address[1]
-                arg = self._max_prefixlen
-            self.network_address = IPv4Address(address[0])
-            self.netmask, self._prefixlen = self._make_netmask(arg)
-            packed = int(self.network_address)
-            if packed & int(self.netmask) != packed:
-                if strict:
-                    raise ValueError('%s has host bits set' % self)
-                else:
-                    self.network_address = IPv4Address(packed &
-                                                       int(self.netmask))
-            return
-
+            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.
-        addr = _split_optional_netmask(address)
-        self.network_address = IPv4Address(self._ip_int_from_string(addr[0]))
-
-        if len(addr) == 2:
-            arg = addr[1]
         else:
-            arg = self._max_prefixlen
-        self.netmask, self._prefixlen = self._make_netmask(arg)
-
-        if strict:
-            if (IPv4Address(int(self.network_address) & int(self.netmask)) !=
-                self.network_address):
+            args = _split_optional_netmask(address)
+            addr = self._ip_int_from_string(args[0])
+            mask = args[1] if len(args) == 2 else self._max_prefixlen
+
+        self.network_address = IPv4Address(addr)
+        self.netmask, self._prefixlen = self._make_netmask(mask)
+        packed = int(self.network_address)
+        if packed & int(self.netmask) != packed:
+            if strict:
                 raise ValueError('%s has host bits set' % self)
-        self.network_address = IPv4Address(int(self.network_address) &
-                                           int(self.netmask))
+            else:
+                self.network_address = IPv4Address(packed &
+                                                   int(self.netmask))
 
         if self._prefixlen == (self._max_prefixlen - 1):
             self.hosts = self.__iter__
@@ -2191,46 +2174,30 @@ class IPv6Network(_BaseV6, _BaseNetwork):
         """
         _BaseNetwork.__init__(self, address)
 
-        # Efficient constructor from integer or packed address
-        if isinstance(address, (bytes, int)):
-            self.network_address = IPv6Address(address)
-            self.netmask, self._prefixlen = self._make_netmask(self._max_prefixlen)
-            return
-
-        if isinstance(address, tuple):
-            if len(address) > 1:
-                arg = address[1]
-            else:
-                arg = self._max_prefixlen
-            self.netmask, self._prefixlen = self._make_netmask(arg)
-            self.network_address = IPv6Address(address[0])
-            packed = int(self.network_address)
-            if packed & int(self.netmask) != packed:
-                if strict:
-                    raise ValueError('%s has host bits set' % self)
-                else:
-                    self.network_address = IPv6Address(packed &
-                                                       int(self.netmask))
-            return
-
+        # 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.
-        addr = _split_optional_netmask(address)
-
-        self.network_address = IPv6Address(self._ip_int_from_string(addr[0]))
-
-        if len(addr) == 2:
-            arg = addr[1]
         else:
-            arg = self._max_prefixlen
-        self.netmask, self._prefixlen = self._make_netmask(arg)
-
-        if strict:
-            if (IPv6Address(int(self.network_address) & int(self.netmask)) !=
-                self.network_address):
+            args = _split_optional_netmask(address)
+            addr = self._ip_int_from_string(args[0])
+            mask = args[1] if len(args) == 2 else self._max_prefixlen
+
+        self.network_address = IPv6Address(addr)
+        self.netmask, self._prefixlen = self._make_netmask(mask)
+        packed = int(self.network_address)
+        if packed & int(self.netmask) != packed:
+            if strict:
                 raise ValueError('%s has host bits set' % self)
-        self.network_address = IPv6Address(int(self.network_address) &
-                                           int(self.netmask))
+            else:
+                self.network_address = IPv6Address(packed &
+                                                   int(self.netmask))
 
         if self._prefixlen == (self._max_prefixlen - 1):
             self.hosts = self.__iter__
index 2432eb4960e121fbf66551be50c4f0d70646f21a..1cef4217bc88344e51ada60cae662a4879d5546b 100644 (file)
@@ -1042,10 +1042,30 @@ class IpaddrUnitTest(unittest.TestCase):
         self.assertEqual(ipaddress.IPv4Address('1.2.3.1'), hosts[0])
         self.assertEqual(ipaddress.IPv4Address('1.2.3.254'), hosts[-1])
 
+        ipv6_network = ipaddress.IPv6Network('2001:658:22a:cafe::/120')
+        hosts = list(ipv6_network.hosts())
+        self.assertEqual(255, len(hosts))
+        self.assertEqual(ipaddress.IPv6Address('2001:658:22a:cafe::1'), hosts[0])
+        self.assertEqual(ipaddress.IPv6Address('2001:658:22a:cafe::ff'), hosts[-1])
+
         # special case where only 1 bit is left for address
-        self.assertEqual([ipaddress.IPv4Address('2.0.0.0'),
-                          ipaddress.IPv4Address('2.0.0.1')],
-                         list(ipaddress.ip_network('2.0.0.0/31').hosts()))
+        addrs = [ipaddress.IPv4Address('2.0.0.0'),
+                 ipaddress.IPv4Address('2.0.0.1')]
+        str_args = '2.0.0.0/31'
+        tpl_args = ('2.0.0.0', 31)
+        self.assertEqual(addrs, list(ipaddress.ip_network(str_args).hosts()))
+        self.assertEqual(addrs, list(ipaddress.ip_network(tpl_args).hosts()))
+        self.assertEqual(list(ipaddress.ip_network(str_args).hosts()),
+                         list(ipaddress.ip_network(tpl_args).hosts()))
+
+        addrs = [ipaddress.IPv6Address('2001:658:22a:cafe::'),
+                 ipaddress.IPv6Address('2001:658:22a:cafe::1')]
+        str_args = '2001:658:22a:cafe::/127'
+        tpl_args = ('2001:658:22a:cafe::', 127)
+        self.assertEqual(addrs, list(ipaddress.ip_network(str_args).hosts()))
+        self.assertEqual(addrs, list(ipaddress.ip_network(tpl_args).hosts()))
+        self.assertEqual(list(ipaddress.ip_network(str_args).hosts()),
+                         list(ipaddress.ip_network(tpl_args).hosts()))
 
     def testFancySubnetting(self):
         self.assertEqual(sorted(self.ipv4_network.subnets(prefixlen_diff=3)),
diff --git a/Misc/NEWS.d/next/Library/2018-03-07-22-28-17.bpo-27683.572Rv4.rst b/Misc/NEWS.d/next/Library/2018-03-07-22-28-17.bpo-27683.572Rv4.rst
new file mode 100644 (file)
index 0000000..4e6dfa8
--- /dev/null
@@ -0,0 +1,3 @@
+Fix a regression in :mod:`ipaddress` that result of :meth:`hosts`
+is empty when the network is constructed by a tuple containing an
+integer mask and only 1 bit left for addresses.