Skip to content

Commit 8e9c47a

Browse files
authored
bpo-28577: Special case added to IP v4 and v6 hosts for /32 and /128 networks (pythonGH-18757)
The `.hosts()` method now returns the single address present in a /32 or /128 network.
1 parent 9229eee commit 8e9c47a

File tree

5 files changed

+25
-5
lines changed

5 files changed

+25
-5
lines changed

Doc/library/ipaddress.rst

+6-1
Original file line numberDiff line numberDiff line change
@@ -509,14 +509,17 @@ dictionaries.
509509
hosts are all the IP addresses that belong to the network, except the
510510
network address itself and the network broadcast address. For networks
511511
with a mask length of 31, the network address and network broadcast
512-
address are also included in the result.
512+
address are also included in the result. Networks with a mask of 32
513+
will return a list containing the single host address.
513514

514515
>>> list(ip_network('192.0.2.0/29').hosts()) #doctest: +NORMALIZE_WHITESPACE
515516
[IPv4Address('192.0.2.1'), IPv4Address('192.0.2.2'),
516517
IPv4Address('192.0.2.3'), IPv4Address('192.0.2.4'),
517518
IPv4Address('192.0.2.5'), IPv4Address('192.0.2.6')]
518519
>>> list(ip_network('192.0.2.0/31').hosts())
519520
[IPv4Address('192.0.2.0'), IPv4Address('192.0.2.1')]
521+
>>> list(ip_network('192.0.2.1/32').hosts())
522+
[IPv4Address('192.0.2.1')]
520523

521524
.. method:: overlaps(other)
522525

@@ -678,6 +681,8 @@ dictionaries.
678681
hosts are all the IP addresses that belong to the network, except the
679682
Subnet-Router anycast address. For networks with a mask length of 127,
680683
the Subnet-Router anycast address is also included in the result.
684+
Networks with a mask of 128 will return a list containing the
685+
single host address.
681686

682687
.. method:: overlaps(other)
683688
.. method:: address_exclude(network)

Lib/ipaddress.py

+4
Original file line numberDiff line numberDiff line change
@@ -1509,6 +1509,8 @@ def __init__(self, address, strict=True):
15091509

15101510
if self._prefixlen == (self._max_prefixlen - 1):
15111511
self.hosts = self.__iter__
1512+
elif self._prefixlen == (self._max_prefixlen):
1513+
self.hosts = lambda: [IPv4Address(addr)]
15121514

15131515
@property
15141516
@functools.lru_cache()
@@ -2212,6 +2214,8 @@ def __init__(self, address, strict=True):
22122214

22132215
if self._prefixlen == (self._max_prefixlen - 1):
22142216
self.hosts = self.__iter__
2217+
elif self._prefixlen == self._max_prefixlen:
2218+
self.hosts = lambda: [IPv6Address(addr)]
22152219

22162220
def hosts(self):
22172221
"""Generate Iterator over usable hosts in a network.

Lib/test/test_ipaddress.py

+13-4
Original file line numberDiff line numberDiff line change
@@ -1424,14 +1424,15 @@ def testHosts(self):
14241424
self.assertEqual(list(ipaddress.ip_network(str_args).hosts()),
14251425
list(ipaddress.ip_network(tpl_args).hosts()))
14261426

1427-
addrs = [ipaddress.IPv6Address('2001:658:22a:cafe::'),
1428-
ipaddress.IPv6Address('2001:658:22a:cafe::1')]
1429-
str_args = '2001:658:22a:cafe::/127'
1430-
tpl_args = ('2001:658:22a:cafe::', 127)
1427+
# special case where the network is a /32
1428+
addrs = [ipaddress.IPv4Address('1.2.3.4')]
1429+
str_args = '1.2.3.4/32'
1430+
tpl_args = ('1.2.3.4', 32)
14311431
self.assertEqual(addrs, list(ipaddress.ip_network(str_args).hosts()))
14321432
self.assertEqual(addrs, list(ipaddress.ip_network(tpl_args).hosts()))
14331433
self.assertEqual(list(ipaddress.ip_network(str_args).hosts()),
14341434
list(ipaddress.ip_network(tpl_args).hosts()))
1435+
14351436
addrs = [ipaddress.IPv6Address('2001:658:22a:cafe::'),
14361437
ipaddress.IPv6Address('2001:658:22a:cafe::1')]
14371438
str_args = '2001:658:22a:cafe::/127'
@@ -1441,6 +1442,14 @@ def testHosts(self):
14411442
self.assertEqual(list(ipaddress.ip_network(str_args).hosts()),
14421443
list(ipaddress.ip_network(tpl_args).hosts()))
14431444

1445+
addrs = [ipaddress.IPv6Address('2001:658:22a:cafe::1'), ]
1446+
str_args = '2001:658:22a:cafe::1/128'
1447+
tpl_args = ('2001:658:22a:cafe::1', 128)
1448+
self.assertEqual(addrs, list(ipaddress.ip_network(str_args).hosts()))
1449+
self.assertEqual(addrs, list(ipaddress.ip_network(tpl_args).hosts()))
1450+
self.assertEqual(list(ipaddress.ip_network(str_args).hosts()),
1451+
list(ipaddress.ip_network(tpl_args).hosts()))
1452+
14441453
def testFancySubnetting(self):
14451454
self.assertEqual(sorted(self.ipv4_network.subnets(prefixlen_diff=3)),
14461455
sorted(self.ipv4_network.subnets(new_prefix=27)))

Misc/ACKS

+1
Original file line numberDiff line numberDiff line change
@@ -1823,6 +1823,7 @@ Jeff Wheeler
18231823
Christopher White
18241824
David White
18251825
Mats Wichmann
1826+
Pete Wicken
18261827
Marcel Widjaja
18271828
Truida Wiedijk
18281829
Felix Wiemann
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The hosts method on 32-bit prefix length IPv4Networks and 128-bit prefix IPv6Networks now returns a list containing the single Address instead of an empty list.

0 commit comments

Comments
 (0)