Skip to content

Commit

Permalink
Fix issues due to backward compatibility breakage in Python 3.13: dis…
Browse files Browse the repository at this point in the history
….findlinestarts may now return line with None.
  • Loading branch information
fabioz committed Nov 9, 2024
1 parent 3e88fcc commit 1edb1cd
Show file tree
Hide file tree
Showing 25 changed files with 4,328 additions and 4,424 deletions.
1 change: 1 addition & 0 deletions .settings/org.eclipse.core.resources.prefs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ encoding//pydev_ipython/inputhookqt5.py=utf-8
encoding//pydev_ipython/inputhooktk.py=utf-8
encoding//pydev_ipython/inputhookwx.py=utf-8
encoding//pydev_ipython/version.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/__init__.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/breakpoint.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/crash.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/interactive.py=utf-8
Expand Down
2 changes: 1 addition & 1 deletion _pydev_runfiles/pydev_runfiles_xml_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def _encode_if_needed(obj):

elif isinstance(obj, bytes):
try:
return xmlrpclib.Binary(obj.decode(sys.stdin.encoding, 'replace').encode("ISO-8859-1", "xmlcharrefreplace"))
return xmlrpclib.Binary(obj.decode(sys.stdin.encoding, "replace").encode("ISO-8859-1", "xmlcharrefreplace"))
except:
return xmlrpclib.Binary(obj) # bytes already

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,9 @@ def update_class_to_generate_init(class_to_generate):
# Note: added kwargs because some messages are expected to be extended by the user (so, we'll actually
# make all extendable so that we don't have to worry about which ones -- we loose a little on typing,
# but may be better than doing a allow list based on something only pointed out in the documentation).
class_to_generate["init"] = '''def __init__(self%(args)s, update_ids_from_dap=False, **kwargs): # noqa (update_ids_from_dap may be unused)
class_to_generate[
"init"
] = '''def __init__(self%(args)s, update_ids_from_dap=False, **kwargs): # noqa (update_ids_from_dap may be unused)
"""
%(docstring)s
"""
Expand Down
3 changes: 2 additions & 1 deletion _pydevd_bundle/pydevd_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ def iterate():
# bodies of nested class and function definitions, as they have their
# own objects.
for _, lineno in dis.findlinestarts(code):
yield lineno
if lineno is not None:
yield lineno

# For nested class and function definitions, their respective code objects
# are constants referenced by this object.
Expand Down
3 changes: 1 addition & 2 deletions _pydevd_bundle/pydevd_code_to_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,8 +529,7 @@ def build_line_to_contents(self):
instruction = instructions[0]
new_line_index = op_offset_to_line.get(instruction.offset)
if new_line_index is not None:
if new_line_index is not None:
curr_line_index = new_line_index
curr_line_index = new_line_index

self._process_next(curr_line_index)
return self.writer.line_to_contents
Expand Down
125 changes: 4 additions & 121 deletions _pydevd_bundle/pydevd_collect_bytecode_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,125 +82,8 @@ def debug(s):
_Instruction = namedtuple("_Instruction", "opname, opcode, starts_line, argval, is_jump_target, offset, argrepr")


def _iter_as_bytecode_as_instructions_py2(co):
code = co.co_code
op_offset_to_line = dict(dis.findlinestarts(co))
labels = set(dis.findlabels(code))
bytecode_len = len(code)
i = 0
extended_arg = 0
free = None

op_to_name = opname

while i < bytecode_len:
c = code[i]
op = ord(c)
is_jump_target = i in labels

curr_op_name = op_to_name[op]
initial_bytecode_offset = i

i = i + 1
if op < HAVE_ARGUMENT:
yield _Instruction(
curr_op_name,
op,
_get_line(op_offset_to_line, initial_bytecode_offset, 0),
None,
is_jump_target,
initial_bytecode_offset,
"",
)

else:
oparg = ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg

extended_arg = 0
i = i + 2
if op == EXTENDED_ARG:
extended_arg = oparg * 65536

