linenumbers.py 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. # Copyright (c) 2015-2016, 2018-2020 by Rocky Bernstein
  2. #
  3. # This program is free software: you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation, either version 3 of the License, or
  6. # (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. from collections import deque
  16. from xdis import (
  17. Bytecode,
  18. iscode,
  19. findlinestarts,
  20. get_opcode,
  21. offset2line,
  22. load_file,
  23. load_module,
  24. )
  25. def line_number_mapping(pyc_filename, src_filename):
  26. (
  27. version,
  28. timestamp,
  29. magic_int,
  30. code1,
  31. is_pypy,
  32. source_size,
  33. sip_hash,
  34. ) = load_module(pyc_filename)
  35. try:
  36. code2 = load_file(src_filename)
  37. except SyntaxError as e:
  38. return str(e)
  39. queue = deque([code1, code2])
  40. mappings = []
  41. opc = get_opcode(version, is_pypy)
  42. number_loop(queue, mappings, opc)
  43. return sorted(mappings, key=lambda x: x[1])
  44. def number_loop(queue, mappings, opc):
  45. while len(queue) > 0:
  46. code1 = queue.popleft()
  47. code2 = queue.popleft()
  48. assert code1.co_name == code2.co_name
  49. linestarts_orig = findlinestarts(code1)
  50. linestarts_uncompiled = list(findlinestarts(code2))
  51. mappings += [
  52. [line, offset2line(offset, linestarts_uncompiled)]
  53. for offset, line in linestarts_orig
  54. ]
  55. bytecode1 = Bytecode(code1, opc)
  56. bytecode2 = Bytecode(code2, opc)
  57. instr2s = bytecode2.get_instructions(code2)
  58. seen = set([code1.co_name])
  59. for instr in bytecode1.get_instructions(code1):
  60. next_code1 = None
  61. if iscode(instr.argval):
  62. next_code1 = instr.argval
  63. if next_code1:
  64. next_code2 = None
  65. while not next_code2:
  66. try:
  67. instr2 = next(instr2s)
  68. if iscode(instr2.argval):
  69. next_code2 = instr2.argval
  70. pass
  71. except StopIteration:
  72. break
  73. pass
  74. if next_code2:
  75. assert next_code1.co_name == next_code2.co_name
  76. if next_code1.co_name not in seen:
  77. seen.add(next_code1.co_name)
  78. queue.append(next_code1)
  79. queue.append(next_code2)
  80. pass
  81. pass
  82. pass
  83. pass