Skip to content

Commit 80fdf61

Browse files
committed
PYTHON-1205 - Use SIGALRM instead of interrupt_main on non-Windows
1 parent 544fd06 commit 80fdf61

File tree

2 files changed

+74
-37
lines changed

2 files changed

+74
-37
lines changed

test/test_client.py

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616

1717
import datetime
1818
import os
19-
import threading
19+
import signal
2020
import socket
2121
import sys
22-
import time
2322
import thread
23+
import threading
24+
import time
2425
import unittest
2526
import warnings
2627

@@ -935,31 +936,53 @@ def test_interrupt_signal(self):
935936
db.drop_collection('foo')
936937
db.foo.insert({'_id': 1})
937938

938-
def interrupter():
939-
# Raises KeyboardInterrupt in the main thread
940-
time.sleep(0.25)
941-
thread.interrupt_main()
942-
943-
thread.start_new_thread(interrupter, ())
944-
945-
raised = False
939+
old_signal_handler = None
946940
try:
947-
# Will be interrupted by a KeyboardInterrupt.
948-
db.foo.find({'$where': where}).next()
949-
except KeyboardInterrupt:
950-
raised = True
951-
952-
# Can't use self.assertRaises() because it doesn't catch system
953-
# exceptions
954-
self.assertTrue(raised, "Didn't raise expected KeyboardInterrupt")
955-
956-
# Raises AssertionError due to PYTHON-294 -- Mongo's response to the
957-
# previous find() is still waiting to be read on the socket, so the
958-
# request id's don't match.
959-
self.assertEqual(
960-
{'_id': 1},
961-
db.foo.find().next()
962-
)
941+
# Platform-specific hacks for raising a KeyboardInterrupt on the
942+
# main thread while find() is in-progress: On Windows, SIGALRM is
943+
# unavailable so we use a second thread. In our Evergreen setup on
944+
# Linux, the thread technique causes an error in the test at
945+
# sock.recv(): TypeError: 'int' object is not callable
946+
# We don't know what causes this, so we hack around it.
947+
948+
if sys.platform == 'win32':
949+
def interrupter():
950+
# Raises KeyboardInterrupt in the main thread
951+
time.sleep(0.25)
952+
thread.interrupt_main()
953+
954+
thread.start_new_thread(interrupter, ())
955+
else:
956+
# Convert SIGALRM to SIGINT -- it's hard to schedule a SIGINT
957+
# for one second in the future, but easy to schedule SIGALRM.
958+
def sigalarm(num, frame):
959+
raise KeyboardInterrupt
960+
961+
old_signal_handler = signal.signal(signal.SIGALRM, sigalarm)
962+
signal.alarm(1)
963+
964+
raised = False
965+
try:
966+
# Will be interrupted by a KeyboardInterrupt.
967+
db.foo.find({'$where': where}).next()
968+
except KeyboardInterrupt:
969+
raised = True
970+
971+
# Can't use self.assertRaises() because it doesn't catch system
972+
# exceptions
973+
self.assertTrue(raised, "Didn't raise expected KeyboardInterrupt")
974+
975+
# Raises AssertionError due to PYTHON-294 -- Mongo's response to
976+
# the previous find() is still waiting to be read on the socket,
977+
# so the request id's don't match.
978+
self.assertEqual(
979+
{'_id': 1},
980+
db.foo.find().next()
981+
)
982+
finally:
983+
if old_signal_handler:
984+
signal.signal(signal.SIGALRM, old_signal_handler)
985+
963986

964987
def test_operation_failure_without_request(self):
965988
# Ensure MongoClient doesn't close socket after it gets an error

test/test_replica_set_client.py

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -956,15 +956,29 @@ def test_interrupt_signal(self):
956956
db.foo.insert({'_id': 1})
957957

958958
old_signal_handler = None
959-
960959
try:
961-
def interrupter():
962-
time.sleep(0.25)
963-
964-
# Raises KeyboardInterrupt in the main thread
965-
thread.interrupt_main()
960+
# Platform-specific hacks for raising a KeyboardInterrupt on the
961+
# main thread while find() is in-progress: On Windows, SIGALRM is
962+
# unavailable so we use a second thread. In our Evergreen setup on
963+
# Linux, the thread technique causes an error in the test at
964+
# sock.recv(): TypeError: 'int' object is not callable
965+
# We don't know what causes this, so we hack around it.
966+
967+
if sys.platform == 'win32':
968+
def interrupter():
969+
# Raises KeyboardInterrupt in the main thread
970+
time.sleep(0.25)
971+
thread.interrupt_main()
972+
973+
thread.start_new_thread(interrupter, ())
974+
else:
975+
# Convert SIGALRM to SIGINT -- it's hard to schedule a SIGINT
976+
# for one second in the future, but easy to schedule SIGALRM.
977+
def sigalarm(num, frame):
978+
raise KeyboardInterrupt
966979

967-
thread.start_new_thread(interrupter, ())
980+
old_signal_handler = signal.signal(signal.SIGALRM, sigalarm)
981+
signal.alarm(1)
968982

969983
raised = False
970984
try:
@@ -975,11 +989,11 @@ def interrupter():
975989

976990
# Can't use self.assertRaises() because it doesn't catch system
977991
# exceptions
978-
self.assertTrue(raised, "Didn't raise expected ConnectionFailure")
992+
self.assertTrue(raised, "Didn't raise expected KeyboardInterrupt")
979993

980-
# Raises AssertionError due to PYTHON-294 -- Mongo's response to the
981-
# previous find() is still waiting to be read on the socket, so the
982-
# request id's don't match.
994+
# Raises AssertionError due to PYTHON-294 -- Mongo's response to
995+
# the previous find() is still waiting to be read on the socket,
996+
# so the request id's don't match.
983997
self.assertEqual(
984998
{'_id': 1},
985999
db.foo.find().next()

0 commit comments

Comments
 (0)