DRY scanner code more...

Expand 2.6 testing
This commit is contained in:
rocky
2018-04-03 10:35:02 -04:00
parent e2dec73a62
commit 1cd2d1e915
9 changed files with 39 additions and 52 deletions

View File

@@ -28,7 +28,7 @@ check-short: pytest
$(MAKE) -C test check-short $(MAKE) -C test check-short
#: Tests for Python 2.7, 3.3 and 3.4 #: Tests for Python 2.7, 3.3 and 3.4
check-2.7 check-3.3 check-3.4: pytest check-2.6 check-2.7 check-3.3 check-3.4: pytest
$(MAKE) -C test $@ $(MAKE) -C test $@
#: Tests for Python 3.2 and 3.5 - pytest doesn't work here #: Tests for Python 3.2 and 3.5 - pytest doesn't work here

View File

@@ -23,12 +23,7 @@ def test_if_in_for():
code = bug.__code__ code = bug.__code__
scan = get_scanner(PYTHON_VERSION) scan = get_scanner(PYTHON_VERSION)
if 2.7 <= PYTHON_VERSION <= 3.0 and not IS_PYPY: if 2.7 <= PYTHON_VERSION <= 3.0 and not IS_PYPY:
bytecode = scan.build_instructions(code) scan.build_instructions(code)
scan.lines = scan.build_lines_data(code)
scan.insts = list(bytecode)
scan.offset2inst_index = {}
for i, inst in enumerate(scan.insts):
scan.offset2inst_index[inst.offset] = i
fjt = scan.find_jump_targets(False) fjt = scan.find_jump_targets(False)
## FIXME: the data below is wrong. ## FIXME: the data below is wrong.
@@ -43,12 +38,7 @@ def test_if_in_for():
# {'start': 62, 'end': 63, 'type': 'for-else'}] # {'start': 62, 'end': 63, 'type': 'for-else'}]
code = bug_loop.__code__ code = bug_loop.__code__
bytecode = scan.build_instructions(code) scan.build_instructions(code)
scan.lines = scan.build_lines_data(code)
scan.insts = list(bytecode)
scan.offset2inst_index = {}
for i, inst in enumerate(scan.insts):
scan.offset2inst_index[inst.offset] = i
fjt = scan.find_jump_targets(False) fjt = scan.find_jump_targets(False)
assert{64: [42], 67: [42, 42], 42: [16, 41], 19: [6]} == fjt assert{64: [42], 67: [42, 42], 42: [16, 41], 19: [6]} == fjt
assert scan.structs == [ assert scan.structs == [
@@ -62,12 +52,7 @@ def test_if_in_for():
{'start': 48, 'end': 67, 'type': 'while-loop'}] {'start': 48, 'end': 67, 'type': 'while-loop'}]
elif 3.2 < PYTHON_VERSION <= 3.4: elif 3.2 < PYTHON_VERSION <= 3.4:
bytecode = scan.build_instructions(code) scan.build_instructions(code)
scan.lines = scan.build_lines_data(code)
scan.insts = list(bytecode)
scan.offset2inst_index = {}
for i, inst in enumerate(scan.insts):
scan.offset2inst_index[inst.offset] = i
fjt = scan.find_jump_targets(False) fjt = scan.find_jump_targets(False)
assert {69: [66], 63: [18]} == fjt assert {69: [66], 63: [18]} == fjt
assert scan.structs == \ assert scan.structs == \

View File

