diff --git a/pytest/test_fstring.py b/pytest/test_fstring.py index 7a9d7328..1f019eb6 100644 --- a/pytest/test_fstring.py +++ b/pytest/test_fstring.py @@ -21,9 +21,13 @@ def expressions(draw): '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' ))) @@ -119,6 +123,8 @@ def test_format_specifiers(format_specifier): def run_test(text): + hypothesis.assume(len(text)) + hypothesis.assume("f'{" in text) expr = text + '\n' code = compile(expr, '', 'single') deparsed = deparse_code(PYTHON_VERSION, code, compile_mode='single') diff --git a/uncompyle6/parsers/parse3.py b/uncompyle6/parsers/parse3.py index 8f218397..02e049c5 100644 --- a/uncompyle6/parsers/parse3.py +++ b/uncompyle6/parsers/parse3.py @@ -516,19 +516,21 @@ class Python3Parser(PythonParser): elif opname == 'FORMAT_VALUE': # Python 3.6+ self.addRule(""" - expr ::= fstring_expr - fstring_expr ::= expr FORMAT_VALUE - str ::= LOAD_CONST - fstring_expr_or_str ::= fstring_expr - fstring_expr_or_str ::= str + expr ::= fstring_single + fstring_single ::= expr FORMAT_VALUE """, nop_func) elif opname == 'BUILD_STRING': # Python 3.6+ v = token.attr fstring_expr_or_str_n = "fstring_expr_or_str_%s" % v rule = """ - expr ::= fstring - fstring ::= %s BUILD_STRING + 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.addRule(rule, nop_func) diff --git a/uncompyle6/parsers/parse36.py b/uncompyle6/parsers/parse36.py index 6a5806f0..ae2777e0 100644 --- a/uncompyle6/parsers/parse36.py +++ b/uncompyle6/parsers/parse36.py @@ -16,9 +16,10 @@ class Python36Parser(Python35Parser): def p_36misc(self, args): """ + fstring_single ::= expr FORMAT_VALUE fstring_expr ::= expr FORMAT_VALUE str ::= LOAD_CONST - fstring ::= fstring_expr_or_strs BUILD_STRING + fstring_multi ::= fstring_expr_or_strs BUILD_STRING fstring_expr_or_strs ::= fstring_expr_or_strs fstring_expr_or_str fstring_expr_or_strs ::= fstring_expr_or_str fstring_expr_or_str ::= fstring_expr diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index c695745f..b95e8912 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -633,7 +633,8 @@ class SourceWalker(GenericASTTraversal, object): ####################### TABLE_DIRECT.update({ 'fstring_expr': ( "{%c%{conversion}}", 0), - 'fstring': ( "f'%c'", 0), + 'fstring_single': ( "f'{%c%{conversion}}'", 0), + 'fstring_multi': ( "f'%c'", 0), }) return @@ -1893,6 +1894,9 @@ class SourceWalker(GenericASTTraversal, object): node.conversion = self.FSTRING_CONVERSION_MAP.get(node.data[1].attr, '') self.default(node) + def n_fstring_single(self, node): + return self.n_fstring_expr(node) + def engine(self, entry, startnode): """The format template interpetation engine. See the comment at the beginning of this module for the how we interpret format specifications such as