Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: improve error output when call unwrap_or_return on a function … #80

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

acostapazo
Copy link
Contributor

…withou @early_return or @async_early_return decorators

…withou @early_return or @async_early_return decorators
@@ -3,12 +3,10 @@
from meiga import Error, Failure, Result, Success


class NoSuchKey(Error):
...
class NoSuchKey(Error): ...
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this depends on some ruff configuration. 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New versions of ruff handle things differently to older versions.

@@ -14,6 +14,7 @@ def expected_error(value: str, called_from: Union[str, None] = None, escape: boo
f"This exception wraps the following result -> Result[status: failure | value: {value}]"
f"\nIf you want to handle this error and return a Failure, please use early_return decorator on your function{called_from}."
f"\nMore info about how to use unwrap_or_return in combination with @early_return decorator on https://alice-biometrics.github.io/meiga/usage/result/#unwrap_or_return"
f"\nUse @async_early_return if your are calling from an async function."
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried to guess if caller is a regular function or an async one with something like the following:

import inspect
from typing import Union

class WaitingForEarlyReturn(Exception):
    result: 'AnyResult'  # Replace with the actual class/type if AnyResult is not defined
    called_from: Union[str, None]
    called_from_coroutine: bool = False

    def __init__(self, result: 'AnyResult') -> None:
        self.result = result
        try:
            # Get the stack frame
            stack = inspect.stack()[2]
            frame = stack.frame
            function_name = frame.f_code.co_name  # Get the name of the function
            filename = stack.filename.split("/")[-1]

            # We can't retrieve the actual function object, so we check by name
            self.called_from_coroutine = frame.f_globals.get(function_name) and inspect.iscoroutinefunction(frame.f_globals[function_name])

            # Create a descriptive string for where this was called from
            if self.called_from_coroutine:
                self.called_from = f"{function_name} (async) on {filename}"
            else:
                self.called_from = f"{function_name} on {filename}"

        except Exception:  # noqa
            self.called_from = None

        # Call the base class initializer
        Exception.__init__(self)

But, I think is not possible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why you say it doesn't work 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because frame.frame.f_globals.get(func) is alway None, so called_from_coroutine is always False.

This is because func is a string not an actual Callable.

If you modify the test, you'll notice the code never pass through called_from_coroutine positive if:

    @pytest.mark.asyncio
    async def should_log_hint_when_called_async_from_class_function_and_not_early_return(self):
        class MyClass:
            async def execute(self) -> AnyResult:
                result = Failure(Error())
                result.unwrap_or_return()
                return isSuccess

        with pytest.raises(
            WaitingForEarlyReturn,
            match=expected_error(
                "Error",
                called_from="execute (async) on test_waiting_for_early_return.py",  # <-------- THIS
                escape=True,
            ),
        ):
            await MyClass().execute()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I found a way (not very elegant). Check my last commit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants