#Setting a span to error (red in dagger.cloud) without leaving the job by bubbling an exception up?

1 messages · Page 1 of 1 (latest)

surreal scaffold
#

How do I make a span set itself as in an error state without bubbling an exception all the way up to the top of the job and erroring out of the whole task?

For example, this task could be an error state but I can't figure out how to say that it is:

    @dagger.function
    async dev lint() -> RuffLintResults:
        result = await (
            _ruff_container(self.source, await _get_version(self.source))
            .with_exec(
                [
                    "ruff",
                    "check",
                    "--output-format=gitlab",
                    "--output-file=/tmp/lint-report.json",
                ],
                expect=dagger.ReturnType.ANY,
            )
            .sync()
        )
        report_file = result.file("/tmp/lint-report.json")
        to_return RuffLintResults(
            exit_code=await result.exit_code(),
            stdout=await result.stdout(),
            stderr=await result.stderr(),
            report_file=report_file,
            report_contents=gitlab.CodeQualityIssue.list_from_json(await report_file.contents()),
        )

        if exit_code != 0:
            dag.set_this_span_as_red_and_broken("Here is a really well formatted error message describing where and how you should fix your commit.")

        return to_return

This is probably related to https://github.com/dagger/dagger/issues/8421

GitHub

What are you trying to do? Problem Dagger functions can currently return an error OR any other value but they can't do both at the same time. This makes it impossible to implement pipelines ste...

teal mason
sharp otter
#

there's no attribute - span statuses don't bubble up on their own, that's up to whether or not the function errors, and yeah in this case if the goal is to return a 'failed report' that leads to the linked issue

#

the current state of the art is to just print to logs and return an error (a short one - not a summary, anything longform should go into logs)

surreal scaffold
#

When you say return an error - do you mean a dag.error or raise an exception?

sharp otter
#

raise an exception

#

at least, i think so - haven't tried with the Python SDK. there is dag.error but I'm not sure if that SDK supports returning it and propagating in the same way

surreal scaffold
#

@sharp otter - interesting, that's not working either! That was actually the first thing I tried after finding #8421.

Here's what I attempted to do but I'm unsure if I messed something up for dagger specifically. Given what you said, however, I would expect this to work as expected! This was my first attempt - in the docstring example, if ruff.lint threw a LintCheckException, I'd expect the ruff lint span to show as red in dagger.cloud even though it was caught by an outside function. This was intended to mimic behavior from docker in dagger where images are not found in some repository but the build still passes.

Some notes about the code:

  • https://github.com/dagger/dagger/issues/4766 discusses python concurrency and was later added to the cookbook
  • anyio task groups are like WaitGroups (sort of, I think) in go (not a go expert at all!)
  • aioresult is a short library to do what could be done as closure capture in go (IIRC!)

(pt 2 coming up)

GitHub

What? Create a multi-language guide to cover the importance of thinking about concurrency for improving performance. It can make a significant impact, but many users aren't familiar with it. No...

GitHub

Capture the result of a Trio or anyio task. Contribute to arthur-tacca/aioresult development by creating an account on GitHub.

#
import logging
from collections.abc import Awaitable, Callable
from typing import Unpack

import aioresult
import anyio.abc

logger = logging.getLogger(__name__)


def start_soon_with_fallback[ResultT, *ArgsT](
    nursery: anyio.abc.TaskGroup,
    routine: Callable[[Unpack[ArgsT]], Awaitable[ResultT]],
    exception: type[Exception],
    no_exc_routine: Callable[[Unpack[ArgsT]], Awaitable[ResultT]],
    *args: *ArgsT,
) -> aioresult.ResultCapture[ResultT]:
    """Run a routine with a fallback routine in case of a specific exception type.

    This is intended to be used as utility for built-in tool chains in this repo.

    Looking at Ruff linting as an example, we have two methods:
    1. Ruff.lint
    2. Ruff.lint_no_exc

    lint wraps lint_no_exc and both return the same thing.
    However, a failure in lint is immediately apparent in dagger cloud.
    It shows up as a big, red, clickable span. # NOT TRUE FIXME

    Also however, a failure in lint doesn't get report files back to the user for an MR!
    This method is a workaround for that problem.

    >>> ruff_result = start_soon_with_fallback(tg, ruff.lint, LintCheckException, ruff.lint_no_exc)

    In this _dagger_ example, since lint_no_exc is wrapped by lint, we'll do no added work!
    This is because of dagger function caching.

    # Args: in-follow-up message due to discord length limitations
    """

    async def _run() -> ResultT:
        try:
            return await routine(*args)
        except exception as e:
            logger.error("Backup method failed", exc_info=e)
            return await no_exc_routine(*args)

    return aioresult.ResultCapture.start_soon(nursery, _run)
#
"""
    Args:
        nursery: The task group to run the routine in.
        routine: The routine to run.
        exception: The exception type to catch and recover from.
          Other exception types bubble up and cancel sibling tasks in the nursery (fail-fast).
        no_exc_routine: The backup routine to run in case of the exception.
        *args: The arguments to pass to the routine and backup routine.

    See:
        Function caching: https://docs.dagger.io/extending/function-caching
"""
sharp otter
#

to be clear the expected behavior is for the function to fail when an exception is raised, either from calling something that raises or from raising one yourself