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:
parent
481cbe8d62
commit
ae2feb32e7
|
@ -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)
|
||||||
|
|
105
Lib/ipaddress.py
105
Lib/ipaddress.py
|
@ -1498,45 +1498,28 @@ 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:
|
|
||||||
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
|
|
||||||
|
|
||||||
# Assume input argument to be string or any object representation
|
# Assume input argument to be string or any object representation
|
||||||
# which converts into a formatted IP prefix string.
|
# 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:
|
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])
|
||||||
|
mask = args[1] if len(args) == 2 else self._max_prefixlen
|
||||||
|
|
||||||
if strict:
|
self.network_address = IPv4Address(addr)
|
||||||
if (IPv4Address(int(self.network_address) & int(self.netmask)) !=
|
self.netmask, self._prefixlen = self._make_netmask(mask)
|
||||||
self.network_address):
|
packed = int(self.network_address)
|
||||||
|
if packed & int(self.netmask) != packed:
|
||||||
|
if strict:
|
||||||
raise ValueError('%s has host bits set' % self)
|
raise ValueError('%s has host bits set' % self)
|
||||||
self.network_address = IPv4Address(int(self.network_address) &
|
else:
|
||||||
int(self.netmask))
|
self.network_address = IPv4Address(packed &
|
||||||
|
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,46 +2174,30 @@ 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]
|
|
||||||
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
|
|
||||||
|
|
||||||
# Assume input argument to be string or any object representation
|
# Assume input argument to be string or any object representation
|
||||||
# which converts into a formatted IP prefix string.
|
# 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:
|
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])
|
||||||
|
mask = args[1] if len(args) == 2 else self._max_prefixlen
|
||||||
|
|
||||||
if strict:
|
self.network_address = IPv6Address(addr)
|
||||||
if (IPv6Address(int(self.network_address) & int(self.netmask)) !=
|
self.netmask, self._prefixlen = self._make_netmask(mask)
|
||||||
self.network_address):
|
packed = int(self.network_address)
|
||||||
|
if packed & int(self.netmask) != packed:
|
||||||
|
if strict:
|
||||||
raise ValueError('%s has host bits set' % self)
|
raise ValueError('%s has host bits set' % self)
|
||||||
self.network_address = IPv6Address(int(self.network_address) &
|
else:
|
||||||
int(self.netmask))
|
self.network_address = IPv6Address(packed &
|
||||||
|
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__
|
||||||
|
|
|
@ -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)),
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue