You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-04 01:09:52 +08:00
Merge pull request #84 from moagstar/property_based_test_function_call
Property based test function call
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -16,3 +16,5 @@
|
|||||||
/unpyc
|
/unpyc
|
||||||
__pycache__
|
__pycache__
|
||||||
build
|
build
|
||||||
|
/.venv*
|
||||||
|
/.idea
|
128
pytest/test_function_call.py
Normal file
128
pytest/test_function_call.py
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# std
|
||||||
|
import string
|
||||||
|
# 3rd party
|
||||||
|
from hypothesis import given, assume, strategies as st
|
||||||
|
import pytest
|
||||||
|
# uncompyle
|
||||||
|
from validate import validate_uncompyle
|
||||||
|
|
||||||
|
|
||||||
|
alpha = st.sampled_from(string.ascii_lowercase)
|
||||||
|
numbers = st.sampled_from(string.digits)
|
||||||
|
alphanum = st.sampled_from(string.ascii_lowercase + string.digits)
|
||||||
|
expressions = st.sampled_from([x for x in string.ascii_lowercase + string.digits] + ['x+1'])
|
||||||
|
|
||||||
|
|
||||||
|
@st.composite
|
||||||
|
def function_calls(draw):
|
||||||
|
"""
|
||||||
|
Strategy factory for generating function calls.
|
||||||
|
|
||||||
|
:param draw: Callable which draws examples from other strategies.
|
||||||
|
|
||||||
|
:return: The function call text.
|
||||||
|
"""
|
||||||
|
list1 = st.lists(alpha, min_size=0, max_size=1)
|
||||||
|
list3 = st.lists(alpha, min_size=0, max_size=3)
|
||||||
|
|
||||||
|
positional_args = draw(list3)
|
||||||
|
named_args = [x + '=0' for x in draw(list3)]
|
||||||
|
star_args = ['*' + x for x in draw(list1)]
|
||||||
|
double_star_args = ['**' + x for x in draw(list1)]
|
||||||
|
|
||||||
|
arguments = positional_args + named_args + star_args + double_star_args
|
||||||
|
draw(st.randoms()).shuffle(arguments)
|
||||||
|
arguments = ','.join(arguments)
|
||||||
|
|
||||||
|
function_call = 'fn({arguments})'.format(arguments=arguments)
|
||||||
|
try:
|
||||||
|
# TODO: Figure out the exact rules for ordering of positional, named,
|
||||||
|
# star args, double star args and in which versions the various
|
||||||
|
# types of arguments are supported so we don't need to check that the
|
||||||
|
# expression compiles like this.
|
||||||
|
compile(function_call, '<string>', 'single')
|
||||||
|
except:
|
||||||
|
assume(False)
|
||||||
|
return function_call
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_CALL_FUNCTION():
|
||||||
|
validate_uncompyle("fn(w,m,f)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_BUILD_CONST_KEY_MAP_BUILD_MAP_UNPACK_WITH_CALL_BUILD_TUPLE_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(w=0,m=0,**v)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_BUILD_MAP_BUILD_MAP_UNPACK_WITH_CALL_BUILD_TUPLE_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(a=0,**g)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_CALL_FUNCTION_KW():
|
||||||
|
validate_uncompyle("fn(j=0)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(*g,**j)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_BUILD_MAP_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(*z,u=0)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_BUILD_TUPLE_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(**a)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_BUILD_MAP_BUILD_TUPLE_BUILD_TUPLE_UNPACK_WITH_CALL_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(b,b,b=0,*a)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_BUILD_TUPLE_BUILD_TUPLE_UNPACK_WITH_CALL_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(*c,v)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_BUILD_CONST_KEY_MAP_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(i=0,y=0,*p)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason='skipping property based test until all individual tests are passing')
|
||||||
|
@given(function_calls())
|
||||||
|
def test_function_call(function_call):
|
||||||
|
validate_uncompyle(function_call)
|
||||||
|
|
||||||
|
|
||||||
|
examples = set()
|
||||||
|
generate_examples = False
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not generate_examples, reason='not generating examples')
|
||||||
|
@given(function_calls())
|
||||||
|
def test_generate_hypothesis(function_call):
|
||||||
|
examples.add(function_call)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not generate_examples, reason='not generating examples')
|
||||||
|
def test_generate_examples():
|
||||||
|
import dis
|
||||||
|
example_opcodes = {}
|
||||||
|
for example in examples:
|
||||||
|
opcodes = tuple(sorted(set(
|
||||||
|
instruction.opname
|
||||||
|
for instruction in dis.Bytecode(example)
|
||||||
|
if instruction.opname not in ('LOAD_CONST', 'LOAD_NAME', 'RETURN_VALUE')
|
||||||
|
)))
|
||||||
|
example_opcodes[opcodes] = example
|
||||||
|
for k, v in example_opcodes.items():
|
||||||
|
print('def test_' + '_'.join(k) + '():\n validate_uncompyle("' + v + '")\n\n')
|
||||||
|
return
|
@@ -2,18 +2,23 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
# std
|
# std
|
||||||
import os
|
import os
|
||||||
import dis
|
|
||||||
import difflib
|
import difflib
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import functools
|
||||||
# compatability
|
# compatability
|
||||||
import six
|
import six
|
||||||
# uncompyle6 / xdis
|
# uncompyle6 / xdis
|
||||||
from uncompyle6 import PYTHON_VERSION, deparse_code
|
from uncompyle6 import PYTHON_VERSION, IS_PYPY, deparse_code
|
||||||
|
# TODO : I think we can get xdis to support the dis api (python 3 version) by doing something like this there
|
||||||
|
from xdis.bytecode import Bytecode
|
||||||
|
from xdis.main import get_opcode
|
||||||
|
opc = get_opcode(PYTHON_VERSION, IS_PYPY)
|
||||||
|
Bytecode = functools.partial(Bytecode, opc=opc)
|
||||||
|
|
||||||
|
|
||||||
def _dis_to_text(co):
|
def _dis_to_text(co):
|
||||||
return dis.Bytecode(co).dis()
|
return Bytecode(co).dis()
|
||||||
|
|
||||||
|
|
||||||
def print_diff(original, uncompyled):
|
def print_diff(original, uncompyled):
|
||||||
@@ -99,9 +104,8 @@ def are_code_objects_equal(co1, co2):
|
|||||||
|
|
||||||
:return: True if the two code objects are approximately equal, otherwise False.
|
:return: True if the two code objects are approximately equal, otherwise False.
|
||||||
"""
|
"""
|
||||||
# TODO : Use xdis for python2 compatability
|
instructions1 = Bytecode(co1)
|
||||||
instructions1 = dis.Bytecode(co1)
|
instructions2 = Bytecode(co2)
|
||||||
instructions2 = dis.Bytecode(co2)
|
|
||||||
for opcode1, opcode2 in zip(instructions1, instructions2):
|
for opcode1, opcode2 in zip(instructions1, instructions2):
|
||||||
if not are_instructions_equal(opcode1, opcode2):
|
if not are_instructions_equal(opcode1, opcode2):
|
||||||
return False
|
return False
|
||||||
|
Reference in New Issue
Block a user