We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[pantsd] sending ctrl-\ in a `./pants repl` can lead to a hung pantsd-runner
repro:

```
[omerta pants-release (master)]$ ps -ef |grep pantsd-runner |grep -v grep
[omerta pants-release (master)]$ ./pants -q repl 3rdparty/python:psutil

Python 2.7.10 (default, Dec 16 2015, 14:09:45) 
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> ^\Quit: 3
[omerta pants-release (master)]$ ps -ef |grep pantsd-runner |grep -v grep
  501 67669     1   0 10:37PM ??         0:01.14 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67670 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67671 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67672 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67673 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67674 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67675 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67676 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
  501 67677 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    
[omerta pants-release (master)]$ 
```

we'll want to better handle `SIGQUIT` in the thin client side of the runner to avoid this.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/python/pants/bin/remote_pants_runner.py`
Content:
```
1 # coding=utf-8
2 # Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
3 # Licensed under the Apache License, Version 2.0 (see LICENSE).
4 
5 from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
6                         unicode_literals, with_statement)
7 
8 import logging
9 import signal
10 import sys
11 from contextlib import contextmanager
12 
13 from pants.java.nailgun_client import NailgunClient
14 from pants.java.nailgun_protocol import NailgunProtocol
15 from pants.pantsd.pants_daemon import PantsDaemon
16 from pants.util.collections import combined_dict
17 from pants.util.memo import memoized_property
18 
19 
20 logger = logging.getLogger(__name__)
21 
22 
23 class RemotePantsRunner(object):
24   """A thin client variant of PantsRunner."""
25 
26   class Fallback(Exception):
27     """Raised when fallback to an alternate execution mode is requested."""
28 
29   class PortNotFound(Exception):
30     """Raised when the pailgun port can't be found."""
31 
32   PANTS_COMMAND = 'pants'
33   RECOVERABLE_EXCEPTIONS = (PortNotFound, NailgunClient.NailgunConnectionError)
34 
35   def __init__(self, exiter, args, env, bootstrap_options, stdin=None, stdout=None, stderr=None):
36     """
37     :param Exiter exiter: The Exiter instance to use for this run.
38     :param list args: The arguments (e.g. sys.argv) for this run.
39     :param dict env: The environment (e.g. os.environ) for this run.
40     :param Options bootstrap_options: The Options bag containing the bootstrap options.
41     :param file stdin: The stream representing stdin.
42     :param file stdout: The stream representing stdout.
43     :param file stderr: The stream representing stderr.
44     """
45     self._exiter = exiter
46     self._args = args
47     self._env = env
48     self._bootstrap_options = bootstrap_options
49     self._stdin = stdin or sys.stdin
50     self._stdout = stdout or sys.stdout
51     self._stderr = stderr or sys.stderr
52 
53   @memoized_property
54   def pantsd(self):
55     return PantsDaemon.Factory.create(bootstrap_options=self._bootstrap_options)
56 
57   @contextmanager
58   def _trapped_control_c(self, client):
59     """A contextmanager that overrides the SIGINT (control-c) handler and handles it remotely."""
60     def handle_control_c(signum, frame):
61       client.send_control_c()
62 
63     existing_sigint_handler = signal.signal(signal.SIGINT, handle_control_c)
64     signal.siginterrupt(signal.SIGINT, False)  # Retry interrupted system calls.
65     try:
66       yield
67     finally:
68       signal.signal(signal.SIGINT, existing_sigint_handler)
69 
70   def _setup_logging(self):
71     """Sets up basic stdio logging for the thin client."""
72     log_level = logging.getLevelName(self._bootstrap_options.for_global_scope().level.upper())
73 
74     formatter = logging.Formatter('%(levelname)s] %(message)s')
75     handler = logging.StreamHandler(sys.stdout)
76     handler.setLevel(log_level)
77     handler.setFormatter(formatter)
78 
79     root = logging.getLogger()
80     root.setLevel(log_level)
81     root.addHandler(handler)
82 
83   def _connect_and_execute(self, port):
84     # Merge the nailgun TTY capability environment variables with the passed environment dict.
85     ng_env = NailgunProtocol.isatty_to_env(self._stdin, self._stdout, self._stderr)
86     modified_env = combined_dict(self._env, ng_env)
87 
88     assert isinstance(port, int), 'port {} is not an integer!'.format(port)
89 
90     # Instantiate a NailgunClient.
91     client = NailgunClient(port=port,
92                            ins=self._stdin,
93                            out=self._stdout,
94                            err=self._stderr,
95                            exit_on_broken_pipe=True)
96 
97     with self._trapped_control_c(client):
98       # Execute the command on the pailgun.
99       result = client.execute(self.PANTS_COMMAND, *self._args, **modified_env)
100 
101     # Exit.
102     self._exiter.exit(result)
103 
104   def run(self, args=None):
105     self._setup_logging()
106     port = self.pantsd.maybe_launch()
107 
108     logger.debug('connecting to pailgun on port {}'.format(port))
109     try:
110       self._connect_and_execute(port)
111     except self.RECOVERABLE_EXCEPTIONS as e:
112       raise self.Fallback(e)
113
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/python/pants/bin/remote_pants_runner.py b/src/python/pants/bin/remote_pants_runner.py
--- a/src/python/pants/bin/remote_pants_runner.py
+++ b/src/python/pants/bin/remote_pants_runner.py
@@ -55,17 +55,25 @@
     return PantsDaemon.Factory.create(bootstrap_options=self._bootstrap_options)
 
   @contextmanager
-  def _trapped_control_c(self, client):
-    """A contextmanager that overrides the SIGINT (control-c) handler and handles it remotely."""
+  def _trapped_signals(self, client):
+    """A contextmanager that overrides the SIGINT (control-c) and SIGQUIT (control-\) handlers
+    and handles them remotely."""
     def handle_control_c(signum, frame):
       client.send_control_c()
 
     existing_sigint_handler = signal.signal(signal.SIGINT, handle_control_c)
-    signal.siginterrupt(signal.SIGINT, False)  # Retry interrupted system calls.
+    # N.B. SIGQUIT will abruptly kill the pantsd-runner, which will shut down the other end
+    # of the Pailgun connection - so we send a gentler SIGINT here instead.
+    existing_sigquit_handler = signal.signal(signal.SIGQUIT, handle_control_c)
+
+    # Retry interrupted system calls.
+    signal.siginterrupt(signal.SIGINT, False)
+    signal.siginterrupt(signal.SIGQUIT, False)
     try:
       yield
     finally:
       signal.signal(signal.SIGINT, existing_sigint_handler)
+      signal.signal(signal.SIGQUIT, existing_sigquit_handler)
 
   def _setup_logging(self):
     """Sets up basic stdio logging for the thin client."""
@@ -94,7 +102,7 @@
                            err=self._stderr,
                            exit_on_broken_pipe=True)
 
-    with self._trapped_control_c(client):
+    with self._trapped_signals(client):
       # Execute the command on the pailgun.
       result = client.execute(self.PANTS_COMMAND, *self._args, **modified_env)

{"golden_diff": "diff --git a/src/python/pants/bin/remote_pants_runner.py b/src/python/pants/bin/remote_pants_runner.py\n--- a/src/python/pants/bin/remote_pants_runner.py\n+++ b/src/python/pants/bin/remote_pants_runner.py\n@@ -55,17 +55,25 @@\n     return PantsDaemon.Factory.create(bootstrap_options=self._bootstrap_options)\n \n   @contextmanager\n-  def _trapped_control_c(self, client):\n-    \"\"\"A contextmanager that overrides the SIGINT (control-c) handler and handles it remotely.\"\"\"\n+  def _trapped_signals(self, client):\n+    \"\"\"A contextmanager that overrides the SIGINT (control-c) and SIGQUIT (control-\\) handlers\n+    and handles them remotely.\"\"\"\n     def handle_control_c(signum, frame):\n       client.send_control_c()\n \n     existing_sigint_handler = signal.signal(signal.SIGINT, handle_control_c)\n-    signal.siginterrupt(signal.SIGINT, False)  # Retry interrupted system calls.\n+    # N.B. SIGQUIT will abruptly kill the pantsd-runner, which will shut down the other end\n+    # of the Pailgun connection - so we send a gentler SIGINT here instead.\n+    existing_sigquit_handler = signal.signal(signal.SIGQUIT, handle_control_c)\n+\n+    # Retry interrupted system calls.\n+    signal.siginterrupt(signal.SIGINT, False)\n+    signal.siginterrupt(signal.SIGQUIT, False)\n     try:\n       yield\n     finally:\n       signal.signal(signal.SIGINT, existing_sigint_handler)\n+      signal.signal(signal.SIGQUIT, existing_sigquit_handler)\n \n   def _setup_logging(self):\n     \"\"\"Sets up basic stdio logging for the thin client.\"\"\"\n@@ -94,7 +102,7 @@\n                            err=self._stderr,\n                            exit_on_broken_pipe=True)\n \n-    with self._trapped_control_c(client):\n+    with self._trapped_signals(client):\n       # Execute the command on the pailgun.\n       result = client.execute(self.PANTS_COMMAND, *self._args, **modified_env)\n", "issue": "[pantsd] sending ctrl-\\ in a `./pants repl` can lead to a hung pantsd-runner\nrepro:\r\n\r\n```\r\n[omerta pants-release (master)]$ ps -ef |grep pantsd-runner |grep -v grep\r\n[omerta pants-release (master)]$ ./pants -q repl 3rdparty/python:psutil\r\n\r\nPython 2.7.10 (default, Dec 16 2015, 14:09:45) \r\n[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin\r\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\r\n(InteractiveConsole)\r\n>>> ^\\Quit: 3\r\n[omerta pants-release (master)]$ ps -ef |grep pantsd-runner |grep -v grep\r\n  501 67669     1   0 10:37PM ??         0:01.14 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    \r\n  501 67670 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    \r\n  501 67671 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    \r\n  501 67672 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    \r\n  501 67673 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    \r\n  501 67674 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    \r\n  501 67675 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    \r\n  501 67676 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    \r\n  501 67677 67669   0 10:37PM ??         0:00.00 pantsd-runner [./pants -q repl 3rdparty/python:psutil]    \r\n[omerta pants-release (master)]$ \r\n```\r\n\r\nwe'll want to better handle `SIGQUIT` in the thin client side of the runner to avoid this.\n", "before_files": [{"content": "# coding=utf-8\n# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import (absolute_import, division, generators, nested_scopes, print_function,\n                        unicode_literals, with_statement)\n\nimport logging\nimport signal\nimport sys\nfrom contextlib import contextmanager\n\nfrom pants.java.nailgun_client import NailgunClient\nfrom pants.java.nailgun_protocol import NailgunProtocol\nfrom pants.pantsd.pants_daemon import PantsDaemon\nfrom pants.util.collections import combined_dict\nfrom pants.util.memo import memoized_property\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass RemotePantsRunner(object):\n  \"\"\"A thin client variant of PantsRunner.\"\"\"\n\n  class Fallback(Exception):\n    \"\"\"Raised when fallback to an alternate execution mode is requested.\"\"\"\n\n  class PortNotFound(Exception):\n    \"\"\"Raised when the pailgun port can't be found.\"\"\"\n\n  PANTS_COMMAND = 'pants'\n  RECOVERABLE_EXCEPTIONS = (PortNotFound, NailgunClient.NailgunConnectionError)\n\n  def __init__(self, exiter, args, env, bootstrap_options, stdin=None, stdout=None, stderr=None):\n    \"\"\"\n    :param Exiter exiter: The Exiter instance to use for this run.\n    :param list args: The arguments (e.g. sys.argv) for this run.\n    :param dict env: The environment (e.g. os.environ) for this run.\n    :param Options bootstrap_options: The Options bag containing the bootstrap options.\n    :param file stdin: The stream representing stdin.\n    :param file stdout: The stream representing stdout.\n    :param file stderr: The stream representing stderr.\n    \"\"\"\n    self._exiter = exiter\n    self._args = args\n    self._env = env\n    self._bootstrap_options = bootstrap_options\n    self._stdin = stdin or sys.stdin\n    self._stdout = stdout or sys.stdout\n    self._stderr = stderr or sys.stderr\n\n  @memoized_property\n  def pantsd(self):\n    return PantsDaemon.Factory.create(bootstrap_options=self._bootstrap_options)\n\n  @contextmanager\n  def _trapped_control_c(self, client):\n    \"\"\"A contextmanager that overrides the SIGINT (control-c) handler and handles it remotely.\"\"\"\n    def handle_control_c(signum, frame):\n      client.send_control_c()\n\n    existing_sigint_handler = signal.signal(signal.SIGINT, handle_control_c)\n    signal.siginterrupt(signal.SIGINT, False)  # Retry interrupted system calls.\n    try:\n      yield\n    finally:\n      signal.signal(signal.SIGINT, existing_sigint_handler)\n\n  def _setup_logging(self):\n    \"\"\"Sets up basic stdio logging for the thin client.\"\"\"\n    log_level = logging.getLevelName(self._bootstrap_options.for_global_scope().level.upper())\n\n    formatter = logging.Formatter('%(levelname)s] %(message)s')\n    handler = logging.StreamHandler(sys.stdout)\n    handler.setLevel(log_level)\n    handler.setFormatter(formatter)\n\n    root = logging.getLogger()\n    root.setLevel(log_level)\n    root.addHandler(handler)\n\n  def _connect_and_execute(self, port):\n    # Merge the nailgun TTY capability environment variables with the passed environment dict.\n    ng_env = NailgunProtocol.isatty_to_env(self._stdin, self._stdout, self._stderr)\n    modified_env = combined_dict(self._env, ng_env)\n\n    assert isinstance(port, int), 'port {} is not an integer!'.format(port)\n\n    # Instantiate a NailgunClient.\n    client = NailgunClient(port=port,\n                           ins=self._stdin,\n                           out=self._stdout,\n                           err=self._stderr,\n                           exit_on_broken_pipe=True)\n\n    with self._trapped_control_c(client):\n      # Execute the command on the pailgun.\n      result = client.execute(self.PANTS_COMMAND, *self._args, **modified_env)\n\n    # Exit.\n    self._exiter.exit(result)\n\n  def run(self, args=None):\n    self._setup_logging()\n    port = self.pantsd.maybe_launch()\n\n    logger.debug('connecting to pailgun on port {}'.format(port))\n    try:\n      self._connect_and_execute(port)\n    except self.RECOVERABLE_EXCEPTIONS as e:\n      raise self.Fallback(e)\n", "path": "src/python/pants/bin/remote_pants_runner.py"}], "after_files": [{"content": "# coding=utf-8\n# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import (absolute_import, division, generators, nested_scopes, print_function,\n                        unicode_literals, with_statement)\n\nimport logging\nimport signal\nimport sys\nfrom contextlib import contextmanager\n\nfrom pants.java.nailgun_client import NailgunClient\nfrom pants.java.nailgun_protocol import NailgunProtocol\nfrom pants.pantsd.pants_daemon import PantsDaemon\nfrom pants.util.collections import combined_dict\nfrom pants.util.memo import memoized_property\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass RemotePantsRunner(object):\n  \"\"\"A thin client variant of PantsRunner.\"\"\"\n\n  class Fallback(Exception):\n    \"\"\"Raised when fallback to an alternate execution mode is requested.\"\"\"\n\n  class PortNotFound(Exception):\n    \"\"\"Raised when the pailgun port can't be found.\"\"\"\n\n  PANTS_COMMAND = 'pants'\n  RECOVERABLE_EXCEPTIONS = (PortNotFound, NailgunClient.NailgunConnectionError)\n\n  def __init__(self, exiter, args, env, bootstrap_options, stdin=None, stdout=None, stderr=None):\n    \"\"\"\n    :param Exiter exiter: The Exiter instance to use for this run.\n    :param list args: The arguments (e.g. sys.argv) for this run.\n    :param dict env: The environment (e.g. os.environ) for this run.\n    :param Options bootstrap_options: The Options bag containing the bootstrap options.\n    :param file stdin: The stream representing stdin.\n    :param file stdout: The stream representing stdout.\n    :param file stderr: The stream representing stderr.\n    \"\"\"\n    self._exiter = exiter\n    self._args = args\n    self._env = env\n    self._bootstrap_options = bootstrap_options\n    self._stdin = stdin or sys.stdin\n    self._stdout = stdout or sys.stdout\n    self._stderr = stderr or sys.stderr\n\n  @memoized_property\n  def pantsd(self):\n    return PantsDaemon.Factory.create(bootstrap_options=self._bootstrap_options)\n\n  @contextmanager\n  def _trapped_signals(self, client):\n    \"\"\"A contextmanager that overrides the SIGINT (control-c) and SIGQUIT (control-\\) handlers\n    and handles them remotely.\"\"\"\n    def handle_control_c(signum, frame):\n      client.send_control_c()\n\n    existing_sigint_handler = signal.signal(signal.SIGINT, handle_control_c)\n    # N.B. SIGQUIT will abruptly kill the pantsd-runner, which will shut down the other end\n    # of the Pailgun connection - so we send a gentler SIGINT here instead.\n    existing_sigquit_handler = signal.signal(signal.SIGQUIT, handle_control_c)\n\n    # Retry interrupted system calls.\n    signal.siginterrupt(signal.SIGINT, False)\n    signal.siginterrupt(signal.SIGQUIT, False)\n    try:\n      yield\n    finally:\n      signal.signal(signal.SIGINT, existing_sigint_handler)\n      signal.signal(signal.SIGQUIT, existing_sigquit_handler)\n\n  def _setup_logging(self):\n    \"\"\"Sets up basic stdio logging for the thin client.\"\"\"\n    log_level = logging.getLevelName(self._bootstrap_options.for_global_scope().level.upper())\n\n    formatter = logging.Formatter('%(levelname)s] %(message)s')\n    handler = logging.StreamHandler(sys.stdout)\n    handler.setLevel(log_level)\n    handler.setFormatter(formatter)\n\n    root = logging.getLogger()\n    root.setLevel(log_level)\n    root.addHandler(handler)\n\n  def _connect_and_execute(self, port):\n    # Merge the nailgun TTY capability environment variables with the passed environment dict.\n    ng_env = NailgunProtocol.isatty_to_env(self._stdin, self._stdout, self._stderr)\n    modified_env = combined_dict(self._env, ng_env)\n\n    assert isinstance(port, int), 'port {} is not an integer!'.format(port)\n\n    # Instantiate a NailgunClient.\n    client = NailgunClient(port=port,\n                           ins=self._stdin,\n                           out=self._stdout,\n                           err=self._stderr,\n                           exit_on_broken_pipe=True)\n\n    with self._trapped_signals(client):\n      # Execute the command on the pailgun.\n      result = client.execute(self.PANTS_COMMAND, *self._args, **modified_env)\n\n    # Exit.\n    self._exiter.exit(result)\n\n  def run(self, args=None):\n    self._setup_logging()\n    port = self.pantsd.maybe_launch()\n\n    logger.debug('connecting to pailgun on port {}'.format(port))\n    try:\n      self._connect_and_execute(port)\n    except self.RECOVERABLE_EXCEPTIONS as e:\n      raise self.Fallback(e)\n", "path": "src/python/pants/bin/remote_pants_runner.py"}]}