if op in hasconst:
yield _Instruction(
curr_op_name,
op,
_get_line(op_offset_to_line, initial_bytecode_offset, 0),
co.co_consts[oparg],
is_jump_target,
initial_bytecode_offset,
repr(co.co_consts[oparg]),
)
elif op in hasname:
yield _Instruction(
curr_op_name,
op,
_get_line(op_offset_to_line, initial_bytecode_offset, 0),
co.co_names[oparg],
is_jump_target,
initial_bytecode_offset,
str(co.co_names[oparg]),
)
elif op in hasjrel:
argval = i + oparg
yield _Instruction(
curr_op_name,
op,
_get_line(op_offset_to_line, initial_bytecode_offset, 0),
argval,
is_jump_target,
initial_bytecode_offset,
"to " + repr(argval),
)
elif op in haslocal:
yield _Instruction(
curr_op_name,
op,
_get_line(op_offset_to_line, initial_bytecode_offset, 0),
co.co_varnames[oparg],
is_jump_target,
initial_bytecode_offset,
str(co.co_varnames[oparg]),
)
elif op in hascompare:
yield _Instruction(
curr_op_name,
op,
_get_line(op_offset_to_line, initial_bytecode_offset, 0),
cmp_op[oparg],
is_jump_target,
initial_bytecode_offset,
cmp_op[oparg],
)
elif op in hasfree:
if free is None:
free = co.co_cellvars + co.co_freevars
yield _Instruction(
curr_op_name,
op,
_get_line(op_offset_to_line, initial_bytecode_offset, 0),
free[oparg],
is_jump_target,
initial_bytecode_offset,
str(free[oparg]),
)
else:
yield _Instruction(
curr_op_name,
op,
_get_line(op_offset_to_line, initial_bytecode_offset, 0),
oparg,
is_jump_target,
initial_bytecode_offset,
str(oparg),
)


def iter_instructions(co):
if sys.version_info[0] < 3:
iter_in = _iter_as_bytecode_as_instructions_py2(co)
else:
iter_in = dis.Bytecode(co)
iter_in = dis.Bytecode(co)
iter_in = list(iter_in)

bytecode_to_instruction = {}
Expand Down Expand Up @@ -327,7 +210,7 @@ def collect_try_except_info(co, use_func_first_line=False):

try_except_info_lst = []

op_offset_to_line = dict(dis.findlinestarts(co))
op_offset_to_line = dict(entry for entry in dis.findlinestarts(co) if entry[1] is not None)

offset_to_instruction_idx = {}

Expand Down Expand Up @@ -445,7 +328,7 @@ def collect_try_except_info(co, use_func_first_line=False):

try_except_info_lst = []

op_offset_to_line = dict(dis.findlinestarts(co))
op_offset_to_line = dict(entry for entry in dis.findlinestarts(co) if entry[1] is not None)

offset_to_instruction_idx = {}

Expand Down Expand Up @@ -645,7 +528,7 @@ def __init__(self, co, firstlineno, level=0):
self.firstlineno = firstlineno
self.level = level
self.instructions = list(iter_instructions(co))
op_offset_to_line = self.op_offset_to_line = dict(dis.findlinestarts(co))
op_offset_to_line = self.op_offset_to_line = dict(entry for entry in dis.findlinestarts(co) if entry[1] is not None)

# Update offsets so that all offsets have the line index (and update it based on
# the passed firstlineno).
Expand Down
11 changes: 5 additions & 6 deletions _pydevd_bundle/pydevd_comm.py
Original file line number Diff line number Diff line change
Expand Up @@ -1344,9 +1344,8 @@ def internal_evaluate_expression(dbg, seq, thread_id, frame_id, expression, is_e


def _set_expression_response(py_db, request, error_message):
body = pydevd_schema.SetExpressionResponseBody(value='')
variables_response = pydevd_base_schema.build_response(request, kwargs={
'body':body, 'success':False, 'message': error_message})
body = pydevd_schema.SetExpressionResponseBody(value="")
variables_response = pydevd_base_schema.build_response(request, kwargs={"body": body, "success": False, "message": error_message})
py_db.writer.add_command(NetCommand(CMD_RETURN, 0, variables_response, is_json=True))


Expand All @@ -1362,18 +1361,18 @@ def internal_set_expression_json(py_db, request, thread_id):
fmt = fmt.to_dict()

frame = py_db.find_frame(thread_id, frame_id)
exec_code = '%s = (%s)' % (expression, value)
exec_code = "%s = (%s)" % (expression, value)
try:
pydevd_vars.evaluate_expression(py_db, frame, exec_code, is_exec=True)
except (Exception, KeyboardInterrupt):
_set_expression_response(py_db, request, error_message='Error executing: %s' % (exec_code,))
_set_expression_response(py_db, request, error_message="Error executing: %s" % (exec_code,))
return

# Ok, we have the result (could be an error), let's put it into the saved variables.
frame_tracker = py_db.suspended_frames_manager.get_frame_tracker(thread_id)
if frame_tracker is None:
# This is not really expected.
_set_expression_response(py_db, request, error_message='Thread id: %s is not current thread id.' % (thread_id,))
_set_expression_response(py_db, request, error_message="Thread id: %s is not current thread id." % (thread_id,))
return

# Now that the exec is done, get the actual value changed to return.
Expand Down
Loading

0 comments on commit 1edb1cd

Please sign in to comment.