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