Skip to content

Commit 49746a0

Browse files
authored
Merge branch '3.10' into pythongh-118486-3.10
2 parents 77ede7c + 333c7dc commit 49746a0

12 files changed

+236
-44
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.10.15
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.10.15
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
@@ -741,8 +741,8 @@ Exceptions defined in this module all inherit from :exc:`SubprocessError`.
741741
Security Considerations
742742
-----------------------
743743

744-
Unlike some other popen functions, this implementation will never
745-
implicitly call a system shell. This means that all characters,
744+
Unlike some other popen functions, this library will not
745+
implicitly choose to call a system shell. This means that all characters,
746746
including shell metacharacters, can safely be passed to child processes.
747747
If the shell is invoked explicitly, via ``shell=True``, it is the application's
748748
responsibility to ensure that all whitespace and metacharacters are
@@ -751,6 +751,14 @@ quoted appropriately to avoid
751751
vulnerabilities. On :ref:`some platforms <shlex-quote-warning>`, it is possible
752752
to use :func:`shlex.quote` for this escaping.
753753

754+
On Windows, batch files (:file:`*.bat` or :file:`*.cmd`) may be launched by the
755+
operating system in a system shell regardless of the arguments passed to this
756+
library. This could result in arguments being parsed according to shell rules,
757+
but without any escaping added by Python. If you are intentionally launching a
758+
batch file with arguments from untrusted sources, consider passing
759+
``shell=True`` to allow Python to escape special characters. See :gh:`114539`
760+
for additional discussion.
761+
754762

755763
Popen Objects
756764
-------------

Doc/whatsnew/3.10.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2363,3 +2363,12 @@ tarfile
23632363
:exc:`DeprecationWarning`.
23642364
In Python 3.14, the default will switch to ``'data'``.
23652365
(Contributed by Petr Viktorin in :pep:`706`.)
2366+
2367+
Notable changes in 3.10.15
2368+
==========================
2369+
2370+
ipaddress
2371+
---------
2372+
2373+
* Fixed ``is_global`` and ``is_private`` behavior in ``IPv4Address``,
2374+
``IPv6Address``, ``IPv4Network`` and ``IPv6Network``.

Lib/ipaddress.py

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

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

13401363
@property
@@ -1538,13 +1561,15 @@ class _IPv4Constants:
15381561

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

1564+
# Not globally reachable address blocks listed on
1565+
# https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
15411566
_private_networks = [
15421567
IPv4Network('0.0.0.0/8'),
15431568
IPv4Network('10.0.0.0/8'),
15441569
IPv4Network('127.0.0.0/8'),
15451570
IPv4Network('169.254.0.0/16'),
15461571
IPv4Network('172.16.0.0/12'),
1547-
IPv4Network('192.0.0.0/29'),
1572+
IPv4Network('192.0.0.0/24'),
15481573
IPv4Network('192.0.0.170/31'),
15491574
IPv4Network('192.0.2.0/24'),
15501575
IPv4Network('192.168.0.0/16'),
@@ -1555,6 +1580,11 @@ class _IPv4Constants:
15551580
IPv4Network('255.255.255.255/32'),
15561581
]
15571582

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

