Skip to content

Incorrect code collapse while prompting where the error is. #128327

Open

Description

@PC-Killer

Bug report

Bug description:

Read the following code:

# incorrect_collapse.py
def fib(number: int) -> int:
    assert number > 0
    if number == 1:
        return 1

    return fib(number - 1) + fib(number - 2)  # Notice here


print(fib(2))

There is a mistake in the code above, so if you try to run it, you will get the following traceback:

PC-Killer@ArchLinuxPC ~/p/bugs> python incorrect_collapse.py
Traceback (most recent call last):
  File "/home/PC-Killer/python_work/bugs/incorrect_collapse.py", line 9, in <module>
    print(fib(2))
          ~~~^^^
  File "/home/PC-Killer/python_work/bugs/incorrect_collapse.py", line 6, in fib
    return fib(number - 1) + fib(number - 2)  # Notice here
                             ~~~^^^^^^^^^^^^
  File "/home/PC-Killer/python_work/bugs/incorrect_collapse.py", line 2, in fib
    assert number > 0
           ^^^^^^^^^^
AssertionError

Notice that the error happened while running the code fib(number - 2).
However, if the number is 5, the duplicate code will be automatically collapsed:

# incorrect_collapse.py
def fib(number: int) -> int:
    assert number > 0
    if number == 1:
        return 1

    return fib(number - 1) + fib(number - 2)  # Notice here


print(fib(5))
PC-Killer@ArchLinuxPC ~/p/bugs [1]> python incorrect_collapse.py
Traceback (most recent call last):
  File "/home/PC-Killer/python_work/bugs/incorrect_collapse.py", line 9, in <module>
    print(fib(5))
          ~~~^^^
  File "/home/PC-Killer/python_work/bugs/incorrect_collapse.py", line 6, in fib
    return fib(number - 1) + fib(number - 2)  # Notice here
           ~~~^^^^^^^^^^^^
  File "/home/PC-Killer/python_work/bugs/incorrect_collapse.py", line 6, in fib
    return fib(number - 1) + fib(number - 2)  # Notice here
           ~~~^^^^^^^^^^^^
  File "/home/PC-Killer/python_work/bugs/incorrect_collapse.py", line 6, in fib
    return fib(number - 1) + fib(number - 2)  # Notice here
           ~~~^^^^^^^^^^^^
  [Previous line repeated 1 more time]
  File "/home/PC-Killer/python_work/bugs/incorrect_collapse.py", line 2, in fib
    assert number > 0
           ^^^^^^^^^^
AssertionError

The error happened in fib(number - 2). However, in the traceback above, I can only suppose the error happened while running fib(number - 1), which increases the time to find where the real bug is.

CPython versions tested on:

3.13, 3.14

Operating systems tested on:

Linux

Linked PRs

