8000 bpo-27683: Fix a regression for host() of ipaddress network objects (… · python/cpython@10b134a · GitHub
[go: up one dir, main page]

Skip to content

Commit 10b134a

Browse files
authored
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.
1 parent 5609b78 commit 10b134a

File tree

4 files changed

+75
-75
lines changed

4 files changed

+75
-75
lines changed

Doc/library/ipaddress.rst

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,12 +485,16 @@ dictionaries.
485485

486486
Returns an iterator over the usable hosts in the network. The usable
487487
hosts are all the IP addresses that belong to the network, except the
488-
network address itself and the network broadcast address.
488+
network address itself and the network broadcast address. For networks
489+
with a mask length of 31, the network address and network broadcast
490+
address are also included in the result.
489491

490492
>>> list(ip_network('192.0.2.0/29').hosts()) #doctest: +NORMALIZE_WHITESPACE
491493
[IPv4Address('192.0.2.1'), IPv4Address('192.0.2.2'),
492494
IPv4Address('192.0.2.3'), IPv4Address('192.0.2.4'),
493495
IPv4Address('192.0.2.5'), IPv4Address('192.0.2.6')]
496+
>>> list(ip_network('192.0.2.0/31').hosts())
497+
[IPv4Address('192.0.2.0'), IPv4Address('192.0.2.1')]
494498

495499
.. method:: overlaps(other)
496500

@@ -647,6 +651,12 @@ dictionaries.
647651
.. attribute:: num_addresses
648652
.. attribute:: prefixlen
649653
.. method:: hosts()
654+
655+
Returns an iterator over the usable hosts in the network. The usable
656+
hosts are all the IP addresses that belong to the network, except the
657+
Subnet-Router anycast address. For networks with a mask length of 127,
658+
the Subnet-Router anycast address is also included in the result.
659+
650660
.. method:: overlaps(other)
651661
.. method:: address_exclude(network)
652662
.. method:: subnets(prefixlen_diff=1, new_prefix=None)

Lib/ipaddress.py

Lines changed: 38 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,45 +1514,28 @@ def __init__(self, address, strict=True):
15141514

15151515
# Constructing from a packed address or integer
15161516
if isinstance(address, (int, bytes)):
1517-
self.network_address = IPv4Address(address)
1518-
self.netmask, self._prefixlen = self._make_netmask(self._max_prefixlen)
1519-
#fixme: address/network test here.
1520-
return
1521-
1522-
if isinstance(address, tuple):
1523-
if len(address) > 1:
1524-
arg = address[1]
1525-
else:
1526-
# We weren't given an address[1]
1527-
arg = self._max_prefixlen
1528-
self.network_address = IPv4Address(address[0])
1529-
self.netmask, self._prefixlen = self._make_netmask(arg)
1530-
packed = int(self.network_address)
1531-
if packed & int(self.netmask) != packed:
1532-
if strict:
1533-
raise ValueError('%s has host bits set' % self)
1534-
else:
1535-
self.network_address = IPv4Address(packed &
1536-
int(self.netmask))
1537-
return
1538-
1517+
addr = address
1518+
mask = self._max_prefixlen
1519+
# Constructing from a tuple (addr, [mask])
1520+
elif isinstance(address, tuple):
1521+
addr = address[0]
1522+
mask = address[1] if len(address) > 1 else self._max_prefixlen
15391523
# Assume input argument to be string or any object representation
15401524
# which converts into a formatted IP prefix string.
1541-
addr = _split_optional_netmask(address)
1542-
self.network_address = IPv4Address(self._ip_int_from_string(addr[0]))
1543-
1544-
if len(addr) == 2:
1545-
arg = addr[1]
15461525
else:
1547-
arg = self._max_prefixlen
1548-
self.netmask, self._prefixlen = self._make_netmask(arg)
1549-
1550-
if strict:
1551-
if (IPv4Address(int(self.network_address) & int(self.netmask)) !=
1552-
self.network_address):
1526+
args = _split_optional_netmask(address)
1527+
addr = self._ip_int_from_string(args[0])
1528+
mask = args[1] if len(args) == 2 else self._max_prefixlen
1529+
1530+
self.network_address = IPv4Address(addr)
1531+
self.netmask, self._prefixlen = self._make_netmask(mask)
1532+
packed = int(self.network_address)
1533+
if packed & int(self.netmask) != packed:
1534+
if strict:
15531535
raise ValueError('%s has host bits set' % self)
1554-
self.network_address = IPv4Address(int(self.network_address) &
1555-
int(self.netmask))
1536+
else:
1537+
self.network_address = IPv4Address(packed &
1538+
int(self.netmask))
15561539

15571540
if self._prefixlen == (self._max_prefixlen - 1):
15581541
self.hosts = self.__iter__
@@ -2207,46 +2190,30 @@ def __init__(self, address, strict=True):
22072190
"""
22082191
_BaseNetwork.__init__(self, address)
22092192

2210-
# Efficient constructor from integer or packed address
2211-
if isinstance(address, (bytes, int)):
2212-
self.network_address = IPv6Address(address)
2213-
self.netmask, self._prefixlen = self._make_netmask(self._max_prefixlen)
2214-
return
2215-
2216-
if isinstance(address, tuple):
2217-
if len(address) > 1:
2218-
arg = address[1]
2219-
else:
2220-
arg = self._max_prefixlen
2221-
self.netmask, self._prefixlen = self._make_netmask(arg)
2222-
self.network_address = IPv6Address(address[0])
2223-
packed = int(self.network_address)
2224-
if packed & int(self.netmask) != packed:
2225-
if strict:
2226-
raise ValueError('%s has host bits set' % self)
2227-
else:
2228-
self.network_address = IPv6Address(packed &
2229-
int(self.netmask))
2230-
return
2231-
2193+
# Constructing from a packed address or integer
2194+
if isinstance(address, (int, bytes)):
2195+
addr = address
2196+
mask = self._max_prefixlen
2197+
# Constructing from a tuple (addr, [mask])
2198+
elif isinstance(address, tuple):
2199+
addr = address[0]
2200+
mask = address[1] if len(address) > 1 else self._max_prefixlen
22322201
# Assume input argument to be string or any object representation
22332202
# which converts into a formatted IP prefix string.
2234-
addr = _split_optional_netmask(address)
2235-
2236-
self.network_add 10000 ress = IPv6Address(self._ip_int_from_string(addr[0]))
2237-
2238-
if len(addr) == 2:
2239-
arg = addr[1]
22402203
else:
2241-
arg = self._max_prefixlen
2242-
self.netmask, self._prefixlen = self._make_netmask(arg)
2243-
2244-
if strict:
2245-
if (IPv6Address(int(self.network_address) & int(self.netmask)) !=
2246-
self.network_address):
2204+
args = _split_optional_netmask(address)
2205+
addr = self._ip_int_from_string(args[0])
2206+
mask = args[1] if len(args) == 2 else self._max_prefixlen
2207+
2208+
self.network_address = IPv6Address(addr)
2209+
self.netmask, self._prefixlen = self._make_netmask(mask)
2210+
packed = int(self.network_address)
2211+
if packed & int(self.netmask) != packed:
2212+
if strict:
22472213
raise ValueError('%s has host bits set' % self)
2248-
self.network_address = IPv6Address(int(self.network_address) &
2249-
int(self.netmask))
2214+
else:
2215+
self.network_address = IPv6Address(packed &
2216+
int(self.netmask))
22502217

22512218
if self._prefixlen == (self._max_prefixlen - 1):
22522219
self.hosts = self.__iter__

Lib/test/test_ipaddress.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,10 +1127,30 @@ def testHosts(self):
11271127
self.assertEqual(ipaddress.IPv4Address('1.2.3.1'), hosts[0])
11281128
self.assertEqual(ipaddress.IPv4Address('1.2.3.254'), hosts[-1])
11291129

1130+
ipv6_network = ipaddress.IPv6Network('2001:658:22a:cafe::/120')
1131+
hosts = list(ipv6_network.hosts())
1132+
self.assertEqual(255, len(hosts))
1133+
self.assertEqual(ipaddress.IPv6Address('2001:658:22a:cafe::1'), hosts[0])
1134+
self.assertEqual(ipaddress.IPv6Address('2001:658:22a:cafe::ff'), hosts[-1])
1135+
11301136
# special case where only 1 bit is left for address
1131-
self.assertEqual([ipaddress.IPv4Address('2.0.0.0'),
1132-
ipaddress.IPv4Address('2.0.0.1')],
1133-
list(ipaddress.ip_network('2.0.0.0/31').hosts()))
1137+
addrs = [ipaddress.IPv4Address('2.0.0.0'),
1138+
ipaddress.IPv4Address('2.0.0.1')]
1139+
str_args = '2.0.0.0/31'
1140+
tpl_args = ('2.0.0.0', 31)
1141+
self.assertEqual(addrs, list(ipaddress.ip_network(str_args).hosts()))
1142+
self.assertEqual(addrs, list(ipaddress.ip_network(tpl_args).hosts()))
1143+
self.assertEqual(list(ipaddress.ip_network(str_args).hosts()),
1144+
list(ipaddress.ip_network(tpl_args).hosts()))
1145+
1146+
addrs = [ipaddress.IPv6Address('2001:658:22a:cafe::'),
1147+
ipaddress.IPv6Address('2001:658:22a:cafe::1')]
1148+
str_args = '2001:658:22a:cafe::/127'
1149+
tpl_args = ('2001:658:22a:cafe::', 127)
1150+
self.assertEqual(addrs, list(ipaddress.ip_network(str_args).hosts()))
1151+
self.assertEqual(addrs, list(ipaddress.ip_network(tpl_args).hosts()))
1152+
self.assertEqual(list(ipaddress.ip_network(str_args).hosts()),
1153+
list(ipaddress.ip_network(tpl_args).hosts()))
11341154

11351155
def testFancySubnetting(self):
11361156
self.assertEqual(sorted(self.ipv4_network.subnets(prefixlen_diff=3)),
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a regression in :mod:`ipaddress` that result of :meth:`hosts`
2+
is empty when the network is constructed by a tuple containing an
3+
integer mask and only 1 bit left for addresses.

0 commit comments

Comments
 (0)
0