Exemplo n.º 1
0
    def test_transitive_params(self):
        # Test that C can be provided and implicitly converted into a B with transitive_b_c() to satisfy
        # the selectors of consumes_a_and_b().
        a, c = A(), C()
        result_str = self.request_single_product(str, Params(a, c))
        self.assertEquals(
            remove_locations_from_traceback(result_str),
            remove_locations_from_traceback(consumes_a_and_b(a, transitive_b_c(c))),
        )

        # Test that an inner Get in transitive_coroutine_rule() is able to resolve B from C due to the
        # existence of transitive_b_c().
        with self.assertDoesNotRaise():
            _ = self.request_single_product(D, Params(c))
Exemplo n.º 2
0
  def test_transitive_params(self):
    # Test that C can be provided and implicitly converted into a B with transitive_b_c() to satisfy
    # the selectors of consumes_a_and_b().
    a, c = A(), C()
    result_str, = self.scheduler.product_request(str, [Params(a, c)])
    self.assertEquals(remove_locations_from_traceback(result_str),
                      remove_locations_from_traceback(consumes_a_and_b(a, transitive_b_c(c))))

    # Test that an inner Get in transitive_coroutine_rule() is able to resolve B from C due to the
    # existence of transitive_b_c().
    result_d, = self.scheduler.product_request(D, [Params(c)])
    # We don't need the inner B objects to be the same, and we know the arguments are type-checked,
    # we're just testing transitively resolving products in this file.
    self.assertTrue(isinstance(result_d, D))
Exemplo n.º 3
0
    def test_include_trace_error_raises_error_with_trace(self):
        rules = [
            RootRule(B),
            nested_raise,
        ]

        scheduler = self.scheduler(rules, include_trace_on_error=True)
        with self.assertRaises(ExecutionError) as cm:
            list(scheduler.product_request(A, subjects=[(B())]))

        self.assert_equal_with_printing(
            dedent(
                f"""
                1 Exception encountered:
                Computing Select(<{__name__}.B object at 0xEEEEEEEEE>, A)
                  Computing Task({fmt_rust_function(nested_raise)}(), <{__name__}.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(f"An exception for {{type(x).__name__}}")
                      Exception: An exception for B
                """
            ).lstrip()
            + "\n",
            remove_locations_from_traceback(str(cm.exception)),
        )
Exemplo n.º 4
0
    def test_include_trace_error_raises_error_with_trace(self):
        rules = [
            RootRule(B),
            nested_raise,
        ]

        scheduler = self.scheduler(rules, include_trace_on_error=True)
        with self.assertRaises(ExecutionError) as cm:
            list(scheduler.product_request(A, subjects=[(B())]))

        self.assert_equal_with_printing(
            dedent(
                """
                1 Exception encountered:

                Traceback (most recent call last):
                  File LOCATION-INFO, in nested_raise
                    fn_raises(x)
                  File LOCATION-INFO, in fn_raises
                    raise Exception(f"An exception for {type(x).__name__}")
                Exception: An exception for B
                """
            ).lstrip(),
            remove_locations_from_traceback(str(cm.exception)),
        )
Exemplo n.º 5
0
    def test_trace_includes_rule_exception_traceback(self):
        # Execute a request that will trigger the nested raise, and then directly inspect its trace.
        request = self.scheduler.execution_request([A], [B()])
        _, throws = self.scheduler.execute(request)

        with self.assertRaises(ExecutionError) as cm:
            self.scheduler._raise_on_error([t for _, t in throws])

        trace = remove_locations_from_traceback(str(cm.exception))
        assert_equal_with_printing(
            self,
            dedent("""\
                 1 Exception encountered:

                 Engine traceback:
                   in Nested raise
                 Traceback (most recent call last):
                   File LOCATION-INFO, in nested_raise
                     fn_raises(x)
                   File LOCATION-INFO, in fn_raises
                     raise Exception(f"An exception for {type(x).__name__}")
                 Exception: An exception for B
                 """),
            trace,
        )
Exemplo n.º 6
0
    def test_trace_includes_rule_exception_traceback(self):
        # Execute a request that will trigger the nested raise, and then directly inspect its trace.
        request = self.scheduler.execution_request([A], [B()])
        self.scheduler.execute(request)

        trace = remove_locations_from_traceback("\n".join(self.scheduler.trace(request)))
        assert_equal_with_printing(
            self,
            dedent(
                f"""\
                Computing Select(B(), A)
                  Computing Task({fmt_rust_function(nested_raise)}(), B(), 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(f"An exception for {{type(x).__name__}}")
                      Exception: An exception for B"""
            )
            + "\n\n",  # Traces include two empty lines after.
            trace,
        )
