From 9d2ce4bcd4c1763ee0ce7451bda123864d4976b6 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Fri, 6 Apr 2018 09:50:07 +0200 Subject: [PATCH] Fix a few fstring error gatherings --- parso/python/errors.py | 20 +++++++++++++++++++- parso/python/grammar36.txt | 2 +- parso/python/tokenize.py | 2 +- test/failing_examples.py | 2 +- test/test_python_errors.py | 14 ++++++++++++++ 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/parso/python/errors.py b/parso/python/errors.py index fe1e273..8dc99b7 100644 --- a/parso/python/errors.py +++ b/parso/python/errors.py @@ -843,7 +843,7 @@ class _TryStmtRule(SyntaxRule): self.add_issue(default_except, message=self.message) -@ErrorFinder.register_rule(type='string') +@ErrorFinder.register_rule(type='fstring') class _FStringRule(SyntaxRule): _fstring_grammar = None message_empty = "f-string: empty expression not allowed" # f'{}' @@ -864,7 +864,25 @@ class _FStringRule(SyntaxRule): cls._fstring_grammar = parso.load_grammar(language='python-f-string') return cls._fstring_grammar + def _check_type(self, fstring_string): + index = -1 + value = fstring_string.value + while True: + index = value.find('}', index + 1) + if index == -1: + break # No further } found, we're finished. + elif index + 1 != len(value) and value[index + 1]: + # It's }}, which is totally ok. + index += 1 + else: + self.add_issue(fstring_string, message=self.message_single_closing) + def is_issue(self, fstring): + for fstring_content in fstring.children[1:-1]: + if fstring_content.type == 'fstring_string': + self._check_type(fstring_content) + return + print(fstring) if 'f' not in fstring.string_prefix.lower(): return diff --git a/parso/python/grammar36.txt b/parso/python/grammar36.txt index 7d112f7..b82c1fe 100644 --- a/parso/python/grammar36.txt +++ b/parso/python/grammar36.txt @@ -153,5 +153,5 @@ strings: (STRING | fstring)+ fstring: FSTRING_START fstring_content* FSTRING_END fstring_content: FSTRING_STRING | fstring_expr fstring_conversion: '!' NAME -fstring_expr: '{' testlist [ fstring_conversion ] [ fstring_format_spec ] '}' +fstring_expr: '{' testlist_comp [ fstring_conversion ] [ fstring_format_spec ] '}' fstring_format_spec: ':' fstring_content* diff --git a/parso/python/tokenize.py b/parso/python/tokenize.py index 150f72a..a410287 100644 --- a/parso/python/tokenize.py +++ b/parso/python/tokenize.py @@ -166,7 +166,7 @@ def _create_token_collection(version_info): # Because of leftmost-then-longest match semantics, be sure to put the # longest operators first (e.g., if = came before ==, == would get # recognized as two instances of =). - Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"!=", + Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"//=?", r"->", r"[+\-*/%&@`|^=<>]=?", r"~") diff --git a/test/failing_examples.py b/test/failing_examples.py index 4227679..25e93ca 100644 --- a/test/failing_examples.py +++ b/test/failing_examples.py @@ -141,7 +141,7 @@ FAILING_EXAMPLES = [ # f-strings 'f"{}"', - 'f"{\\}"', + r'f"{\}"', 'f"{\'\\\'}"', 'f"{#}"', "f'{1!b}'", diff --git a/test/test_python_errors.py b/test/test_python_errors.py index b724a41..ca7be7f 100644 --- a/test/test_python_errors.py +++ b/test/test_python_errors.py @@ -114,6 +114,20 @@ def _get_actual_exception(code): # Python 3.4/3.4 have a bit of a different warning than 3.5/3.6 in # certain places. But in others this error makes sense. return [wanted, "SyntaxError: can't use starred expression here"], line_nr + elif wanted == 'SyntaxError: f-string: unterminated string': + wanted = 'SyntaxError: EOL while scanning string literal' + elif wanted == 'SyntaxError: f-string expression part cannot include a backslash': + return [ + wanted, + "SyntaxError: EOL while scanning string literal", + "SyntaxError: unexpected character after line continuation character", + ], line_nr + elif wanted == 'SyntaxError: f-string: empty expression not allowed': + wanted = 'SyntaxError: invalid syntax' + elif wanted == "SyntaxError: f-string expression part cannot include '#'": + wanted = 'SyntaxError: invalid syntax' + elif wanted == "SyntaxError: f-string: expecting '}'": + wanted = 'SyntaxError: EOL while scanning string literal' return [wanted], line_nr