예제 #1
0
    def test_unreachable_rule(self):
        """Test that when one rule "shadows" another, we get an error."""
        @rule
        def d_singleton() -> D:
            return D()

        @rule
        def d_for_b(b: B) -> D:
            return D()

        rules = [
            d_singleton,
            d_for_b,
            RootRule(B),
        ]

        with self.assertRaises(Exception) as cm:
            create_scheduler(rules)

        self.assert_equal_with_printing(
            dedent(f"""\
                Rules with errors: 1

                  {fmt_rule(d_for_b)}:
                    Was not reachable, either because no rules could produce the params or because it was shadowed by another @rule.
                """).strip(),
            str(cm.exception),
        )
예제 #2
0
    def test_ruleset_with_failure_due_to_incompatible_subject_for_singleton(
            self):
        @rule
        def d_from_c(c: C) -> D:
            pass

        @rule
        def b_singleton() -> B:
            return B()

        rules = [
            RootRule(A),
            d_from_c,
            b_singleton,
        ]

        with self.assertRaises(Exception) as cm:
            create_scheduler(rules)

        # TODO: This error message could note near matches like the singleton.
        self.assert_equal_with_printing(
            dedent(f"""\
                Rules with errors: 1

                  {fmt_rule(d_from_c)}:
                    No rule was available to compute C with parameter type A
                """).strip(),
            str(cm.exception),
        )
예제 #3
0
    def test_ruleset_with_selector_only_provided_as_root_subject(self):
        @rule
        def a_from_b(b: B) -> A:
            pass

        rules = [RootRule(B), a_from_b]
        create_scheduler(rules)
예제 #4
0
    def test_ruleset_with_superclass_of_selected_type_produced_fails(self):
        @rule
        def a_from_b(b: B) -> A:
            pass

        @rule
        def b_from_suba(suba: SubA) -> B:
            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(f"""\
                Rules with errors: 2

                  {fmt_rule(a_from_b)}:
                    No rule was available to compute B with parameter type C

                  {fmt_rule(b_from_suba)}:
                    No rule was available to compute SubA with parameter type C
                """).strip(),
            str(cm.exception),
        )
예제 #5
0
    def test_unreachable_rule(self):
        """Test that when one rule "shadows" another, we get an error."""
        @rule
        def d_singleton() -> D:
            return D()

        @rule
        def d_for_b(b: B) -> D:
            return 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))
예제 #6
0
    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
        def b_from_d(d: D) -> B:
            pass

        @rule
        async def d_from_a_and_suba(a: A, suba: SubA) -> D:
            _ = await Get(A, C, C())  # noqa: F841

        @rule
        def a_from_c(c: C) -> A:
            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: 3
                        (A, [C], a_from_c()):
                          Was not usable by any other @rule.
                        (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))
예제 #7
0
    def test_ruleset_with_rule_with_two_missing_selects(self):
        @rule
        def a_from_b_and_c(b: B, c: C) -> A:
            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))
예제 #8
0
    def test_ruleset_with_missing_product_type(self):
        @rule
        def a_from_b_noop(b: B) -> A:
            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))
예제 #9
0
    def test_ruleset_with_ambiguity(self):
        @rule
        def a_from_b_and_c(b: B, c: C) -> A:
            pass

        @rule
        def a_from_c_and_b(c: C, b: B) -> A:
            pass

        @rule
        def d_from_a(a: A) -> D:
            pass

        rules = [
            a_from_b_and_c,
            a_from_c_and_b,
            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)

        assert_equal_graph_output(
            self,
            dedent(f"""\
                Rules with errors: 3

                  {fmt_rule(a_from_b_and_c)}:
                    Was not reachable, either because no rules could produce the params or because it was shadowed by another @rule.

                  {fmt_rule(a_from_c_and_b)}:
                    Was not reachable, either because no rules could produce the params or because it was shadowed by another @rule.

                  {fmt_rule(d_from_a)}:
                    Ambiguous rules to compute A with parameter types (B, C):
                      {fmt_graph_rule(a_from_b_and_c)}
                for (B, C)
                      {fmt_graph_rule(a_from_c_and_b)}
                for (B, C)
                """),
            str(cm.exception),
        )
예제 #10
0
    def test_ruleset_with_missing_product_type(self):
        @rule
        def a_from_b(b: B) -> A:
            pass

        rules = [RootRule(SubA), a_from_b]

        with self.assertRaises(Exception) as cm:
            create_scheduler(rules)

        self.assert_equal_with_printing(
            dedent(f"""\
                Rules with errors: 1

                  {fmt_rule(a_from_b)}:
                    No rule was available to compute B with parameter type SubA
                """).strip(),
            str(cm.exception),
        )
예제 #11
0
 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))
예제 #12
0
    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
        def a_from_c(c: C) -> A:
            pass

        @rule
        def b_from_d(d: D) -> B:
            pass

        @rule
        async def d_from_a_and_suba(a: A,
                                    suba: SubA) -> D:  # type: ignore[return]
            _ = await Get[A](C, C())  # noqa: F841

        rules = _suba_root_rules + [
            a_from_c,
            b_from_d,
            d_from_a_and_suba,
        ]

        with self.assertRaises(Exception) as cm:
            create_scheduler(rules)

        assert_equal_graph_output(
            self,
            dedent(f"""\
                Rules with errors: 3

                  {fmt_rule(a_from_c)}:
                    Was not reachable, either because no rules could produce the params or because it was shadowed by another @rule.
                  {fmt_rule(b_from_d)}:

                    No rule was available to compute D with parameter type SubA
                  {fmt_rule(d_from_a_and_suba, gets=[("A", "C")])}:

                    No rule was available to compute A with parameter type SubA
                """).strip(),
            str(cm.exception),
        )
예제 #13
0
    def test_ruleset_with_ambiguity(self):
        @rule
        def a_from_c_and_b(c: C, b: B) -> A:
            pass

        @rule
        def a_from_b_and_c(b: B, c: C) -> A:
            pass

        @rule
        def d_from_a(a: A) -> D:
            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: 3
                       (A, [B, C], a_from_b_and_c()):
                         Was not usable by any other @rule.
                       (A, [C, B], a_from_c_and_b()):
                         Was not usable by any other @rule.
                       (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))
예제 #14
0
 def create_full_graph(self, rules, validate=True):
     scheduler = create_scheduler(rules, validate=validate)
     return "\n".join(scheduler.rule_graph_visualization())