Skip to content

Commit 63df19b

Browse files
committed
fix(tests): use process groups for reliable signal handling in PyPy
- Use preexec_fn=os.setsid to create new process group - Send signals to process group with os.killpg() instead of single process - Add explicit timeout and graceful-timeout to gunicorn command - Fixes test failures on PyPy 3.10 where signals weren't propagating properly
1 parent cd77bcc commit 63df19b

1 file changed

Lines changed: 25 additions & 9 deletions

File tree

tests/test_signal_integration.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,19 @@ def gunicorn_server(app_module):
9393
'--access-logfile', '-',
9494
'--error-logfile', '-',
9595
'--log-level', 'info',
96+
'--timeout', '30',
97+
'--graceful-timeout', '30',
9698
app_name
9799
]
98100

101+
# Use setsid to create new process group for proper signal handling
99102
proc = subprocess.Popen(
100103
cmd,
101104
cwd=app_dir,
102105
stdout=subprocess.PIPE,
103106
stderr=subprocess.PIPE,
104-
env={**os.environ, 'PYTHONPATH': app_dir}
107+
env={**os.environ, 'PYTHONPATH': app_dir},
108+
preexec_fn=os.setsid
105109
)
106110

107111
# Wait for server to start
@@ -113,13 +117,19 @@ def gunicorn_server(app_module):
113117

114118
yield proc, port
115119

116-
# Cleanup
120+
# Cleanup - use process group kill for better cleanup
117121
if proc.poll() is None:
118-
proc.terminate()
122+
try:
123+
os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
124+
except (ProcessLookupError, OSError):
125+
pass
119126
try:
120127
proc.wait(timeout=5)
121128
except subprocess.TimeoutExpired:
122-
proc.kill()
129+
try:
130+
os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
131+
except (ProcessLookupError, OSError):
132+
pass
123133
proc.wait()
124134

125135

@@ -141,8 +151,11 @@ def test_graceful_shutdown_sigterm(self, gunicorn_server):
141151
response = make_request('127.0.0.1', port)
142152
assert b'Hello, World!' in response
143153

144-
# Send SIGTERM
145-
proc.send_signal(signal.SIGTERM)
154+
# Send SIGTERM to the process group for reliable signal delivery
155+
try:
156+
os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
157+
except (ProcessLookupError, OSError):
158+
proc.send_signal(signal.SIGTERM)
146159

147160
# Wait for process to exit
148161
try:
@@ -160,8 +173,11 @@ def test_graceful_shutdown_sigint(self, gunicorn_server):
160173
response = make_request('127.0.0.1', port)
161174
assert b'Hello, World!' in response
162175

163-
# Send SIGINT
164-
proc.send_signal(signal.SIGINT)
176+
# Send SIGINT to the process group for reliable signal delivery
177+
try:
178+
os.killpg(os.getpgid(proc.pid), signal.SIGINT)
179+
except (ProcessLookupError, OSError):
180+
proc.send_signal(signal.SIGINT)
165181

166182
# Wait for process to exit
167183
try:
@@ -179,7 +195,7 @@ def test_sighup_reload(self, gunicorn_server):
179195
response = make_request('127.0.0.1', port)
180196
assert b'Hello, World!' in response
181197

182-
# Send SIGHUP
198+
# Send SIGHUP to the master process (not process group - only master handles reload)
183199
proc.send_signal(signal.SIGHUP)
184200

185201
# Wait a moment for reload

0 commit comments

Comments
 (0)