classification
Title: Incorrect tracing of nested if/if/for/yield
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.10
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: Mark.Shannon, Sergey.Kirpichev, nedbat, pablogsal
Priority: release blocker Keywords: 3.10regression, patch

Created on 2021-07-13 18:23 by nedbat, last changed 2021-07-16 10:49 by pablogsal. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 27138 merged Mark.Shannon, 2021-07-14 14:25
PR 27182 merged Mark.Shannon, 2021-07-16 09:53
Messages (4)
msg397434 - (view) Author: Ned Batchelder (nedbat) * (Python triager) Date: 2021-07-13 18:23
In Python 3.10, this code is traced incorrectly. I don't know if there's a simpler format that would show the problem. This code is already simplified from https://github.com/nedbat/coveragepy/issues/1188.

------------------------------
import linecache, sys

def trace(frame, event, arg):
    # The weird globals here is to avoid a NameError on shutdown...
    if frame.f_code.co_filename == globals().get("__file__"):
        lineno = frame.f_lineno
        print("{} {}: {}".format(event[:4], lineno, linecache.getline(__file__, lineno).rstrip()))
    return trace

print(sys.version)
sys.settrace(trace)

def f(a, p, z):
    if p:
        print(f"{a=}")
        if a:
            if z:
                for x in [1,2]:
                    yield x
            else:
                for x in [1,2]:
                    yield x
    else:
        print("nope")

import itertools

for a, p, z in itertools.product([0, 1], repeat=3):
    list(f(a, p, z))
------------------------------------

Running this with 3.10.0b4 produces this output. The starred lines show a is false, but tracing then indicates a line which is not executed:

  3.10.0b4 (default, Jul 11 2021, 13:51:53) [Clang 12.0.0 (clang-1200.0.32.29)]
  call 13: def f(a, p, z):
  line 14:     if p:
  line 24:         print("nope")
  nope
  retu 24:         print("nope")
  call 13: def f(a, p, z):
  line 14:     if p:
  line 24:         print("nope")
  nope
  retu 24:         print("nope")
  call 13: def f(a, p, z):
  line 14:     if p:
  line 15:         print(f"{a=}")
  a=0
* line 16:         if a:
  line 22:                     yield x
  retu 22:                     yield x
  call 13: def f(a, p, z):
  line 14:     if p:
  line 15:         print(f"{a=}")
  a=0
* line 16:         if a:
  line 22:                     yield x
  retu 22:                     yield x
  call 13: def f(a, p, z):
  line 14:     if p:
  line 24:         print("nope")
  nope
  retu 24:         print("nope")
  call 13: def f(a, p, z):
  line 14:     if p:
  line 24:         print("nope")
  nope
  retu 24:         print("nope")
  call 13: def f(a, p, z):
  line 14:     if p:
  line 15:         print(f"{a=}")
  a=1
  line 16:         if a:
  line 17:             if z:
  line 21:                 for x in [1,2]:
  line 22:                     yield x
  retu 22:                     yield x
  call 22:                     yield x
  line 21:                 for x in [1,2]:
  line 22:                     yield x
  retu 22:                     yield x
  call 22:                     yield x
  line 21:                 for x in [1,2]:
  line 22:                     yield x
  retu 22:                     yield x
  call 13: def f(a, p, z):
  line 14:     if p:
  line 15:         print(f"{a=}")
  a=1
  line 16:         if a:
  line 17:             if z:
  line 18:                 for x in [1,2]:
  line 19:                     yield x
  retu 19:                     yield x
  call 19:                     yield x
  line 18:                 for x in [1,2]:
  line 19:                     yield x
  retu 19:                     yield x
  call 19:                     yield x
  line 18:                 for x in [1,2]:
  retu 18:                 for x in [1,2]:
msg397472 - (view) Author: Ned Batchelder (nedbat) * (Python triager) Date: 2021-07-14 10:06
The original reporter of the coverage.py issue says they have a simpler reproducer: https://github.com/nedbat/coveragepy/issues/1188#issuecomment-879572874
msg397568 - (view) Author: Pablo Galindo Salgado (pablogsal) * (Python committer) Date: 2021-07-15 16:47
New changeset a86f7dae0acf918d54086cb85e5a0b0bedeedce7 by Mark Shannon in branch 'main':
bpo-44626: Merge basic blocks earlier to enable better handling of exit blocks without line numbers (GH-27138)
https://github.com/python/cpython/commit/a86f7dae0acf918d54086cb85e5a0b0bedeedce7
msg397611 - (view) Author: Pablo Galindo Salgado (pablogsal) * (Python committer) Date: 2021-07-16 10:49
New changeset 37686f78ccef5f1cf4776419a4270cf0ea7eadf0 by Mark Shannon in branch '3.10':
bpo-44626: Merge basic blocks earlier to enable better handling of exit blocks without line numbers (GH-27138) (GH-27182)
https://github.com/python/cpython/commit/37686f78ccef5f1cf4776419a4270cf0ea7eadf0
History
Date User Action Args
2021-07-16 10:49:20pablogsalsetstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2021-07-16 10:49:14pablogsalsetmessages: + msg397611
2021-07-16 09:53:45Mark.Shannonsetpull_requests: + pull_request25719
2021-07-15 16:47:03pablogsalsetmessages: + msg397568
2021-07-14 22:09:29pablogsalsetpriority: normal -> release blocker
nosy: + pablogsal
2021-07-14 14:25:45Mark.Shannonsetkeywords: + patch
stage: patch review
pull_requests: + pull_request25680
2021-07-14 10:06:00nedbatsetmessages: + msg397472
2021-07-13 19:03:35Sergey.Kirpichevsetnosy: + Sergey.Kirpichev
2021-07-13 18:23:06nedbatcreate