Activity

  1. picnixz commented on Dec 29, 2024

    @picnixz
    Member

    This the the expected behaviour. Because we are running fib(4) before fib(3), we'll see fib(4) raising. If you write fib(2) you execute fib(1) and fib(0). but it's fib(0) that is having an issue, not fib(1) (it immediately returns).

    If you unroll the calls you have:

    fib(2) -> fib(1) + fib(0)
    fib(1) -> 1
    fib(0) -> assertion error

    So it's normal that fib(n-2) is the error location. For fib(5), if you unroll the call you have:

    fib(5) -> fib(4) + fib(3)
    ----
    fib(4) -> fib(3) + fib(2)
    fib(3) -> fib(2) + fib(1) 
    fib(2) -> fib(1) + fib(0)  # f(0) will error
    ----
    fib(3) -> fib(2) + fib(1)
    fib(2) -> fib(1) + fib(0)  # f(0) will error
    

    So the error happened in the n-2 inner branch of the top n-1 branch. It would also fail on the n-2 inner branch of the top n-2 branch but this n-2 branch wouldn't be even executed yet.

    This can be verified as follows:

    def fib(number: int, s='') -> int:
        print("called with:", number, s)
        assert number > 0, (number, s)
        if number == 1: return 1
        return fib(number - 1, s + 'l') + fib(number - 2, s + "r")
    
    fib(5)

    This wil print:

    called with: 5
    called with: 4 l
    called with: 3 ll
    called with: 2 lll
    called with: 1 llll
    called with: 0 lllr
    Traceback (most recent call last):
      File "test.py", line 8, in <module>
        fib(5)
      File "test.py", line 5, in fib
        return fib(number - 1, s + 'l') + fib(number - 2, s + "r")
               ^^^^^^^^^^^^^^^^^^^^^^^^
      File "test.py", line 5, in fib
        return fib(number - 1, s + 'l') + fib(number - 2, s + "r")
               ^^^^^^^^^^^^^^^^^^^^^^^^
      File "test.py", line 5, in fib
        return fib(number - 1, s + 'l') + fib(number - 2, s + "r")
               ^^^^^^^^^^^^^^^^^^^^^^^^
      [Previous line repeated 1 more time]
      File "test.py", line 3, in fib
        assert number > 0, (number, s)
               ^^^^^^^^^^
    AssertionError: (0, 'lllr')
    
  2. added
    pendingThe issue will be closed if no feedback is provided
    on Dec 29, 2024
  3. PC-Killer commented on Dec 29, 2024

    @PC-Killer
    Author

    Thanks for reading my issue! I am sorry that I may have not explained myself clearly.
    Please notice your output above:

    -- skip --
    called with: 1 llll
    called with: 0 lllr
    Traceback (most recent call last):
      File "test.py", line 8, in <module>
        fib(5)
      File "test.py", line 5, in fib
        return fib(number - 1, s + 'l') + fib(number - 2, s + "r")
               ^^^^^^^^^^^^^^^^^^^^^^^^
      File "test.py", line 5, in fib
        return fib(number - 1, s + 'l') + fib(number - 2, s + "r")
               ^^^^^^^^^^^^^^^^^^^^^^^^
      File "test.py", line 5, in fib
        return fib(number - 1, s + 'l') + fib(number - 2, s + "r")
               ^^^^^^^^^^^^^^^^^^^^^^^^
      [Previous line repeated 1 more time]
      File "test.py", line 3, in fib
        assert number > 0, (number, s)
               ^^^^^^^^^^
    AssertionError: (0, 'lllr')
    

    That is to say, while calculating fib(2), the former, fib(number - 1) has been calculated successfully, and the mistake happened while calculating fib(number - 2), which refers to fib(0)
    Therefore, in my opinion, the traceback should be like the following:

    Traceback (most recent call last):
      File "/home/PC-Killer/python_work/bugs/incorrect_collapse.py", line 9, in <module>
        print(fib(5))
              ~~~^^^
      File "/home/PC-Killer/python_work/bugs/incorrect_collapse.py", line 6, in fib
        return fib(number - 1) + fib(number - 2)  # Notice here
               ~~~^^^^^^^^^^^^
      File "/home/PC-Killer/python_work/bugs/incorrect_collapse.py", line 6, in fib
        return fib(number - 1) + fib(number - 2)  # Notice here
               ~~~^^^^^^^^^^^^
      File "/home/PC-Killer/python_work/bugs/incorrect_collapse.py", line 6, in fib
        return fib(number - 1) + fib(number - 2)  # Notice here
               ~~~^^^^^^^^^^^^
      File "/home/PC-Killer/python_work/bugs/incorrect_collapse.py", line 6, in fib
        return fib(number - 1) + fib(number - 2)  # Notice here
                                 ~~~^^^^^^^^^^^^
      File "/home/PC-Killer/python_work/bugs/incorrect_collapse.py", line 2, in fib
        assert number > 0
               ^^^^^^^^^^
    AssertionError
    
  4. picnixz commented on Dec 29, 2024

    @picnixz
    Member
    File "/home/PC-Killer/python_work/bugs/incorrect_collapse.py", line 6, in fib
        return fib(number - 1) + fib(number - 2)  # Notice here
                                 ~~~^^^^^^^^^^^^
    

    Ah I see what you mean. I think the issue is that we are considering the traceback to be identical because we haven't yet added the markers (^). I'm not on my dev session so it's a bit hard to investigate, but we should decide if the rendered lines are the same by also checking where the carrets would be rendered before hiding them.

    For instance, fib(4) shows:

    called with: 4
    called with: 3 l
    called with: 2 ll
    called with: 1 lll
    called with: 0 llr
    Traceback (most recent call last):
      File "test.py", line 8, in <module>
        fib(3)
      File "test.py", line 5, in fib
        return fib(number - 1, s + 'l') + fib(number - 2, s + "r")
               ^^^^^^^^^^^^^^^^^^^^^^^^
      File "test.py", line 5, in fib
        return fib(number - 1, s + 'l') + fib(number - 2, s + "r")
               ^^^^^^^^^^^^^^^^^^^^^^^^
      File "test.py", line 5, in fib
        return fib(number - 1, s + 'l') + fib(number - 2, s + "r")
                                          ^^^^^^^^^^^^^^^^^^^^^^^^
      File "test.py", line 3, in fib
        assert number > 0, (number, s)
               ^^^^^^^^^^
    AssertionError: (0, 'llr')
    

    cc @pablogsal

  5. added
    stdlibStandard Library Python modules in the Lib/ directory
    topic-replRelated to the interactive shell
    and removed
    pendingThe issue will be closed if no feedback is provided
    on Dec 29, 2024
  6. picnixz commented on Jan 23, 2025

    @picnixz
    Member

    @pablogsal I guess it's a traceback issue (or maybe an interpreter core issue, I don't really know in this case)

  7. pablogsal commented on Jan 23, 2025

    @pablogsal
    Member

    Is a traceback module issue (the repl topic normally is for thing is pyrepl)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    ⌂ Proxy