def test_ruleset_with_failure_due_to_incompatible_subject_for_singleton(self): @rule(D, [C]) def d_from_c(c): pass @rule(B, []) def b_singleton(): return B() rules = [ RootRule(A), d_from_c, b_singleton, ] with self.assertRaises(Exception) as cm: create_scheduler(rules) # This error message could note near matches like the singleton. self.assert_equal_with_printing(dedent(""" Rules with errors: 1 (D, [C], d_from_c()): No rule was available to compute C with parameter type A """).strip(), str(cm.exception))
def test_ruleset_with_superclass_of_selected_type_produced_fails(self): @rule(A, [B]) def a_from_b(b): pass @rule(B, [SubA]) def b_from_suba(suba): pass rules = [ RootRule(C), a_from_b, b_from_suba, ] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing(dedent(""" Rules with errors: 2 (A, [B], a_from_b()): No rule was available to compute B with parameter type C (B, [SubA], b_from_suba()): No rule was available to compute SubA with parameter type C """).strip(), str(cm.exception))
def test_not_fulfillable_duplicated_dependency(self): # If a rule depends on another rule+subject in two ways, and one of them is unfulfillable # Only the unfulfillable one should be in the errors. @rule(B, [D]) def b_from_d(d): pass @rule(D, [A, SubA]) def d_from_a_and_suba(a, suba): _ = yield Get(A, C, C()) # noqa: F841 @rule(A, [C]) def a_from_c(c): pass rules = _suba_root_rules + [ b_from_d, d_from_a_and_suba, a_from_c, ] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing(dedent(""" Rules with errors: 2 (B, [D], b_from_d()): No rule was available to compute D with parameter type SubA (D, [A, SubA], [Get(A, C)], d_from_a_and_suba()): No rule was available to compute A with parameter type SubA """).strip(), str(cm.exception))
def test_ruleset_with_selector_only_provided_as_root_subject(self): @rule(A, [B]) def a_from_b(b): pass rules = [RootRule(B), a_from_b] create_scheduler(rules)
def test_ruleset_with_ambiguity(self): @rule(A, [C, B]) def a_from_c_and_b(c, b): pass @rule(A, [B, C]) def a_from_b_and_c(b, c): pass @rule(D, [A]) def d_from_a(a): pass rules = [ a_from_c_and_b, a_from_b_and_c, RootRule(B), RootRule(C), # TODO: Without a rule triggering the selection of A, we don't detect ambiguity here. d_from_a, ] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing( dedent(""" Rules with errors: 1 (D, [A], d_from_a()): Ambiguous rules to compute A with parameter types (B+C): (A, [B, C], a_from_b_and_c()) for (B+C) (A, [C, B], a_from_c_and_b()) for (B+C) """).strip(), str(cm.exception))
def test_ruleset_with_ambiguity(self): @rule(A, [C, B]) def a_from_c_and_b(c, b): pass @rule(A, [B, C]) def a_from_b_and_c(b, c): pass @rule(D, [A]) def d_from_a(a): pass rules = [ a_from_c_and_b, a_from_b_and_c, RootRule(B), RootRule(C), # TODO: Without a rule triggering the selection of A, we don't detect ambiguity here. d_from_a, ] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing(dedent(""" Rules with errors: 1 (D, [A], d_from_a()): Ambiguous rules to compute A with parameter types (B+C): (A, [B, C], a_from_b_and_c()) for (B+C) (A, [C, B], a_from_c_and_b()) for (B+C) """).strip(), str(cm.exception))
def test_ruleset_with_superclass_of_selected_type_produced_fails(self): @rule(A, [B]) def a_from_b(b): pass @rule(B, [SubA]) def b_from_suba(suba): pass rules = [ RootRule(C), a_from_b, b_from_suba, ] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing( dedent(""" Rules with errors: 2 (A, [B], a_from_b()): No rule was available to compute B with parameter type C (B, [SubA], b_from_suba()): No rule was available to compute SubA with parameter type C """).strip(), str(cm.exception))
def test_ruleset_with_failure_due_to_incompatible_subject_for_singleton( self): @rule(D, [C]) def d_from_c(c): pass @rule(B, []) def b_singleton(): return B() rules = [ RootRule(A), d_from_c, b_singleton, ] with self.assertRaises(Exception) as cm: create_scheduler(rules) # This error message could note near matches like the singleton. self.assert_equal_with_printing( dedent(""" Rules with errors: 1 (D, [C], d_from_c()): No rule was available to compute C with parameter type A """).strip(), str(cm.exception))
def test_not_fulfillable_duplicated_dependency(self): # If a rule depends on another rule+subject in two ways, and one of them is unfulfillable # Only the unfulfillable one should be in the errors. @rule(B, [D]) def b_from_d(d): pass @rule(D, [A, SubA]) def d_from_a_and_suba(a, suba): _ = yield Get(A, C, C()) # noqa: F841 @rule(A, [C]) def a_from_c(c): pass rules = _suba_root_rules + [ b_from_d, d_from_a_and_suba, a_from_c, ] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing( dedent(""" Rules with errors: 2 (B, [D], b_from_d()): No rule was available to compute D with parameter type SubA (D, [A, SubA], [Get(A, C)], d_from_a_and_suba()): No rule was available to compute A with parameter type SubA """).strip(), str(cm.exception))
def test_unreachable_rule(self): """Test that when one rule "shadows" another, we get an error.""" @rule(D, []) def d_singleton(): yield D() @rule(D, [B]) def d_for_b(b): yield D() rules = [ d_singleton, d_for_b, RootRule(B), ] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing( dedent(""" Rules with errors: 1 (D, [B], d_for_b()): Was not usable by any other @rule. """).strip(), str(cm.exception))
def test_ruleset_with_rule_with_two_missing_selects(self): rules = _suba_root_rules + [TaskRule(A, [Select(B), Select(C)], noop)] with self.assertRaises(ValueError) as cm: create_scheduler(rules) self.assert_equal_with_printing(dedent(""" Rules with errors: 1 (A, (Select(B), Select(C)), noop): no matches for Select(B) with subject types: SubA no matches for Select(C) with subject types: SubA """).strip(), str(cm.exception))
def test_ruleset_with_rule_with_two_missing_selects(self): rules = _suba_root_rules + [TaskRule(A, [Select(B), Select(C)], noop)] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing( dedent(""" Rules with errors: 1 (A, (Select(B), Select(C)), noop): no rule was available to compute B for subject type SubA no rule was available to compute C for subject type SubA """).strip(), str(cm.exception))
def test_ruleset_with_rule_with_two_missing_selects(self): rules = _suba_root_rules + [TaskRule(A, [Select(B), Select(C)], noop)] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing(dedent(""" Rules with errors: 1 (A, (Select(B), Select(C)), noop): no rule was available to compute B for subject type SubA no rule was available to compute C for subject type SubA """).strip(), str(cm.exception))
def test_ruleset_with_missing_product_type(self): rules = _suba_root_rules + [TaskRule(A, [Select(B)], noop)] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing( dedent(""" Rules with errors: 1 (A, [Select(B)], noop): No rule was available to compute B with parameter type SubA """).strip(), str(cm.exception))
def test_ruleset_with_explicit_type_constraint(self): @rule(Exactly(A), [Select(B)]) def a_from_b(b): pass @rule(B, [Select(A)]) def b_from_a(a): pass rules = _suba_root_rules + [ a_from_b, b_from_a, ] create_scheduler(rules)
def test_ruleset_with_rule_with_two_missing_selects(self): @rule(A, [B, C]) def a_from_b_and_c(b, c): pass rules = _suba_root_rules + [a_from_b_and_c] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing(dedent(""" Rules with errors: 1 (A, [B, C], a_from_b_and_c()): No rule was available to compute B with parameter type SubA No rule was available to compute C with parameter type SubA """).strip(), str(cm.exception))
def test_ruleset_with_missing_product_type(self): @rule(A, [B]) def a_from_b_noop(b): pass rules = _suba_root_rules + [a_from_b_noop] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing( dedent(""" Rules with errors: 1 (A, [B], a_from_b_noop()): No rule was available to compute B with parameter type SubA """).strip(), str(cm.exception))
def test_ruleset_with_rule_with_two_missing_selects(self): @rule(A, [B, C]) def a_from_b_and_c(b, c): pass rules = _suba_root_rules + [a_from_b_and_c] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing( dedent(""" Rules with errors: 1 (A, [B, C], a_from_b_and_c()): No rule was available to compute B with parameter type SubA No rule was available to compute C with parameter type SubA """).strip(), str(cm.exception))
def test_trace_includes_rule_exception_traceback(self): rules = [ RootRule(B), TaskRule(A, [Select(B)], nested_raise) ] scheduler = create_scheduler(rules) request = scheduler._native.new_execution_request() subject = B() scheduler.add_root_selection(request, subject, A) session = scheduler.new_session() scheduler._run_and_return_roots(session._session, request) trace = '\n'.join(scheduler.graph_trace(request)) # NB removing location info to make trace repeatable trace = remove_locations_from_traceback(trace) assert_equal_with_printing(self, dedent(''' Computing Select(<pants_test.engine.test_scheduler.B object at 0xEEEEEEEEE>, =A) Computing Task(nested_raise, <pants_test.engine.test_scheduler.B object at 0xEEEEEEEEE>, =A) Throw(An exception for B) Traceback (most recent call last): File LOCATION-INFO, in call val = func(*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\n', # Traces include two empty lines after. trace)
def test_trace_includes_rule_exception_traceback(self): rules = [ RootRule(B), nested_raise, ] scheduler = create_scheduler(rules) request = scheduler._native.new_execution_request() subject = B() scheduler.add_root_selection(request, subject, A) session = scheduler.new_session() scheduler._run_and_return_roots(session._session, request) trace = '\n'.join(scheduler.graph_trace(request)) # NB removing location info to make trace repeatable trace = remove_locations_from_traceback(trace) assert_equal_with_printing( self, dedent(''' Computing Select(<pants_test.engine.test_scheduler.B object at 0xEEEEEEEEE>, A) Computing Task(nested_raise(), <pants_test.engine.test_scheduler.B object at 0xEEEEEEEEE>, A, true) Throw(An exception for B) Traceback (most recent call last): File LOCATION-INFO, in call val = func(*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\n', # Traces include two empty lines after. trace)
def test_ruleset_with_missing_product_type(self): @rule(A, [B]) def a_from_b_noop(b): pass rules = _suba_root_rules + [a_from_b_noop] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing(dedent(""" Rules with errors: 1 (A, [B], a_from_b_noop()): No rule was available to compute B with parameter type SubA """).strip(), str(cm.exception))
def test_ruleset_with_failure_due_to_incompatible_subject_for_singleton(self): rules = [ RootRule(A), TaskRule(D, [Select(C)], noop), SingletonRule(B, B()), ] with self.assertRaises(ValueError) as cm: create_scheduler(rules) # This error message could note near matches like the singleton. self.assert_equal_with_printing(dedent(""" Rules with errors: 1 (D, (Select(C),), noop): no matches for Select(C) with subject types: A """).strip(), str(cm.exception))
def test_ruleset_with_superclass_of_selected_type_produced_fails(self): rules = [ RootRule(C), TaskRule(A, [Select(B)], noop), TaskRule(B, [Select(SubA)], noop) ] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing( dedent(""" Rules with errors: 2 (A, [Select(B)], noop): No rule was available to compute B with parameter type C (B, [Select(SubA)], noop): No rule was available to compute SubA with parameter type C """).strip(), str(cm.exception))
def test_ruleset_with_superclass_of_selected_type_produced_fails(self): rules = [ RootRule(C), TaskRule(A, [Select(B)], noop), TaskRule(B, [Select(SubA)], noop) ] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing(dedent(""" Rules with errors: 2 (A, (Select(B),), noop): no rule was available to compute B for subject type C (B, (Select(SubA),), noop): no rule was available to compute SubA for subject type C """).strip(), str(cm.exception))
def test_ruleset_with_superclass_of_selected_type_produced_fails(self): rules = [ RootRule(C), TaskRule(A, [Select(B)], noop), TaskRule(B, [Select(SubA)], noop) ] with self.assertRaises(ValueError) as cm: create_scheduler(rules) self.assert_equal_with_printing(dedent(""" Rules with errors: 2 (A, (Select(B),), noop): depends on unfulfillable (B, (Select(SubA),), noop) of C with subject types: C (B, (Select(SubA),), noop): no matches for Select(SubA) with subject types: C """).strip(), str(cm.exception))
def test_ruleset_with_failure_due_to_incompatible_subject_for_singleton( self): rules = [ RootRule(A), TaskRule(D, [Select(C)], noop), SingletonRule(B, B()), ] with self.assertRaises(Exception) as cm: create_scheduler(rules) # This error message could note near matches like the singleton. self.assert_equal_with_printing( dedent(""" Rules with errors: 1 (D, [Select(C)], noop): No rule was available to compute C with parameter type A """).strip(), str(cm.exception))
def create_subgraph(self, requested_product, rules, subject, validate=True): scheduler = create_scheduler(rules + _suba_root_rules, validate=validate) return "\n".join( scheduler.rule_subgraph_visualization(type(subject), requested_product))
def test_not_fulfillable_duplicated_dependency(self): # If a rule depends on another rule+subject in two ways, and one of them is unfulfillable # Only the unfulfillable one should be in the errors. rules = _suba_root_rules + [ TaskRule(B, [Select(D)], noop), TaskRule(D, [Select(A), Select(SubA)], noop, input_gets=[Get(A, C)]), TaskRule(A, [Select(SubA)], noop) ] with self.assertRaises(ValueError) as cm: create_scheduler(rules) self.assert_equal_with_printing(dedent(""" Rules with errors: 2 (B, (Select(D),), noop): depends on unfulfillable (D, (Select(A), Select(SubA)), [Get(A, C)], noop) of SubA with subject types: SubA (D, (Select(A), Select(SubA)), [Get(A, C)], noop): depends on unfulfillable (A, (Select(SubA),), noop) of C with subject types: SubA """).strip(), str(cm.exception))
def test_ruleset_with_ambiguity(self): rules = [ TaskRule(A, [Select(C), Select(B)], noop), TaskRule(A, [Select(B), Select(C)], noop), RootRule(B), RootRule(C), # TODO: Without a rule triggering the selection of A, we don't detect ambiguity here. TaskRule(D, [Select(A)], noop), ] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing( dedent(""" Rules with errors: 1 (D, [Select(A)], noop): ambiguous rules for Select(A) with parameter types (B+C): (A, [Select(B), Select(C)], noop) for (B+C) (A, [Select(C), Select(B)], noop) for (B+C) """).strip(), str(cm.exception))
def test_not_fulfillable_duplicated_dependency(self): # If a rule depends on another rule+subject in two ways, and one of them is unfulfillable # Only the unfulfillable one should be in the errors. rules = _suba_root_rules + [ TaskRule(B, [Select(D)], noop), TaskRule( D, [Select(A), Select(SubA)], noop, input_gets=[Get(A, C)]), TaskRule(A, [Select(C)], noop) ] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing( dedent(""" Rules with errors: 2 (B, [Select(D)], noop): No rule was available to compute D with parameter type SubA (D, [Select(A), Select(SubA)], [Get(A, C)], noop): No rule was available to compute A with parameter type SubA """).strip(), str(cm.exception))
def test_ruleset_with_explicit_type_constraint(self): rules = _suba_root_rules + [ TaskRule(Exactly(A), [Select(B)], noop), TaskRule(B, [Select(A)], noop) ] create_scheduler(rules)
def test_ruleset_with_selector_only_provided_as_root_subject(self): rules = [RootRule(B), TaskRule(A, [Select(B)], noop)] create_scheduler(rules)
def create_full_graph(self, rules, validate=True): scheduler = create_scheduler(rules, validate=validate) return "\n".join(scheduler.rule_graph_visualization())
def create_subgraph(self, requested_product, rules, subject, validate=True): scheduler = create_scheduler(rules + _suba_root_rules, validate=validate) return "\n".join(scheduler.rule_subgraph_visualization(type(subject), requested_product))