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

Skip to content

Commit 3326c92

Browse files
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 10b134a) Co-authored-by: Xiang Zhang <angwerzx@126.com>
1 parent a323eee commit 3326c92

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
@@ -1515,45 +1515,28 @@ def __init__(self, address, strict=True):
15151515

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

15581541
if self._prefixlen == (self._max_prefixlen - 1):
15591542
self.hosts = self.__iter__
@@ -2208,46 +2191,30 @@ def __init__(self, address, strict=True):
22082191
"""
22092192
_BaseNetwork.__init__(self, address)
22102193

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

22522219
if self._prefixlen == (self._max_prefixlen - 1):
22532220
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::', 1C6A 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