diff --git a/src/rust/engine/src/graph.rs b/src/rust/engine/src/graph.rs index c9ac3ca7fbb..76e91c961ba 100644 --- a/src/rust/engine/src/graph.rs +++ b/src/rust/engine/src/graph.rs @@ -379,11 +379,16 @@ impl InnerGraph { let is_bottom = |eid: EntryId| -> bool { match self.unsafe_entry_for_id(eid).peek::() { - None | Some(Err(Failure::Invalidated)) => false, Some(Err(Failure::Noop(..))) => true, Some(Err(Failure::Throw(..))) => false, Some(Ok(_)) => true, + None => { + // A Node with no state is either still running, or effectively cancelled + // because a dependent failed. In either case, it's not useful to render + // them, as we don't know whether they would have succeeded or failed. + true + } } }; diff --git a/tests/python/pants_test/engine/test_engine.py b/tests/python/pants_test/engine/test_engine.py index 612ba0a2226..63c4843a808 100644 --- a/tests/python/pants_test/engine/test_engine.py +++ b/tests/python/pants_test/engine/test_engine.py @@ -56,6 +56,14 @@ class B(object): pass +class C(object): + pass + + +class D(object): + pass + + def fn_raises(x): raise Exception('An exception for {}'.format(type(x).__name__)) @@ -126,3 +134,34 @@ def test_include_trace_error_raises_error_with_trace(self): Exception: An exception for B ''').lstrip()+'\n', remove_locations_from_traceback(str(cm.exception))) + + def test_trace_does_not_include_cancellations(self): + # Tests that when the computation of `Select(C)` fails, the cancellation of `Select(D)` + # is not rendered as a failure. + rules = [ + RootRule(B), + TaskRule(D, [Select(B)], D), + TaskRule(C, [Select(B)], nested_raise), + TaskRule(A, [Select(C), Select(D)], A), + ] + + scheduler = self.scheduler(rules, include_trace_on_error=True) + with self.assertRaises(Exception) as cm: + list(scheduler.product_request(A, subjects=[(B())])) + + self.assert_equal_with_printing(dedent(''' + Received unexpected Throw state(s): + Computing Select(, =A) + Computing Task(, , =A) + Computing Task(, , =C) + Throw(An exception for B) + Traceback (most recent call last): + File LOCATION-INFO, in extern_invoke_runnable + val = runnable(*args) + File LOCATION-INFO, in nested_raise + fn_raises(x) + File LOCATION-INFO, in fn_raises + raise Exception('An exception for {}'.format(type(x).__name__)) + Exception: An exception for B + ''').lstrip()+'\n', + remove_locations_from_traceback(str(cm.exception)))