Skip to content

Commit 9fd87d2

Browse files
Merge branch 'main' into openbsd-not-multiarch
2 parents af15f2f + 8a4ee5c commit 9fd87d2

6 files changed

Lines changed: 103 additions & 19 deletions

File tree

.github/workflows/build.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,12 @@ jobs:
245245
os: ${{ matrix.os }}
246246
test-opts: ${{ matrix.test-opts || '' }}
247247

248+
build-ubuntu-installed:
249+
name: 'Ubuntu (installed)'
250+
needs: build-context
251+
if: needs.build-context.outputs.run-ubuntu == 'true'
252+
uses: ./.github/workflows/reusable-install.yml
253+
248254
build-ubuntu-ssltests:
249255
name: 'Ubuntu SSL tests'
250256
runs-on: ${{ matrix.os }}
@@ -636,6 +642,7 @@ jobs:
636642
- build-windows
637643
- build-macos
638644
- build-ubuntu
645+
- build-ubuntu-installed
639646
- build-ubuntu-ssltests
640647
- build-ios
641648
- build-emscripten
@@ -685,6 +692,7 @@ jobs:
685692
!fromJSON(needs.build-context.outputs.run-ubuntu)
686693
&& '
687694
build-ubuntu,
695+
build-ubuntu-installed,
688696
build-ubuntu-ssltests,
689697
test-hypothesis,
690698
build-asan,
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Reusable Ubuntu (installed)
2+
3+
on:
4+
workflow_call:
5+
6+
permissions:
7+
contents: read
8+
9+
env:
10+
FORCE_COLOR: 1
11+
12+
jobs:
13+
build-install-test:
14+
name: build, install and test
15+
runs-on: ubuntu-26.04-arm
16+
timeout-minutes: 60
17+
env:
18+
PYTHONSTRICTEXTENSIONBUILD: 1
19+
steps:
20+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
21+
with:
22+
persist-credentials: false
23+
- name: Register gcc problem matcher
24+
run: echo "::add-matcher::.github/problem-matchers/gcc.json"
25+
- name: Set install dir
26+
run:
27+
echo "INSTALL_DIR=$(realpath "${GITHUB_WORKSPACE}/../installed-python")" >> "$GITHUB_ENV"
28+
- name: Install dependencies
29+
run: sudo ./.github/workflows/posix-deps-apt.sh
30+
- name: Configure CPython
31+
run: ./configure --config-cache --prefix="$INSTALL_DIR"
32+
- name: Build CPython
33+
run: make -j
34+
- name: Install CPython
35+
run: make install
36+
- name: Set installed interpreter
37+
run: |
38+
ldversion=$(./python -c 'import sysconfig; print(sysconfig.get_config_var("LDVERSION"))')
39+
echo "INSTALLED_PYTHON=${INSTALL_DIR}/bin/python${ldversion}" >> "$GITHUB_ENV"
40+
- name: Display build info
41+
run: |
42+
"$INSTALLED_PYTHON" -m test.pythoninfo
43+
- name: Test the installed Python
44+
run: xvfb-run "$INSTALLED_PYTHON" -m test --fast-ci --timeout=900

Doc/library/asyncio-task.rst

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -668,12 +668,12 @@ Eager task factory
668668
Shielding from cancellation
669669
===========================
670670

671-
.. awaitablefunction:: shield(aw)
671+
.. awaitablefunction:: shield(arg)
672672

673673
Protect an :ref:`awaitable object <asyncio-awaitables>`
674674
from being :meth:`cancelled <Task.cancel>`.
675675

676-
If *aw* is a coroutine it is automatically scheduled as a Task.
676+
If *arg* is a coroutine it is automatically scheduled as a Task.
677677

678678
The statement::
679679

@@ -714,7 +714,7 @@ Shielding from cancellation
714714
Removed the *loop* parameter.
715715

716716
.. deprecated:: 3.10
717-
Deprecation warning is emitted if *aw* is not Future-like object
717+
Deprecation warning is emitted if *arg* is not Future-like object
718718
and there is no running event loop.
719719

720720

@@ -837,13 +837,13 @@ Timeouts
837837

838838
.. versionadded:: 3.11
839839

840-
.. function:: wait_for(aw, timeout)
840+
.. function:: wait_for(fut, timeout)
841841
:async:
842842

843-
Wait for the *aw* :ref:`awaitable <asyncio-awaitables>`
843+
Wait for the *fut* :ref:`awaitable <asyncio-awaitables>`
844844
to complete with a timeout.
845845

846-
If *aw* is a coroutine it is automatically scheduled as a Task.
846+
If *fut* is a coroutine it is automatically scheduled as a Task.
847847

848848
*timeout* can either be ``None`` or a float or int number of seconds
849849
to wait for. If *timeout* is ``None``, block until the future
@@ -859,7 +859,7 @@ Timeouts
859859
so the total wait time may exceed the *timeout*. If an exception
860860
happens during cancellation, it is propagated.
861861

862-
If the wait is cancelled, the future *aw* is also cancelled.
862+
If the wait is cancelled, the future *fut* is also cancelled.
863863

864864
.. _asyncio_example_waitfor:
865865

@@ -884,8 +884,8 @@ Timeouts
884884
# timeout!
885885

886886
.. versionchanged:: 3.7
887-
When *aw* is cancelled due to a timeout, ``wait_for`` waits
888-
for *aw* to be cancelled. Previously, it raised
887+
When *fut* is cancelled due to a timeout, ``wait_for`` waits
888+
for *fut* to be cancelled. Previously, it raised
889889
:exc:`TimeoutError` immediately.
890890