Exemplo n.º 7
0
    def test_trace_multi(self):
        # Tests that when multiple distinct failures occur, they are each rendered.

        @rule
        def d_from_b_nested_raise(b: B) -> D:  # type: ignore[return]
            fn_raises(b)

        @rule
        def c_from_b_nested_raise(b: B) -> C:  # type: ignore[return]
            fn_raises(b)

        @rule
        def a_from_c_and_d(c: C, d: D) -> A:
            return A()

        rules = [
            RootRule(B),
            d_from_b_nested_raise,
            c_from_b_nested_raise,
            a_from_c_and_d,
        ]

        scheduler = self.scheduler(rules, include_trace_on_error=True)
        with self.assertRaises(ExecutionError) as cm:
            list(scheduler.product_request(A, subjects=[(B())]))

        self.assert_equal_with_printing(
            dedent(f"""
                1 Exception encountered:
                Computing Select(<{__name__}..B object at 0xEEEEEEEEE>, A)
                  Computing Task(a_from_c_and_d(), <{__name__}..B object at 0xEEEEEEEEE>, A, true)
                    Computing Task(d_from_b_nested_raise(), <{__name__}..B object at 0xEEEEEEEEE>, =D, true)
                      Throw(An exception for B)
                        Traceback (most recent call last):
                          File LOCATION-INFO, in call
                            val = func(*args)
                          File LOCATION-INFO, in d_from_b_nested_raise
                            fn_raises(b)
                          File LOCATION-INFO, in fn_raises
                            raise Exception('An exception for {{}}'.format(type(x).__name__))
                        Exception: An exception for B
        
        
                Computing Select(<{__name__}..B object at 0xEEEEEEEEE>, A)
                  Computing Task(a_from_c_and_d(), <{__name__}..B object at 0xEEEEEEEEE>, A, true)
                    Computing Task(c_from_b_nested_raise(), <{__name__}..B object at 0xEEEEEEEEE>, =C, true)
                      Throw(An exception for B)
                        Traceback (most recent call last):
                          File LOCATION-INFO, in call
                            val = func(*args)
                          File LOCATION-INFO, in c_from_b_nested_raise
                            fn_raises(b)
                          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)),
        )
Exemplo n.º 8
0
def assert_execution_error(test_case, expected_msg):
    with test_case.assertRaises(ExecutionError) as cm:
        yield
    test_case.assertIn(expected_msg,
                       remove_locations_from_traceback(str(cm.exception)))
Exemplo n.º 9
0
    def test_unhashable_failure(self):
        """Test that unhashable Get(...) params result in a structured error."""
        def assert_has_cffi_extern_traceback_header(exception: str) -> None:
            assert exception.startswith(
                dedent("""\
                    1 Exception raised in CFFI extern methods:
                    Traceback (most recent call last):
                    """))

        def assert_has_end_of_cffi_extern_error_traceback(
                exception: str) -> None:
            assert "TypeError: unhashable type: 'list'" in exception
            canonical_exception_text = dedent("""\
                    The above exception was the direct cause of the following exception:

                    Traceback (most recent call last):
                      File LOCATION-INFO, in extern_identify
                        return c.identify(obj)
                      File LOCATION-INFO, in identify
                        raise TypeError(f"failed to hash object {obj}: {e}") from e
                    TypeError: failed to hash object CollectionType(items=[1, 2, 3]): unhashable type: 'list'
                    """)

            assert canonical_exception_text in exception

        resulting_engine_error = dedent("""\
            Exception: Types that will be passed as Params at the root of a graph need to be registered via RootRule:
              Any\n\n\n""")

        # Test that the error contains the full traceback from within the CFFI context as well
        # (mentioning which specific extern method ended up raising the exception).
        with self.assertRaises(ExecutionError) as cm:
            self.request_single_product(C, Params(CollectionType([1, 2, 3])))
        exc_str = remove_locations_from_traceback(str(cm.exception))
        assert_has_cffi_extern_traceback_header(exc_str)
        assert_has_end_of_cffi_extern_error_traceback(exc_str)
        self.assertIn(
            dedent("""\
                The engine execution request raised this error, which is probably due to the errors in the
                CFFI extern methods listed above, as CFFI externs return None upon error:
                """),
            exc_str,
        )
        self.assertTrue(exc_str.endswith(resulting_engine_error),
                        f"exc_str was: {exc_str}")

        PATCH_OPTS = dict(autospec=True, spec_set=True)

        def create_cffi_exception():
            try:
                raise Exception("test cffi exception")
            except:  # noqa: T803
                return Native.CFFIExternMethodRuntimeErrorInfo(
                    *sys.exc_info()[0:3])

        # Test that CFFI extern method errors result in an ExecutionError, even if .execution_request()
        # succeeds.
        with self.assertRaises(ExecutionError) as cm:
            with unittest.mock.patch.object(SchedulerSession,
                                            "execution_request",
                                            **PATCH_OPTS) as mock_exe_request:
                with unittest.mock.patch.object(
                        Native, "_peek_cffi_extern_method_runtime_exceptions",
                        **PATCH_OPTS) as mock_cffi_exceptions:
                    mock_exe_request.return_value = None
                    mock_cffi_exceptions.return_value = [
                        create_cffi_exception()
                    ]
                    self.request_single_product(
                        C, Params(CollectionType([1, 2, 3])))
        exc_str = remove_locations_from_traceback(str(cm.exception))
        assert_has_cffi_extern_traceback_header(exc_str)
        self.assertIn("Exception: test cffi exception", exc_str)
        self.assertNotIn(resulting_engine_error, exc_str)

        # Test that an error in the .execution_request() method is propagated directly, even if there
        # are no CFFI extern methods.
        class TestError(Exception):
            pass

        with self.assertRaisesWithMessage(TestError, "non-CFFI error"):
            with unittest.mock.patch.object(SchedulerSession,
                                            "execution_request",
                                            **PATCH_OPTS) as mock_exe_request:
                mock_exe_request.side_effect = TestError("non-CFFI error")
                self.request_single_product(C, Params(CollectionType([1, 2,
                                                                      3])))
Exemplo n.º 10
0
  def test_unhashable_failure(self):
    """Test that unhashable Get(...) params result in a structured error."""

    def assert_has_cffi_extern_traceback_header(exc_str):
      self.assertTrue(exc_str.startswith(dedent("""\
        1 Exception raised in CFFI extern methods:
        Traceback (most recent call last):
        """)), f"exc_str was: {exc_str}")

    def assert_has_end_of_cffi_extern_error_traceback(exc_str):
      self.assertIn(dedent("""\
        Traceback (most recent call last):
          File LOCATION-INFO, in extern_identify
            return c.identify(obj)
          File LOCATION-INFO, in identify
            hash_ = hash(obj)
          File "<string>", line 2, in __hash__
        TypeError: unhashable type: 'list'
        """), exc_str, f"exc_str was: {exc_str}")

    resulting_engine_error = dedent("""\
        Exception: Types that will be passed as Params at the root of a graph need to be registered via RootRule:
          Any\n\n\n""")

    # Test that the error contains the full traceback from within the CFFI context as well
    # (mentioning which specific extern method ended up raising the exception).
    with self.assertRaises(ExecutionError) as cm:
      self.scheduler.product_request(C, [Params(CollectionType([1, 2, 3]))])
    exc_str = remove_locations_from_traceback(str(cm.exception))
    # TODO: convert these manual self.assertTrue() conditionals to a self.assertStartsWith() method
    # in TestBase!
    assert_has_cffi_extern_traceback_header(exc_str)
    assert_has_end_of_cffi_extern_error_traceback(exc_str)
    self.assertIn(dedent("""\
      The engine execution request raised this error, which is probably due to the errors in the
      CFFI extern methods listed above, as CFFI externs return None upon error:
      """), exc_str)
    self.assertTrue(exc_str.endswith(resulting_engine_error), f"exc_str was: {exc_str}")

    PATCH_OPTS = dict(autospec=True, spec_set=True)
    def create_cffi_exception():
      try:
        raise Exception('test cffi exception')
      except:                   # noqa: T803
        return Native.CFFIExternMethodRuntimeErrorInfo(*sys.exc_info()[0:3])

    # Test that CFFI extern method errors result in an ExecutionError, even if .execution_request()
    # succeeds.
    with self.assertRaises(ExecutionError) as cm:
      with unittest.mock.patch.object(SchedulerSession, 'execution_request',
                             **PATCH_OPTS) as mock_exe_request:
        with unittest.mock.patch.object(Native, '_peek_cffi_extern_method_runtime_exceptions',
                               **PATCH_OPTS) as mock_cffi_exceptions:
          mock_exe_request.return_value = None
          mock_cffi_exceptions.return_value = [create_cffi_exception()]
          self.scheduler.product_request(C, [Params(CollectionType([1, 2, 3]))])
    exc_str = remove_locations_from_traceback(str(cm.exception))
    assert_has_cffi_extern_traceback_header(exc_str)
    self.assertIn("Exception: test cffi exception", exc_str)
    self.assertNotIn(resulting_engine_error, exc_str)

    # Test that an error in the .execution_request() method is propagated directly, even if there
    # are no CFFI extern methods.
    class TestError(Exception): pass
    with self.assertRaisesWithMessage(TestError, 'non-CFFI error'):
      with unittest.mock.patch.object(SchedulerSession, 'execution_request',
                             **PATCH_OPTS) as mock_exe_request:
        mock_exe_request.side_effect = TestError('non-CFFI error')
        self.scheduler.product_request(C, [Params(CollectionType([1, 2, 3]))])