mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-19 18:38:56 +07:00
The previous version of the procedure could miss breakpoints when extra `EXTENDED_ARG` instructions are added before the injected code. This happened because in some corner cases after updating the opcodes to lines mapping an `EXTENDED_ARG` instruction could land as the last instruction in a line, and the instruction which argument it extends - on another. This lead to the situation when it was impossible to detect that we have hit the next line. These changes also add the ability to trace opcodes when the `PYDEVD_TRACE_OPCODES` environment variable is set to `True` (available only for Cython) to ease the debugging of such cases in the future. (cherry picked from commit 77399d3b7b2b9bc6aac1b85d59ae305500cb6ecb) GitOrigin-RevId: 105bf9d3d2cc51a940b03b1cb11c07e97074048b
602 lines
24 KiB
Python
602 lines
24 KiB
Python
import dis
|
|
import sys
|
|
import unittest
|
|
from io import StringIO
|
|
from types import CodeType
|
|
import pytest
|
|
|
|
from _pydevd_frame_eval.pydevd_modify_bytecode import insert_code
|
|
from opcode import EXTENDED_ARG
|
|
|
|
TRACE_MESSAGE = "Trace called"
|
|
|
|
def tracing():
|
|
print(TRACE_MESSAGE)
|
|
|
|
|
|
def call_tracing():
|
|
return tracing()
|
|
|
|
|
|
def bar(a, b):
|
|
return a + b
|
|
|
|
|
|
IS_PY36_OR_GREATER = sys.version_info > (3, 6)
|
|
IS_PY37_OR_GREATER = sys.version_info > (3, 7)
|
|
|
|
LOAD_OPCODES = [dis.opmap[x] for x in ('LOAD_ATTR', 'LOAD_CONST', 'LOAD_FAST', 'LOAD_GLOBAL', 'LOAD_NAME')]
|
|
|
|
if IS_PY37_OR_GREATER:
|
|
LOAD_OPCODES.append(dis.opmap['LOAD_METHOD'])
|
|
|
|
|
|
@pytest.mark.skipif(not IS_PY36_OR_GREATER, reason='Test requires Python 3.6 or greater')
|
|
class TestInsertCode(unittest.TestCase):
|
|
lines_separator = "---Line tested---"
|
|
|
|
def check_insert_every_line(self, func_to_modify, func_to_insert, number_of_lines):
|
|
first_line = func_to_modify.__code__.co_firstlineno + 1
|
|
last_line = first_line + number_of_lines
|
|
for i in range(first_line, last_line):
|
|
self.check_insert_to_line_with_exec(func_to_modify, func_to_insert, i)
|
|
print(self.lines_separator)
|
|
|
|
def check_insert_to_line_with_exec(self, func_to_modify, func_to_insert, line_number):
|
|
code_orig = func_to_modify.__code__
|
|
code_to_insert = func_to_insert.__code__
|
|
success, result = insert_code(code_orig, code_to_insert, line_number)
|
|
exec(result)
|
|
output = sys.stdout.getvalue().strip().split(self.lines_separator)[-1]
|
|
self.assertTrue(TRACE_MESSAGE in output)
|
|
|
|
def check_insert_to_line_by_symbols(self, func_to_modify, func_to_insert, line_number, code_for_check):
|
|
code_orig = func_to_modify.__code__
|
|
code_to_insert = func_to_insert.__code__
|
|
success, result = insert_code(code_orig, code_to_insert, line_number)
|
|
self.compare_bytes_sequence(result, code_for_check, len(code_to_insert.co_code))
|
|
|
|
def compare_bytes_sequence(self, code1, code2, inserted_code_size):
|
|
"""
|
|
Compare code after modification and the real code
|
|
Since we add POP_JUMP_IF_TRUE instruction, we can't compare modified code and the real code. That's why we
|
|
allow some inaccuracies while code comparison
|
|
:param code1: result code after modification
|
|
:param code2: a real code for checking
|
|
:param inserted_code_size: size of inserted code
|
|
"""
|
|
seq1 = [(offset, op, arg) for offset, op, arg in dis._unpack_opargs(list(code1.co_code))]
|
|
seq2 = [(offset, op, arg) for offset, op, arg in dis._unpack_opargs(list(code2.co_code))]
|
|
assert len(seq1) == len(seq2), "Bytes sequences have different lengths %s != %s" % (len(seq1), len(seq2))
|
|
for i in range(len(seq1)):
|
|
of, op1, arg1 = seq1[i]
|
|
_, op2, arg2 = seq2[i]
|
|
if op1 != op2:
|
|
if op1 == 115 and op2 == 1:
|
|
# it's ok, because we added POP_JUMP_IF_TRUE manually, but it's POP_TOP in the real code
|
|
# inserted code - 2 (removed return instruction) - real code inserted
|
|
# Jump should be done to the beginning of inserted fragment
|
|
self.assertEqual(arg1, of - (inserted_code_size - 2))
|
|
continue
|
|
elif op1 == EXTENDED_ARG and op2 == 12:
|
|
# we added a real UNARY_NOT to balance EXTENDED_ARG added by new jump instruction
|
|
# i.e. inserted code size was increased as well
|
|
inserted_code_size += 2
|
|
continue
|
|
|
|
self.assertEqual(op1, op2, "Different operators at offset {}".format(of))
|
|
|
|
if op1 in LOAD_OPCODES:
|
|
# When comparing arguments of the load operations we shouldn't rely only on arguments themselves,
|
|
# because their order may change. It's better to compare the actual values instead.
|
|
self.comapre_load_args(of, code1, code2, op1, arg1, arg2)
|
|
elif arg1 != arg2:
|
|
self.assertEquals(arg1, arg2, "Different arguments at offset {}".format(of))
|
|
|
|
def comapre_load_args(self, offset, code1, code2, opcode, arg1, arg2):
|
|
err_msg = "Different arguments at offset {}".format(offset)
|
|
if opcode == dis.opmap['LOAD_ATTR']:
|
|
assert code1.co_names[arg1] == code2.co_names[arg2], err_msg
|
|
elif opcode == dis.opmap['LOAD_CONST']:
|
|
const1, const2 = code1.co_consts[arg1], code2.co_consts[arg2]
|
|
if isinstance(const1, CodeType) and isinstance(const2, CodeType):
|
|
if const1.co_filename != const2.co_filename:
|
|
self.fail(err_msg)
|
|
elif opcode == dis.opmap['LOAD_FAST']:
|
|
assert code1.co_varnames[arg1] == code2.co_varnames[arg2], err_msg
|
|
elif opcode == dis.opmap['LOAD_GLOBAL']:
|
|
assert code1.co_names[arg1] == code2.co_names[arg2], err_msg
|
|
elif opcode == dis.opmap['LOAD_NAME']:
|
|
assert code1.co_names[arg1] == code2.co_names[arg2], err_msg
|
|
|
|
def test_line(self):
|
|
|
|
def foo():
|
|
global global_loaded
|
|
global_loaded()
|
|
|
|
def method():
|
|
a = 10
|
|
b = 20
|
|
c = 20
|
|
|
|
success, result = insert_code(method.__code__, foo.__code__, method.__code__.co_firstlineno + 1)
|
|
assert success
|
|
assert list(result.co_lnotab) == [10, 1, 4, 1, 4, 1]
|
|
|
|
success, result = insert_code(method.__code__, foo.__code__, method.__code__.co_firstlineno + 2)
|
|
assert success
|
|
assert list(result.co_lnotab) == [0, 1, 14, 1, 4, 1]
|
|
|
|
success, result = insert_code(method.__code__, foo.__code__, method.__code__.co_firstlineno + 3)
|
|
assert success
|
|
assert list(result.co_lnotab) == [0, 1, 4, 1, 14, 1]
|
|
|
|
def test_assignment(self):
|
|
self.original_stdout = sys.stdout
|
|
sys.stdout = StringIO()
|
|
|
|
try:
|
|
def original():
|
|
a = 1
|
|
b = 2
|
|
c = 3
|
|
|
|
self.check_insert_every_line(original, tracing, 3)
|
|
|
|
finally:
|
|
sys.stdout = self.original_stdout
|
|
|
|
def test_for_loop(self):
|
|
self.original_stdout = sys.stdout
|
|
sys.stdout = StringIO()
|
|
|
|
try:
|
|
def original():
|
|
n = 3
|
|
sum = 0
|
|
for i in range(n):
|
|
sum += i
|
|
return sum
|
|
|
|
self.check_insert_every_line(original, tracing, 5)
|
|
|
|
finally:
|
|
sys.stdout = self.original_stdout
|
|
|
|
def test_if(self):
|
|
self.original_stdout = sys.stdout
|
|
sys.stdout = StringIO()
|
|
|
|
try:
|
|
def original():
|
|
if True:
|
|
a = 1
|
|
else:
|
|
a = 0
|
|
print(a)
|
|
|
|
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 2)
|
|
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 5)
|
|
|
|
finally:
|
|
sys.stdout = self.original_stdout
|
|
|
|
def test_else(self):
|
|
self.original_stdout = sys.stdout
|
|
sys.stdout = StringIO()
|
|
|
|
try:
|
|
def original():
|
|
if False:
|
|
a = 1
|
|
else:
|
|
a = 0
|
|
print(a)
|
|
|
|
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 4)
|
|
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 5)
|
|
|
|
finally:
|
|
sys.stdout = self.original_stdout
|
|
|
|
def test_for_else(self):
|
|
self.original_stdout = sys.stdout
|
|
sys.stdout = StringIO()
|
|
|
|
try:
|
|
def original():
|
|
sum = 0
|
|
for i in range(3):
|
|
sum += i
|
|
else:
|
|
print(sum)
|
|
|
|
def check_line_1():
|
|
tracing()
|
|
sum = 0
|
|
for i in range(3):
|
|
sum += i
|
|
else:
|
|
print(sum)
|
|
|
|
def check_line_3():
|
|
sum = 0
|
|
for i in range(3):
|
|
tracing()
|
|
sum += i
|
|
else:
|
|
print(sum)
|
|
|
|
def check_line_5():
|
|
sum = 0
|
|
for i in range(3):
|
|
sum += i
|
|
else:
|
|
tracing()
|
|
print(sum)
|
|
|
|
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 1)
|
|
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 3)
|
|
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 5)
|
|
|
|
sys.stdout = self.original_stdout
|
|
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 1,
|
|
check_line_1.__code__)
|
|
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 3,
|
|
check_line_3.__code__)
|
|
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 5,
|
|
check_line_5.__code__)
|
|
|
|
finally:
|
|
sys.stdout = self.original_stdout
|
|
|
|
def test_elif(self):
|
|
self.original_stdout = sys.stdout
|
|
sys.stdout = StringIO()
|
|
|
|
try:
|
|
def original():
|
|
a = 5
|
|
b = 0
|
|
if a < 0:
|
|
print("a < 0")
|
|
elif a < 3:
|
|
print("a < 3")
|
|
else:
|
|
print("a >= 3")
|
|
b = a
|
|
return b
|
|
|
|
def check_line_1():
|
|
tracing()
|
|
a = 5
|
|
b = 0
|
|
if a < 0:
|
|
print("a < 0")
|
|
elif a < 3:
|
|
print("a < 3")
|
|
else:
|
|
print("a >= 3")
|
|
b = a
|
|
return b
|
|
|
|
def check_line_8():
|
|
a = 5
|
|
b = 0
|
|
if a < 0:
|
|
print("a < 0")
|
|
elif a < 3:
|
|
print("a < 3")
|
|
else:
|
|
tracing()
|
|
print("a >= 3")
|
|
b = a
|
|
return b
|
|
|
|
def check_line_9():
|
|
a = 5
|
|
b = 0
|
|
if a < 0:
|
|
print("a < 0")
|
|
elif a < 3:
|
|
print("a < 3")
|
|
else:
|
|
print("a >= 3")
|
|
tracing()
|
|
b = a
|
|
return b
|
|
|
|
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 1)
|
|
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 2)
|
|
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 8)
|
|
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 9)
|
|
|
|
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 1,
|
|
check_line_1.__code__)
|
|
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 8,
|
|
check_line_8.__code__)
|
|
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 9,
|
|
check_line_9.__code__)
|
|
|
|
finally:
|
|
sys.stdout = self.original_stdout
|
|
|
|
def test_call_other_function(self):
|
|
self.original_stdout = sys.stdout
|
|
sys.stdout = StringIO()
|
|
|
|
try:
|
|
def original():
|
|
a = 1
|
|
b = 3
|
|
c = bar(a, b)
|
|
return c
|
|
|
|
def check_line_3():
|
|
a = 1
|
|
b = 3
|
|
tracing()
|
|
c = bar(a, b)
|
|
return c
|
|
|
|
def check_line_4():
|
|
a = 1
|
|
b = 3
|
|
c = bar(a, b)
|
|
tracing()
|
|
return c
|
|
|
|
self.check_insert_every_line(original, tracing, 4)
|
|
sys.stdout = self.original_stdout
|
|
|
|
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 3,
|
|
check_line_3.__code__)
|
|
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 4,
|
|
check_line_4.__code__)
|
|
|
|
finally:
|
|
sys.stdout = self.original_stdout
|
|
|
|
def test_class_method(self):
|
|
self.original_stdout = sys.stdout
|
|
sys.stdout = StringIO()
|
|
|
|
try:
|
|
class A(object):
|
|
@staticmethod
|
|
def foo():
|
|
print("i'm in foo")
|
|
|
|
@staticmethod
|
|
def check_line_2():
|
|
tracing()
|
|
print("i'm in foo")
|
|
|
|
original = A.foo
|
|
self.check_insert_to_line_with_exec(original, tracing, original.__code__.co_firstlineno + 2)
|
|
|
|
self.check_insert_to_line_by_symbols(original, call_tracing, original.__code__.co_firstlineno + 2,
|
|
A.check_line_2.__code__)
|
|
|
|
finally:
|
|
sys.stdout = self.original_stdout
|
|
|
|
def test_offset_overflow(self):
|
|
self.original_stdout = sys.stdout
|
|
sys.stdout = StringIO()
|
|
|
|
try:
|
|
def foo():
|
|
a = 1
|
|
b = 2 # breakpoint
|
|
c = 3
|
|
a1 = 1 if a > 1 else 2
|
|
a2 = 1 if a > 1 else 2
|
|
a3 = 1 if a > 1 else 2
|
|
a4 = 1 if a > 1 else 2
|
|
a5 = 1 if a > 1 else 2
|
|
a6 = 1 if a > 1 else 2
|
|
a7 = 1 if a > 1 else 2
|
|
a8 = 1 if a > 1 else 2
|
|
a9 = 1 if a > 1 else 2
|
|
a10 = 1 if a > 1 else 2
|
|
a11 = 1 if a > 1 else 2
|
|
a12 = 1 if a > 1 else 2
|
|
a13 = 1 if a > 1 else 2
|
|
|
|
for i in range(1):
|
|
if a > 0:
|
|
print("111")
|
|
# a = 1
|
|
else:
|
|
print("222")
|
|
return b
|
|
|
|
def check_line_2():
|
|
a = 1
|
|
tracing()
|
|
b = 2
|
|
c = 3
|
|
a1 = 1 if a > 1 else 2
|
|
a2 = 1 if a > 1 else 2
|
|
a3 = 1 if a > 1 else 2
|
|
a4 = 1 if a > 1 else 2
|
|
a5 = 1 if a > 1 else 2
|
|
a6 = 1 if a > 1 else 2
|
|
a7 = 1 if a > 1 else 2
|
|
a8 = 1 if a > 1 else 2
|
|
a9 = 1 if a > 1 else 2
|
|
a10 = 1 if a > 1 else 2
|
|
a11 = 1 if a > 1 else 2
|
|
a12 = 1 if a > 1 else 2
|
|
a13 = 1 if a > 1 else 2
|
|
|
|
for i in range(1):
|
|
if a > 0:
|
|
print("111")
|
|
# a = 1
|
|
else:
|
|
print("222")
|
|
return b
|
|
|
|
self.check_insert_to_line_with_exec(foo, tracing, foo.__code__.co_firstlineno + 2)
|
|
|
|
self.check_insert_to_line_by_symbols(foo, call_tracing, foo.__code__.co_firstlineno + 2,
|
|
check_line_2.__code__)
|
|
|
|
finally:
|
|
sys.stdout = self.original_stdout
|
|
|
|
def test_long_lines(self):
|
|
self.original_stdout = sys.stdout
|
|
sys.stdout = StringIO()
|
|
|
|
try:
|
|
def foo():
|
|
a = 1
|
|
b = 1 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23
|
|
c = 1 if b > 1 else 2 if b > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23
|
|
d = 1 if c > 1 else 2 if c > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23
|
|
e = d + 1
|
|
return e
|
|
|
|
def check_line_2():
|
|
a = 1
|
|
tracing()
|
|
b = 1 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23
|
|
c = 1 if b > 1 else 2 if b > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23
|
|
d = 1 if c > 1 else 2 if c > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23 if a > 1 else 2 if a > 0 else 3 if a > 4 else 23
|
|
e = d + 1
|
|
return e
|
|
|
|
self.check_insert_to_line_with_exec(foo, tracing, foo.__code__.co_firstlineno + 2)
|
|
sys.stdout = self.original_stdout
|
|
|
|
self.check_insert_to_line_by_symbols(foo, call_tracing, foo.__code__.co_firstlineno + 2,
|
|
check_line_2.__code__)
|
|
|
|
finally:
|
|
sys.stdout = self.original_stdout
|
|
|
|
def test_many_names(self):
|
|
self.original_stdout = sys.stdout
|
|
sys.stdout = StringIO()
|
|
|
|
try:
|
|
from pydev_tests_python.resources._bytecode_many_names_example import foo
|
|
self.check_insert_to_line_with_exec(foo, tracing, foo.__code__.co_firstlineno + 2)
|
|
|
|
finally:
|
|
sys.stdout = self.original_stdout
|
|
|
|
def test_extended_arg_overflow(self):
|
|
|
|
from pydev_tests_python.resources._bytecode_overflow_example import Dummy, DummyTracing
|
|
self.check_insert_to_line_by_symbols(Dummy.fun, call_tracing, Dummy.fun.__code__.co_firstlineno + 3,
|
|
DummyTracing.fun.__code__)
|
|
|
|
def test_double_extended_arg(self):
|
|
self.original_stdout = sys.stdout
|
|
sys.stdout = StringIO()
|
|
|
|
try:
|
|
def foo():
|
|
a = 1
|
|
b = 2
|
|
if b > 0:
|
|
d = a + b
|
|
d += 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
a = a + 1
|
|
return a
|
|
|
|
def foo_check():
|
|
a = 1
|
|
b = 2
|
|
tracing()
|
|
if b > 0:
|
|
d = a + b
|
|
d += 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
a = a + 1
|
|
return a
|
|
|
|
def foo_check_2():
|
|
a = 1
|
|
b = 2
|
|
if b > 0:
|
|
d = a + b
|
|
d += 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
not tracing() # add 'not' to balance EXTENDED_ARG when jumping
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
b = b - 1 if a > 0 else b + 1
|
|
a = a + 1
|
|
return a
|
|
|
|
self.check_insert_to_line_with_exec(foo, tracing, foo.__code__.co_firstlineno + 2)
|
|
sys.stdout = self.original_stdout
|
|
|
|
self.check_insert_to_line_by_symbols(foo, call_tracing, foo.__code__.co_firstlineno + 3,
|
|
foo_check.__code__)
|
|
|
|
self.check_insert_to_line_by_symbols(foo, call_tracing, foo.__code__.co_firstlineno + 21,
|
|
foo_check_2.__code__)
|
|
|
|
|
|
finally:
|
|
sys.stdout = self.original_stdout
|