From: 777GE90 Date: Wed, 8 Jul 2026 14:06:55 +0000 (+0100) Subject: bpo-42861: Add `next_network` function to ipaddress library (#24180) X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=fadb78565c1add98b1a7b3daf08b23f0c65e6863;p=thirdparty%2FPython%2Fcpython.git bpo-42861: Add `next_network` function to ipaddress library (#24180) Add `next_network` method that returns the next closest network of prefix size *next_prefix*. to Lib.ipaddress --------- Co-authored-by: Faisal Mahmood Co-authored-by: Senthil Kumaran --- diff --git a/Doc/library/ipaddress.rst b/Doc/library/ipaddress.rst index 9ccd8602bcb2..387d8034bb8c 100644 --- a/Doc/library/ipaddress.rst +++ b/Doc/library/ipaddress.rst @@ -718,6 +718,35 @@ dictionaries. .. deprecated:: 3.7 It uses the same ordering and comparison algorithm as "<", "==", and ">" + .. method:: next_network(next_prefix=None) + + Finds the next closest network of prefix size *next_prefix*. If + *next_prefix=None*, then the current network prefix will be used. + Returns a single network object. + + Raises :exc:`ValueError` if *next_prefix* is out of range or no further + network of the requested size exists. + + >>> IPv4Network('192.0.2.0/24').next_network() + IPv4Network('192.0.3.0/24') + >>> IPv4Network('192.0.2.0/24').next_network(next_prefix=25) + IPv4Network('192.0.3.0/25') + >>> IPv4Network('192.0.2.0/24').next_network(next_prefix=23) + IPv4Network('192.0.4.0/23') + >>> IPv4Network('192.0.80.0/22').next_network(next_prefix=18) + IPv4Network('192.0.128.0/18') + >>> IPv4Network('192.0.80.0/22').next_network(next_prefix=50) + Traceback (most recent call last): + File "", line 1, in + raise ValueError( + ValueError: next prefix must be between 1 and 32 + >>> IPv4Network('255.255.255.0/24').next_network() + Traceback (most recent call last): + File "", line 1, in + raise ValueError( + ValueError: out of address space, cannot make another /24 network + + .. versionadded:: 3.16 .. class:: IPv6Network(address, strict=True) @@ -790,6 +819,7 @@ dictionaries. .. method:: supernet(prefixlen_diff=1, new_prefix=None) .. method:: subnet_of(other) .. method:: supernet_of(other) + .. method:: next_network(next_prefix=None) .. method:: compare_networks(other) Refer to the corresponding attribute documentation in diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index 7bf3cc3c22a5..e871ad822bbc 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -253,6 +253,14 @@ imaplib (Contributed by Przemysław Buczkowski and Serhiy Storchaka in :gh:`89869`.) +ipaddress +--------- + +Add :meth:`~ipaddress.IPv4Network.next_network` and +:meth:`~ipaddress.IPv6Network.next_network` methods to find the next nearest +network with a specific prefix size. + + logging ------- diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py index f1062a8cd052..9c7a0a46f4f0 100644 --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -1119,6 +1119,47 @@ class _BaseNetwork(_IPAddressBase): return (self.network_address.is_loopback and self.broadcast_address.is_loopback) + def next_network(self, next_prefix=None): + """Get the next closest network with a specific prefix. + + Args: + next_prefix: The desired next prefix length, if not specified the + same self.prefixlen will be used + + Returns: + An IPv(4|6) Network object of the next closest network. + + """ + if next_prefix is None: + next_prefix = self.prefixlen + new_netmask = self.netmask + else: + if next_prefix < 1 or next_prefix > self.max_prefixlen: + raise ValueError( + f"next prefix must be between 1 and {self.max_prefixlen}" + ) + new_netmask, _ = self._make_netmask(next_prefix) + + bit_shift = ( + self.max_prefixlen - next_prefix + if next_prefix <= self.prefixlen + else self.max_prefixlen - self.prefixlen + ) + + next_ip = ( + ((new_netmask._ip & self.network_address._ip) >> bit_shift) + 1 + ) << bit_shift + + try: + return self.__class__( + f"{self._string_from_ip_int(next_ip)}/{next_prefix}" + ) + except OverflowError: + raise ValueError( + f"out of address space, cannot make another /{next_prefix} " + "network" + ) from None + class _BaseConstants: diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index 3f017b97dc28..a74b692784eb 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -1559,6 +1559,47 @@ class IpaddrUnitTest(unittest.TestCase): self.assertEqual(list(ipaddress.ip_network(str_args).hosts()), list(ipaddress.ip_network(tpl_args).hosts())) + def testNextNetwork(self): + ipv4 = ipaddress.IPv4Network('1.2.3.0/24') + self.assertEqual( + ipv4.next_network(), + ipaddress.IPv4Network('1.2.4.0/24'), + ) + self.assertEqual( + ipv4.next_network(next_prefix=16), + ipaddress.IPv4Network('1.3.0.0/16'), + ) + self.assertEqual( + ipv4.next_network(next_prefix=25), + ipaddress.IPv4Network('1.2.4.0/25'), + ) + + ipv6 = ipaddress.IPv6Network('2001:dbb8:aaaa:aaaa::/64') + self.assertEqual( + ipv6.next_network(), + ipaddress.IPv6Network('2001:dbb8:aaaa:aaab::/64'), + ) + self.assertEqual( + ipv6.next_network(next_prefix=48), + ipaddress.IPv6Network('2001:dbb8:aaab::/48'), + ) + self.assertEqual( + ipv6.next_network(next_prefix=88), + ipaddress.IPv6Network('2001:dbb8:aaaa:aaab::/88'), + ) + + def testNextNetworkWithBadPrefix(self): + self.assertRaises(ValueError, self.ipv4_network.next_network, 0) + self.assertRaises(ValueError, self.ipv4_network.next_network, 35) + self.assertRaises(ValueError, self.ipv6_network.next_network, 0) + self.assertRaises(ValueError, self.ipv6_network.next_network, 150) + + def testNextNetworkOutOfAddressSpace(self): + ipv4 = ipaddress.IPv4Network('255.255.255.0/24') + self.assertRaises(ValueError, ipv4.next_network) + ipv6 = ipaddress.IPv6Network('ffff:ffff:ffff:ffff:ffff:ffff:ffff:0/112') + self.assertRaises(ValueError, ipv6.next_network) + def testFancySubnetting(self): self.assertEqual(sorted(self.ipv4_network.subnets(prefixlen_diff=3)), sorted(self.ipv4_network.subnets(new_prefix=27))) diff --git a/Misc/NEWS.d/next/Library/2021-01-09-18-40-15.bpo-42861.T7Ge9O.rst b/Misc/NEWS.d/next/Library/2021-01-09-18-40-15.bpo-42861.T7Ge9O.rst new file mode 100644 index 000000000000..46ed99d1e1b4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-01-09-18-40-15.bpo-42861.T7Ge9O.rst @@ -0,0 +1,2 @@ +Add :meth:`~ipaddress.IPv4Network.next_network` and +:meth:`~ipaddress.IPv6Network.next_network`. Patch by Faisal Mahmood.