Disable hypothesis on 2.6.9

This commit is contained in:
rocky
2018-06-12 14:34:54 -04:00
parent 3b8e6635e2
commit ac2bbfc65a
3 changed files with 292 additions and 286 deletions

View File

@@ -1,150 +1,154 @@
# std # std
import os
# test # test
import pytest
import hypothesis
from hypothesis import strategies as st
# uncompyle6
from uncompyle6 import PYTHON_VERSION, deparse_code from uncompyle6 import PYTHON_VERSION, deparse_code
import pytest
pytestmark = pytest.mark.skipif(PYTHON_VERSION <= 2.6,
reason='hypothesis needs 2.7 or later')
if PYTHON_VERSION > 2.6:
import hypothesis
from hypothesis import strategies as st
# uncompyle6
@st.composite @st.composite
def expressions(draw): def expressions(draw):
# todo : would be nice to generate expressions using hypothesis however # todo : would be nice to generate expressions using hypothesis however
# this is pretty involved so for now just use a corpus of expressions # this is pretty involved so for now just use a corpus of expressions
# from which to select. # from which to select.
return draw(st.sampled_from(( return draw(st.sampled_from((
'abc', 'abc',
'len(items)', 'len(items)',
'x + 1', 'x + 1',
'lineno', 'lineno',
'container', 'container',
'self.attribute', 'self.attribute',
'self.method()', 'self.method()',
# These expressions are failing, I think these are control # These expressions are failing, I think these are control
# flow problems rather than problems with FORMAT_VALUE, # flow problems rather than problems with FORMAT_VALUE,
# however I need to confirm this... # however I need to confirm this...
#'sorted(items, key=lambda x: x.name)', #'sorted(items, key=lambda x: x.name)',
#'func(*args, **kwargs)', #'func(*args, **kwargs)',
#'text or default', #'text or default',
#'43 if life_the_universe and everything else None' #'43 if life_the_universe and everything else None'
))) )))
@st.composite @st.composite
def format_specifiers(draw): def format_specifiers(draw):
""" """
Generate a valid format specifier using the rules: Generate a valid format specifier using the rules:
format_spec ::= [[fill]align][sign][#][0][width][,][.precision][type] format_spec ::= [[fill]align][sign][#][0][width][,][.precision][type]
fill ::= <any character> fill ::= <any character>
align ::= "<" | ">" | "=" | "^" align ::= "<" | ">" | "=" | "^"
sign ::= "+" | "-" | " " sign ::= "+" | "-" | " "
width ::= integer width ::= integer
precision ::= integer precision ::= integer
type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%" type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"
See https://docs.python.org/2/library/string.html See https://docs.python.org/2/library/string.html
:param draw: Let hypothesis draw from other strategies. :param draw: Let hypothesis draw from other strategies.
:return: An example format_specifier. :return: An example format_specifier.
""" """
alphabet_strategy = st.characters(min_codepoint=ord('a'), max_codepoint=ord('z')) alphabet_strategy = st.characters(min_codepoint=ord('a'), max_codepoint=ord('z'))
fill = draw(st.one_of(alphabet_strategy, st.none())) fill = draw(st.one_of(alphabet_strategy, st.none()))
align = draw(st.sampled_from(list('<>=^'))) align = draw(st.sampled_from(list('<>=^')))
fill_align = (fill + align or '') if fill else '' fill_align = (fill + align or '') if fill else ''
type_ = draw(st.sampled_from('bcdeEfFgGnosxX%')) type_ = draw(st.sampled_from('bcdeEfFgGnosxX%'))
can_have_sign = type_ in 'deEfFgGnoxX%' can_have_sign = type_ in 'deEfFgGnoxX%'
can_have_comma = type_ in 'deEfFgG%' can_have_comma = type_ in 'deEfFgG%'
can_have_precision = type_ in 'fFgG' can_have_precision = type_ in 'fFgG'
can_have_pound = type_ in 'boxX%' can_have_pound = type_ in 'boxX%'
can_have_zero = type_ in 'oxX' can_have_zero = type_ in 'oxX'
sign = draw(st.sampled_from(list('+- ') + [''])) if can_have_sign else '' sign = draw(st.sampled_from(list('+- ') + [''])) if can_have_sign else ''
pound = draw(st.sampled_from(('#', '',))) if can_have_pound else '' pound = draw(st.sampled_from(('#', '',))) if can_have_pound else ''
zero = draw(st.sampled_from(('0', '',))) if can_have_zero else '' zero = draw(st.sampled_from(('0', '',))) if can_have_zero else ''
int_strategy = st.integers(min_value=1, max_value=1000) int_strategy = st.integers(min_value=1, max_value=1000)
width = draw(st.one_of(int_strategy, st.none())) width = draw(st.one_of(int_strategy, st.none()))
width = str(width) if width is not None else '' width = str(width) if width is not None else ''
comma = draw(st.sampled_from((',', '',))) if can_have_comma else '' comma = draw(st.sampled_from((',', '',))) if can_have_comma else ''
if can_have_precision: if can_have_precision:
precision = draw(st.one_of(int_strategy, st.none())) precision = draw(st.one_of(int_strategy, st.none()))
precision = '.' + str(precision) if precision else '' precision = '.' + str(precision) if precision else ''
else: else:
precision = '' precision = ''
return ''.join((fill_align, sign, pound, zero, width, comma, precision, type_,)) return ''.join((fill_align, sign, pound, zero, width, comma, precision, type_,))
@st.composite @st.composite
def fstrings(draw): def fstrings(draw):
""" """
Generate a valid f-string. Generate a valid f-string.
See https://www.python.org/dev/peps/pep-0498/#specification See https://www.python.org/dev/peps/pep-0498/#specification
:param draw: Let hypothsis draw from other strategies. :param draw: Let hypothsis draw from other strategies.
:return: A valid f-string. :return: A valid f-string.
""" """
character_strategy = st.characters( character_strategy = st.characters(
blacklist_characters='\r\n\'\\s{}', blacklist_characters='\r\n\'\\s{}',
min_codepoint=1, min_codepoint=1,
max_codepoint=1000, max_codepoint=1000,
) )
is_raw = draw(st.booleans()) is_raw = draw(st.booleans())
integer_strategy = st.integers(min_value=0, max_value=3) integer_strategy = st.integers(min_value=0, max_value=3)
expression_count = draw(integer_strategy) expression_count = draw(integer_strategy)
content = [] content = []
for _ in range(expression_count): for _ in range(expression_count):
expression = draw(expressions()) expression = draw(expressions())
conversion = draw(st.sampled_from(('', '!s', '!r', '!a',))) conversion = draw(st.sampled_from(('', '!s', '!r', '!a',)))
has_specifier = draw(st.booleans()) has_specifier = draw(st.booleans())
specifier = ':' + draw(format_specifiers()) if has_specifier else '' specifier = ':' + draw(format_specifiers()) if has_specifier else ''
content.append('{{{}{}}}'.format(expression, conversion, specifier)) content.append('{{{}{}}}'.format(expression, conversion, specifier))
content.append(draw(st.text(character_strategy))) content.append(draw(st.text(character_strategy)))
content = ''.join(content) content = ''.join(content)
return "f{}'{}'".format('r' if is_raw else '', content) return "f{}'{}'".format('r' if is_raw else '', content)
@pytest.mark.skipif(PYTHON_VERSION < 3.6, reason='need at least python 3.6') @pytest.mark.skipif(PYTHON_VERSION < 3.6, reason='need at least python 3.6')
@hypothesis.given(format_specifiers()) @hypothesis.given(format_specifiers())
def test_format_specifiers(format_specifier): def test_format_specifiers(format_specifier):
"""Verify that format_specifiers generates valid specifiers""" """Verify that format_specifiers generates valid specifiers"""
try: try:
exec('"{:' + format_specifier + '}".format(0)') exec('"{:' + format_specifier + '}".format(0)')
except ValueError as e: except ValueError as e:
if 'Unknown format code' not in str(e): if 'Unknown format code' not in str(e):
raise raise
def run_test(text): def run_test(text):
hypothesis.assume(len(text)) hypothesis.assume(len(text))
hypothesis.assume("f'{" in text) hypothesis.assume("f'{" in text)
expr = text + '\n' expr = text + '\n'
code = compile(expr, '<string>', 'single') code = compile(expr, '<string>', 'single')
deparsed = deparse_code(PYTHON_VERSION, code, compile_mode='single') deparsed = deparse_code(PYTHON_VERSION, code, compile_mode='single')
recompiled = compile(deparsed.text, '<string>', 'single') recompiled = compile(deparsed.text, '<string>', 'single')
if recompiled != code: if recompiled != code:
assert 'dis(' + deparsed.text.strip('\n') + ')' == 'dis(' + expr.strip('\n') + ')' assert 'dis(' + deparsed.text.strip('\n') + ')' == 'dis(' + expr.strip('\n') + ')'
@pytest.mark.skipif(PYTHON_VERSION < 3.6, reason='need at least python 3.6') @pytest.mark.skipif(PYTHON_VERSION < 3.6, reason='need at least python 3.6')
@hypothesis.given(fstrings()) @hypothesis.given(fstrings())
def test_uncompyle_fstring(fstring): def test_uncompyle_fstring(fstring):
"""Verify uncompyling fstring bytecode""" """Verify uncompyling fstring bytecode"""
run_test(fstring) run_test(fstring)
@pytest.mark.skipif(PYTHON_VERSION < 3.6, reason='need at least python 3.6') @pytest.mark.skipif(PYTHON_VERSION < 3.6, reason='need at least python 3.6')
@pytest.mark.parametrize('fstring', [ @pytest.mark.parametrize('fstring', [
"f'{abc}{abc!s}'", "f'{abc}{abc!s}'",
"f'{abc}0'", "f'{abc}0'",
]) ])
def test_uncompyle_direct(fstring): def test_uncompyle_direct(fstring):
"""useful for debugging""" """useful for debugging"""
run_test(fstring) run_test(fstring)

View File

@@ -1,184 +1,185 @@
# std
import string import string
# 3rd party
from hypothesis import given, assume, example, settings, strategies as st
import pytest
# uncompyle
from validate import validate_uncompyle
from test_fstring import expressions
from uncompyle6 import PYTHON_VERSION from uncompyle6 import PYTHON_VERSION
import pytest
pytestmark = pytest.mark.skip(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
alpha = st.sampled_from(string.ascii_lowercase) if PYTHON_VERSION > 2.6:
numbers = st.sampled_from(string.digits) from hypothesis import given, assume, example, settings, strategies as st
alphanum = st.sampled_from(string.ascii_lowercase + string.digits) from validate import validate_uncompyle
from test_fstring import expressions
alpha = st.sampled_from(string.ascii_lowercase)
numbers = st.sampled_from(string.digits)
alphanum = st.sampled_from(string.ascii_lowercase + string.digits)
@st.composite @st.composite
def function_calls(draw, def function_calls(draw,
min_keyword_args=0, max_keyword_args=5, min_keyword_args=0, max_keyword_args=5,
min_positional_args=0, max_positional_args=5, min_positional_args=0, max_positional_args=5,
min_star_args=0, max_star_args=1, min_star_args=0, max_star_args=1,
min_double_star_args=0, max_double_star_args=1): min_double_star_args=0, max_double_star_args=1):
""" """
Strategy factory for generating function calls. Strategy factory for generating function calls.
:param draw: Callable which draws examples from other strategies. :param draw: Callable which draws examples from other strategies.
:return: The function call text. :return: The function call text.
""" """
st_positional_args = st.lists( st_positional_args = st.lists(
alpha, alpha,
min_size=min_positional_args, min_size=min_positional_args,
max_size=max_positional_args max_size=max_positional_args
) )
st_keyword_args = st.lists( st_keyword_args = st.lists(
alpha, alpha,
min_size=min_keyword_args, min_size=min_keyword_args,
max_size=max_keyword_args max_size=max_keyword_args
) )
st_star_args = st.lists( st_star_args = st.lists(
alpha, alpha,
min_size=min_star_args, min_size=min_star_args,
max_size=max_star_args max_size=max_star_args
) )
st_double_star_args = st.lists( st_double_star_args = st.lists(
alpha, alpha,
min_size=min_double_star_args, min_size=min_double_star_args,
max_size=max_double_star_args max_size=max_double_star_args
) )
positional_args = draw(st_positional_args) positional_args = draw(st_positional_args)
keyword_args = draw(st_keyword_args) keyword_args = draw(st_keyword_args)
st_values = st.lists( st_values = st.lists(
expressions(), expressions(),
min_size=len(keyword_args), min_size=len(keyword_args),
max_size=len(keyword_args) max_size=len(keyword_args)
) )
keyword_args = [ keyword_args = [
x + '=' + e x + '=' + e
for x, e in for x, e in
zip(keyword_args, draw(st_values)) zip(keyword_args, draw(st_values))
] ]
star_args = ['*' + x for x in draw(st_star_args)] star_args = ['*' + x for x in draw(st_star_args)]
double_star_args = ['**' + x for x in draw(st_double_star_args)] double_star_args = ['**' + x for x in draw(st_double_star_args)]
arguments = positional_args + keyword_args + star_args + double_star_args arguments = positional_args + keyword_args + star_args + double_star_args
draw(st.randoms()).shuffle(arguments) draw(st.randoms()).shuffle(arguments)
arguments = ','.join(arguments) arguments = ','.join(arguments)
function_call = 'fn({arguments})'.format(arguments=arguments) function_call = 'fn({arguments})'.format(arguments=arguments)
try: try:
# TODO: Figure out the exact rules for ordering of positional, keyword, # TODO: Figure out the exact rules for ordering of positional, keyword,
# star args, double star args and in which versions the various # 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 # types of arguments are supported so we don't need to check that the
# expression compiles like this. # expression compiles like this.
compile(function_call, '<string>', 'single') compile(function_call, '<string>', 'single')
except: except:
assume(False) assume(False)
return function_call return function_call
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):
"""
Returns a strategy for generating function calls, but isolated to
particular types of arguments, for example only positional arguments.
This can help reason about debugging errors in specific types of function
calls.
:param which: One of 'keyword', 'positional', 'star', 'double_star'
:return: Strategy for generating an function call isolated to specific
argument types.
"""
kwargs = dict(
max_keyword_args=0,
max_positional_args=0,
max_star_args=0,
max_double_star_args=0,
)
kwargs['_'.join(('min', which, 'args'))] = 1
kwargs['_'.join(('max', which, 'args'))] = 5 if 'star' not in which else 1
return function_calls(**kwargs)
with settings(max_examples=25):
@pytest.mark.skipif(PYTHON_VERSION < 2.7, @pytest.mark.skipif(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7") reason="need at least Python 2.7")
@given(isolated_function_calls('positional')) def isolated_function_calls(which):
@example("fn(0)") """
def test_function_positional_only(expr): Returns a strategy for generating function calls, but isolated to
validate_uncompyle(expr) particular types of arguments, for example only positional arguments.
@pytest.mark.skipif(PYTHON_VERSION < 2.7, This can help reason about debugging errors in specific types of function
reason="need at least Python 2.7") calls.
@given(isolated_function_calls('keyword'))
@example("fn(a=0)")
def test_function_call_keyword_only(expr):
validate_uncompyle(expr)
@pytest.mark.skipif(PYTHON_VERSION < 2.7, :param which: One of 'keyword', 'positional', 'star', 'double_star'
reason="need at least Python 2.7")
@given(isolated_function_calls('star'))
@example("fn(*items)")
def test_function_call_star_only(expr):
validate_uncompyle(expr)
@pytest.mark.skipif(PYTHON_VERSION < 2.7, :return: Strategy for generating an function call isolated to specific
reason="need at least Python 2.7") argument types.
@given(isolated_function_calls('double_star')) """
@example("fn(**{})") kwargs = dict(
def test_function_call_double_star_only(expr): max_keyword_args=0,
validate_uncompyle(expr) max_positional_args=0,
max_star_args=0,
max_double_star_args=0,
)
kwargs['_'.join(('min', which, 'args'))] = 1
kwargs['_'.join(('max', which, 'args'))] = 5 if 'star' not in which else 1
return function_calls(**kwargs)
@pytest.mark.xfail() with settings(max_examples=25):
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.skipif(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
@given(isolated_function_calls('positional'))
@example("fn(0)")
def test_function_positional_only(expr):
validate_uncompyle(expr)
@pytest.mark.skipif(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
@given(isolated_function_calls('keyword'))
@example("fn(a=0)")
def test_function_call_keyword_only(expr):
validate_uncompyle(expr)
@pytest.mark.skipif(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
@given(isolated_function_calls('star'))
@example("fn(*items)")
def test_function_call_star_only(expr):
validate_uncompyle(expr)
@pytest.mark.skipif(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
@given(isolated_function_calls('double_star'))
@example("fn(**{})")
def test_function_call_double_star_only(expr):
validate_uncompyle(expr)
@pytest.mark.xfail() @pytest.mark.xfail()
def test_BUILD_MAP_BUILD_MAP_UNPACK_WITH_CALL_BUILD_TUPLE_CALL_FUNCTION_EX(): def test_BUILD_CONST_KEY_MAP_BUILD_MAP_UNPACK_WITH_CALL_BUILD_TUPLE_CALL_FUNCTION_EX():
validate_uncompyle("fn(a=0,**g)") validate_uncompyle("fn(w=0,m=0,**v)")
@pytest.mark.xfail() @pytest.mark.xfail()
def test_CALL_FUNCTION_EX(): def test_BUILD_MAP_BUILD_MAP_UNPACK_WITH_CALL_BUILD_TUPLE_CALL_FUNCTION_EX():
validate_uncompyle("fn(*g,**j)") validate_uncompyle("fn(a=0,**g)")
@pytest.mark.xfail() @pytest.mark.xfail()
def test_BUILD_MAP_CALL_FUNCTION_EX(): def test_CALL_FUNCTION_EX():
validate_uncompyle("fn(*z,u=0)") validate_uncompyle("fn(*g,**j)")
@pytest.mark.xfail() @pytest.mark.xfail()
def test_BUILD_TUPLE_CALL_FUNCTION_EX(): def test_BUILD_MAP_CALL_FUNCTION_EX():
validate_uncompyle("fn(**a)") validate_uncompyle("fn(*z,u=0)")
@pytest.mark.xfail() @pytest.mark.xfail()
def test_BUILD_MAP_BUILD_TUPLE_BUILD_TUPLE_UNPACK_WITH_CALL_CALL_FUNCTION_EX(): def test_BUILD_TUPLE_CALL_FUNCTION_EX():
validate_uncompyle("fn(b,b,b=0,*a)") validate_uncompyle("fn(**a)")
@pytest.mark.xfail() @pytest.mark.xfail()
def test_BUILD_TUPLE_BUILD_TUPLE_UNPACK_WITH_CALL_CALL_FUNCTION_EX(): def test_BUILD_MAP_BUILD_TUPLE_BUILD_TUPLE_UNPACK_WITH_CALL_CALL_FUNCTION_EX():
validate_uncompyle("fn(*c,v)") validate_uncompyle("fn(b,b,b=0,*a)")
@pytest.mark.xfail() @pytest.mark.xfail()
def test_BUILD_CONST_KEY_MAP_CALL_FUNCTION_EX(): def test_BUILD_TUPLE_BUILD_TUPLE_UNPACK_WITH_CALL_CALL_FUNCTION_EX():
validate_uncompyle("fn(i=0,y=0,*p)") validate_uncompyle("fn(*c,v)")
@pytest.mark.skip(reason='skipping property based test until all individual tests are passing') @pytest.mark.xfail()
@given(function_calls()) def test_BUILD_CONST_KEY_MAP_CALL_FUNCTION_EX():
def test_function_call(function_call): validate_uncompyle("fn(i=0,y=0,*p)")
validate_uncompyle(function_call)
@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)

View File

@@ -1,21 +1,22 @@
import pytest import pytest
from uncompyle6 import PYTHON_VERSION, deparse_code from uncompyle6 import PYTHON_VERSION, deparse_code
pytestmark = pytest.mark.skip(PYTHON_VERSION < 2.7,
reason="need at least Python 2.7")
@pytest.mark.skip(PYTHON_VERSION < 2.7, if PYTHON_VERSION > 2.6:
reason="need at least Python 2.7") def test_single_mode():
def test_single_mode(): single_expressions = (
single_expressions = ( 'i = 1',
'i = 1', 'i and (j or k)',
'i and (j or k)', 'i += 1',
'i += 1', 'i = j % 4',
'i = j % 4', 'i = {}',
'i = {}', 'i = []',
'i = []', 'for i in range(10):\n i\n',
'for i in range(10):\n i\n', 'for i in range(10):\n for j in range(10):\n i + j\n',
'for i in range(10):\n for j in range(10):\n i + j\n', 'try:\n i\nexcept Exception:\n j\nelse:\n k\n'
'try:\n i\nexcept Exception:\n j\nelse:\n k\n' )
)
for expr in single_expressions: for expr in single_expressions:
code = compile(expr + '\n', '<string>', 'single') code = compile(expr + '\n', '<string>', 'single')
assert deparse_code(PYTHON_VERSION, code, compile_mode='single').text == expr + '\n' assert deparse_code(PYTHON_VERSION, code, compile_mode='single').text == expr + '\n'