# Buggy code
def reset_interactive_output_stream(cls, interactive_output_stream):
    """
    Class state:
    - Overwrites `cls._interactive_output_stream`.
    OS state:
    - Overwrites the SIGUSR2 handler.

    This is where the the error message on exit will be printed to as well.
    """
    try:
        # NB: mutate process-global state!
        if faulthandler.unregister(signal.SIGUSR2):
            logger.debug("re-registering a SIGUSR2 handler")
        # This permits a non-fatal `kill -31 <pants pid>` for stacktrace retrieval.
        faulthandler.register(
            signal.SIGUSR2, interactive_output_stream, all_threads=True, chain=False
        )

        # NB: mutate the class variables!
        # We don't *necessarily* need to keep a reference to this, but we do here for clarity.
        cls._interactive_output_stream = interactive_output_stream
    except ValueError:
        # Warn about "ValueError: IO on closed file" when stderr is closed.
        ExceptionSink.log_exception("Cannot reset output stream - sys.stderr is closed")


# Traceback (AssertionError)
23:26:42 01:10       [run]
============== test session starts ===============
platform darwin -- Python 2.7.10, pytest-3.6.4, py-1.7.0, pluggy-0.7.1 -- /System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
cachedir: .pants.d/.pytest_cache
rootdir: /Users/travis/build/pantsbuild/pants/.pants.d, inifile: /Users/travis/build/pantsbuild/pants/.pants.d/test/pytest-prep/CPython-2.7.10/e241a5f8b7de6d34689cc9307d8d06eb155c7c78/pytest.ini
plugins: timeout-1.2.1, cov-2.4.0
collecting ... collected 7 items

tests/python/pants_test/base/test_exception_sink_integration.py::ExceptionSinkIntegrationTest::test_dumps_logs_on_terminate <- ../../../../../../System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/case.py SKIPPED [ 14%]
tests/python/pants_test/base/test_exception_sink_integration.py::ExceptionSinkIntegrationTest::test_dumps_traceback_on_sigabrt <- pyprep/sources/4d9f3a72ced4c3b5cabbfead9552e9e3e78a30ca/pants_test/base/test_exception_sink_integration.py PASSED [ 28%]
tests/python/pants_test/base/test_exception_sink_integration.py::ExceptionSinkIntegrationTest::test_keyboardinterrupt_signals <- pyprep/sources/4d9f3a72ced4c3b5cabbfead9552e9e3e78a30ca/pants_test/base/test_exception_sink_integration.py FAILED [ 42%]
tests/python/pants_test/base/test_exception_sink_integration.py::ExceptionSinkIntegrationTest::test_logs_unhandled_exception <- pyprep/sources/4d9f3a72ced4c3b5cabbfead9552e9e3e78a30ca/pants_test/base/test_exception_sink_integration.py PASSED [ 57%]
tests/python/pants_test/base/test_exception_sink_integration.py::ExceptionSinkIntegrationTest::test_prints_traceback_on_sigusr2 <- pyprep/sources/4d9f3a72ced4c3b5cabbfead9552e9e3e78a30ca/pants_test/base/test_exception_sink_integration.py PASSED [ 71%]
tests/python/pants_test/base/test_exception_sink_integration.py::ExceptionSinkIntegrationTest::test_reset_exiter <- pyprep/sources/4d9f3a72ced4c3b5cabbfead9552e9e3e78a30ca/pants_test/base/test_exception_sink_integration.py PASSED [ 85%]
tests/python/pants_test/base/test_exception_sink_integration.py::ExceptionSinkIntegrationTest::test_reset_interactive_output_stream <- pyprep/sources/4d9f3a72ced4c3b5cabbfead9552e9e3e78a30ca/pants_test/base/test_exception_sink_integration.py PASSED [100%]

==================== FAILURES ====================
ExceptionSinkIntegrationTest.test_keyboardinterrupt_signals

self = <pants_test.base.test_exception_sink_integration.ExceptionSinkIntegrationTest testMethod=test_keyboardinterrupt_signals>

