ソースを参照

Fixed #27954 -- Allowed keyboard interrupt to abort queries in PostgreSQL dbshell.

Thanks Tim Martin for review.
Chris Sinchok 8 年 前
コミット
66150f7cf6

+ 6 - 0
django/db/backends/postgresql/client.py

@@ -1,4 +1,5 @@
 import os
+import signal
 import subprocess
 
 from django.core.files.temp import NamedTemporaryFile
@@ -34,6 +35,7 @@ class DatabaseClient(BaseDatabaseClient):
         args += [dbname]
 
         temp_pgpass = None
+        sigint_handler = signal.getsignal(signal.SIGINT)
         try:
             if passwd:
                 # Create temporary .pgpass file.
@@ -54,8 +56,12 @@ class DatabaseClient(BaseDatabaseClient):
                     # If the current locale can't encode the data, we let
                     # the user input the password manually.
                     pass
+            # Allow SIGINT to pass to psql to abort queries.
+            signal.signal(signal.SIGINT, signal.SIG_IGN)
             subprocess.check_call(args)
         finally:
+            # Restore the orignal SIGINT handler.
+            signal.signal(signal.SIGINT, sigint_handler)
             if temp_pgpass:
                 temp_pgpass.close()
                 if 'PGPASSFILE' in os.environ:  # unit tests need cleanup

+ 15 - 0
tests/dbshell/test_postgresql_psycopg2.py

@@ -1,4 +1,5 @@
 import os
+import signal
 from unittest import mock
 
 from django.db.backends.postgresql.client import DatabaseClient
@@ -99,3 +100,17 @@ class PostgreSqlDbshellCommandTestCase(SimpleTestCase):
                 pgpass_string,
             )
         )
+
+    def test_sigint_handler(self):
+        """SIGINT is ignored in Python and passed to psql to abort quries."""
+        def _mock_subprocess_call(*args):
+            handler = signal.getsignal(signal.SIGINT)
+            self.assertEqual(handler, signal.SIG_IGN)
+
+        sigint_handler = signal.getsignal(signal.SIGINT)
+        # The default handler isn't SIG_IGN.
+        self.assertNotEqual(sigint_handler, signal.SIG_IGN)
+        with mock.patch('subprocess.check_call', new=_mock_subprocess_call):
+            DatabaseClient.runshell_db({})
+        # dbshell restores the orignal handler.
+        self.assertEqual(sigint_handler, signal.getsignal(signal.SIGINT))