891891
.. versionchanged:: 3.10
@@ -898,20 +898,20 @@ Timeouts
898898
Waiting primitives
899899
==================
900900

901-
.. function:: wait(aws, *, timeout=None, return_when=ALL_COMPLETED)
901+
.. function:: wait(fs, *, timeout=None, return_when=ALL_COMPLETED)
902902
:async:
903903

904-
Run :class:`~asyncio.Future` and :class:`~asyncio.Task` instances in the *aws*
904+
Run :class:`~asyncio.Future` and :class:`~asyncio.Task` instances in the *fs*
905905
iterable concurrently and block until the condition specified
906906
by *return_when*.
907907

908-
The *aws* iterable must not be empty.
908+
The *fs* iterable must not be empty.
909909

910910
Returns two sets of Tasks/Futures: ``(done, pending)``.
911911

912912
Usage::
913913

914-
done, pending = await asyncio.wait(aws)
914+
done, pending = await asyncio.wait(fs)
915915

916916
*timeout* (a float or int), if specified, can be used to control
917917
the maximum number of seconds to wait before returning.
@@ -943,7 +943,7 @@ Waiting primitives
943943
Unlike :func:`~asyncio.wait_for`, ``wait()`` does not cancel the
944944
futures when a timeout occurs.
945945

946-
If ``wait()`` is cancelled, the futures in *aws* are not cancelled
946+
If ``wait()`` is cancelled, the futures in *fs* are not cancelled
947947
and continue to run.
948948

949949
.. versionchanged:: 3.10
@@ -956,9 +956,9 @@ Waiting primitives
956956
Added support for generators yielding tasks.
957957

958958

959-
.. function:: as_completed(aws, *, timeout=None)
959+
.. function:: as_completed(fs, *, timeout=None)
960960

961-
Run :ref:`awaitable objects <asyncio-awaitables>` in the *aws* iterable
961+
Run :ref:`awaitable objects <asyncio-awaitables>` in the *fs* iterable
962962
concurrently. The returned object can be iterated to obtain the results
963963
of the awaitables as they finish.
964964

@@ -1011,7 +1011,7 @@ Waiting primitives
10111011
Removed the *loop* parameter.
10121012

10131013
.. deprecated:: 3.10
1014-
Deprecation warning is emitted if not all awaitable objects in the *aws*
1014+
Deprecation warning is emitted if not all awaitable objects in the *fs*
10151015
iterable are Future-like objects and there is no running event loop.
10161016

10171017
.. versionchanged:: 3.12

Lib/subprocess.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,8 +1688,9 @@ def _execute_child(self, args, executable, preexec_fn, close_fds,
16881688
close_fds = False
16891689

16901690
if shell:
1691-
startupinfo.dwFlags |= _winapi.STARTF_USESHOWWINDOW
1692-
startupinfo.wShowWindow = _winapi.SW_HIDE
1691+
if not startupinfo.dwFlags & _winapi.STARTF_USESHOWWINDOW:
1692+
startupinfo.dwFlags |= _winapi.STARTF_USESHOWWINDOW
1693+
startupinfo.wShowWindow = _winapi.SW_HIDE
16931694
if not executable:
16941695
# gh-101283: without a fully-qualified path, before Windows
16951696
# checks the system directories, it first looks in the

Lib/test/test_subprocess.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3777,6 +3777,34 @@ def test_startupinfo_copy(self):
37773777
self.assertEqual(startupinfo.wShowWindow, subprocess.SW_HIDE)
37783778
self.assertEqual(startupinfo.lpAttributeList, {"handle_list": []})
37793779

3780+
def test_startupinfo_shell_show_window(self):
3781+
# gh-85028: shell=True must not override wShowWindow set by the caller
3782+
import _winapi
3783+
SW_MAXIMIZE = 3
3784+
used = []
3785+
create_process = _winapi.CreateProcess
3786+
3787+
def spy(*args):
3788+
# The startup info is the last argument of CreateProcess()
3789+
used.append(args[-1])
3790+
return create_process(*args)
3791+
3792+
startupinfo = subprocess.STARTUPINFO(
3793+
dwFlags=subprocess.STARTF_USESHOWWINDOW,
3794+
wShowWindow=SW_MAXIMIZE)
3795+
with mock.patch.object(_winapi, 'CreateProcess', spy):
3796+
rc = subprocess.call(ZERO_RETURN_CMD, shell=True,
3797+
startupinfo=startupinfo)
3798+
self.assertEqual(rc, 0)
3799+
rc = subprocess.call(ZERO_RETURN_CMD, shell=True)
3800+
self.assertEqual(rc, 0)
3801+
3802+
requested, default = used
3803+
self.assertEqual(requested.wShowWindow, SW_MAXIMIZE)
3804+
# Without STARTF_USESHOWWINDOW the shell window is still hidden.
3805+
self.assertTrue(default.dwFlags & subprocess.STARTF_USESHOWWINDOW)
3806+
self.assertEqual(default.wShowWindow, subprocess.SW_HIDE)
3807+
37803808
# CREATE_NEW_CONSOLE creates a "popup" window.
37813809
@support.requires_resource('gui')
37823810
def test_creationflags(self):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:class:`subprocess.Popen` with ``shell=True`` on Windows now honors
2+
``wShowWindow`` of the *startupinfo* argument, so the console window of the
3+
started program can be shown. Previously it was always hidden.

0 commit comments

Comments
 (0)