def test_keyboardinterrupt_signals(self):
for interrupt_signal in [signal.SIGINT, signal.SIGQUIT]:
with self._send_signal_to_waiter_handle(interrupt_signal) as (workdir, waiter_run):
>         self.assertIn('Interrupted by user.\n', waiter_run.stderr_data)
E         AssertionError: u'Interrupted by user.\n' not found in u'From cffi callback <function extern_clone_val at 0x11098a938>:\nTraceback (most recent call last):\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/engine/native.py", line 427, in extern_clone_val\n    @ffi.def_extern()\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/base/exception_sink.py", line 351, in handle_signal_gracefully\n    raise KeyboardInterrupt(\'User interrupted execution with control-c!\')\nKeyboardInterrupt: User interrupted execution with control-c!\ntimestamp: 2018-11-29T23:28:22.606384\nException caught: (pants.build_graph.address_lookup_error.AddressLookupError)\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py", line 75, in <module>\n    main()\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py", line 71, in main\n    PantsLoader.run()\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py", line 67, in run\n    cls.load_and_execute(entrypoint)\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py", line 60, in load_and_execute\n    entrypoint_main()\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_exe.py", line 39, in main\n    PantsRunner(exiter, start_time=start_time).run()\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_runner.py", line 62, in run\n    return runner.run()\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/local_pants_runner.py", line 159, in run\n    self._run()\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/local_pants_runner.py", line 227, in _run\n    goal_runner_result = self._maybe_run_v1(run_tracker, reporting)\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/local_pants_runner.py", line 176, in _maybe_run_v1\n    return goal_runner_factory.create().run()\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/goal_runner.py", line 125, in create\n    goals, context = self._setup_context()\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/goal_runner.py", line 95, in _setup_context\n    self._root_dir\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/init/engine_initializer.py", line 226, in create_build_graph\n    for _ in graph.inject_roots_closure(target_roots):\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/engine/legacy/graph.py", line 217, in inject_roots_closure\n    for address in self._inject_specs(target_roots.specs):\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/engine/legacy/graph.py", line 270, in _inject_specs\n    subjects)\n  File "/usr/local/Cellar/python/2.7.14/Frameworks/Python.framework/Versions/2.7/lib/python2.7/contextlib.py", line 35, in __exit__\n    self.gen.throw(type, value, traceback)\n  File "/Users/travis/build/pantsbuild/pants/src/python/pants/engine/legacy/graph.py", line 237, in _resolve_context\n    \'Build graph construction failed: {} {}\'.format(type(e).__name__, str(e))\n\nException message: Build graph construction failed: RuntimeError cannot use from_handle() on NULL pointer\n\n\n'

