Skip to content

Commit 8f0e88d

Browse files
authored
Merge branch '3.9' into pythongh-118486-3.9
2 parents 1946e26 + b228655 commit 8f0e88d

13 files changed

+334
-42
lines changed

Doc/library/ipaddress.rst

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,18 +188,53 @@ write code that handles both IP versions correctly. Address objects are
188188

189189
.. attribute:: is_private
190190

191-
``True`` if the address is allocated for private networks. See
191+
``True`` if the address is defined as not globally reachable by
192192
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
193-
(for IPv6).
193+
(for IPv6) with the following exceptions:
194+
195+
* ``is_private`` is ``False`` for the shared address space (``100.64.0.0/10``)
196+
* For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
197+
semantics of the underlying IPv4 addresses and the following condition holds
198+
(see :attr:`IPv6Address.ipv4_mapped`)::
199+
200+
address.is_private == address.ipv4_mapped.is_private
201+
202+
``is_private`` has value opposite to :attr:`is_global`, except for the shared address space
203+
(``100.64.0.0/10`` range) where they are both ``False``.
204+
205+
.. versionchanged:: 3.9.20
206+
207+
Fixed some false positives and false negatives.
208+
209+
* ``192.0.0.0/24`` is considered private with the exception of ``192.0.0.9/32`` and
210+
``192.0.0.10/32`` (previously: only the ``192.0.0.0/29`` sub-range was considered private).
211+
* ``64:ff9b:1::/48`` is considered private.
212+
* ``2002::/16`` is considered private.
213+
* There are exceptions within ``2001::/23`` (otherwise considered private): ``2001:1::1/128``,
214+
``2001:1::2/128``, ``2001:3::/32``, ``2001:4:112::/48``, ``2001:20::/28``, ``2001:30::/28``.
215+
The exceptions are not considered private.
194216

195217
.. attribute:: is_global
196218

197-
``True`` if the address is allocated for public networks. See
219+
``True`` if the address is defined as globally reachable by
198220
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
199-
(for IPv6).
221+
(for IPv6) with the following exception:
222+
223+
For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
224+
semantics of the underlying IPv4 addresses and the following condition holds
225+
(see :attr:`IPv6Address.ipv4_mapped`)::
226+
227+
address.is_global == address.ipv4_mapped.is_global
228+
229+
``is_global`` has value opposite to :attr:`is_private`, except for the shared address space
230+
(``100.64.0.0/10`` range) where they are both ``False``.
200231

201232
.. versionadded:: 3.4
202233

234+
.. versionchanged:: 3.9.20
235+
236+
Fixed some false positives and false negatives, see :attr:`is_private` for details.
237+
203238
.. attribute:: is_unspecified
204239

205240
``True`` if the address is unspecified. See :RFC:`5735` (for IPv4)

Doc/library/subprocess.rst

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -713,8 +713,8 @@ Exceptions defined in this module all inherit from :exc:`SubprocessError`.
713713
Security Considerations
714714
-----------------------
715715

716-
Unlike some other popen functions, this implementation will never
717-
implicitly call a system shell. This means that all characters,
716+
Unlike some other popen functions, this library will not
717+
implicitly choose to call a system shell. This means that all characters,
718718
including shell metacharacters, can safely be passed to child processes.
719719
If the shell is invoked explicitly, via ``shell=True``, it is the application's
720720
responsibility to ensure that all whitespace and metacharacters are
@@ -726,6 +726,14 @@ When using ``shell=True``, the :func:`shlex.quote` function can be
726726
used to properly escape whitespace and shell metacharacters in strings
727727
that are going to be used to construct shell commands.
728728

729+
On Windows, batch files (:file:`*.bat` or :file:`*.cmd`) may be launched by the
730+
operating system in a system shell regardless of the arguments passed to this
731+
library. This could result in arguments being parsed according to shell rules,
732+
but without any escaping added by Python. If you are intentionally launching a
733+
batch file with arguments from untrusted sources, consider passing
734+
``shell=True`` to allow Python to escape special characters. See :gh:`114539`
735+
for additional discussion.
736+
729737

730738
Popen Objects
731739
-------------

Doc/tools/susp-ignored.csv

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ library/ipaddress,,:db00,2001:db00::0/24
169169
library/ipaddress,,::,2001:db00::0/24
170170
library/ipaddress,,:db00,2001:db00::0/ffff:ff00::
171171
library/ipaddress,,::,2001:db00::0/ffff:ff00::
172+
library/ipaddress,,:ff9b,64:ff9b:1::/48
173+
library/ipaddress,,::,64:ff9b:1::/48
174+
library/ipaddress,,::,2001::
175+
library/ipaddress,,::,2001:1::
176+
library/ipaddress,,::,2001:3::
177+
library/ipaddress,,::,2001:4:112::
178+
library/ipaddress,,::,2001:20::
179+
library/ipaddress,,::,2001:30::
172180
library/itertools,,:step,elements from seq[start:stop:step]
173181
library/itertools,,:stop,elements from seq[start:stop:step]
174182
library/itertools,,::,kernel = tuple(kernel)[::-1]

