diff --git a/Doc/library/ipaddress.rst b/Doc/library/ipaddress.rst index ead841b0581e21..c1d9020e0c42e4 100644 --- a/Doc/library/ipaddress.rst +++ b/Doc/library/ipaddress.rst @@ -180,17 +180,15 @@ write code that handles both IP versions correctly. Address objects are ``True`` if the address is defined as not globally reachable by iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_ - (for IPv6) with the following exceptions: + (for IPv6) with the following exception: - * ``is_private`` is ``False`` for the shared address space (``100.64.0.0/10``) - * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the - semantics of the underlying IPv4 addresses and the following condition holds - (see :attr:`IPv6Address.ipv4_mapped`):: + For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the + semantics of the underlying IPv4 addresses and the following condition holds + (see :attr:`IPv6Address.ipv4_mapped`):: - address.is_private == address.ipv4_mapped.is_private + address.is_private == address.ipv4_mapped.is_private - ``is_private`` has value opposite to :attr:`is_global`, except for the shared address space - (``100.64.0.0/10`` range) where they are both ``False``. + ``is_private`` has value opposite to :attr:`is_global`. .. versionchanged:: 3.13 @@ -204,6 +202,10 @@ write code that handles both IP versions correctly. Address objects are ``2001:1::2/128``, ``2001:3::/32``, ``2001:4:112::/48``, ``2001:20::/28``, ``2001:30::/28``. The exceptions are not considered private. + Additionally ``100.64.0.0/10`` is no longer exceptional – both ``is_private`` and + ``is_global`` used to return ``False`` for addresses in that range, now ``is_private`` + returns ``True``. + .. attribute:: is_global ``True`` if the address is defined as globally reachable by @@ -216,8 +218,7 @@ write code that handles both IP versions correctly. Address objects are address.is_global == address.ipv4_mapped.is_global - ``is_global`` has value opposite to :attr:`is_private`, except for the shared address space - (``100.64.0.0/10`` range) where they are both ``False``. + ``is_global`` has value opposite to :attr:`is_private`. .. versionadded:: 3.4 diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py index 8e4d49c859534d..9d0d7c92671f0a 100644 --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -1339,17 +1339,15 @@ def is_reserved(self): def is_private(self): """``True`` if the address is defined as not globally reachable by iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_ - (for IPv6) with the following exceptions: + (for IPv6) with the following exception: - * ``is_private`` is ``False`` for ``100.64.0.0/10`` - * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the - semantics of the underlying IPv4 addresses and the following condition holds - (see :attr:`IPv6Address.ipv4_mapped`):: + For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the + semantics of the underlying IPv4 addresses and the following condition holds + (see :attr:`IPv6Address.ipv4_mapped`):: - address.is_private == address.ipv4_mapped.is_private + address.is_private == address.ipv4_mapped.is_private - ``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10`` - IPv4 range where they are both ``False``. + ``is_private`` has value opposite to :attr:`is_global`. """ return ( any(self in net for net in self._constants._private_networks) @@ -1369,10 +1367,9 @@ def is_global(self): address.is_global == address.ipv4_mapped.is_global - ``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10`` - IPv4 range where they are both ``False``. + ``is_global`` has value opposite to :attr:`is_private`. """ - return self not in self._constants._public_network and not self.is_private + return not self.is_private @property def is_multicast(self): @@ -1571,9 +1568,7 @@ def is_global(self): iana-ipv4-special-registry. """ - return (not (self.network_address in IPv4Network('100.64.0.0/10') and - self.broadcast_address in IPv4Network('100.64.0.0/10')) and - not self.is_private) + return not self.is_private class _IPv4Constants: @@ -1583,13 +1578,12 @@ class _IPv4Constants: _multicast_network = IPv4Network('224.0.0.0/4') - _public_network = IPv4Network('100.64.0.0/10') - # Not globally reachable address blocks listed on # https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml _private_networks = [ IPv4Network('0.0.0.0/8'), IPv4Network('10.0.0.0/8'), + IPv4Network('100.64.0.0/10'), IPv4Network('127.0.0.0/8'), IPv4Network('169.254.0.0/16'), IPv4Network('172.16.0.0/12'), @@ -2085,17 +2079,15 @@ def is_site_local(self): def is_private(self): """``True`` if the address is defined as not globally reachable by iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_ - (for IPv6) with the following exceptions: + (for IPv6) with the following exception: - * ``is_private`` is ``False`` for ``100.64.0.0/10`` - * For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the - semantics of the underlying IPv4 addresses and the following condition holds - (see :attr:`IPv6Address.ipv4_mapped`):: + For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the + semantics of the underlying IPv4 addresses and the following condition holds + (see :attr:`IPv6Address.ipv4_mapped`):: - address.is_private == address.ipv4_mapped.is_private + address.is_private == address.ipv4_mapped.is_private - ``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10`` - IPv4 range where they are both ``False``. + ``is_private`` has value opposite to :attr:`is_global`. """ ipv4_mapped = self.ipv4_mapped if ipv4_mapped is not None: @@ -2117,8 +2109,7 @@ def is_global(self): address.is_global == address.ipv4_mapped.is_global - ``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10`` - IPv4 range where they are both ``False``. + ``is_global`` has value opposite to :attr:`is_private`. """ return not self.is_private diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index c3ecf2a742941a..1dea20d2907b28 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -2263,7 +2263,7 @@ def testReservedIpv4(self): self.assertEqual(True, ipaddress.ip_network( '127.42.0.0/16').is_loopback) self.assertEqual(False, ipaddress.ip_network('128.0.0.0').is_loopback) - self.assertEqual(False, + self.assertEqual(True, ipaddress.ip_network('100.64.0.0/10').is_private) self.assertEqual(False, ipaddress.ip_network('100.64.0.0/10').is_global) @@ -2285,6 +2285,9 @@ def testReservedIpv4(self): self.assertEqual(True, ipaddress.ip_address( '10.255.255.255').is_private) self.assertEqual(False, ipaddress.ip_address('11.0.0.0').is_private) + self.assertEqual(True, + ipaddress.ip_address('100.64.0.0').is_private) + self.assertEqual(False, ipaddress.ip_address('100.64.0.0').is_global) self.assertEqual(True, ipaddress.ip_address( '172.31.255.255').is_private) self.assertEqual(False, ipaddress.ip_address('172.32.0.0').is_private) diff --git a/Misc/NEWS.d/next/Library/2024-05-31-00-34-38.gh-issue-119812.eNvvk0.rst b/Misc/NEWS.d/next/Library/2024-05-31-00-34-38.gh-issue-119812.eNvvk0.rst new file mode 100644 index 00000000000000..014b0ba1ecfb2d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-05-31-00-34-38.gh-issue-119812.eNvvk0.rst @@ -0,0 +1,9 @@ +:attr:`ipaddress.IPv4Address.is_private` and +:attr:`ipaddress.IPv4Network.is_private` no longer treat the +``100.64.0.0/10`` address block in a special way. + +Previously the range was considered not private but not global either. + +Now ``is_private`` returns ``True`` for these addresses, matching the wider +``is_private`` semantics and the IANA IPv4 Special-Purpose Address Registry +registry saying the range is not globally reachable.