Skip to content

Commit 18afc1f

Browse files
authored
Merge branch '3.8' into pythongh-118486-3.8
2 parents c4b3f7a + 29c9728 commit 18afc1f

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
@@ -179,18 +179,53 @@ write code that handles both IP versions correctly. Address objects are
179179

180180
.. attribute:: is_private
181181

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

186208
.. attribute:: is_global
187209

188-
``True`` if the address is allocated for public networks. See
210+
``True`` if the address is defined as globally reachable by
189211
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
190-
(for IPv6).
212+
(for IPv6) with the following exception:
213+
214+
For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
215+
semantics of the underlying IPv4 addresses and the following condition holds
216+
(see :attr:`IPv6Address.ipv4_mapped`)::
217+
218+
address.is_global == address.ipv4_mapped.is_global
219+
220+
``is_global`` has value opposite to :attr:`is_private`, except for the shared address space
221+
(``100.64.0.0/10`` range) where they are both ``False``.
191222

192223
.. versionadded:: 3.4
193224

225+
.. versionchanged:: 3.8.20
226+
227+
Fixed some false positives and false negatives, see :attr:`is_private` for details.
228+
194229
.. attribute:: is_unspecified
195230

196231
``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
@@ -666,8 +666,8 @@ Exceptions defined in this module all inherit from :exc:`SubprocessError`.
666666
Security Considerations
667667
-----------------------
668668

669-
Unlike some other popen functions, this implementation will never
670-
implicitly call a system shell. This means that all characters,
669+
Unlike some other popen functions, this library will not
670+
implicitly choose to call a system shell. This means that all characters,
671671
including shell metacharacters, can safely be passed to child processes.
672672
If the shell is invoked explicitly, via ``shell=True``, it is the application's
673673
responsibility to ensure that all whitespace and metacharacters are
@@ -679,6 +679,14 @@ When using ``shell=True``, the :func:`shlex.quote` function can be
679679
used to properly escape whitespace and shell metacharacters in strings
680680
that are going to be used to construct shell commands.
681681

682+
On Windows, batch files (:file:`*.bat` or :file:`*.cmd`) may be launched by the
683+
operating system in a system shell regardless of the arguments passed to this
684+
library. This could result in arguments being parsed according to shell rules,
685+
but without any escaping added by Python. If you are intentionally launching a
686+
batch file with arguments from untrusted sources, consider passing
687+
``shell=True`` to allow Python to escape special characters. See :gh:`114539`
688+
for additional discussion.
689+
682690

683691
Popen Objects
684692
-------------

Doc/tools/susp-ignored.csv

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,14 @@ library/ipaddress,,:db00,2001:db00::0/24
158158
library/ipaddress,,::,2001:db00::0/24
159159
library/ipaddress,,:db00,2001:db00::0/ffff:ff00::
160160
library/ipaddress,,::,2001:db00::0/ffff:ff00::
161+
library/ipaddress,,:ff9b,64:ff9b:1::/48
162+
library/ipaddress,,::,64:ff9b:1::/48
163+
library/ipaddress,,::,2001::
164+
library/ipaddress,,::,2001:1::
165+
library/ipaddress,,::,2001:3::
166+
library/ipaddress,,::,2001:4:112::
167+
library/ipaddress,,::,2001:20::
168+
library/ipaddress,,::,2001:30::
161169
library/itertools,,:step,elements from seq[start:stop:step]
162170
library/itertools,,:stop,elements from seq[start:stop:step]
163171
library/logging.handlers,,:port,host:port

Doc/whatsnew/3.8.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2371,3 +2371,12 @@ tarfile
23712371
:exc:`DeprecationWarning`.
23722372
In Python 3.14, the default will switch to ``'data'``.
23732373
(Contributed by Petr Viktorin in :pep:`706`.)
2374+
2375+
Notable changes in 3.8.20
2376+
=========================
2377+
2378+
ipaddress
2379+
---------
2380+
2381+
* Fixed ``is_global`` and ``is_private`` behavior in ``IPv4Address``,
2382+
``IPv6Address``, ``IPv4Network`` and ``IPv6Network``.

Lib/ipaddress.py

