Skip to content

Commit 855773a

Browse files
committed
try adding a test
1 parent 21ae510 commit 855773a

File tree

2 files changed

+41
-13
lines changed

2 files changed

+41
-13
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
strategy:
1313
fail-fast: false
1414
matrix:
15-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
15+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.13t", "3.14", "3.14t"]
1616
os: [ubuntu-latest, windows-latest, macOS-latest]
1717

1818
steps:

test_duct.py

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,7 @@ def head_bytes(c):
7070
stdout = os.fdopen(1, 'w')
7171
input_str = stdin.read({0})
7272
stdout.write(input_str)
73-
""".format(
74-
c
75-
)
73+
""".format(c)
7674
)
7775
return cmd("python", "-c", code)
7876

@@ -86,9 +84,7 @@ def echo_var(var_name):
8684
"""\
8785
import os
8886
print(os.environ.get("{0}", ""))
89-
""".format(
90-
var_name
91-
)
87+
""".format(var_name)
9288
)
9389
return cmd("python", "-c", code)
9490

@@ -103,9 +99,7 @@ def replace(a, b):
10399
import sys
104100
input_str = sys.stdin.read()
105101
sys.stdout.write(input_str.replace({0}, {1}))
106-
""".format(
107-
repr(a), repr(b)
108-
)
102+
""".format(repr(a), repr(b))
109103
)
110104
return cmd("python", "-c", code)
111105

@@ -684,9 +678,7 @@ def test_kill_with_grandchild():
684678
print("started")
685679
sys.stdout.flush()
686680
p.wait()
687-
""".format(
688-
grandchild_code
689-
)
681+
""".format(grandchild_code)
690682

691683
# Capturing stderr means an IO thread is spawned, even though we're using a
692684
# ReaderHandle to read stdout. What we're testing here is that kill()
@@ -1005,3 +997,39 @@ def test_zombies_reaped():
1005997
while ps_observes_pid(pid):
1006998
tries += 1
1007999
assert tries < 100, f"child #{i} (PID {pid}) never went away?"
1000+
1001+
1002+
def test_pipe_inheritance():
1003+
waiters = []
1004+
threads = []
1005+
1006+
def reader_fn():
1007+
# Open a pipe for the child to read from. This can deadlock if a waiter
1008+
# inherits this pipe and keeps it open.
1009+
reader, writer = os.pipe()
1010+
os.fdopen(writer, "wb").write(b"foo")
1011+
output = cat_cmd().stdin_file(reader).read()
1012+
assert output == "foo"
1013+
1014+
def waiter_fn():
1015+
# Spawn a child that won't exit until we kill it. This is intended to
1016+
# keep pipes open if they're inherited unintentionally. Without
1017+
# something like this, we'd need an inheritance *cycle* between
1018+
# readers, which might be unlikely.
1019+
waiters.append(sleep_cmd(365 * 24 * 60 * 60).unchecked().start())
1020+
1021+
for _ in range(100):
1022+
thread = threading.Thread(target=reader_fn)
1023+
thread.start()
1024+
threads.append(thread)
1025+
1026+
thread = threading.Thread(target=waiter_fn)
1027+
thread.start()
1028+
threads.append(thread)
1029+
1030+
for thread in threads:
1031+
thread.join()
1032+
1033+
for waiter in waiters:
1034+
waiter.kill()
1035+
waiter.wait()

0 commit comments

Comments
 (0)