You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-04 09:22:40 +08:00
@@ -21,9 +21,13 @@ def expressions(draw):
|
|||||||
'container',
|
'container',
|
||||||
'self.attribute',
|
'self.attribute',
|
||||||
'self.method()',
|
'self.method()',
|
||||||
'sorted(items, key=lambda x: x.name)',
|
# These expressions are failing, I think these are control
|
||||||
'func(*args, **kwargs)',
|
# flow problems rather than problems with FORMAT_VALUE,
|
||||||
'text or default',
|
# 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'
|
||||||
)))
|
)))
|
||||||
|
|
||||||
|
|
||||||
@@ -119,6 +123,8 @@ def test_format_specifiers(format_specifier):
|
|||||||
|
|
||||||
|
|
||||||
def run_test(text):
|
def run_test(text):
|
||||||
|
hypothesis.assume(len(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')
|
||||||
@@ -136,9 +142,9 @@ def test_uncompyle_fstring(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!s}'",
|
"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)
|
||||||
|
Binary file not shown.
@@ -1,3 +1,5 @@
|
|||||||
var1 = 'x'
|
var1 = 'x'
|
||||||
var2 = 'y'
|
var2 = 'y'
|
||||||
print(f'interpolate {var1} strings {var2!r} {var2!s} py36')
|
print(f'interpolate {var1} strings {var2!r} {var2!s} py36')
|
||||||
|
print(f'{abc}0')
|
||||||
|
print(f'{abc}{abc!s}')
|
||||||
|
@@ -42,13 +42,22 @@ class PythonParser(GenericASTBuilder):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def add_unique_rules(self, rules, customize):
|
def add_unique_rules(self, rules, customize):
|
||||||
"""Add rules to grammar
|
"""Add rules (a list of string) to grammar
|
||||||
"""
|
"""
|
||||||
for rule in rules:
|
for rule in rules:
|
||||||
|
if len(rule) == 0:
|
||||||
|
continue
|
||||||
opname = rule.split('::=')[0].strip()
|
opname = rule.split('::=')[0].strip()
|
||||||
self.add_unique_rule(rule, opname, 0, customize)
|
self.add_unique_rule(rule, opname, 0, customize)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def add_unique_doc_rules(self, rules_str, customize):
|
||||||
|
"""Add rules (a docstring-like list of rules) to grammar
|
||||||
|
"""
|
||||||
|
rules = [r.strip() for r in rules_str.split("\n")]
|
||||||
|
self.add_unique_rules(rules, customize)
|
||||||
|
return
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
"""
|
"""
|
||||||
Remove recursive references to allow garbage
|
Remove recursive references to allow garbage
|
||||||
|
@@ -500,7 +500,6 @@ class Python3Parser(PythonParser):
|
|||||||
load_attr ::= expr LOOKUP_METHOD
|
load_attr ::= expr LOOKUP_METHOD
|
||||||
call_function ::= expr CALL_METHOD
|
call_function ::= expr CALL_METHOD
|
||||||
"""
|
"""
|
||||||
saw_format_value = False
|
|
||||||
for i, token in enumerate(tokens):
|
for i, token in enumerate(tokens):
|
||||||
opname = token.type
|
opname = token.type
|
||||||
opname_base = opname[:opname.rfind('_')]
|
opname_base = opname[:opname.rfind('_')]
|
||||||
@@ -513,13 +512,6 @@ class Python3Parser(PythonParser):
|
|||||||
assign2_pypy ::= expr expr designator designator
|
assign2_pypy ::= expr expr designator designator
|
||||||
""", nop_func)
|
""", nop_func)
|
||||||
continue
|
continue
|
||||||
elif opname == 'FORMAT_VALUE':
|
|
||||||
# Python 3.6+
|
|
||||||
self.addRule("""
|
|
||||||
expr ::= fstring_expr
|
|
||||||
fstring_expr ::= expr FORMAT_VALUE
|
|
||||||
""", nop_func)
|
|
||||||
|
|
||||||
elif opname in ('CALL_FUNCTION', 'CALL_FUNCTION_VAR',
|
elif opname in ('CALL_FUNCTION', 'CALL_FUNCTION_VAR',
|
||||||
'CALL_FUNCTION_VAR_KW', 'CALL_FUNCTION_KW'):
|
'CALL_FUNCTION_VAR_KW', 'CALL_FUNCTION_KW'):
|
||||||
self.custom_classfunc_rule(opname, token, customize)
|
self.custom_classfunc_rule(opname, token, customize)
|
||||||
@@ -542,14 +534,6 @@ class Python3Parser(PythonParser):
|
|||||||
if opname_base == 'BUILD_TUPLE':
|
if opname_base == 'BUILD_TUPLE':
|
||||||
rule = ('load_closure ::= %s%s' % (('LOAD_CLOSURE ' * v), opname))
|
rule = ('load_closure ::= %s%s' % (('LOAD_CLOSURE ' * v), opname))
|
||||||
self.add_unique_rule(rule, opname, token.attr, customize)
|
self.add_unique_rule(rule, opname, token.attr, customize)
|
||||||
if opname_base == 'BUILD_LIST' and saw_format_value:
|
|
||||||
format_or_str_n = "formatted_value_or_str_%s" % v
|
|
||||||
self.addRule("""
|
|
||||||
expr ::= joined_str
|
|
||||||
joined_str ::= LOAD_CONST LOAD_ATTR %s CALL_FUNCTION_1
|
|
||||||
%s ::= %s%s
|
|
||||||
""" % (format_or_str_n, format_or_str_n, ("formatted_value_or_str " *v), opname),
|
|
||||||
nop_func)
|
|
||||||
|
|
||||||
elif opname == 'LOOKUP_METHOD':
|
elif opname == 'LOOKUP_METHOD':
|
||||||
# A PyPy speciality - DRY with parse2
|
# A PyPy speciality - DRY with parse2
|
||||||
@@ -734,7 +718,6 @@ class Python33ParserSingle(Python33Parser, PythonParserSingle):
|
|||||||
|
|
||||||
def info(args):
|
def info(args):
|
||||||
# Check grammar
|
# Check grammar
|
||||||
# Should also add a way to dump grammar
|
|
||||||
p = Python3Parser()
|
p = Python3Parser()
|
||||||
if len(args) > 0:
|
if len(args) > 0:
|
||||||
arg = args[0]
|
arg = args[0]
|
||||||
@@ -746,7 +729,9 @@ def info(args):
|
|||||||
elif arg == '3.2':
|
elif arg == '3.2':
|
||||||
p = Python32Parser()
|
p = Python32Parser()
|
||||||
p.checkGrammar()
|
p.checkGrammar()
|
||||||
|
if len(sys.argv) > 1 and sys.argv[1] == 'dump':
|
||||||
|
print('-' * 50)
|
||||||
|
p.dumpGrammar()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys
|
import sys
|
||||||
|
@@ -16,16 +16,37 @@ class Python36Parser(Python35Parser):
|
|||||||
|
|
||||||
def p_36misc(self, args):
|
def p_36misc(self, args):
|
||||||
"""
|
"""
|
||||||
formatted_value ::= LOAD_FAST FORMAT_VALUE
|
fstring_multi ::= fstring_expr_or_strs BUILD_STRING
|
||||||
str ::= LOAD_CONST
|
fstring_expr_or_strs ::= fstring_expr_or_strs fstring_expr_or_str
|
||||||
joined_str ::= LOAD_CONST LOAD_ATTR format_value_or_strs
|
fstring_expr_or_strs ::= fstring_expr_or_str
|
||||||
BUILD_LIST CALL_FUNCTION
|
|
||||||
format_value_or_strs ::= format_value_or_strs format_value_or_str
|
|
||||||
format_value_or_strs ::= format_value_or_str
|
|
||||||
format_value_or_str ::= format_value
|
|
||||||
format_value_or_str ::= str
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def add_custom_rules(self, tokens, customize):
|
||||||
|
super(Python36Parser, self).add_custom_rules(tokens, customize)
|
||||||
|
for i, token in enumerate(tokens):
|
||||||
|
opname = token.type
|
||||||
|
if opname == 'FORMAT_VALUE':
|
||||||
|
rules_str = """
|
||||||
|
expr ::= fstring_single
|
||||||
|
fstring_single ::= expr FORMAT_VALUE
|
||||||
|
"""
|
||||||
|
self.add_unique_doc_rules(rules_str, customize)
|
||||||
|
elif opname == 'BUILD_STRING':
|
||||||
|
v = token.attr
|
||||||
|
fstring_expr_or_str_n = "fstring_expr_or_str_%s" % v
|
||||||
|
rules_str = """
|
||||||
|
expr ::= fstring_expr
|
||||||
|
fstring_expr ::= expr FORMAT_VALUE
|
||||||
|
str ::= LOAD_CONST
|
||||||
|
fstring_expr_or_str ::= fstring_expr
|
||||||
|
fstring_expr_or_str ::= str
|
||||||
|
|
||||||
|
expr ::= fstring_multi
|
||||||
|
fstring_multi ::= %s BUILD_STRING
|
||||||
|
%s ::= %sBUILD_STRING
|
||||||
|
""" % (fstring_expr_or_str_n, fstring_expr_or_str_n, "fstring_expr_or_str " * v)
|
||||||
|
self.add_unique_doc_rules(rules_str, customize)
|
||||||
|
|
||||||
class Python36ParserSingle(Python36Parser, PythonParserSingle):
|
class Python36ParserSingle(Python36Parser, PythonParserSingle):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@@ -514,12 +514,11 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
self.name = None
|
self.name = None
|
||||||
self.version = version
|
self.version = version
|
||||||
self.is_pypy = is_pypy
|
self.is_pypy = is_pypy
|
||||||
|
|
||||||
self.customize_for_version(is_pypy, version)
|
self.customize_for_version(is_pypy, version)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@staticmethod
|
def customize_for_version(self, is_pypy, version):
|
||||||
def customize_for_version(is_pypy, version):
|
|
||||||
if is_pypy:
|
if is_pypy:
|
||||||
########################
|
########################
|
||||||
# PyPy changes
|
# PyPy changes
|
||||||
@@ -632,8 +631,26 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
# Python 3.6+ Additions
|
# Python 3.6+ Additions
|
||||||
#######################
|
#######################
|
||||||
TABLE_DIRECT.update({
|
TABLE_DIRECT.update({
|
||||||
'fstring_expr': ( "f'{%c%{conversion}}'", 0),
|
'fstring_expr': ( "{%c%{conversion}}", 0),
|
||||||
|
'fstring_single': ( "f'{%c%{conversion}}'", 0),
|
||||||
|
'fstring_multi': ( "f'%c'", 0),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
FSTRING_CONVERSION_MAP = {1: '!s', 2: '!r', 3: '!a'}
|
||||||
|
def f_conversion(node):
|
||||||
|
node.conversion = FSTRING_CONVERSION_MAP.get(node.data[1].attr, '')
|
||||||
|
|
||||||
|
def n_fstring_expr(node):
|
||||||
|
f_conversion(node)
|
||||||
|
self.default(node)
|
||||||
|
self.n_fstring_expr = n_fstring_expr
|
||||||
|
|
||||||
|
def n_fstring_single(node):
|
||||||
|
f_conversion(node)
|
||||||
|
self.default(node)
|
||||||
|
|
||||||
|
self.n_fstring_single = n_fstring_single
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
f = property(lambda s: s.params['f'],
|
f = property(lambda s: s.params['f'],
|
||||||
@@ -1886,12 +1903,6 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
node[-2][0].type = 'unpack_w_parens'
|
node[-2][0].type = 'unpack_w_parens'
|
||||||
self.default(node)
|
self.default(node)
|
||||||
|
|
||||||
FSTRING_CONVERSION_MAP = {1: '!s', 2: '!r', 3: '!a'}
|
|
||||||
|
|
||||||
def n_fstring_expr(self, node):
|
|
||||||
node.conversion = self.FSTRING_CONVERSION_MAP.get(node.data[1].attr, '')
|
|
||||||
self.default(node)
|
|
||||||
|
|
||||||
def engine(self, entry, startnode):
|
def engine(self, entry, startnode):
|
||||||
"""The format template interpetation engine. See the comment at the
|
"""The format template interpetation engine. See the comment at the
|
||||||
beginning of this module for the how we interpret format specifications such as
|
beginning of this module for the how we interpret format specifications such as
|
||||||
|
Reference in New Issue
Block a user