8000 bpo-27683: Fix a regression for ipaddress networks that hosts() result by zhangyangyu · Pull Request #6016 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-27683: Fix a regression for ipaddress networks that hosts() result #6016

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion Doc/library/ipaddress.rst
Original file line number Diff line number Diff line change
Expand Up @@ -477,12 +477,16 @@ so to avoid duplication they are only documented for :class:`IPv4Network`.

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)

Expand Down Expand Up @@ -639,6 +643,12 @@ so to avoid duplication they are only documented for :class:`IPv4Network`.
.. 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)
Expand Down
109 changes: 38 additions & 71 deletions Lib/ipaddress.py
Original file line number Diff line number Diff line change
Expand Up @@ -1514,45 +1514,28 @@ def __init__(self, address, strict=True):

# 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__
Expand Down Expand Up @@ -2207,46 +2190,30 @@ def __init__(self, address, strict=True):
"""
_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__
Expand Down
26 changes: 23 additions & 3 deletions Lib/test/test_ipaddress.py
Original file line number Diff line number Diff line change
Expand Up @@ -1124,10 +1124,30 @@ def testHosts(self):
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)),
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
0