Add 'f-string: expressions nested too deeply' issue.

This commit is contained in:
Dave Halter
2017-08-26 11:10:04 +02:00
parent 7d4aa755a5
commit 296ecc6728
3 changed files with 28 additions and 11 deletions

View File

@@ -841,13 +841,12 @@ class _FStringRule(SyntaxRule):
_fstring_grammar = None
message_empty = "f-string: empty expression not allowed" # f'{}'
"f-string: single '}' is not allowed" # f'}'
"f-string: expressions nested too deeply" # f'{1:{5:{3}}}'
message_nested = "f-string: expressions nested too deeply"
message_backslash = "f-string expression part cannot include a backslash" # f'{"\"}' or f'{"\\"}'
message_comment = "f-string expression part cannot include '#'" # f'{#}'
"f-string: unterminated string" # f'{"}'
"f-string: mismatched '(', '{', or '['"
message_conversion = "f-string: invalid conversion character: expected 's', 'r', or 'a'"
"f-string: unexpected end of string" # Doesn't really happen?!
"f-string: expecting '}'" # f'{'
@classmethod
@@ -865,15 +864,9 @@ class _FStringRule(SyntaxRule):
parsed = self._load_grammar().parse_leaf(fstring)
for child in parsed.children:
if child.type == 'expression':
for c in child.children:
if c.type == 'python_expr':
self._check_expression(c)
elif c.type == 'conversion':
if c.value not in ('s', 'r', 'a'):
self.add_issue(c, message=self.message_conversion)
self._check_expression(child.children[1])
self._check_expression(child)
def _check_expression(self, python_expr):
def _check_python_expr(self, python_expr):
value = python_expr.value
if '\\' in value:
self.add_issue(python_expr, message=self.message_backslash)
@@ -888,6 +881,28 @@ class _FStringRule(SyntaxRule):
if parsed_expr.type == 'endmarker':
self.add_issue(python_expr, message=self.message_empty)
def _check_format_spec(self, format_spec):
for expression in format_spec.children[1:]:
nested_format_spec = expression.children[-2]
if nested_format_spec.type == 'format_spec':
if len(nested_format_spec.children) > 1:
self.add_issue(
nested_format_spec.children[1],
message=self.message_nested
)
self._check_expression(expression)
def _check_expression(self, expression):
for c in expression.children:
if c.type == 'python_expr':
self._check_python_expr(c)
elif c.type == 'conversion':
if c.value not in ('s', 'r', 'a'):
self.add_issue(c, message=self.message_conversion)
elif c.type == 'format_spec':
self._check_format_spec(c)
class _CheckAssignmentRule(SyntaxRule):
def _check_assignment(self, node, is_deletion=False):

View File

@@ -38,7 +38,8 @@ class TokenNamespace:
START_SYMBOL = 'fstring'
GRAMMAR = """
fstring: expression* ENDMARKER
expression: '{' PYTHON_EXPR [ '!' CONVERSION ] [ ':' expression* ] '}'
format_spec: ':' expression*
expression: '{' PYTHON_EXPR [ '!' CONVERSION ] [ format_spec ] '}'
"""
_prefix = r'((?:[^{}]+)*)'

View File

@@ -145,6 +145,7 @@ FAILING_EXAMPLES = [
'f"{\'\\\'}"',
'f"{#}"',
"f'{1!b}'",
"f'{1:{5:{3}}}'",
]
GLOBAL_NONLOCAL_ERROR = [