Lines changed: 78 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,18 +1275,41 @@ def is_reserved(self):
12751275
@property
12761276
@functools.lru_cache()
12771277
def is_private(self):
1278-
"""Test if this address is allocated for private networks.
1278+
"""``True`` if the address is defined as not globally reachable by
1279+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
1280+
(for IPv6) with the following exceptions:
12791281
1280-
Returns:
1281-
A boolean, True if the address is reserved per
1282-
iana-ipv4-special-registry.
1282+
* ``is_private`` is ``False`` for ``100.64.0.0/10``
1283+
* For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
1284+
semantics of the underlying IPv4 addresses and the following condition holds
1285+
(see :attr:`IPv6Address.ipv4_mapped`)::
1286+
1287+
address.is_private == address.ipv4_mapped.is_private
12831288
1289+
``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
1290+
IPv4 range where they are both ``False``.
12841291
"""
1285-
return any(self in net for net in self._constants._private_networks)
1292+
return (
1293+
any(self in net for net in self._constants._private_networks)
1294+
and all(self not in net for net in self._constants._private_networks_exceptions)
1295+
)
12861296

12871297
@property
12881298
@functools.lru_cache()
12891299
def is_global(self):
1300+
"""``True`` if the address is defined as globally reachable by
1301+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
1302+
(for IPv6) with the following exception:
1303+
1304+
For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
1305+
semantics of the underlying IPv4 addresses and the following condition holds
1306+
(see :attr:`IPv6Address.ipv4_mapped`)::
1307+
1308+
address.is_global == address.ipv4_mapped.is_global
1309+
1310+
``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
1311+
IPv4 range where they are both ``False``.
1312+
"""
12901313
return self not in self._constants._public_network and not self.is_private
12911314

12921315
@property
@@ -1490,13 +1513,15 @@ class _IPv4Constants:
14901513

14911514
_public_network = IPv4Network('100.64.0.0/10')
14921515

1516+
# Not globally reachable address blocks listed on
1517+
# https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
14931518
_private_networks = [
14941519
IPv4Network('0.0.0.0/8'),
14951520
IPv4Network('10.0.0.0/8'),
14961521
IPv4Network('127.0.0.0/8'),
14971522
IPv4Network('169.254.0.0/16'),
14981523
IPv4Network('172.16.0.0/12'),
1499-
IPv4Network('192.0.0.0/29'),
1524+
IPv4Network('192.0.0.0/24'),
15001525
IPv4Network('192.0.0.170/31'),
15011526
IPv4Network('192.0.2.0/24'),
15021527
IPv4Network('192.168.0.0/16'),
@@ -1507,6 +1532,11 @@ class _IPv4Constants:
15071532
IPv4Network('255.255.255.255/32'),
15081533
]
15091534

1535+
_private_networks_exceptions = [
1536+
IPv4Network('192.0.0.9/32'),
1537+
IPv4Network('192.0.0.10/32'),
1538+
]
1539+
15101540
_reserved_network = IPv4Network('240.0.0.0/4')
15111541

15121542
_unspecified_address = IPv4Address('0.0.0.0')
@@ -1897,23 +1927,42 @@ def is_site_local(self):
18971927
@property
18981928
@functools.lru_cache()
18991929
def is_private(self):
1900-
"""Test if this address is allocated for private networks.
1930+
"""``True`` if the address is defined as not globally reachable by
1931+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
1932+
(for IPv6) with the following exceptions:
19011933
1902-
Returns:
1903-
A boolean, True if the address is reserved per
1904-
iana-ipv6-special-registry.
1934+
* ``is_private`` is ``False`` for ``100.64.0.0/10``
1935+
* For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
1936+
semantics of the underlying IPv4 addresses and the following condition holds
1937+
(see :attr:`IPv6Address.ipv4_mapped`)::
1938+
1939+
address.is_private == address.ipv4_mapped.is_private
19051940
1941+
``is_private`` has value opposite to :attr:`is_global`, except for the ``100.64.0.0/10``
1942+
IPv4 range where they are both ``False``.
19061943
"""
1907-
return any(self in net for net in self._constants._private_networks)
1944+
ipv4_mapped = self.ipv4_mapped
1945+
if ipv4_mapped is not None:
1946+
return ipv4_mapped.is_private
1947+
return (
1948+
any(self in net for net in self._constants._private_networks)
1949+
and all(self not in net for net in self._constants._private_networks_exceptions)
1950+
)
19081951

