Repository: conda/conda

Issue:
signal handler can only be used in main thread
Often get these with conda-build tests

https://travis-ci.org/conda/conda/jobs/225296134#L1380

```
Traceback (most recent call last):
  File "/home/travis/build/conda/conda/conda-build/conda_build/build.py", line 688, in create_env
    execute_actions(actions, index, verbose=config.debug)
  File "/home/travis/build/conda/conda/conda/plan.py", line 612, in execute_actions
    execute_instructions(plan, index, verbose)
  File "/home/travis/build/conda/conda/conda/instructions.py", line 243, in execute_instructions
    cmd(state, arg)
  File "/home/travis/build/conda/conda/conda/instructions.py", line 98, in PROGRESSIVEFETCHEXTRACT_CMD
    progressive_fetch_extract.execute()
  File "/home/travis/build/conda/conda/conda/core/package_cache.py", line 491, in execute
    with signal_handler(conda_signal_handler):
  File "/home/travis/miniconda/lib/python3.6/contextlib.py", line 82, in __enter__
    return next(self.gen)
  File "/home/travis/build/conda/conda/conda/common/signals.py", line 41, in signal_handler
    prev_handler = signal.signal(sig, handler)
  File "/home/travis/miniconda/lib/python3.6/signal.py", line 47, in signal
    handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
ValueError: signal only works in main thread
```
signal handler can only be used in main thread
Often get these with conda-build tests

https://travis-ci.org/conda/conda/jobs/225296134#L1380

```
Traceback (most recent call last):
  File "/home/travis/build/conda/conda/conda-build/conda_build/build.py", line 688, in create_env
    execute_actions(actions, index, verbose=config.debug)
  File "/home/travis/build/conda/conda/conda/plan.py", line 612, in execute_actions
    execute_instructions(plan, index, verbose)
  File "/home/travis/build/conda/conda/conda/instructions.py", line 243, in execute_instructions
    cmd(state, arg)
  File "/home/travis/build/conda/conda/conda/instructions.py", line 98, in PROGRESSIVEFETCHEXTRACT_CMD
    progressive_fetch_extract.execute()
  File "/home/travis/build/conda/conda/conda/core/package_cache.py", line 491, in execute
    with signal_handler(conda_signal_handler):
  File "/home/travis/miniconda/lib/python3.6/contextlib.py", line 82, in __enter__
    return next(self.gen)
  File "/home/travis/build/conda/conda/conda/common/signals.py", line 41, in signal_handler
    prev_handler = signal.signal(sig, handler)
  File "/home/travis/miniconda/lib/python3.6/signal.py", line 47, in signal
    handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
ValueError: signal only works in main thread
```


Discussion:

Resolving patch:
diff --git a/conda/common/signals.py b/conda/common/signals.py
--- a/conda/common/signals.py
+++ b/conda/common/signals.py
@@ -4,6 +4,7 @@
 from contextlib import contextmanager
 from logging import getLogger
 import signal
+import threading
 
 from .compat import iteritems
 
@@ -17,7 +18,6 @@
     'SIGBREAK',
 )
 
-
 def get_signal_name(signum):
     """
     Examples:
@@ -33,18 +33,23 @@ def get_signal_name(signum):
 
 @contextmanager
 def signal_handler(handler):
-    previous_handlers = []
+    _thread_local = threading.local()
+    _thread_local.previous_handlers = []
     for signame in INTERRUPT_SIGNALS:
         sig = getattr(signal, signame, None)
         if sig:
             log.debug("registering handler for %s", signame)
-            prev_handler = signal.signal(sig, handler)
-            previous_handlers.append((sig, prev_handler))
+            try:
+                prev_handler = signal.signal(sig, handler)
+                _thread_local.previous_handlers.append((sig, prev_handler))
+            except ValueError as e:
+                # ValueError: signal only works in main thread
+                log.debug('%r', e)
     try:
         yield
     finally:
         standard_handlers = signal.SIG_IGN, signal.SIG_DFL
-        for sig, previous_handler in previous_handlers:
+        for sig, previous_handler in _thread_local.previous_handlers:
             if callable(previous_handler) or previous_handler in standard_handlers:
                 log.debug("de-registering handler for %s", sig)
                 signal.signal(sig, previous_handler)
diff --git a/conda/exports.py b/conda/exports.py
--- a/conda/exports.py
+++ b/conda/exports.py
@@ -30,7 +30,7 @@
 from .gateways.connection import CondaSession  # NOQA
 CondaSession = CondaSession
 
-from .common.toposort import _toposort
+from .common.toposort import _toposort  # NOQA
 _toposort = _toposort
 
 from .gateways.disk.link import lchmod  # NOQA


Test patch: