Skip to content

Commit db33117

Browse files
Merge branch 'main' into gh-159041-asyncio-task-finalizer
2 parents da6789b + e96d943 commit db33117

25 files changed

Lines changed: 459 additions & 136 deletions

File tree

‎.github/workflows/build.yml‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,18 +346,28 @@ jobs:
346346
run: python3 Platforms/Android ci --fast-ci ${{ matrix.arch }}-linux-android
347347

348348
build-ios:
349-
name: iOS
349+
name: >-
350+
iOS
351+
${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }}
350352
needs: build-context
351353
if: needs.build-context.outputs.run-ios == 'true'
352354
timeout-minutes: 60
353355
runs-on: macos-26
356+
strategy:
357+
fail-fast: false
358+
matrix:
359+
free-threading:
360+
- false
361+
- true
354362
steps:
355363
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
356364
with:
357365
persist-credentials: false
358366

359367
- name: Build and test
360-
run: python3 Platforms/Apple ci iOS --fast-ci
368+
run: >-
369+
python3 Platforms/Apple ci iOS --fast-ci
370+
${{ fromJSON(matrix.free-threading) && '--abiflags t' || '' }}
361371
362372
build-emscripten:
363373
name: 'Emscripten'

‎.gitignore‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ Lib/test/data/*
7676
Apple/iOS/Frameworks/
7777
Apple/iOS/Resources/Info.plist
7878
Apple/testbed/build
79-
Apple/testbed/Python.xcframework/*/bin
80-
Apple/testbed/Python.xcframework/*/include
81-
Apple/testbed/Python.xcframework/*/lib
82-
Apple/testbed/Python.xcframework/*/Python.framework
79+
Platforms/Apple/testbed/Python*.xcframework/*/bin
80+
Platforms/Apple/testbed/Python*.xcframework/*/include
81+
Platforms/Apple/testbed/Python*.xcframework/*/lib
82+
Platforms/Apple/testbed/Python*.xcframework/*/Python*.framework
8383
Apple/testbed/*Testbed.xcodeproj/project.xcworkspace
8484
Apple/testbed/*Testbed.xcodeproj/xcuserdata
8585
Mac/Makefile

‎Lib/http/client.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,10 @@ def begin(self, *, _max_headers=None):
397397
# NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked"
398398
self.length = None
399399
length = self.headers.get("content-length")
400+
if length is not None:
401+
# RFC 9112, section 5.1: optional whitespace (SP / HTAB) around
402+
# the field value is not part of the value.
403+
length = length.strip(' \t')
400404
if length and not self.chunked and _is_legal_content_length(length):
401405
self.length = int(length)
402406
else:

‎Lib/test/test_ast/test_ast.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,13 +1107,11 @@ def next(self):
11071107
enum._test_simple_enum(_Precedence, _ast_unparse._Precedence)
11081108

11091109
@support.cpython_only
1110-
@support.run_with_limited_c_stack(
1111-
100_000 if sys.platform == "android" else 500_000)
1110+
@support.run_with_limited_c_stack(500_000)
11121111
@skip_wasi_stack_overflow()
11131112
@skip_emscripten_stack_overflow()
11141113
def test_ast_recursion_limit(self):
1115-
# Android test devices have less memory.
1116-
crash_depth = 100_000 if sys.platform == "android" else 500_000
1114+
crash_depth = 500_000
11171115
success_depth = 200
11181116
if _testinternalcapi is not None:
11191117
remaining = _testinternalcapi.get_c_recursion_remaining()

‎Lib/test/test_cmd_line.py‎

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -176,35 +176,24 @@ def get_xoptions(*args):
176176
self.assertEqual(opts, {'a': True, 'b': 'c,d=e'})
177177

178178
def test_showrefcount(self):
179-
def run_python(*args):
180-
# this is similar to assert_python_ok but doesn't strip
181-
# the refcount from stderr. It can be replaced once
182-
# assert_python_ok stops doing that.
183-
cmd = [sys.executable]
184-
cmd.extend(args)
185-
PIPE = subprocess.PIPE
186-
p = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE)
187-
out, err = p.communicate()
188-
p.stdout.close()
189-
p.stderr.close()
190-
rc = p.returncode
191-
self.assertEqual(rc, 0)
192-
return rc, out, err
193-
code = 'import sys; print(sys._xoptions)'
194179
# normally the refcount is hidden
195-
rc, out, err = run_python('-c', code)
196-
self.assertEqual(out.rstrip(), b'{}')
197-
self.assertEqual(err, b'')
180+
# (assert_python_ok() enables faulthandler)
181+
code = 'import sys; print(sys._xoptions)'
182+
proc = assert_python_ok('-c', code)
183+
self.assertEqual(proc.out.rstrip(), b"{'faulthandler': True}")
184+
self.assertEqual(proc.err, b'')
185+
198186
# "-X showrefcount" shows the refcount, but only in debug builds
199-
rc, out, err = run_python('-I', '-X', 'showrefcount', '-c', code)
200-
self.assertEqual(out.rstrip(), b"{'showrefcount': True}")
187+
proc = assert_python_ok('-I', '-X', 'showrefcount', '-c', code)
188+
self.assertEqual(proc.out.rstrip(),
189+
b"{'faulthandler': True, 'showrefcount': True}")
201190
if support.Py_DEBUG:
202191
# bpo-46417: Tolerate negative reference count which can occur
203192
# because of bugs in C extensions. This test is only about checking
204193
# the showrefcount feature.
205-
self.assertRegex(err, br'^\[-?\d+ refs, \d+ blocks\]')
194+
self.assertRegex(proc.err, br'^\[-?\d+ refs, \d+ blocks\]')
206195
else:
207-
self.assertEqual(err, b'')
196+
self.assertEqual(proc.err, b'')
208197

209198
@support.cpython_only
210199
def test_xoption_frozen_modules(self):

‎Lib/test/test_compile.py‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -773,14 +773,12 @@ def test_yet_more_evil_still_undecodable(self):
773773

774774
@support.cpython_only
775775
@unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
776-
@support.run_with_limited_c_stack(
777-
100_000 if sys.platform == "android" else 500_000)
776+
@support.run_with_limited_c_stack(500_000)
778777
@support.skip_emscripten_stack_overflow()
779778
def test_compiler_recursion_limit(self):
780779
# Compiler frames are small
781780
limit = 100
782-
# Android test devices have less memory.
783-
crash_depth = limit * (1000 if sys.platform == "android" else 5000)
781+
crash_depth = limit * 5000
784782
success_depth = limit
785783

786784
def check_limit(prefix, repeated, mode="single"):
@@ -1092,8 +1090,7 @@ def test_path_like_objects(self):
10921090
# complex statements.
10931091
@support.requires_resource('cpu')
10941092
def test_stack_overflow(self):
1095-
# Android test devices have less memory.
1096-
size = 100_000 if sys.platform == "android" else 200_000
1093+
size = 200_000
10971094
compile("if a: b\n" * size, "<dummy>", "exec")
10981095

10991096
# Multiple users rely on the fact that CPython does not generate

‎Lib/test/test_httplib.py‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1351,7 +1351,7 @@ def test_negative_content_length(self):
13511351
def test_malformed_content_length(self):
13521352
# RFC 9112: Content-Length = 1*DIGIT. Values that int() accepts but
13531353
# the grammar forbids must not be used to frame the body.
1354-
for value in ('+5', '5_0'):
1354+
for value in ('+5', '5_0', '5 0'):
13551355
with self.subTest(value=value):
13561356
sock = FakeSocket(
13571357
'HTTP/1.1 200 OK\r\nContent-Length: %s\r\n\r\nHello\r\n' % value)
@@ -1361,6 +1361,22 @@ def test_malformed_content_length(self):
13611361
self.assertEqual(resp.read(), b'Hello\r\n')
13621362
resp.close()
13631363

1364+
def test_content_length_with_whitespace(self):
1365+
# RFC 9112, section 5.1: optional whitespace (SP / HTAB) around
1366+
# the field value is not part of the value, so it must not
1367+
# prevent the Content-Length from being used to frame the body.
1368+
for value in ('7 ', '7\t', '7 \t ', ' 7 '):
1369+
with self.subTest(value=value):
1370+
sock = FakeSocket(
1371+
'HTTP/1.1 200 OK\r\nContent-Length: %s\r\n\r\n'
1372+
'Hello\r\nextra' % value)
1373+
resp = client.HTTPResponse(sock, method="GET")
1374+
resp.begin()
1375+
self.assertEqual(resp.length, 7)
1376+
self.assertEqual(resp.read(), b'Hello\r\n')
1377+
self.assertTrue(resp.isclosed())
1378+
resp.close()
1379+
13641380
def test_malformed_chunk_size(self):
13651381
# RFC 9112: chunk-size = 1*HEXDIG. Reject sizes that int(_, 16) accepts
13661382
# but the grammar forbids (a sign, an "0x" prefix, underscores or

‎Lib/test/test_zipapp.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,24 @@ def test_shebang_is_executable(self):
366366
zipapp.create_archive(str(source), str(target), interpreter='python')
367367
self.assertTrue(target.stat().st_mode & stat.S_IEXEC)
368368

369+
@unittest.skipIf(sys.platform == 'win32',
370+
'Windows does not support an executable bit')
371+
@os_helper.skip_unless_working_chmod
372+
def test_copied_archive_with_pathlike_target_is_executable(self):
373+
# Test that copying an archive to a PathLike target makes it executable.
374+
source = self.tmpdir / 'source'
375+
source.mkdir()
376+
(source / '__main__.py').touch()
377+
target = self.tmpdir / 'source.pyz'
378+
zipapp.create_archive(source, target, interpreter='python')
379+
# Copying an archive uses a different code path than creating
380+
# one from scratch. Ensure that the executable bit is set
381+
# even if the target is a path-like object.
382+
# See https://github.com/python/cpython/issues/156568
383+
new_target = self.tmpdir / 'changed.pyz'
384+
zipapp.create_archive(target, new_target, interpreter='python')
385+
self.assertTrue(new_target.stat().st_mode & stat.S_IEXEC)
386+
369387
@unittest.skipIf(sys.platform == 'win32',
370388
'Windows does not support an executable bit')
371389
def test_no_shebang_is_not_executable(self):

‎Lib/zipapp.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def _copy_archive(archive, new_archive, interpreter=None):
6969
dst.write(first_2)
7070
shutil.copyfileobj(src, dst)
7171

72-
if interpreter and isinstance(new_archive, str):
72+
if interpreter and isinstance(new_archive, (str, os.PathLike)):
7373
os.chmod(new_archive, os.stat(new_archive).st_mode | stat.S_IEXEC)
7474

7575

‎Makefile.pre.in‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2286,7 +2286,9 @@ testuniversal: all
22862286
# Run the test suite on the iOS simulator. Must be run on a macOS machine with
22872287
# a full Xcode install that has an iPhone SE (3rd edition) simulator available.
22882288
# This must be run *after* a `make install` has completed the build. The
2289-
# `--with-framework-name` argument *cannot* be used when configuring the build.
2289+
# testbed is configured for the framework name used to configure the build;
2290+
# that name is expected to be `Python`, optionally followed by uppercased ABI
2291+
# flags (e.g., `PythonT`).
22902292
XCFOLDER:=iOSTestbed.$(MULTIARCH).$(shell date +%s).$$PPID
22912293
.PHONY: testios
22922294
testios:
@@ -2298,10 +2300,6 @@ testios:
22982300
echo "Cannot run the iOS testbed for non-simulator builds."; \
22992301
exit 1;\
23002302
fi
2301-
@if test $(PYTHONFRAMEWORK) != "Python"; then \
2302-
echo "Cannot run the iOS testbed with a non-default framework name."; \
2303-
exit 1;\
2304-
fi
23052303
@if ! test -d $(PYTHONFRAMEWORKPREFIX); then \
23062304
echo "Cannot find a finalized iOS Python.framework. Have you run 'make install' to finalize the framework build?"; \
23072305
exit 1;\

0 commit comments

Comments
 (0)