Doc/whatsnew/3.9.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,3 +1631,12 @@ tarfile
16311631
:exc:`DeprecationWarning`.
16321632
In Python 3.14, the default will switch to ``'data'``.
16331633
(Contributed by Petr Viktorin in :pep:`706`.)
1634+
1635+
Notable changes in 3.9.20
1636+
=========================
1637+
1638+
ipaddress
1639+
---------
1640+
1641+
* Fixed ``is_global`` and ``is_private`` behavior in ``IPv4Address``,
1642+
``IPv6Address``, ``IPv4Network`` and ``IPv6Network``.

Lib/ipaddress.py

Lines changed: 78 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,18 +1322,41 @@ def is_reserved(self):
13221322
@property
13231323
@functools.lru_cache()
13241324
def is_private(self):
1325-
"""Test if this address is allocated for private networks.
1325+
"""``True`` if the address is defined as not globally reachable by
1326+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
1327+
(for IPv6) with the following exceptions:
13261328
1327-
Returns:
1328-
A boolean, True if the address is reserved per
1329-
iana-ipv4-special-registry.
1329+
* ``is_private`` is ``False`` for ``100.64.0.0/10``
1330+
* For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
1331+
semantics of the underlying IPv4 addresses and the following condition holds
1332+
(see :attr:`IPv6Address.ipv4_mapped`)::
1333+
1334+
address.is_private == address.ipv4_mapped.is_private
13301335
1336+
``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
1337+
IPv4 range where they are both ``False``.
13311338
"""
1332-
return any(self in net for net in self._constants._private_networks)
1339+
return (
1340+
any(self in net for net in self._constants._private_networks)
1341+
and all(self not in net for net in self._constants._private_networks_exceptions)
1342+
)
13331343

13341344
@property
13351345
@functools.lru_cache()
13361346
def is_global(self):
1347+
"""``True`` if the address is defined as globally reachable by
1348+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
1349+
(for IPv6) with the following exception:
1350+
1351+
For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
1352+
semantics of the underlying IPv4 addresses and the following condition holds
1353+
(see :attr:`IPv6Address.ipv4_mapped`)::
1354+
1355+
address.is_global == address.ipv4_mapped.is_global
1356+
1357+
``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
1358+
IPv4 range where they are both ``False``.
1359+
"""
13371360
return self not in self._constants._public_network and not self.is_private
13381361

13391362
@property
@@ -1537,13 +1560,15 @@ class _IPv4Constants:
15371560

15381561
_public_network = IPv4Network('100.64.0.0/10')
15391562

1563+
# Not globally reachable address blocks listed on
1564+
# https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
15401565
_private_networks = [
15411566
IPv4Network('0.0.0.0/8'),
15421567
IPv4Network('10.0.0.0/8'),
15431568
IPv4Network('127.0.0.0/8'),
15441569
IPv4Network('169.254.0.0/16'),
15451570
IPv4Network('172.16.0.0/12'),
1546-
IPv4Network('192.0.0.0/29'),
1571+
IPv4Network('192.0.0.0/24'),
15471572
IPv4Network('192.0.0.170/31'),
15481573
IPv4Network('192.0.2.0/24'),
15491574
IPv4Network('192.168.0.0/16'),
@@ -1554,6 +1579,11 @@ class _IPv4Constants:
15541579
IPv4Network('255.255.255.255/32'),
15551580
]
15561581

1582+
_private_networks_exceptions = [
1583+
IPv4Network('192.0.0.9/32'),
1584+
IPv4Network('192.0.0.10/32'),
1585+
]
1586+
15571587
_reserved_network = IPv4Network('240.0.0.0/4')
15581588

15591589
_unspecified_address = IPv4Address('0.0.0.0')
@@ -1995,23 +2025,42 @@ def is_site_local(self):
19952025
@property
19962026
@functools.lru_cache()
19972027
def is_private(self):
1998-
"""Test if this address is allocated for private networks.
2028+
"""``True`` if the address is defined as not globally reachable by
2029+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
2030+
(for IPv6) with the following exceptions:
19992031
2000-
Returns:
2001-
A boolean, True if the address is reserved per
2002-
iana-ipv6-special-registry.
2032+
* ``is_private`` is ``False`` for ``100.64.0.0/10``
2033+
* For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
2034+
semantics of the underlying IPv4 addresses and the following condition holds
2035+
(see :attr:`IPv6Address.ipv4_mapped`)::
2036+
2037+
address.is_private == address.ipv4_mapped.is_private
20032038
2039+
``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
2040+
IPv4 range where they are both ``False``.
20042041
"""
2005-
return any(self in net for net in self._constants._private_networks)
2042+
ipv4_mapped = self.ipv4_mapped
2043+
if ipv4_mapped is not None:
2044+
return ipv4_mapped.is_private
2045+
return (
2046+
any(self in net for net in self._constants._private_networks)
2047+
and all(self not in net for net in self._constants._private_networks_exceptions)
2048+
)
20062049

