returns icon indicating copy to clipboard operation
returns copied to clipboard

`assert_trace` does not work correctly with functions that use the same decorator

Open thepabloaguilar opened this issue 4 years ago • 6 comments

Bug report

What's wrong

Our assert_trace from pytest plugin doesn't work correctly when two or more different functions are using the same decorator, it not considers that those functions are different! See the example below:

def test_two_functios_using_same_decorator(returns: ReturnsAsserts):
    def decorator(func):
        def decorated(*args, **kwargs):
            return func(*args, **kwargs)
        return decorated

    @decorator
    def func_one():
        return Success(42)

    @decorator
    def func_two():
        return Success(42)

    with returns.assert_trace(Success, func_one):
        func_two()

That test should fail because we want to assert the Success creation inside func_one, but I'm calling func_two and the test is passing!

How is that should be

Our pytest plugin should consider that those functions are different!

System information

  • python version: 3.9
  • returns version: 0.16.0

thepabloaguilar avatar Mar 27 '21 06:03 thepabloaguilar

What if we use @wraps on def decorated? 🤔

sobolevn avatar Mar 27 '21 07:03 sobolevn

Also doesn't work, I tested with safe too (safe uses wraps) wraps just keep some metadata like name and docstring. But if we get the code from them they are the same

thepabloaguilar avatar Mar 27 '21 07:03 thepabloaguilar

I know how to solve this issue! Reading the Python docs I discovered that wrapped functions has the __wrapped__ attribute which we can use to get the "original" function!

The users just need to pay attention to use the wraps decorator inside their decorators!

Docs: https://docs.python.org/3/library/functools.html#functools.update_wrapper

thepabloaguilar avatar Nov 03 '22 05:11 thepabloaguilar

Let's add this to the docs :)

sobolevn avatar Nov 03 '22 08:11 sobolevn

I made the fix and broke other part 😆

The thing is, fixing this (with the actual code ive made) we won't be able to handle creations within decorators cause we're getting the most inner function, @safe won't work for example:

@safe
def _safe_decorated_function(return_failure: bool = False):
    if return_failure:
        raise ValueError('Function failed')

def test_something():
    with returns.assert_trace(container_type, _safe_decorated_function):
        _safe_decorated_function(True)

Failure is being created inside safe and not _safe_decorated_function

Suggestions?

thepabloaguilar avatar Nov 10 '22 03:11 thepabloaguilar

Suggestions?

No :(

sobolevn avatar Nov 10 '22 07:11 sobolevn