Skip to content

Commit 2da4e6a

Browse files
generatedunixname1734921407115435meta-codesync[bot]
authored andcommitted
Sync pre-release CPython 3.15 branch from GitHub (2026-05-15)
Summary: Imported python/cpython `3.15.0b1+` from upstream rev [`6136ad6`](https://www.github.com/python/cpython/commit/6136ad68010756edfc4474ad24793e62e1b9907e) (committed 2026-05-15 16:57:59+00:00). # Commit Info Base: (`3.15.0b1+`) - [`ed27363`](https://www.github.com/python/cpython/commit/ed27363ddf340577afd5b7b67e604489f5ee7aa3) (commit date: 2026-05-14 22:38:11+00:00) Imported: (`3.15.0b1+`) - [`6136ad6`](https://www.github.com/python/cpython/commit/6136ad68010756edfc4474ad24793e62e1b9907e) (commit date: 2026-05-15 16:57:59+00:00) # Noteworthy file changes - Low-signal files (6 added) (NEWS.d, docs, .github) - Other files (1 added) Complete list of added/removed files: https://www.internalfb.com/intern/everpaste/?color=0&handle=GPGjXCbboa-qYZAEAO6VnR1nazkTbr0LAAAz Differential Revision: D105416454 fbshipit-source-id: eac8a662bb6038451e448aea9eb1033348a60565
1 parent 9f2ed68 commit 2da4e6a

30 files changed

Lines changed: 440 additions & 196 deletions

Doc/library/codecs.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,7 +1155,7 @@ particular, the following variants typically exist:
11551155
+-----------------+--------------------------------+--------------------------------+
11561156
| cp857 | 857, IBM857 | Turkish |
11571157
+-----------------+--------------------------------+--------------------------------+
1158-
| cp858 | 858, IBM858 | Western Europe |
1158+
| cp858 | 858, IBM00858 | Western Europe |
11591159
+-----------------+--------------------------------+--------------------------------+
11601160
| cp860 | 860, IBM860 | Portuguese |
11611161
+-----------------+--------------------------------+--------------------------------+
@@ -1192,7 +1192,7 @@ particular, the following variants typically exist:
11921192
| | | |
11931193
| | | .. versionadded:: 3.4 |
11941194
+-----------------+--------------------------------+--------------------------------+
1195-
| cp1140 | ibm1140 | Western Europe |
1195+
| cp1140 | IBM01140 | Western Europe |
11961196
+-----------------+--------------------------------+--------------------------------+
11971197
| cp1250 | windows-1250 | Central and Eastern Europe |
11981198
+-----------------+--------------------------------+--------------------------------+

Doc/library/select.rst

Lines changed: 135 additions & 125 deletions
Large diffs are not rendered by default.

Doc/tools/extensions/pydoc_topics.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"in",
6969
"integers",
7070
"lambda",
71+
"lazy",
7172
"lists",
7273
"naming",
7374
"nonlocal",

Lib/encodings/aliases.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@
7171

7272
# cp1140 codec
7373
'1140' : 'cp1140',
74+
'cp01140' : 'cp1140',
75+
'csibm01140' : 'cp1140',
76+
'ebcdic_us_37_euro' : 'cp1140',
77+
'ibm01140' : 'cp1140',
7478
'ibm1140' : 'cp1140',
7579

7680
# cp1250 codec
@@ -159,8 +163,12 @@
159163

160164
# cp858 codec
161165
'858' : 'cp858',
166+
'cp00858' : 'cp858',
167+
'csibm00858' : 'cp858',
162168
'csibm858' : 'cp858',
169+
'ibm00858' : 'cp858',
163170
'ibm858' : 'cp858',
171+
'pc_multilingual_850_euro' : 'cp858',
164172

165173
# cp860 codec
166174
'860' : 'cp860',

Lib/ftplib.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,16 @@ def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
883883
type = 'TYPE ' + type
884884
source.voidcmd(type)
885885
target.voidcmd(type)
886-
sourcehost, sourceport = parse227(source.sendcmd('PASV'))
886+
# Don't trust the IPv4 address the source server advertises in its PASV
887+
# reply: a malicious source could otherwise point the target's data
888+
# connection at an arbitrary host (SSRF). A caller that needs the old
889+
# behavior can set trust_server_pasv_ipv4_address on the source FTP
890+
# object. See FTP.makepasv(), which applies the same rule.
891+
untrusted_host, sourceport = parse227(source.sendcmd('PASV'))
892+
if source.trust_server_pasv_ipv4_address:
893+
sourcehost = untrusted_host
894+
else:
895+
sourcehost = source.sock.getpeername()[0]
887896
target.sendport(sourcehost, sourceport)
888897
# RFC 959: the user must "listen" [...] BEFORE sending the
889898
# transfer request.

Lib/pydoc.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1845,6 +1845,7 @@ class Helper:
18451845
'in': ('in', 'SEQUENCEMETHODS'),
18461846
'is': 'COMPARISON',
18471847
'lambda': ('lambda', 'FUNCTIONS'),
1848+
'lazy': ('lazy', 'MODULES'),
18481849
'nonlocal': ('nonlocal', 'global NAMESPACES'),
18491850
'not': 'BOOLEAN',
18501851
'or': 'BOOLEAN',

Lib/test/test_ctypes/test_parameters.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import sys
22
import unittest
33
import test.support
4+
import ctypes
45
from ctypes import (CDLL, PyDLL, ArgumentError,
56
Structure, Array, Union,
67
_Pointer, _SimpleCData, _CFuncPtr,
@@ -247,6 +248,13 @@ def test_parameter_repr(self):
247248
self.assertRegex(repr(c_char_p.from_param(b'hihi')), r"^<cparam 'z' \(0x[A-Fa-f0-9]+\)>$")
248249
self.assertRegex(repr(c_wchar_p.from_param('hihi')), r"^<cparam 'Z' \(0x[A-Fa-f0-9]+\)>$")
249250
self.assertRegex(repr(c_void_p.from_param(0x12)), r"^<cparam 'P' \(0x0*12\)>$")
251+
if hasattr(ctypes, 'c_double_complex'):
252+
self.assertRegex(repr(ctypes.c_double_complex.from_param(0)),
253+
r"^<cparam 'Zd' at 0x[A-Fa-f0-9]+>$")
254+
self.assertRegex(repr(ctypes.c_float_complex.from_param(0)),
255+
r"^<cparam 'Zf' at 0x[A-Fa-f0-9]+>$")
256+
self.assertRegex(repr(ctypes.c_longdouble_complex.from_param(0)),
257+
r"^<cparam 'Zg' at 0x[A-Fa-f0-9]+>$")
250258

251259
@test.support.cpython_only
252260
def test_from_param_result_refcount(self):

Lib/test/test_ftplib.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
except ImportError:
1717
ssl = None
1818

19-
from unittest import TestCase, skipUnless
19+
from unittest import mock, TestCase, skipUnless
2020
from test import support
2121
from test.support import requires_subprocess
2222
from test.support import threading_helper
@@ -1145,6 +1145,40 @@ def testTimeoutDirectAccess(self):
11451145
ftp.close()
11461146

11471147

1148+
class TestFtpcpSecurity(TestCase):
1149+
"""ftpcp() must not trust the host a source server advertises in PASV.
1150+
1151+
A malicious source server can otherwise redirect the target server's
1152+
data connection to an arbitrary host:port (SSRF), so ftpcp() uses the
1153+
source server's actual peer address instead, the same as FTP.makepasv().
1154+
"""
1155+
1156+
def _make_pair(self, *, advertised_host, real_host, trust=False):
1157+
source = mock.Mock(spec=ftplib.FTP)
1158+
source.trust_server_pasv_ipv4_address = trust
1159+
source.sock.getpeername.return_value = (real_host, 21)
1160+
# PASV replies give the host as comma-separated octets, not dotted.
1161+
advertised = advertised_host.replace('.', ',')
1162+
source.sendcmd.side_effect = lambda cmd: (
1163+
f'227 Entering Passive Mode ({advertised},1,2).'
1164+
if cmd == 'PASV' else '150 ok')
1165+
target = mock.Mock(spec=ftplib.FTP)
1166+
target.sendcmd.return_value = '150 ok'
1167+
return source, target
1168+
1169+
def test_ftpcp_ignores_untrusted_pasv_host(self):
1170+
source, target = self._make_pair(advertised_host='10.0.0.5',
1171+
real_host='198.51.100.7')
1172+
ftplib.ftpcp(source, 'a', target, 'b')
1173+
target.sendport.assert_called_once_with('198.51.100.7', 258)
1174+
1175+
def test_ftpcp_trust_server_pasv_ipv4_address(self):
1176+
source, target = self._make_pair(advertised_host='10.0.0.5',
1177+
real_host='198.51.100.7', trust=True)
1178+
ftplib.ftpcp(source, 'a', target, 'b')
1179+
target.sendport.assert_called_once_with('10.0.0.5', 258)
1180+
1181+
11481182
class MiscTestCase(TestCase):
11491183
def test__all__(self):
11501184
not_exported = {

Lib/test/test_pydoc/test_pydoc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2172,7 +2172,7 @@ def mock_getline(prompt):
21722172

21732173
def test_keywords(self):
21742174
self.assertEqual(sorted(pydoc.Helper.keywords),
2175-
sorted(keyword.kwlist))
2175+
sorted(keyword.kwlist + ['lazy']))
21762176

21772177
def test_interact_empty_line_continues(self):
21782178
# gh-138568: test pressing Enter without input should continue in help session

Lib/test/test_venv.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -301,9 +301,9 @@ def test_sysconfig(self):
301301
self.assertEqual(out.strip(), expected, err)
302302
for attr, expected in (
303303
('executable', self.envpy()),
304-
# Usually compare to sys.executable, but if we're running in our own
305-
# venv then we really need to compare to our base executable
306-
('_base_executable', sys._base_executable),
304+
# Usually compare to sys.prefix, but if we're running in our own
305+
# venv then we really need to compare to our base prefix
306+
('base_prefix', sys.base_prefix),
307307
):
308308
with self.subTest(attr):
309309
cmd[2] = f'import sys; print(sys.{attr})'
@@ -916,10 +916,10 @@ def test_venvwlauncher(self):
916916
exename = exename.replace("python", "pythonw")
917917
envpyw = os.path.join(self.env_dir, self.bindir, exename)
918918
try:
919-
subprocess.check_call([envpyw, "-c", "import sys; "
920-
"assert sys._base_executable.endswith('%s')" % exename])
919+
subprocess.check_call([envpyw, "-c", "import fnmatch, sys; "
920+
"assert fnmatch.fnmatch(sys._base_executable, '**/pythonw*.exe')"])
921921
except subprocess.CalledProcessError:
922-
self.fail("venvwlauncher.exe did not run %s" % exename)
922+
self.fail("venvwlauncher.exe did not run pythonw.exe")
923923

924924

925925
@requireVenvCreate

0 commit comments

Comments
 (0)