New GitHub issue #95921 from 15r10nk:<br>

<hr>

<pre>

# Bug report

expressions like 
``` python
assert 1==x==1
```
have wrong position information in the bytecode (it has the range of the whole assertion instead only the comparison).
This becomes a problem if `==` is defined for a class and raises an exception.
The traceback does not shows the correct error location in this case.

test script:
``` python
import dis
import ast

class T:
    def __eq__(self,other):
        # use the other assert here to see how the traceback should look like

        # this makes the 5==T() in foo() fail -> we get no error location
        assert other==7

        # this makes the 7==T() in foo() fail -> we get correct error location
        #assert other==5

        return True

def foo():
    assert 5==T()==5 and 7==T()

print("show an example ast where the positions are correct")
node=ast.parse("assert 5==T()==5 and 7==T")
print(ast.dump(node,indent=2,include_attributes=True))

print("bytecode where the double comparison operations have the positions of the assertion")
for bc in dis.get_instructions(foo.__code__):
    print(bc.positions,bc.opname,bc.argval)

print("the error message without error position information (this ^^^^^ lines)")
foo()
```
output:
```
show an example ast where the positions are correct
Module(
  body=[
    Assert(
      test=BoolOp(
        op=And(),
        values=[
          Compare(
            left=Constant(
              value=5,
              lineno=1,
              col_offset=7,
              end_lineno=1,
              end_col_offset=8),
            ops=[
              Eq(),
              Eq()],
            comparators=[
              Call(
                func=Name(
                  id='T',
                  ctx=Load(),
                  lineno=1,
                  col_offset=10,
                  end_lineno=1,
                  end_col_offset=11),
                args=[],
                keywords=[],
                lineno=1,
                col_offset=10,
                end_lineno=1,
                end_col_offset=13),
              Constant(
                value=5,
                lineno=1,
                col_offset=15,
                end_lineno=1,
                end_col_offset=16)],
            lineno=1,
            col_offset=7,
            end_lineno=1,
            end_col_offset=16),
          Compare(
            left=Constant(
              value=7,
              lineno=1,
              col_offset=21,
              end_lineno=1,
              end_col_offset=22),
            ops=[
              Eq()],
            comparators=[
              Name(
                id='T',
                ctx=Load(),
                lineno=1,
                col_offset=24,
                end_lineno=1,
                end_col_offset=25)],
            lineno=1,
            col_offset=21,
            end_lineno=1,
            end_col_offset=25)],
        lineno=1,
        col_offset=7,
        end_lineno=1,
        end_col_offset=25),
      lineno=1,
      col_offset=0,
      end_lineno=1,
      end_col_offset=25)],
  type_ignores=[])
bytecode where the double comparison operations have the positions of the assertion
Positions(lineno=17, end_lineno=17, col_offset=0, end_col_offset=0) RESUME 0
Positions(lineno=18, end_lineno=18, col_offset=11, end_col_offset=12) LOAD_CONST 5
Positions(lineno=18, end_lineno=18, col_offset=14, end_col_offset=15) LOAD_GLOBAL T
Positions(lineno=18, end_lineno=18, col_offset=14, end_col_offset=17) CALL 0
Positions(lineno=18, end_lineno=18, col_offset=4, end_col_offset=31) SWAP 2
Positions(lineno=18, end_lineno=18, col_offset=4, end_col_offset=31) COPY 2
Positions(lineno=18, end_lineno=18, col_offset=4, end_col_offset=31) COMPARE_OP ==
Positions(lineno=18, end_lineno=18, col_offset=4, end_col_offset=31) POP_JUMP_FORWARD_IF_FALSE 50
Positions(lineno=18, end_lineno=18, col_offset=19, end_col_offset=20) LOAD_CONST 5
Positions(lineno=18, end_lineno=18, col_offset=4, end_col_offset=31) COMPARE_OP ==
Positions(lineno=18, end_lineno=18, col_offset=4, end_col_offset=31) POP_JUMP_FORWARD_IF_FALSE 86
Positions(lineno=18, end_lineno=18, col_offset=4, end_col_offset=31) JUMP_FORWARD 54
Positions(lineno=18, end_lineno=18, col_offset=4, end_col_offset=31) POP_TOP None
Positions(lineno=18, end_lineno=18, col_offset=4, end_col_offset=31) JUMP_FORWARD 86
Positions(lineno=18, end_lineno=18, col_offset=25, end_col_offset=26) LOAD_CONST 7
Positions(lineno=18, end_lineno=18, col_offset=28, end_col_offset=29) LOAD_GLOBAL T
Positions(lineno=18, end_lineno=18, col_offset=28, end_col_offset=31) CALL 0
Positions(lineno=18, end_lineno=18, col_offset=25, end_col_offset=31) COMPARE_OP ==
Positions(lineno=18, end_lineno=18, col_offset=4, end_col_offset=31) POP_JUMP_FORWARD_IF_TRUE 90
Positions(lineno=18, end_lineno=18, col_offset=4, end_col_offset=31) LOAD_ASSERTION_ERROR None
Positions(lineno=18, end_lineno=18, col_offset=4, end_col_offset=31) RAISE_VARARGS 1
Positions(lineno=18, end_lineno=18, col_offset=4, end_col_offset=31) LOAD_CONST None
Positions(lineno=18, end_lineno=18, col_offset=4, end_col_offset=31) RETURN_VALUE None
the error message without error position information (this ^^^^^ lines)
Traceback (most recent call last):
  File "/home/frank/cpython/../executing/assert_test.py", line 30, in <module>
    foo()
  File "/home/frank/cpython/../executing/assert_test.py", line 18, in foo
    assert 5==T()==5 and 7==T()
  File "/home/frank/cpython/../executing/assert_test.py", line 9, in __eq__
    assert other==7
AssertionError
```

The ast seems to be correct.
The first two `COMPARE_OP ==` operators have the wrong column range (4-31).
The operator without changing has the correct one (25-31).

traceback with `assert other==5` in `T.__eq__`:

```
Traceback (most recent call last):
  File "/home/frank/cpython/../executing/assert_test.py", line 30, in <module>
    foo()
  File "/home/frank/cpython/../executing/assert_test.py", line 18, in foo
    assert 5==T()==5 and 7==T()
                         ^^^^^^
  File "/home/frank/cpython/../executing/assert_test.py", line 12, in __eq__
    assert other==5
AssertionError
```
This shows the correct source range for 7==T()

I would expect the same for the chained comparison.
I think it would be only possible to mark `5==T()==5` because the ast has only a single node for the comparison.

The bug appears also for other comparisons like `<=` but requires the `assert`.

# Your environment

verified with current cpython main 7da4937748eb588bb0e977839061ce76cab1b252
(on a wsl2 but this should not be important)


</pre>

<hr>

<a href="https://github.com/python/cpython/issues/95921">View on GitHub</a>
<p>Labels: type-bug</p>
<p>Assignee: </p>