Skip to content

Commit 4f213ab

Browse files
authored
Merge pull request #1270 from moreati/stable
Release 0.3.24
2 parents 0eb7ee2 + 1b137f1 commit 4f213ab

28 files changed

+104
-45
lines changed

.github/workflows/tests.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ name: Tests
44

55
# env:
66
# ANSIBLE_VERBOSITY: 3
7-
# MITOGEN_LOG_LEVEL: DEBUG
7+
# MITOGEN_LOG_LEVEL: DEBUG
88

99
on:
1010
pull_request:
@@ -19,6 +19,7 @@ jobs:
1919
name: u2204 ${{ matrix.tox_env }}
2020
# https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md
2121
runs-on: ubuntu-22.04
22+
timeout-minutes: 25
2223

2324
strategy:
2425
fail-fast: false
@@ -140,6 +141,7 @@ jobs:
140141
name: u2404 ${{ matrix.tox_env }}
141142
# https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md
142143
runs-on: ubuntu-24.04
144+
timeout-minutes: 25
143145

144146
strategy:
145147
fail-fast: false

ansible_mitogen/target.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import logging
4343
import operator
4444
import os
45+
import pty
4546
import pwd
4647
import re
4748
import signal
@@ -120,7 +121,7 @@ def subprocess__Popen__close_fds(self, but):
120121
continue
121122

122123
fd = int(name, 10)
123-
if fd > 2 and fd != but:
124+
if fd > pty.STDERR_FILENO and fd != but:
124125
try:
125126
os.close(fd)
126127
except OSError:

docs/changelog.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ To avail of fixes in an unreleased version, please download a ZIP file
1818
`directly from GitHub <https://github.com/mitogen-hq/mitogen/>`_.
1919

2020

21+
v0.3.24 (2025-05-29)
22+
--------------------
23+
24+
* :gh:issue:`1268` :mod:`mitogen` Only close stdin, stdout, and stderr file
25+
descriptors (0, 1, and 2) if they were open at process startup.
26+
27+
2128
v0.3.23 (2025-04-28)
2229
--------------------
2330

mitogen/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636

3737
#: Library version as a tuple.
38-
__version__ = (0, 3, 23)
38+
__version__ = (0, 3, 24)
3939

4040

4141
#: This is :data:`False` in slave contexts. Previously it was used to prevent

