bpo-27683: Fix a regression for host() of ipaddress network objects (GH-6016)

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 10b134a07c)

Co-authored-by: Xiang Zhang <angwerzx@126.com>
This commit is contained in:
Miss Islington (bot) 2018-03-20 18:49:41 -07:00 committed by GitHub
parent 481cbe8d62
commit ae2feb32e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 73 deletions

View File

@ -483,12 +483,16 @@ dictionaries.
Returns an iterator over the usable hosts in the network. The usable 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 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 >>> 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.1'), IPv4Address('192.0.2.2'),
IPv4Address('192.0.2.3'), IPv4Address('192.0.2.4'), IPv4Address('192.0.2.3'), IPv4Address('192.0.2.4'),
IPv4Address('192.0.2.5'), IPv4Address('192.0.2.6')] 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) .. method:: overlaps(other)
@ -620,6 +624,12 @@ dictionaries.
.. attribute:: num_addresses .. attribute:: num_addresses
.. attribute:: prefixlen .. attribute:: prefixlen
.. method:: hosts() .. 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:: overlaps(other)
.. method:: address_exclude(network) .. method:: address_exclude(network)
.. method:: subnets(prefixlen_diff=1, new_prefix=None) .. method:: subnets(prefixlen_diff=1, new_prefix=None)

View File

@ -1498,19 +1498,21 @@ class IPv4Network(_BaseV4, _BaseNetwork):
# Constructing from a packed address or integer # Constructing from a packed address or integer
if isinstance(address, (int, bytes)): if isinstance(address, (int, bytes)):
self.network_address = IPv4Address(address) addr = address
self.netmask, self._prefixlen = self._make_netmask(self._max_prefixlen) mask = self._max_prefixlen
#fixme: address/network test here. # Constructing from a tuple (addr, [mask])
return elif isinstance(address, tuple):
addr = address[0]
if isinstance(address, tuple): mask = address[1] if len(address) > 1 else self._max_prefixlen
if len(address) > 1: # Assume input argument to be string or any object representation
arg = address[1] # which converts into a formatted IP prefix string.
else: else:
# We weren't given an address[1] args = _split_optional_netmask(address)
arg = self._max_prefixlen addr = self._ip_int_from_string(args[0])
self.network_address = IPv4Address(address[0]) mask = args[1] if len(args) == 2 else self._max_prefixlen
self.netmask, self._prefixlen = self._make_netmask(arg)
self.network_address = IPv4Address(addr)
self.netmask, self._prefixlen = self._make_netmask(mask)
packed = int(self.network_address) packed = int(self.network_address)
if packed & int(self.netmask) != packed: if packed & int(self.netmask) != packed:
if strict: if strict:
@ -1518,25 +1520,6 @@ class IPv4Network(_BaseV4, _BaseNetwork):
else: else:
self.network_address = IPv4Address(packed & self.network_address = IPv4Address(packed &
int(self.netmask)) int(self.netmask))
return
# 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):
raise ValueError('%s has host bits set' % self)
self.network_address = IPv4Address(int(self.network_address) &
int(self.netmask))
if self._prefixlen == (self._max_prefixlen - 1): if self._prefixlen == (self._max_prefixlen - 1):
self.hosts = self.__iter__ self.hosts = self.__iter__
@ -2191,19 +2174,23 @@ class IPv6Network(_BaseV6, _BaseNetwork):
""" """
_BaseNetwork.__init__(self, address) _BaseNetwork.__init__(self, address)
# Efficient constructor from integer or packed address # Constructing from a packed address or integer
if isinstance(address, (bytes, int)): if isinstance(address, (int, bytes)):
self.network_address = IPv6Address(address) addr = address
self.netmask, self._prefixlen = self._make_netmask(self._max_prefixlen) mask = self._max_prefixlen
return # Constructing from a tuple (addr, [mask])
elif isinstance(address, tuple):
if isinstance(address, tuple): addr = address[0]
if len(address) > 1: mask = address[1] if len(address) > 1 else self._max_prefixlen
arg = address[1] # Assume input argument to be string or any object representation
# which converts into a formatted IP prefix string.
else: else:
arg = self._max_prefixlen args = _split_optional_netmask(address)
self.netmask, self._prefixlen = self._make_netmask(arg) addr = self._ip_int_from_string(args[0])
self.network_address = IPv6Address(address[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) packed = int(self.network_address)
if packed & int(self.netmask) != packed: if packed & int(self.netmask) != packed:
if strict: if strict:
@ -2211,26 +2198,6 @@ class IPv6Network(_BaseV6, _BaseNetwork):
else: else:
self.network_address = IPv6Address(packed & self.network_address = IPv6Address(packed &
int(self.netmask)) int(self.netmask))
return
# 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):
raise ValueError('%s has host bits set' % self)
self.network_address = IPv6Address(int(self.network_address) &
int(self.netmask))
if self._prefixlen == (self._max_prefixlen - 1): if self._prefixlen == (self._max_prefixlen - 1):
self.hosts = self.__iter__ self.hosts = self.__iter__

View 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.1'), hosts[0])
self.assertEqual(ipaddress.IPv4Address('1.2.3.254'), hosts[-1]) 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 # special case where only 1 bit is left for address
self.assertEqual([ipaddress.IPv4Address('2.0.0.0'), addrs = [ipaddress.IPv4Address('2.0.0.0'),
ipaddress.IPv4Address('2.0.0.1')], ipaddress.IPv4Address('2.0.0.1')]
list(ipaddress.ip_network('2.0.0.0/31').hosts())) 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): def testFancySubnetting(self):
self.assertEqual(sorted(self.ipv4_network.subnets(prefixlen_diff=3)), self.assertEqual(sorted(self.ipv4_network.subnets(prefixlen_diff=3)),

View File

@ -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.