# Buggy code
def iter_chunks(cls, sock, return_bytes=False):
    """Generates chunks from a connected socket until an Exit chunk is sent."""
    while 1:
        chunk_type, payload = cls.read_chunk(sock, return_bytes)
        yield chunk_type, payload
        if chunk_type == ChunkType.EXIT:
            break


# 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 iter_chunks(cls, sock, return_bytes=False, timeout_object=None):
    """Generates chunks from a connected socket until an Exit chunk is sent or a timeout occurs.

    :param sock: the socket to read from.
    :param bool return_bytes: If False, decode the payload into a utf-8 string.
    :param cls.TimeoutProvider timeout_object: If provided, will be checked every iteration for a
                                               possible timeout.
    :raises: :class:`cls.ProcessStreamTimeout`
    """
    assert timeout_object is None or isinstance(timeout_object, cls.TimeoutProvider)
    orig_timeout_time = None
    timeout_interval = None
    while 1:
        if orig_timeout_time is not None:
            remaining_time = time.time() - (orig_timeout_time + timeout_interval)
            if remaining_time > 0:
                original_timestamp = datetime.datetime.fromtimestamp(
                    orig_timeout_time
                ).isoformat()
                raise cls.ProcessStreamTimeout(
                    "iterating over bytes from nailgun timed out with timeout interval {} starting at {}, "
                    "overtime seconds: {}".format(
                        timeout_interval, original_timestamp, remaining_time
                    )
                )
        elif timeout_object is not None:
            opts = timeout_object.maybe_timeout_options()
            if opts:
                orig_timeout_time = opts.start_time
                timeout_interval = opts.interval
                continue
            remaining_time = None
        else:
            remaining_time = None

        with cls._set_socket_timeout(sock, timeout=remaining_time):
            chunk_type, payload = cls.read_chunk(sock, return_bytes)
            yield chunk_type, payload
            if chunk_type == ChunkType.EXIT:
                break