20072050
@property
20082051
def is_global(self):
2009-
"""Test if this address is allocated for public networks.
2052+
"""``True`` if the address is defined as globally reachable by
2053+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
2054+
(for IPv6) with the following exception:
20102055
2011-
Returns:
2012-
A boolean, true if the address is not reserved per
2013-
iana-ipv6-special-registry.
2056+
For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
2057+
semantics of the underlying IPv4 addresses and the following condition holds
2058+
(see :attr:`IPv6Address.ipv4_mapped`)::
2059+
2060+
address.is_global == address.ipv4_mapped.is_global
20142061
2062+
``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
2063+
IPv4 range where they are both ``False``.
20152064
"""
20162065
return not self.is_private
20172066

@@ -2252,19 +2301,31 @@ class _IPv6Constants:
22522301

22532302
_multicast_network = IPv6Network('ff00::/8')
22542303

2304+
# Not globally reachable address blocks listed on
2305+
# https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
22552306
_private_networks = [
22562307
IPv6Network('::1/128'),
22572308
IPv6Network('::/128'),
22582309
IPv6Network('::ffff:0:0/96'),
2310+
IPv6Network('64:ff9b:1::/48'),
22592311
IPv6Network('100::/64'),
22602312
IPv6Network('2001::/23'),
2261-
IPv6Network('2001:2::/48'),
22622313
IPv6Network('2001:db8::/32'),
2263-
IPv6Network('2001:10::/28'),
2314+
# IANA says N/A, let's consider it not globally reachable to be safe
2315+
IPv6Network('2002::/16'),
22642316
IPv6Network('fc00::/7'),
22652317
IPv6Network('fe80::/10'),
22662318
]
22672319

2320+
_private_networks_exceptions = [
2321+
IPv6Network('2001:1::1/128'),
2322+
IPv6Network('2001:1::2/128'),
2323+
IPv6Network('2001:3::/32'),
2324+
IPv6Network('2001:4:112::/48'),
2325+
IPv6Network('2001:20::/28'),
2326+
IPv6Network('2001:30::/28'),
2327+
]
2328+
22682329
_reserved_networks = [
22692330
IPv6Network('::/8'), IPv6Network('100::/8'),
22702331
IPv6Network('200::/7'), IPv6Network('400::/6'),

Lib/test/test_ipaddress.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2263,6 +2263,10 @@ def testReservedIpv4(self):
22632263
self.assertEqual(True, ipaddress.ip_address(
22642264
'172.31.255.255').is_private)
22652265
self.assertEqual(False, ipaddress.ip_address('172.32.0.0').is_private)
2266+
self.assertFalse(ipaddress.ip_address('192.0.0.0').is_global)
2267+
self.assertTrue(ipaddress.ip_address('192.0.0.9').is_global)
2268+
self.assertTrue(ipaddress.ip_address('192.0.0.10').is_global)
2269+
self.assertFalse(ipaddress.ip_address('192.0.0.255').is_global)
22662270

22672271
self.assertEqual(True,
22682272
ipaddress.ip_address('169.254.100.200').is_link_local)
@@ -2278,6 +2282,40 @@ def testReservedIpv4(self):
22782282
self.assertEqual(False, ipaddress.ip_address('128.0.0.0').is_loopback)
22792283
self.assertEqual(True, ipaddress.ip_network('0.0.0.0').is_unspecified)
22802284

2285+
def testPrivateNetworks(self):
2286+
self.assertEqual(True, ipaddress.ip_network("0.0.0.0/0").is_private)
2287+
self.assertEqual(False, ipaddress.ip_network("1.0.0.0/8").is_private)
2288+
2289+
self.assertEqual(True, ipaddress.ip_network("0.0.0.0/8").is_private)
2290+
self.assertEqual(True, ipaddress.ip_network("10.0.0.0/8").is_private)
2291+
self.assertEqual(True, ipaddress.ip_network("127.0.0.0/8").is_private)
2292+
self.assertEqual(True, ipaddress.ip_network("169.254.0.0/16").is_private)
2293+
self.assertEqual(True, ipaddress.ip_network("172.16.0.0/12").is_private)
2294+
self.assertEqual(True, ipaddress.ip_network("192.0.0.0/29").is_private)
2295+
self.assertEqual(False, ipaddress.ip_network("192.0.0.9/32").is_private)
2296+
self.assertEqual(True, ipaddress.ip_network("192.0.0.170/31").is_private)
2297+
self.assertEqual(True, ipaddress.ip_network("192.0.2.0/24").is_private)
2298+
self.assertEqual(True, ipaddress.ip_network("192.168.0.0/16").is_private)
2299+
self.assertEqual(True, ipaddress.ip_network("198.18.0.0/15").is_private)
2300+
self.assertEqual(True, ipaddress.ip_network("198.51.100.0/24").is_private)
2301+
self.assertEqual(True, ipaddress.ip_network("203.0.113.0/24").is_private)
2302+
self.assertEqual(True, ipaddress.ip_network("240.0.0.0/4").is_private)
2303+
self.assertEqual(True, ipaddress.ip_network("255.255.255.255/32").is_private)
2304+
2305+
self.assertEqual(False, ipaddress.ip_network("::/0").is_private)
2306+
self.assertEqual(False, ipaddress.ip_network("::ff/128").is_private)
2307+
2308+
self.assertEqual(True, ipaddress.ip_network("::1/128").is_private)
2309+
self.assertEqual(True, ipaddress.ip_network("::/128").is_private)
2310+
self.assertEqual(True, ipaddress.ip_network("::ffff:0:0/96").is_private)
2311+
self.assertEqual(True, ipaddress.ip_network("100::/64").is_private)
2312+
self.assertEqual(True, ipaddress.ip_network("2001:2::/48").is_private)
2313+
self.assertEqual(False, ipaddress.ip_network("2001:3::/48").is_private)
2314+
self.assertEqual(True, ipaddress.ip_network("2001:db8::/32").is_private)
2315+
self.assertEqual(True, ipaddress.ip_network("2001:10::/28").is_private)
2316+
self.assertEqual(True, ipaddress.ip_network("fc00::/7").is_private)
2317+
self.assertEqual(True, ipaddress.ip_network("fe80::/10").is_private)
2318+
22812319
def testReservedIpv6(self):
22822320

22832321
self.assertEqual(True, ipaddress.ip_network('ffff::').is_multicast)
@@ -2351,6 +2389,20 @@ def testReservedIpv6(self):
23512389
self.assertEqual(True, ipaddress.ip_address('0::0').is_unspecified)
23522390
self.assertEqual(False, ipaddress.ip_address('::1').is_unspecified)
23532391

2392+
self.assertFalse(ipaddress.ip_address('64:ff9b:1::').is_global)
2393+
self.assertFalse(ipaddress.ip_address('2001::').is_global)
2394+
self.assertTrue(ipaddress.ip_address('2001:1::1').is_global)
2395+
self.assertTrue(ipaddress.ip_address('2001:1::2').is_global)
2396+
self.assertFalse(ipaddress.ip_address('2001:2::').is_global)
2397+
self.assertTrue(ipaddress.ip_address('2001:3::').is_global)
2398+
self.assertFalse(ipaddress.ip_address('2001:4::').is_global)
2399+
self.assertTrue(ipaddress.ip_address('2001:4:112::').is_global)
2400+
self.assertFalse(ipaddress.ip_address('2001:10::').is_global)
2401+
self.assertTrue(ipaddress.ip_address('2001:20::').is_global)
2402+
self.assertTrue(ipaddress.ip_address('2001:30::').is_global)
2403+
self.assertFalse(ipaddress.ip_address('2001:40::').is_global)
2404+
self.assertFalse(ipaddress.ip_address('2002::').is_global)
2405+
23542406
# some generic IETF reserved addresses
23552407
self.assertEqual(True, ipaddress.ip_address('100::').is_reserved)
23562408
self.assertEqual(True, ipaddress.ip_network('4000::1/128').is_reserved)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Fixed various false positives and false negatives in
2+
3+
* :attr:`ipaddress.IPv4Address.is_private` (see these docs for details)
4+
* :attr:`ipaddress.IPv4Address.is_global`
5+
* :attr:`ipaddress.IPv6Address.is_private`
6+
* :attr:`ipaddress.IPv6Address.is_global`
7+
8+
Also in the corresponding :class:`ipaddress.IPv4Network` and :class:`ipaddress.IPv6Network`
9+
attributes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Update bundled libexpat to 2.6.2
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:meth:`ssl.SSLContext.cert_store_stats` and
2+
:meth:`ssl.SSLContext.get_ca_certs` now correctly lock access to the
3+
certificate store, when the :class:`ssl.SSLContext` is shared across
4+
multiple threads.

0 commit comments

Comments
 (0)