.pants.d/pyprep/sources/4d9f3a72ced4c3b5cabbfead9552e9e3e78a30ca/pants_test/base/test_exception_sink_integration.py:151: AssertionError
-------------- Captured stdout call --------------
logs/exceptions.84142.log +++
logs/exceptions.84142.log >>> timestamp: 2018-11-29T23:28:22.605908
logs/exceptions.84142.log >>> process title: python /Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py --no-pantsrc --pants-workdir=/var/folders/bb/n7t3rs157850byt_jfdcq9k80000gn/T/tmpxqSksJ/.pants.d --kill-nailguns --print-exception-stacktrace=True --no-enable-pantsd run testprojects/src/python/coordinated_runs:waiter -- /var/folders/bb/n7t3rs157850byt_jfdcq9k80000gn/T/tmpxqSksJ/some_file
logs/exceptions.84142.log >>> sys.argv: ['/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py', '--no-pantsrc', '--pants-workdir=/var/folders/bb/n7t3rs157850byt_jfdcq9k80000gn/T/tmpxqSksJ/.pants.d', '--kill-nailguns', '--print-exception-stacktrace=True', '--no-enable-pantsd', 'run', 'testprojects/src/python/coordinated_runs:waiter', '--', '/var/folders/bb/n7t3rs157850byt_jfdcq9k80000gn/T/tmpxqSksJ/some_file']
logs/exceptions.84142.log >>> pid: 84142
logs/exceptions.84142.log >>> Exception caught: (pants.build_graph.address_lookup_error.AddressLookupError)
logs/exceptions.84142.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py", line 75, in <module>
logs/exceptions.84142.log >>> main()
logs/exceptions.84142.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py", line 71, in main
logs/exceptions.84142.log >>> PantsLoader.run()
logs/exceptions.84142.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py", line 67, in run
logs/exceptions.84142.log >>> cls.load_and_execute(entrypoint)
logs/exceptions.84142.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py", line 60, in load_and_execute
logs/exceptions.84142.log >>> entrypoint_main()
logs/exceptions.84142.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_exe.py", line 39, in main
logs/exceptions.84142.log >>> PantsRunner(exiter, start_time=start_time).run()
logs/exceptions.84142.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_runner.py", line 62, in run
logs/exceptions.84142.log >>> return runner.run()
logs/exceptions.84142.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/local_pants_runner.py", line 159, in run
logs/exceptions.84142.log >>> self._run()
logs/exceptions.84142.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/local_pants_runner.py", line 227, in _run
logs/exceptions.84142.log >>> goal_runner_result = self._maybe_run_v1(run_tracker, reporting)
logs/exceptions.84142.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/local_pants_runner.py", line 176, in _maybe_run_v1
logs/exceptions.84142.log >>> return goal_runner_factory.create().run()
logs/exceptions.84142.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/goal_runner.py", line 125, in create
logs/exceptions.84142.log >>> goals, context = self._setup_context()
logs/exceptions.84142.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/goal_runner.py", line 95, in _setup_context
logs/exceptions.84142.log >>> self._root_dir
logs/exceptions.84142.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/init/engine_initializer.py", line 226, in create_build_graph
logs/exceptions.84142.log >>> for _ in graph.inject_roots_closure(target_roots):
logs/exceptions.84142.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/engine/legacy/graph.py", line 217, in inject_roots_closure
logs/exceptions.84142.log >>> for address in self._inject_specs(target_roots.specs):
logs/exceptions.84142.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/engine/legacy/graph.py", line 270, in _inject_specs
logs/exceptions.84142.log >>> subjects)
logs/exceptions.84142.log >>> File "/usr/local/Cellar/python/2.7.14/Frameworks/Python.framework/Versions/2.7/lib/python2.7/contextlib.py", line 35, in __exit__
logs/exceptions.84142.log >>> self.gen.throw(type, value, traceback)
logs/exceptions.84142.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/engine/legacy/graph.py", line 237, in _resolve_context
logs/exceptions.84142.log >>> 'Build graph construction failed: {} {}'.format(type(e).__name__, str(e))
logs/exceptions.84142.log >>>
logs/exceptions.84142.log >>> Exception message: Build graph construction failed: RuntimeError cannot use from_handle() on NULL pointer
logs/exceptions.84142.log >>>
logs/exceptions.84142.log ---
logs/exceptions.log +++
logs/exceptions.log >>> timestamp: 2018-11-29T23:28:22.605908
logs/exceptions.log >>> process title: python /Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py --no-pantsrc --pants-workdir=/var/folders/bb/n7t3rs157850byt_jfdcq9k80000gn/T/tmpxqSksJ/.pants.d --kill-nailguns --print-exception-stacktrace=True --no-enable-pantsd run testprojects/src/python/coordinated_runs:waiter -- /var/folders/bb/n7t3rs157850byt_jfdcq9k80000gn/T/tmpxqSksJ/some_file
logs/exceptions.log >>> sys.argv: ['/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py', '--no-pantsrc', '--pants-workdir=/var/folders/bb/n7t3rs157850byt_jfdcq9k80000gn/T/tmpxqSksJ/.pants.d', '--kill-nailguns', '--print-exception-stacktrace=True', '--no-enable-pantsd', 'run', 'testprojects/src/python/coordinated_runs:waiter', '--', '/var/folders/bb/n7t3rs157850byt_jfdcq9k80000gn/T/tmpxqSksJ/some_file']
logs/exceptions.log >>> pid: 84142
logs/exceptions.log >>> Exception caught: (pants.build_graph.address_lookup_error.AddressLookupError)
logs/exceptions.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py", line 75, in <module>
logs/exceptions.log >>> main()
logs/exceptions.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py", line 71, in main
logs/exceptions.log >>> PantsLoader.run()
logs/exceptions.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py", line 67, in run
logs/exceptions.log >>> cls.load_and_execute(entrypoint)
logs/exceptions.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_loader.py", line 60, in load_and_execute
logs/exceptions.log >>> entrypoint_main()
logs/exceptions.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_exe.py", line 39, in main
logs/exceptions.log >>> PantsRunner(exiter, start_time=start_time).run()
logs/exceptions.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/pants_runner.py", line 62, in run
logs/exceptions.log >>> return runner.run()
logs/exceptions.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/local_pants_runner.py", line 159, in run
logs/exceptions.log >>> self._run()
logs/exceptions.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/local_pants_runner.py", line 227, in _run
logs/exceptions.log >>> goal_runner_result = self._maybe_run_v1(run_tracker, reporting)
logs/exceptions.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/local_pants_runner.py", line 176, in _maybe_run_v1
logs/exceptions.log >>> return goal_runner_factory.create().run()
logs/exceptions.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/goal_runner.py", line 125, in create
logs/exceptions.log >>> goals, context = self._setup_context()
logs/exceptions.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/bin/goal_runner.py", line 95, in _setup_context
logs/exceptions.log >>> self._root_dir
logs/exceptions.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/init/engine_initializer.py", line 226, in create_build_graph
logs/exceptions.log >>> for _ in graph.inject_roots_closure(target_roots):
logs/exceptions.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/engine/legacy/graph.py", line 217, in inject_roots_closure
logs/exceptions.log >>> for address in self._inject_specs(target_roots.specs):
logs/exceptions.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/engine/legacy/graph.py", line 270, in _inject_specs
logs/exceptions.log >>> subjects)
logs/exceptions.log >>> File "/usr/local/Cellar/python/2.7.14/Frameworks/Python.framework/Versions/2.7/lib/python2.7/contextlib.py", line 35, in __exit__
logs/exceptions.log >>> self.gen.throw(type, value, traceback)
logs/exceptions.log >>> File "/Users/travis/build/pantsbuild/pants/src/python/pants/engine/legacy/graph.py", line 237, in _resolve_context
logs/exceptions.log >>> 'Build graph construction failed: {} {}'.format(type(e).__name__, str(e))
logs/exceptions.log >>>
logs/exceptions.log >>> Exception message: Build graph construction failed: RuntimeError cannot use from_handle() on NULL pointer
logs/exceptions.log >>>
logs/exceptions.log ---
generated xml file: /Users/travis/build/pantsbuild/pants/.pants.d/test/pytest/tests.python.pants_test.base.exception_sink_integration/junitxml/TEST-tests.python.pants_test.base.exception_sink_integration.xml
============ slowest 3 test durations ============
93.67s call     ../tests/python/pants_test/base/test_exception_sink_integration.py::ExceptionSinkIntegrationTest::test_dumps_traceback_on_sigabrt
6.08s call     ../tests/python/pants_test/base/test_exception_sink_integration.py::ExceptionSinkIntegrationTest::test_prints_traceback_on_sigusr2
5.55s call     ../tests/python/pants_test/base/test_exception_sink_integration.py::ExceptionSinkIntegrationTest::test_reset_interactive_output_stream
1 failed, 5 passed, 1 skipped in 118.85 seconds =

# Fixed code
def reset_interactive_output_stream(cls, interactive_output_stream):
    """
    Class state:
    - Overwrites `cls._interactive_output_stream`.
    OS state:
    - Overwrites the SIGUSR2 handler.

    This method registers a SIGUSR2 handler, which permits a non-fatal `kill -31 <pants pid>` for
    stacktrace retrieval. This is also where the the error message on fatal exit will be printed to.
    """
    try:
        # NB: mutate process-global state!
        # This permits a non-fatal `kill -31 <pants pid>` for stacktrace retrieval.
        faulthandler.register(
            signal.SIGUSR2, interactive_output_stream, all_threads=True, chain=False
        )
        # NB: mutate the class variables!
        cls._interactive_output_stream = interactive_output_stream
    except ValueError:
        # Warn about "ValueError: IO on closed file" when the stream is closed.
        cls.log_exception(
            "Cannot reset interactive_output_stream -- stream (probably stderr) is closed"
        )