You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Walrus operator causes different behavior in PyTest. #10743
I am currently testing the following function but find that the function passes the test in the normal Python terminal but fails in a PyTest run. The walrus operator is relatively new and not many people use it. I think that there may be an inconsistency in the execution environment.
import numpy as np
def test_walrus_conversion():
a = np.random.random(16)
assert not np.array_equal(a, a := a.astype(np.uint8))
assert np.all(a == 0)
Hi, I did some tests on this since I was curious about how this works.
I created a module like this
def my_func(before, after):
return before == after
def change_value(value):
return value.lower()
def test_walrus_conversion():
a = "Hello"
assert not my_func(a, a := change_value(a))
assert a == "hello"
and run it. It fails as the issuer stated. Then I added """PYTEST_DONT_REWRITE""" at the beginning of the module and now it passes.
If it's ok with you I'd like to try to fix this.
As a side note, this is a very edge case and I don't know if it is clean to write something like this. Anyway, since the unittest mode says that this test should pass, we should align with that
Quick update: I think I fixed the bug by telling the AssertionRewrite to visit the NamedExpr expr and not by using the generic visitor.
That said, I got into another possible problem, because right now the error messages are not what we expect. Here is an example
def test_walrus_conversion_fails():
a = "Hello"
assert a == (a := a.lower())
this test fails correctly, but this is the message I've got
def test_walrus_conversion_fails():
a = "Hello"
> assert a == (a := a.lower())
E AssertionError: assert 'hello' == 'hello'
E hello
and this is not what we expect, it's even worse! I'm trying to fix this behavior and I succeeded, but then this other test
def test_walrus_conversion_fails():
a = "Hello"
assert a != (a := a.lower())
assert a == 'hello'
fails with this message
> assert a == 'hello'
E AssertionError: assert 'Hello' == 'hello'
E - hello
E ? ^
E + Hello
E ? ^
, because the solution I found does not remember that we change the variable value.
Another bit on this topic. It looks like there is something strange on how this thing works with mutable objects. For example
def change_me(d: dict) -> dict:
d['change'] = True
return d
def my_func(before, after):
return before == after
def test_change_me():
a = {}
assert my_func(a, change_me(a))
assert a == {"change": True}
this test has the same behavior in pytest and unittest, but it looks like the same pattern. In fact, there is a variable that is passed to a function with another argument that changes its value.
To me these two tests below
def change_me(d: dict) -> dict:
d['change'] = True
return d
def change_me_str(value: str) -> str:
return value.lower()
def my_func(before, after):
return before == after
def test_walrus_conversion():
a = "Hello"
assert not my_func(a, a := change_me_str(a))
assert a == "hello"
def test_change_me():
a = {}
assert my_func(a, change_me(a))
assert a == {"change": True}
````
should behave the same, but in the first case (with the walrus operator) the change is not applied until the function is executed.
Ok that strings are immutable and dict aren't but it's very strange to understand and explain.
Activity
RonnyPfannschmidt commentedon Feb 16, 2023
It's very likely a missed case in the asset rewrite
aless10 commentedon Feb 19, 2023
Hi, I did some tests on this since I was curious about how this works.
I created a module like this
and run it. It fails as the issuer stated. Then I added """PYTEST_DONT_REWRITE""" at the beginning of the module and now it passes.
If it's ok with you I'd like to try to fix this.
As a side note, this is a very edge case and I don't know if it is clean to write something like this. Anyway, since the unittest mode says that this test should pass, we should align with that
RonnyPfannschmidt commentedon Feb 19, 2023
@aless10 thnks for providing a self-contained reproducer
i wonder if it also behaves that way if change_value is inlined
we should align with whatever is the result when using
--assert=plainaless10 commentedon Feb 22, 2023
Quick update: I think I fixed the bug by telling the AssertionRewrite to visit the NamedExpr expr and not by using the generic visitor.
That said, I got into another possible problem, because right now the error messages are not what we expect. Here is an example
this test fails correctly, but this is the message I've got
and this is not what we expect, it's even worse! I'm trying to fix this behavior and I succeeded, but then this other test
fails with this message
, because the solution I found does not remember that we change the variable value.
Another bit on this topic. It looks like there is something strange on how this thing works with mutable objects. For example
this test has the same behavior in pytest and unittest, but it looks like the same pattern. In fact, there is a variable that is passed to a function with another argument that changes its value.
To me these two tests below
RonnyPfannschmidt commentedon Feb 22, 2023
Aliasing is important to consider, assertion side effects can hide deep changes, which is acceptable (just normal python)
We might have to evaluate when to get a repr
We better warn that we only capture references, not deep mutable state and strongly recommend doing assertion on immutable without side effects
Fix walrus operator support in assertion rewriting (#10758)
chore(deps): update dependency pytest to v7 (#3)
Fix walrus operator support in assertion rewriting (pytest-dev#10758)
Fix walrus operator support in assertion rewriting (pytest-dev#10758)