mitogen/core.py

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def _path_importer_cache(cls, path):
7474
import os
7575
import pickle as py_pickle
7676
import pstats
77+
import pty
7778
import signal
7879
import socket
7980
import struct
@@ -544,8 +545,17 @@ def set_cloexec(fd):
544545
they must be explicitly closed through some other means, such as
545546
:func:`mitogen.fork.on_fork`.
546547
"""
548+
stdfds = [
549+
stdfd
550+
for stdio, stdfd in [
551+
(sys.stdin, pty.STDIN_FILENO),
552+
(sys.stdout, pty.STDOUT_FILENO),
553+
(sys.stderr, pty.STDERR_FILENO),
554+
]
555+
if stdio is not None and not stdio.closed
556+
]
557+
assert fd not in stdfds, 'fd %r is one of the stdio fds: %r' % (fd, stdfds)
547558
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
548-
assert fd > 2, 'fd %r <= 2' % (fd,)
549559
fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
550560

551561

@@ -4019,7 +4029,9 @@ def _setup_master(self):
40194029
in_fp = os.fdopen(os.dup(in_fd), 'rb', 0)
40204030
os.close(in_fd)
40214031

4022-
out_fp = os.fdopen(os.dup(self.config.get('out_fd', 1)), 'wb', 0)
4032+
out_fd = self.config.get('out_fd', pty.STDOUT_FILENO)
4033+
out_fd2 = os.dup(out_fd)
4034+
out_fp = os.fdopen(out_fd2, 'wb', 0)
40234035
self.stream = MitogenProtocol.build_stream(
40244036
self.router,
40254037
parent_id,
@@ -4103,7 +4115,13 @@ def _nullify_stdio(self):
41034115
Open /dev/null to replace stdio temporarily. In case of odd startup,
41044116
assume we may be allocated a standard handle.
41054117
"""
4106-
for stdfd, mode in ((0, os.O_RDONLY), (1, os.O_RDWR), (2, os.O_RDWR)):
4118+
for stdio, stdfd, mode in [
4119+
(sys.stdin, pty.STDIN_FILENO, os.O_RDONLY),
4120+
(sys.stdout, pty.STDOUT_FILENO, os.O_RDWR),
4121+
(sys.stderr, pty.STDERR_FILENO, os.O_RDWR),
4122+
]:
4123+
if stdio is None:
4124+
continue
41074125
fd = os.open('/dev/null', mode)
41084126
if fd != stdfd:
41094127
os.dup2(fd, stdfd)
@@ -4119,8 +4137,9 @@ def _preserve_tty_fp(self):
41194137
avoid receiving SIGHUP.
41204138
"""
41214139
try:
4122-
if os.isatty(2):
4123-
self.reserve_tty_fp = os.fdopen(os.dup(2), 'r+b', 0)
4140+
if os.isatty(pty.STDERR_FILENO):
4141+
reserve_tty_fd = os.dup(pty.STDERR_FILENO)
4142+
self.reserve_tty_fp = os.fdopen(reserve_tty_fd, 'r+b', 0)
41244143
set_cloexec(self.reserve_tty_fp.fileno())
41254144
except OSError:
41264145
pass
@@ -4140,13 +4159,18 @@ def _setup_stdio(self):
41404159
self._nullify_stdio()
41414160

41424161
self.loggers = []
4143-
for name, fd in (('stdout', 1), ('stderr', 2)):
4144-
log = IoLoggerProtocol.build_stream(name, fd)
4162+
for stdio, stdfd, name in [
4163+
(sys.stdout, pty.STDOUT_FILENO, 'stdout'),
4164+
(sys.stderr, pty.STDERR_FILENO, 'stderr'),
4165+
]:
4166+
if stdio is None:
4167+
continue
4168+
log = IoLoggerProtocol.build_stream(name, stdfd)
41454169
self.broker.start_receive(log)
41464170
self.loggers.append(log)
41474171

41484172
# Reopen with line buffering.
4149-
sys.stdout = os.fdopen(1, 'w', 1)
4173+
sys.stdout = os.fdopen(pty.STDOUT_FILENO, 'w', 1)
41504174

41514175
def main(self):
41524176
self._setup_master()

mitogen/fakessh.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
import getopt
9696
import inspect
9797
import os
98+
import pty
9899
import shutil
99100
import socket
100101
import subprocess
@@ -354,8 +355,9 @@ def _fakessh_main(dest_context_id, econtext):
354355
control_handle, stdin_handle)
355356

356357
process = Process(econtext.router,
357-
stdin=os.fdopen(1, 'w+b', 0),
358-
stdout=os.fdopen(0, 'r+b', 0))
358+
stdin=os.fdopen(pty.STDOUT_FILENO, 'w+b', 0),
359+
stdout=os.fdopen(pty.STDIN_FILENO, 'r+b', 0),
360+
)
359361
process.start_master(
360362
stdin=mitogen.core.Sender(dest, stdin_handle),
361363
control=mitogen.core.Sender(dest, control_handle),

mitogen/parent.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import logging
4444
import os
4545
import re
46+
import pty
4647
import signal
4748
import socket
4849
import struct
@@ -361,13 +362,13 @@ def create_child(args, merge_stdio=False, stderr_pipe=False,
361362
escalates_privilege=escalates_privilege
362363
)
363364

364-
stderr = None
365-
stderr_r = None
366365
if merge_stdio:
367-
stderr = child_wfp
366+
stderr_r, stderr = None, child_wfp
368367
elif stderr_pipe:
369368
stderr_r, stderr = mitogen.core.pipe()
370369
mitogen.core.set_cloexec(stderr_r.fileno())
370+
else:
371+
stderr_r, stderr = None, None
371372

372373
try:
373374
proc = popen(
@@ -406,12 +407,14 @@ def _acquire_controlling_tty():
406407
if sys.platform in ('linux', 'linux2'):
407408
# On Linux, the controlling tty becomes the first tty opened by a
408409
# process lacking any prior tty.
409-
os.close(os.open(os.ttyname(2), os.O_RDWR))
410+
tty_path = os.ttyname(pty.STDERR_FILENO)
411+
tty_fd = os.open(tty_path, os.O_RDWR)
412+
os.close(tty_fd)
410413
if hasattr(termios, 'TIOCSCTTY') and not mitogen.core.IS_WSL and not IS_SOLARIS:
411414
# #550: prehistoric WSL does not like TIOCSCTTY.
412415
# On BSD an explicit ioctl is required. For some inexplicable reason,
413416
# Python 2.6 on Travis also requires it.
414-
fcntl.ioctl(2, termios.TIOCSCTTY)
417+
fcntl.ioctl(pty.STDERR_FILENO, termios.TIOCSCTTY)
415418

416419

417420
def _linux_broken_devpts_openpty():
@@ -1415,7 +1418,7 @@ def __repr__(self):
14151418
# w: write side of core_src FD.
14161419
# C: the decompressed core source.
14171420

1418-
# Final os.close(2) to avoid --py-debug build from corrupting stream with
1421+
# Final os.close(STDOUT_FILENO) to avoid --py-debug build corrupting stream with
14191422
# "[1234 refs]" during exit.
14201423
@staticmethod
14211424
def _first_stage():
@@ -1683,9 +1686,7 @@ def _async_connect(self):
16831686

16841687
LOG.debug('child for %r started: pid:%r stdin:%r stdout:%r stderr:%r',
16851688
self, self.proc.pid,
1686-
self.proc.stdin.fileno(),
1687-
self.proc.stdout.fileno(),
1688-
self.proc.stderr and self.proc.stderr.fileno())
1689+
self.proc.stdin, self.proc.stdout, self.proc.stderr)
16891690

16901691
self.stdio_stream = self._setup_stdio_stream()
16911692
if self.context.name is None:

tests/ansible/hosts/group_vars/all.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ become_unpriv_available: >-
1212
{{-
1313
(
1414
not is_mitogen
15-
and ansible_facts.distribution in ["MacOSX"]
15+
and is_macos_controller
1616
and ansible_version.full is version("2.11", ">=", strict=True)
1717
)
1818
or (
1919
is_mitogen
20-
and not ansible_facts.distribution in ["MacOSX"]
20+
and not is_macos_controller
2121
)
2222
or (
2323
is_mitogen

tests/ansible/integration/async/multiple_items_loop.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
jid: "{{ item.ansible_job_id }}"
2020
become: yes
2121
register: out
22-
until: out.finished
22+
until: out is finished
2323
retries: 30
2424
with_items:
2525
- "{{ jobs.results }}"

tests/ansible/integration/async/result_binary_producing_json.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
async_status:
3535
jid: "{{job.ansible_job_id}}"
3636
register: result
37-
until: result.finished
37+
until: result is finished
3838
retries: 100000
3939
delay: 0
4040

0 commit comments

Comments
 (0)