15601590
_unspecified_address = IPv4Address('0.0.0.0')
@@ -1996,27 +2026,42 @@ def is_site_local(self):
19962026
@property
19972027
@functools.lru_cache()
19982028
def is_private(self):
1999-
"""Test if this address is allocated for private networks.
2029+
"""``True`` if the address is defined as not globally reachable by
2030+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
2031+
(for IPv6) with the following exceptions:
20002032
2001-
Returns:
2002-
A boolean, True if the address is reserved per
2003-
iana-ipv6-special-registry, or is ipv4_mapped and is
2004-
reserved in the iana-ipv4-special-registry.
2033+
* ``is_private`` is ``False`` for ``100.64.0.0/10``
2034+
* For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
2035+
semantics of the underlying IPv4 addresses and the following condition holds
2036+
(see :attr:`IPv6Address.ipv4_mapped`)::
2037+
2038+
address.is_private == address.ipv4_mapped.is_private
20052039
2040+
``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
2041+
IPv4 range where they are both ``False``.
20062042
"""
20072043
ipv4_mapped = self.ipv4_mapped
20082044
if ipv4_mapped is not None:
20092045
return ipv4_mapped.is_private
2010-
return any(self in net for net in self._constants._private_networks)
2046+
return (
2047+
any(self in net for net in self._constants._private_networks)
2048+
and all(self not in net for net in self._constants._private_networks_exceptions)
2049+
)
20112050

20122051
@property
20132052
def is_global(self):
2014-
"""Test if this address is allocated for public networks.
2053+
"""``True`` if the address is defined as globally reachable by
2054+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
2055+
(for IPv6) with the following exception:
20152056
2016-
Returns:
2017-
A boolean, true if the address is not reserved per
2018-
iana-ipv6-special-registry.
2057+
For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
2058+
semantics of the underlying IPv4 addresses and the following condition holds
2059+
(see :attr:`IPv6Address.ipv4_mapped`)::
2060+
2061+
address.is_global == address.ipv4_mapped.is_global
20192062
2063+
``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
2064+
IPv4 range where they are both ``False``.
20202065
"""
20212066
return not self.is_private
20222067

@@ -2257,19 +2302,31 @@ class _IPv6Constants:
22572302

22582303
_multicast_network = IPv6Network('ff00::/8')
22592304

2305+
# Not globally reachable address blocks listed on
2306+
# https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
22602307
_private_networks = [
22612308
IPv6Network('::1/128'),
22622309
IPv6Network('::/128'),
22632310
IPv6Network('::ffff:0:0/96'),
2311+
IPv6Network('64:ff9b:1::/48'),
22642312
IPv6Network('100::/64'),
22652313
IPv6Network('2001::/23'),
2266-
IPv6Network('2001:2::/48'),
22672314
IPv6Network('2001:db8::/32'),
2268-
IPv6Network('2001:10::/28'),
2315+
# IANA says N/A, let's consider it not globally reachable to be safe
2316+
IPv6Network('2002::/16'),
22692317
IPv6Network('fc00::/7'),
22702318
IPv6Network('fe80::/10'),
22712319
]
22722320

2321+
_private_networks_exceptions = [
2322+
IPv6Network('2001:1::1/128'),
2323+
IPv6Network('2001:1::2/128'),
2324+
IPv6Network('2001:3::/32'),
2325+
IPv6Network('2001:4:112::/48'),
2326+
IPv6Network('2001:20::/28'),
2327+
IPv6Network('2001:30::/28'),
2328+
]
2329+
22732330
_reserved_networks = [
22742331
IPv6Network('::/8'), IPv6Network('100::/8'),
22752332
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix SSL tests CI for OpenSSL 3.1+

Modules/expat/expat.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
Copyright (c) 2022 Thijs Schreijer <[email protected]>
1919
Copyright (c) 2023 Hanno Böck <[email protected]>
2020
Copyright (c) 2023 Sony Corporation / Snild Dolkow <[email protected]>
21+
Copyright (c) 2024 Taichi Haradaguchi <[email protected]>
2122
Licensed under the MIT license:
2223
2324
Permission is hereby granted, free of charge, to any person obtaining
@@ -1042,7 +1043,7 @@ typedef struct {
10421043
XMLPARSEAPI(const XML_Feature *)
10431044
XML_GetFeatureList(void);
10441045

1045-
#if XML_GE == 1
1046+
#if defined(XML_DTD) || (defined(XML_GE) && XML_GE == 1)
10461047
/* Added in Expat 2.4.0 for XML_DTD defined and
10471048
* added in Expat 2.6.0 for XML_GE == 1. */
10481049
XMLPARSEAPI(XML_Bool)
@@ -1065,7 +1066,7 @@ XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled);
10651066
*/
10661067
#define XML_MAJOR_VERSION 2
10671068
#define XML_MINOR_VERSION 6
1068-
#define XML_MICRO_VERSION 0
1069+
#define XML_MICRO_VERSION 2
10691070

10701071
#ifdef __cplusplus
10711072
}

0 commit comments

Comments
 (0)