Files
openide/python/helpers/pydev/_pydevd_bundle/pydevd_bytecode_utils.py
Egor Eliseev 9d01505b25 PY-78884 Linux in Docker for PyCharm config PyEnvTestsDebugger
Fix "smart step into" command.
Test segmentation into classes.
Fix and add debugger tests to the Aggregator (was 70, now > 160).
Generate Cython files.

Merge-request: IJ-MR-156774
Merged-by: Egor Eliseev <Egor.Eliseev@jetbrains.com>

(cherry picked from commit bcd2335a774aab0441235d9a8c98315ebb021883)


Merge-request: IJ-MR-158451
Merged-by: Egor Eliseev <Egor.Eliseev@jetbrains.com>

GitOrigin-RevId: e4f3f8b8fd282246c2aa2e1860225bc3559e33ed
2025-04-02 17:32:29 +00:00

96 lines
3.6 KiB
Python

"""Bytecode analyzing utils."""
from _pydevd_bundle.smart_step_into import get_stepping_variants
from _pydevd_bundle.pydevd_constants import IS_PY311_OR_GREATER, IS_PY39_OR_GREATER
import dis
def find_last_func_call_order(frame, start_line):
"""Find the call order of the last function call between ``start_line``
and last executed instruction.
:param frame: A frame inside which we are looking for the function call.
:type frame: :py:class:`types.FrameType`
:param start_line:
:return: call order or -1 if we fail to find the call order for some
reason.
:rtype: int
"""
code = frame.f_code
lasti = frame.f_lasti
cache = {}
call_order = -1
def process_instructions(order, start_offset=None, end_offset=None, find_reraise=False):
"""Processes instructions within the given bytecode offsets."""
stepping_generator = get_stepping_variants(code)
for inst in stepping_generator:
if end_offset is not None and inst.offset > end_offset:
if not find_reraise:
break
else:
for forward in get_stepping_variants(code):
if forward.lineno > frame.f_lineno:
break
cache_for_reraise = cache.copy()
name = forward.argval
call_order_reraise = cache_for_reraise.setdefault(name, -1)
call_order_reraise += 1
cache_for_reraise[name] = call_order_reraise
if name == "RERAISE":
order = call_order_reraise
break
break
if start_offset is not None and inst.offset < start_offset:
continue
if start_line <= inst.lineno <= frame.f_lineno:
name = inst.argval
order = cache.setdefault(name, -1)
order += 1
cache[name] = order
return order
if IS_PY311_OR_GREATER:
exception_table = dis._parse_exception_table(code)
if exception_table:
for i, entry in enumerate(exception_table):
if entry.start < lasti < entry.end:
target_entry = None
for next_entry in exception_table[i + 1:]:
if next_entry.start == entry.target:
target_entry = next_entry
break
if target_entry is not None:
call_order = process_instructions(call_order, target_entry.start, target_entry.end)
break
else:
call_order = process_instructions(call_order, None, lasti)
else:
call_order = process_instructions(call_order, None, lasti)
elif IS_PY39_OR_GREATER:
call_order = process_instructions(call_order, None, lasti, find_reraise=True)
else:
call_order = process_instructions(call_order, None, lasti)
return call_order
def find_last_call_name(frame):
"""Find the name of the last call made in the frame.
:param frame: A frame inside which we are looking the last call.
:type frame: :py:class:`types.FrameType`
:return: The name of a function or method that has been called last.
:rtype: str
"""
last_call_name = None
code = frame.f_code
last_instruction = frame.f_lasti
for inst in get_stepping_variants(code):
if inst.offset > last_instruction:
break
last_call_name = inst.argval
return last_call_name