Skip to content

dis.dis() reports line numbers where there are none. #107932

Closed

Description

@markshannon

Take this example function from #107901:

def f():
   for i in range(30000000):
      if not i%1000000:
          pass

dis.dis produces the following output:

  1           0 RESUME                   0

  2           2 LOAD_GLOBAL              1 (range + NULL)
             12 LOAD_CONST               1 (30000000)
             14 CALL                     1
             22 GET_ITER
        >>   24 FOR_ITER                14 (to 56)
             28 STORE_FAST               0 (i)

  3          30 LOAD_FAST                0 (i)
             32 LOAD_CONST               2 (1000000)
             34 BINARY_OP                6 (%)
             38 TO_BOOL
             46 POP_JUMP_IF_FALSE        2 (to 52)
             48 JUMP_BACKWARD           14 (to 24)

  4     >>   52 JUMP_BACKWARD           16 (to 24)

  2     >>   56 END_FOR
             58 RETURN_CONST             0 (None)

Which has incorrect line numbers.
The issue is not that the line numbers are wrong, but that you can't tell from the dis output.
The whole point of dis is show what is going on at the bytecode level, so it is failing if it gives wrong line numbers.
The actual line numbers can be see from co_lines()

>>> list(f.__code__.co_lines())
[(0, 2, 1), (2, 30, 2), (30, 48, 3), (48, 52, None), (52, 56, 4), (56, 60, 2)]

The correct output should be:

   1           0 RESUME                   0

   2           2 LOAD_GLOBAL              1 (range + NULL)
              12 LOAD_CONST               1 (30000000)
              14 CALL                     1
              22 GET_ITER
         >>   24 FOR_ITER                14 (to 56)
              28 STORE_FAST               0 (i)

   3          30 LOAD_FAST                0 (i)
              32 LOAD_CONST               2 (1000000)
              34 BINARY_OP                6 (%)
              38 TO_BOOL
              46 POP_JUMP_IF_FALSE        2 (to 52)
None          48 JUMP_BACKWARD           14 (to 24)

   4     >>   52 JUMP_BACKWARD           16 (to 24)

   2     >>   56 END_FOR
              58 RETURN_CONST             0 (None)

Linked PRs

Activity

  1. markshannon commented on Aug 14, 2023

    @markshannon
    MemberAuthor

    I've submitted this as a "bug", but it could be argued it is a behavior change, so I think it shouldn't be backported.

    @iritkatriel thoughts?

  2. iritkatriel commented on Aug 14, 2023

    @iritkatriel
    Member

    I think I'd call it a bug and backport.

  3. CorvinM commented on Aug 14, 2023

    @CorvinM
    Contributor

    Are bytecodes that do not map back to any source line intended behavior? Older versions of python seem to not generate the bytecode that has no source line for this fragment (tested on 3.10)

    Was working on a PR for this but most of dis.py makes the assumption of every bytecode has a source line so the behavior change is getting vague and messy quickly. For example findlinestarts() ignores line=None so dis() (and many of the other exported functions that depend on it) group these lines in with the previous block of bytecodes.

    I'm happy to keep working on the patch but looking for some assurance the actual "bug" is disassembly related rather than in the bytecode generator before continuing further.

  4. carljm commented on Aug 15, 2023

    @carljm
    Member

    It is definitely intentional that the compiler can generate instructions that are "synthetic" and don't map directly to any line number in the source. And yes, this is a change in recent Python versions, that didn't used to be possible. So it's not too surprising that dis.py may have "every instruction has a line number" assumptions built into it, and it may not be a trivial change (and may require some design decisions) to untangle those assumptions correctly.

  5. added 2 commits that reference this issue on Aug 15, 2023
    751f57f
    c8aaa8d
  6. added a commit that references this issue on Aug 25, 2023
  7. added a commit that references this issue on Aug 25, 2023
  8. serhiy-storchaka commented on Aug 25, 2023

    @serhiy-storchaka
    Member

    It broke the tests.

  9. AlexWaygood commented on Aug 25, 2023

    @AlexWaygood
    Member

    It broke the tests.

    They will be fixed by #108478 @serhiy-storchaka

  10. added a commit that references this issue on Aug 25, 2023
  11. AlexWaygood commented on Aug 25, 2023

    @AlexWaygood
    Member

    It broke the tests.

    They will be fixed by #108478 @serhiy-storchaka

    now merged

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

    type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions