Merge pull request #11057 from pytest-dev/backport-11041-to-7.3.x
[7.3.x] 11028 - Fix warlus operator behavior when called by a function
This commit is contained in:
		
						commit
						682fc81781
					
				| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
Fixed bug in assertion rewriting where a variable assigned with the walrus operator could not be used later in a function call.
 | 
			
		||||
| 
						 | 
				
			
			@ -996,7 +996,9 @@ class AssertionRewriter(ast.NodeVisitor):
 | 
			
		|||
                    ]
 | 
			
		||||
                ):
 | 
			
		||||
                    pytest_temp = self.variable()
 | 
			
		||||
                    self.variables_overwrite[v.left.target.id] = pytest_temp
 | 
			
		||||
                    self.variables_overwrite[
 | 
			
		||||
                        v.left.target.id
 | 
			
		||||
                    ] = v.left  # type:ignore[assignment]
 | 
			
		||||
                    v.left.target.id = pytest_temp
 | 
			
		||||
            self.push_format_context()
 | 
			
		||||
            res, expl = self.visit(v)
 | 
			
		||||
| 
						 | 
				
			
			@ -1037,10 +1039,19 @@ class AssertionRewriter(ast.NodeVisitor):
 | 
			
		|||
        new_args = []
 | 
			
		||||
        new_kwargs = []
 | 
			
		||||
        for arg in call.args:
 | 
			
		||||
            if isinstance(arg, ast.Name) and arg.id in self.variables_overwrite:
 | 
			
		||||
                arg = self.variables_overwrite[arg.id]  # type:ignore[assignment]
 | 
			
		||||
            res, expl = self.visit(arg)
 | 
			
		||||
            arg_expls.append(expl)
 | 
			
		||||
            new_args.append(res)
 | 
			
		||||
        for keyword in call.keywords:
 | 
			
		||||
            if (
 | 
			
		||||
                isinstance(keyword.value, ast.Name)
 | 
			
		||||
                and keyword.value.id in self.variables_overwrite
 | 
			
		||||
            ):
 | 
			
		||||
                keyword.value = self.variables_overwrite[
 | 
			
		||||
                    keyword.value.id
 | 
			
		||||
                ]  # type:ignore[assignment]
 | 
			
		||||
            res, expl = self.visit(keyword.value)
 | 
			
		||||
            new_kwargs.append(ast.keyword(keyword.arg, res))
 | 
			
		||||
            if keyword.arg:
 | 
			
		||||
| 
						 | 
				
			
			@ -1075,7 +1086,13 @@ class AssertionRewriter(ast.NodeVisitor):
 | 
			
		|||
        self.push_format_context()
 | 
			
		||||
        # We first check if we have overwritten a variable in the previous assert
 | 
			
		||||
        if isinstance(comp.left, ast.Name) and comp.left.id in self.variables_overwrite:
 | 
			
		||||
            comp.left.id = self.variables_overwrite[comp.left.id]
 | 
			
		||||
            comp.left = self.variables_overwrite[
 | 
			
		||||
                comp.left.id
 | 
			
		||||
            ]  # type:ignore[assignment]
 | 
			
		||||
        if isinstance(comp.left, namedExpr):
 | 
			
		||||
            self.variables_overwrite[
 | 
			
		||||
                comp.left.target.id
 | 
			
		||||
            ] = comp.left  # type:ignore[assignment]
 | 
			
		||||
        left_res, left_expl = self.visit(comp.left)
 | 
			
		||||
        if isinstance(comp.left, (ast.Compare, ast.BoolOp)):
 | 
			
		||||
            left_expl = f"({left_expl})"
 | 
			
		||||
| 
						 | 
				
			
			@ -1093,7 +1110,9 @@ class AssertionRewriter(ast.NodeVisitor):
 | 
			
		|||
                and next_operand.target.id == left_res.id
 | 
			
		||||
            ):
 | 
			
		||||
                next_operand.target.id = self.variable()
 | 
			
		||||
                self.variables_overwrite[left_res.id] = next_operand.target.id
 | 
			
		||||
                self.variables_overwrite[
 | 
			
		||||
                    left_res.id
 | 
			
		||||
                ] = next_operand  # type:ignore[assignment]
 | 
			
		||||
            next_res, next_expl = self.visit(next_operand)
 | 
			
		||||
            if isinstance(next_operand, (ast.Compare, ast.BoolOp)):
 | 
			
		||||
                next_expl = f"({next_expl})"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1436,6 +1436,96 @@ class TestIssue10743:
 | 
			
		|||
        assert result.ret == 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.skipif(
 | 
			
		||||
    sys.version_info < (3, 8), reason="walrus operator not available in py<38"
 | 
			
		||||
)
 | 
			
		||||