19091952
@property
19101953
def is_global(self):
1911-
"""Test if this address is allocated for public networks.
1954+
"""``True`` if the address is defined as globally reachable by
1955+
iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
1956+
(for IPv6) with the following exception:
19121957
1913-
Returns:
1914-
A boolean, true if the address is not reserved per
1915-
iana-ipv6-special-registry.
1958+
For IPv4-mapped IPv6-addresses the ``is_private`` value is determined by the
1959+
semantics of the underlying IPv4 addresses and the following condition holds
1960+
(see :attr:`IPv6Address.ipv4_mapped`)::
1961+
1962+
address.is_global == address.ipv4_mapped.is_global
19161963
1964+
``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
1965+
IPv4 range where they are both ``False``.
19171966
"""
19181967
return not self.is_private
19191968

@@ -2154,19 +2203,31 @@ class _IPv6Constants:
21542203

21552204
_multicast_network = IPv6Network('ff00::/8')
21562205

2206+
# Not globally reachable address blocks listed on
2207+
# https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
21572208
_private_networks = [
21582209
IPv6Network('::1/128'),
21592210
IPv6Network('::/128'),
21602211
IPv6Network('::ffff:0:0/96'),
2212+
IPv6Network('64:ff9b:1::/48'),
21612213
IPv6Network('100::/64'),
21622214
IPv6Network('2001::/23'),
2163-
IPv6Network('2001:2::/48'),
21642215
IPv6Network('2001:db8::/32'),
2165-
IPv6Network('2001:10::/28'),
2216+
# IANA says N/A, let's consider it not globally reachable to be safe
2217+
IPv6Network('2002::/16'),
21662218
IPv6Network('fc00::/7'),
21672219
IPv6Network('fe80::/10'),
21682220
]
21692221

2222+
_private_networks_exceptions = [
2223+
IPv6Network('2001:1::1/128'),
2224+
IPv6Network('2001:1::2/128'),
2225+
IPv6Network('2001:3::/32'),
2226+
IPv6Network('2001:4:112::/48'),
2227+
IPv6Network('2001:20::/28'),
2228+
IPv6Network('2001:30::/28'),
2229+
]
2230+
21702231
_reserved_networks = [
21712232
IPv6Network('::/8'), IPv6Network('100::/8'),
21722233
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
@@ -1761,6 +1761,10 @@ def testReservedIpv4(self):
17611761
self.assertEqual(True, ipaddress.ip_address(
17621762
'172.31.255.255').is_private)
17631763
self.assertEqual(False, ipaddress.ip_address('172.32.0.0').is_private)
1764+
self.assertFalse(ipaddress.ip_address('192.0.0.0').is_global)
1765+
self.assertTrue(ipaddress.ip_address('192.0.0.9').is_global)
1766+
self.assertTrue(ipaddress.ip_address('192.0.0.10').is_global)
1767+
self.assertFalse(ipaddress.ip_address('192.0.0.255').is_global)
17641768

17651769
self.assertEqual(True,
17661770
ipaddress.ip_address('169.254.100.200').is_link_local)
@@ -1776,6 +1780,40 @@ def testReservedIpv4(self):
17761780
self.assertEqual(False, ipaddress.ip_address('128.0.0.0').is_loopback)
17771781
self.assertEqual(True, ipaddress.ip_network('0.0.0.0').is_unspecified)
17781782

1783+
def testPrivateNetworks(self):
1784+
self.assertEqual(True, ipaddress.ip_network("0.0.0.0/0").is_private)
1785+
self.assertEqual(False, ipaddress.ip_network("1.0.0.0/8").is_private)
1786+
1787+
self.assertEqual(True, ipaddress.ip_network("0.0.0.0/8").is_private)
1788+
self.assertEqual(True, ipaddress.ip_network("10.0.0.0/8").is_private)
1789+
self.assertEqual(True, ipaddress.ip_network("127.0.0.0/8").is_private)
1790+
self.assertEqual(True, ipaddress.ip_network("169.254.0.0/16").is_private)
1791+
self.assertEqual(True, ipaddress.ip_network("172.16.0.0/12").is_private)
1792+
self.assertEqual(True, ipaddress.ip_network("192.0.0.0/29").is_private)
1793+
self.assertEqual(False, ipaddress.ip_network("192.0.0.9/32").is_private)
1794+
self.assertEqual(True, ipaddress.ip_network("192.0.0.170/31").is_private)
1795+
self.assertEqual(True, ipaddress.ip_network("192.0.2.0/24").is_private)
1796+
self.assertEqual(True, ipaddress.ip_network("192.168.0.0/16").is_private)
1797+
self.assertEqual(True, ipaddress.ip_network("198.18.0.0/15").is_private)
1798+
self.assertEqual(True, ipaddress.ip_network("198.51.100.0/24").is_private)
1799+
self.assertEqual(True, ipaddress.ip_network("203.0.113.0/24").is_private)
1800+
self.assertEqual(True, ipaddress.ip_network("240.0.0.0/4").is_private)
1801+
self.assertEqual(True, ipaddress.ip_network("255.255.255.255/32").is_private)
1802+
1803+
self.assertEqual(False, ipaddress.ip_network("::/0").is_private)
1804+
self.assertEqual(False, ipaddress.ip_network("::ff/128").is_private)
1805+
1806+
self.assertEqual(True, ipaddress.ip_network("::1/128").is_private)
1807+
self.assertEqual(True, ipaddress.ip_network("::/128").is_private)
1808+
self.assertEqual(True, ipaddress.ip_network("::ffff:0:0/96").is_private)
1809+
self.assertEqual(True, ipaddress.ip_network("100::/64").is_private)
1810+
self.assertEqual(True, ipaddress.ip_network("2001:2::/48").is_private)
1811+
self.assertEqual(False, ipaddress.ip_network("2001:3::/48").is_private)
1812+
self.assertEqual(True, ipaddress.ip_network("2001:db8::/32").is_private)
1813+
self.assertEqual(True, ipaddress.ip_network("2001:10::/28").is_private)
1814+
self.assertEqual(True, ipaddress.ip_network("fc00::/7").is_private)
1815+
self.assertEqual(True, ipaddress.ip_network("fe80::/10").is_private)
1816+
17791817
def testReservedIpv6(self):
17801818

17811819
self.assertEqual(True, ipaddress.ip_network('ffff::').is_multicast)
@@ -1849,6 +1887,20 @@ def testReservedIpv6(self):
18491887
self.assertEqual(True, ipaddress.ip_address('0::0').is_unspecified)
18501888
self.assertEqual(False, ipaddress.ip_address('::1').is_unspecified)
18511889

1890+
self.assertFalse(ipaddress.ip_address('64:ff9b:1::').is_global)
1891+
self.assertFalse(ipaddress.ip_address('2001::').is_global)
1892+
self.assertTrue(ipaddress.ip_address('2001:1::1').is_global)
1893+
self.assertTrue(ipaddress.ip_address('2001:1::2').is_global)
1894+
self.assertFalse(ipaddress.ip_address('2001:2::').is_global)
1895+
self.assertTrue(ipaddress.ip_address('2001:3::').is_global)
1896+
self.assertFalse(ipaddress.ip_address('2001:4::').is_global)
1897+
self.assertTrue(ipaddress.ip_address('2001:4:112::').is_global)
1898+
self.assertFalse(ipaddress.ip_address('2001:10::').is_global)
1899+
self.assertTrue(ipaddress.ip_address('2001:20::').is_global)
1900+
self.assertTrue(ipaddress.ip_address('2001:30::').is_global)
1901+
self.assertFalse(ipaddress.ip_address('2001:40::').is_global)
1902+
self.assertFalse(ipaddress.ip_address('2002::').is_global)
1903+
18521904
# some generic IETF reserved addresses
18531905
self.assertEqual(True, ipaddress.ip_address('100::').is_reserved)
18541906
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)