@@ -6,7 +6,7 @@ import pytest
# uncompyle # uncompyle
from validate import validate_uncompyle from validate import validate_uncompyle
from test_fstring import expressions from test_fstring import expressions
from uncompyle6 import PYTHON_VERSION
alpha = st.sampled_from(string.ascii_lowercase) alpha = st.sampled_from(string.ascii_lowercase)
numbers = st.sampled_from(string.digits) numbers = st.sampled_from(string.digits)
@@ -81,7 +81,8 @@ def function_calls(draw,
def test_function_no_args(): def test_function_no_args():
validate_uncompyle("fn()") validate_uncompyle("fn()")
@pytest.mark.skipif(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
def isolated_function_calls(which): def isolated_function_calls(which):
""" """
Returns a strategy for generating function calls, but isolated to Returns a strategy for generating function calls, but isolated to
@@ -108,21 +109,29 @@ def isolated_function_calls(which):
with settings(max_examples=25): with settings(max_examples=25):
@pytest.mark.skipif(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
@given(isolated_function_calls('positional')) @given(isolated_function_calls('positional'))
@example("fn(0)") @example("fn(0)")
def test_function_positional_only(expr): def test_function_positional_only(expr):
validate_uncompyle(expr) validate_uncompyle(expr)
@pytest.mark.skipif(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
@given(isolated_function_calls('keyword')) @given(isolated_function_calls('keyword'))
@example("fn(a=0)") @example("fn(a=0)")
def test_function_call_keyword_only(expr): def test_function_call_keyword_only(expr):
validate_uncompyle(expr) validate_uncompyle(expr)
@pytest.mark.skipif(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
@given(isolated_function_calls('star')) @given(isolated_function_calls('star'))
@example("fn(*items)") @example("fn(*items)")
def test_function_call_star_only(expr): def test_function_call_star_only(expr):
validate_uncompyle(expr) validate_uncompyle(expr)
@pytest.mark.skipif(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
@given(isolated_function_calls('double_star')) @given(isolated_function_calls('double_star'))
@example("fn(**{})") @example("fn(**{})")
def test_function_call_double_star_only(expr): def test_function_call_double_star_only(expr):

View File

@@ -20,13 +20,17 @@ def test_grammar():
# We have custom rules that create the below # We have custom rules that create the below
expect_lhs = set(['pos_arg', 'get_iter', 'attribute']) expect_lhs = set(['pos_arg', 'get_iter', 'attribute'])
unused_rhs = set(['list', 'mkfunc', 'dict', unused_rhs = set(['list', 'mkfunc',
'mklambda', 'mklambda',
'unpack',]) 'unpack',])
expect_right_recursive = set([('designList', expect_right_recursive = set([('designList',
('store', 'DUP_TOP', 'designList'))]) ('store', 'DUP_TOP', 'designList'))])
if PYTHON_VERSION > 2.6:
expect_lhs.add('kvlist') expect_lhs.add('kvlist')
expect_lhs.add('kv3') expect_lhs.add('kv3')
unused_rhs.add('dict')
if PYTHON3: if PYTHON3:
expect_lhs.add('load_genexpr') expect_lhs.add('load_genexpr')
@@ -85,6 +89,8 @@ def test_grammar():
""".split()) """.split())
if 2.6 <= PYTHON_VERSION <= 2.7: if 2.6 <= PYTHON_VERSION <= 2.7:
opcode_set = set(s.opc.opname).union(ignore_set) opcode_set = set(s.opc.opname).union(ignore_set)
if PYTHON_VERSION == 2.6:
opcode_set.add("THEN")
check_tokens(tokens, opcode_set) check_tokens(tokens, opcode_set)
elif PYTHON_VERSION == 3.4: elif PYTHON_VERSION == 3.4:
ignore_set.add('LOAD_CLASSNAME') ignore_set.add('LOAD_CLASSNAME')

View File

@@ -1,6 +1,8 @@
import pytest import pytest
from uncompyle6 import PYTHON_VERSION, PYTHON3, deparse_code from uncompyle6 import PYTHON_VERSION, deparse_code
@pytest.mark.skip(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
def test_single_mode(): def test_single_mode():
single_expressions = ( single_expressions = (
'i = 1', 'i = 1',

View File

@@ -105,6 +105,11 @@ class Scanner(object):
bytecode = Bytecode(co, self.opc) bytecode = Bytecode(co, self.opc)
self.build_prev_op() self.build_prev_op()
self.insts = self.remove_extended_args(list(bytecode)) self.insts = self.remove_extended_args(list(bytecode))
self.lines = self.build_lines_data(co)
self.offset2inst_index = {}
for i, inst in enumerate(self.insts):
self.offset2inst_index[inst.offset] = i
return bytecode return bytecode
def build_lines_data(self, code_obj): def build_lines_data(self, code_obj):
@@ -117,10 +122,6 @@ class Scanner(object):
linestarts = list(self.opc.findlinestarts(code_obj)) linestarts = list(self.opc.findlinestarts(code_obj))
self.linestarts = dict(linestarts) self.linestarts = dict(linestarts)
# Plain set with offsets of first ops on line.
# FIXME: we probably could do without
self.linestart_offsets = set(a for (a, _) in linestarts)
# 'List-map' which shows line number of current op and offset of # 'List-map' which shows line number of current op and offset of
# first op on following line, given offset of op as index # first op on following line, given offset of op as index
lines = [] lines = []
@@ -452,6 +453,9 @@ class Scanner(object):
Go through passed offsets, filtering ifs Go through passed offsets, filtering ifs
located somewhere mid-line. located somewhere mid-line.
""" """
# FIXME: this doesn't work for Python 3.6+
filtered = [] filtered = []
for i in ifs: for i in ifs:
# For each offset, if line number of current and next op # For each offset, if line number of current and next op

View File

@@ -171,11 +171,6 @@ class Scanner2(Scanner):
customize['PyPy'] = 0 customize['PyPy'] = 0
codelen = len(self.code) codelen = len(self.code)
self.lines = self.build_lines_data(co)
self.offset2inst_index = {}
for i, inst in enumerate(self.insts):
self.offset2inst_index[inst.offset] = i
free, names, varnames = self.unmangle_code_names(co, classname) free, names, varnames = self.unmangle_code_names(co, classname)
self.names = names self.names = names
@@ -186,8 +181,6 @@ class Scanner2(Scanner):
self.load_asserts = set() self.load_asserts = set()
for i in self.op_range(0, codelen): for i in self.op_range(0, codelen):
self.offset2inst_index[inst.offset] = i
# We need to detect the difference between: # We need to detect the difference between:
# raise AssertionError # raise AssertionError
# and # and
@@ -358,7 +351,7 @@ class Scanner2(Scanner):
if (offset in self.stmts and if (offset in self.stmts and
self.code[offset+3] not in (self.opc.END_FINALLY, self.code[offset+3] not in (self.opc.END_FINALLY,
self.opc.POP_BLOCK)): self.opc.POP_BLOCK)):
if ((offset in self.linestart_offsets and if ((offset in self.linestarts and
self.code[self.prev[offset]] == self.opc.JUMP_ABSOLUTE) self.code[self.prev[offset]] == self.opc.JUMP_ABSOLUTE)
or self.code[target] == self.opc.FOR_ITER or self.code[target] == self.opc.FOR_ITER
or offset not in self.not_continue): or offset not in self.not_continue):
@@ -956,7 +949,7 @@ class Scanner2(Scanner):
'end': pre_rtarget}) 'end': pre_rtarget})
# FIXME: this is yet another case were we need dominators. # FIXME: this is yet another case were we need dominators.
if (pre_rtarget not in self.linestart_offsets if (pre_rtarget not in self.linestarts
or self.version < 2.7): or self.version < 2.7):
self.not_continue.add(pre_rtarget) self.not_continue.add(pre_rtarget)

View File

@@ -84,13 +84,6 @@ class Scanner26(scan.Scanner2):
codelen = len(self.code) codelen = len(self.code)
self.lines = self.build_lines_data(co)
self.insts = list(bytecode)
self.offset2inst_index = {}
for i, inst in enumerate(self.insts):
self.offset2inst_index[inst.offset] = i
free, names, varnames = self.unmangle_code_names(co, classname) free, names, varnames = self.unmangle_code_names(co, classname)
self.names = names self.names = names
@@ -248,7 +241,7 @@ class Scanner26(scan.Scanner2):
if (offset in self.stmts if (offset in self.stmts
and self.code[offset+3] not in (self.opc.END_FINALLY, and self.code[offset+3] not in (self.opc.END_FINALLY,
self.opc.POP_BLOCK)): self.opc.POP_BLOCK)):
if ((offset in self.linestart_offsets and if ((offset in self.linestarts and
tokens[-1].kind == 'JUMP_BACK') tokens[-1].kind == 'JUMP_BACK')
or offset not in self.not_continue): or offset not in self.not_continue):
op_name = 'CONTINUE' op_name = 'CONTINUE'

View File

@@ -187,19 +187,14 @@ class Scanner3(Scanner):
if self.is_pypy: if self.is_pypy:
customize['PyPy'] = 0 customize['PyPy'] = 0
self.lines = self.build_lines_data(co)
# Scan for assertions. Later we will # Scan for assertions. Later we will
# turn 'LOAD_GLOBAL' to 'LOAD_ASSERT'. # turn 'LOAD_GLOBAL' to 'LOAD_ASSERT'.
# 'LOAD_ASSERT' is used in assert statements. # 'LOAD_ASSERT' is used in assert statements.
self.load_asserts = set() self.load_asserts = set()
self.offset2inst_index = {}
n = len(self.insts) n = len(self.insts)
for i, inst in enumerate(self.insts): for i, inst in enumerate(self.insts):
self.offset2inst_index[inst.offset] = i
# We need to detect the difference between: # We need to detect the difference between:
# raise AssertionError # raise AssertionError
# and # and