class TestIssue11028:
 | 
			
		||||
    def test_assertion_walrus_operator_in_operand(self, pytester: Pytester) -> None:
 | 
			
		||||
        pytester.makepyfile(
 | 
			
		||||
            """
 | 
			
		||||
            def test_in_string():
 | 
			
		||||
              assert (obj := "foo") in obj
 | 
			
		||||
        """
 | 
			
		||||
        )
 | 
			
		||||
        result = pytester.runpytest()
 | 
			
		||||
        assert result.ret == 0
 | 
			
		||||
 | 
			
		||||
    def test_assertion_walrus_operator_in_operand_json_dumps(
 | 
			
		||||
        self, pytester: Pytester
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        pytester.makepyfile(
 | 
			
		||||
            """
 | 
			
		||||
            import json
 | 
			
		||||
 | 
			
		||||
            def test_json_encoder():
 | 
			
		||||
                assert (obj := "foo") in json.dumps(obj)
 | 
			
		||||
        """
 | 
			
		||||
        )
 | 
			
		||||
        result = pytester.runpytest()
 | 
			
		||||
        assert result.ret == 0
 | 
			
		||||
 | 
			
		||||
    def test_assertion_walrus_operator_equals_operand_function(
 | 
			
		||||
        self, pytester: Pytester
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        pytester.makepyfile(
 | 
			
		||||
            """
 | 
			
		||||
            def f(a):
 | 
			
		||||
                return a
 | 
			
		||||
 | 
			
		||||
            def test_call_other_function_arg():
 | 
			
		||||
              assert (obj := "foo") == f(obj)
 | 
			
		||||
        """
 | 
			
		||||
        )
 | 
			
		||||
        result = pytester.runpytest()
 | 
			
		||||
        assert result.ret == 0
 | 
			
		||||
 | 
			
		||||
    def test_assertion_walrus_operator_equals_operand_function_keyword_arg(
 | 
			
		||||
        self, pytester: Pytester
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        pytester.makepyfile(
 | 
			
		||||
            """
 | 
			
		||||
            def f(a='test'):
 | 
			
		||||
                return a
 | 
			
		||||
 | 
			
		||||
            def test_call_other_function_k_arg():
 | 
			
		||||
              assert (obj := "foo") == f(a=obj)
 | 
			
		||||
        """
 | 
			
		||||
        )
 | 
			
		||||
        result = pytester.runpytest()
 | 
			
		||||
        assert result.ret == 0
 | 
			
		||||
 | 
			
		||||
    def test_assertion_walrus_operator_equals_operand_function_arg_as_function(
 | 
			
		||||
        self, pytester: Pytester
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        pytester.makepyfile(
 | 
			
		||||
            """
 | 
			
		||||
            def f(a='test'):
 | 
			
		||||
                return a
 | 
			
		||||
 | 
			
		||||
            def test_function_of_function():
 | 
			
		||||
              assert (obj := "foo") == f(f(obj))
 | 
			
		||||
        """
 | 
			
		||||
        )
 | 
			
		||||
        result = pytester.runpytest()
 | 
			
		||||
        assert result.ret == 0
 | 
			
		||||
 | 
			
		||||
    def test_assertion_walrus_operator_gt_operand_function(
 | 
			
		||||
        self, pytester: Pytester
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        pytester.makepyfile(
 | 
			
		||||
            """
 | 
			
		||||
            def add_one(a):
 | 
			
		||||
                return a + 1
 | 
			
		||||
 | 
			
		||||
            def test_gt():
 | 
			
		||||
              assert (obj := 4) > add_one(obj)
 | 
			
		||||
        """
 | 
			
		||||
        )
 | 
			
		||||
        result = pytester.runpytest()
 | 
			
		||||
        assert result.ret == 1
 | 
			
		||||
        result.stdout.fnmatch_lines(["*assert 4 > 5", "*where 5 = add_one(4)"])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.skipif(
 | 
			
		||||
    sys.maxsize <= (2**31 - 1), reason="Causes OverflowError on 32bit systems"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue