diff --git a/test/test_client.py b/test/test_client.py index 71f452faf..d3fe4e120 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -16,11 +16,12 @@ import datetime import os -import threading +import signal import socket import sys -import time import thread +import threading +import time import unittest import warnings @@ -935,31 +936,53 @@ with client.start_request() as request: db.drop_collection('foo') db.foo.insert({'_id': 1}) - def interrupter(): - # Raises KeyboardInterrupt in the main thread - time.sleep(0.25) - thread.interrupt_main() - - thread.start_new_thread(interrupter, ()) - - raised = False + old_signal_handler = None try: - # Will be interrupted by a KeyboardInterrupt. - db.foo.find({'$where': where}).next() - except KeyboardInterrupt: - raised = True + # Platform-specific hacks for raising a KeyboardInterrupt on the + # main thread while find() is in-progress: On Windows, SIGALRM is + # unavailable so we use a second thread. In our Evergreen setup on + # Linux, the thread technique causes an error in the test at + # sock.recv(): TypeError: 'int' object is not callable + # We don't know what causes this, so we hack around it. - # Can't use self.assertRaises() because it doesn't catch system - # exceptions - self.assertTrue(raised, "Didn't raise expected KeyboardInterrupt") + if sys.platform == 'win32': + def interrupter(): + # Raises KeyboardInterrupt in the main thread + time.sleep(0.25) + thread.interrupt_main() + + thread.start_new_thread(interrupter, ()) + else: + # Convert SIGALRM to SIGINT -- it's hard to schedule a SIGINT + # for one second in the future, but easy to schedule SIGALRM. + def sigalarm(num, frame): + raise KeyboardInterrupt + + old_signal_handler = signal.signal(signal.SIGALRM, sigalarm) + signal.alarm(1) + + raised = False + try: + # Will be interrupted by a KeyboardInterrupt. + db.foo.find({'$where': where}).next() + except KeyboardInterrupt: + raised = True + + # Can't use self.assertRaises() because it doesn't catch system + # exceptions + self.assertTrue(raised, "Didn't raise expected KeyboardInterrupt") + + # Raises AssertionError due to PYTHON-294 -- Mongo's response to + # the previous find() is still waiting to be read on the socket, + # so the request id's don't match. + self.assertEqual( + {'_id': 1}, + db.foo.find().next() + ) + finally: + if old_signal_handler: + signal.signal(signal.SIGALRM, old_signal_handler) - # Raises AssertionError due to PYTHON-294 -- Mongo's response to the - # previous find() is still waiting to be read on the socket, so the - # request id's don't match. - self.assertEqual( - {'_id': 1}, - db.foo.find().next() - ) def test_operation_failure_without_request(self): # Ensure MongoClient doesn't close socket after it gets an error diff --git a/test/test_replica_set_client.py b/test/test_replica_set_client.py index 53fd3210d..91ed6769f 100644 --- a/test/test_replica_set_client.py +++ b/test/test_replica_set_client.py @@ -956,15 +956,29 @@ class TestReplicaSetClient(TestReplicaSetClientBase, TestRequestMixin): db.foo.insert({'_id': 1}) old_signal_handler = None - try: - def interrupter(): - time.sleep(0.25) + # Platform-specific hacks for raising a KeyboardInterrupt on the + # main thread while find() is in-progress: On Windows, SIGALRM is + # unavailable so we use a second thread. In our Evergreen setup on + # Linux, the thread technique causes an error in the test at + # sock.recv(): TypeError: 'int' object is not callable + # We don't know what causes this, so we hack around it. - # Raises KeyboardInterrupt in the main thread - thread.interrupt_main() + if sys.platform == 'win32': + def interrupter(): + # Raises KeyboardInterrupt in the main thread + time.sleep(0.25) + thread.interrupt_main() - thread.start_new_thread(interrupter, ()) + thread.start_new_thread(interrupter, ()) + else: + # Convert SIGALRM to SIGINT -- it's hard to schedule a SIGINT + # for one second in the future, but easy to schedule SIGALRM. + def sigalarm(num, frame): + raise KeyboardInterrupt + + old_signal_handler = signal.signal(signal.SIGALRM, sigalarm) + signal.alarm(1) raised = False try: @@ -975,11 +989,11 @@ class TestReplicaSetClient(TestReplicaSetClientBase, TestRequestMixin): # Can't use self.assertRaises() because it doesn't catch system # exceptions - self.assertTrue(raised, "Didn't raise expected ConnectionFailure") + self.assertTrue(raised, "Didn't raise expected KeyboardInterrupt") - # Raises AssertionError due to PYTHON-294 -- Mongo's response to the - # previous find() is still waiting to be read on the socket, so the - # request id's don't match. + # Raises AssertionError due to PYTHON-294 -- Mongo's response to + # the previous find() is still waiting to be read on the socket, + # so the request id's don't match. self.assertEqual( {'_id': 1}, db.foo.find().next()