mirror of
https://github.com/davidhalter/parso.git
synced 2025-12-08 05:34:51 +08:00
Fix Python 3.8 error issues
This commit is contained in:
@@ -16,6 +16,7 @@ ALLOWED_FUTURES = (
|
|||||||
'all_feature_names', 'nested_scopes', 'generators', 'division',
|
'all_feature_names', 'nested_scopes', 'generators', 'division',
|
||||||
'absolute_import', 'with_statement', 'print_function', 'unicode_literals',
|
'absolute_import', 'with_statement', 'print_function', 'unicode_literals',
|
||||||
)
|
)
|
||||||
|
_COMP_FOR_TYPES = ('comp_for', 'sync_comp_for')
|
||||||
|
|
||||||
|
|
||||||
def _iter_stmts(scope):
|
def _iter_stmts(scope):
|
||||||
@@ -34,12 +35,12 @@ def _iter_stmts(scope):
|
|||||||
|
|
||||||
def _get_comprehension_type(atom):
|
def _get_comprehension_type(atom):
|
||||||
first, second = atom.children[:2]
|
first, second = atom.children[:2]
|
||||||
if second.type == 'testlist_comp' and second.children[1].type == 'comp_for':
|
if second.type == 'testlist_comp' and second.children[1].type in _COMP_FOR_TYPES:
|
||||||
if first == '[':
|
if first == '[':
|
||||||
return 'list comprehension'
|
return 'list comprehension'
|
||||||
else:
|
else:
|
||||||
return 'generator expression'
|
return 'generator expression'
|
||||||
elif second.type == 'dictorsetmaker' and second.children[-1].type == 'comp_for':
|
elif second.type == 'dictorsetmaker' and second.children[-1].type in _COMP_FOR_TYPES:
|
||||||
if second.children[1] == ':':
|
if second.children[1] == ':':
|
||||||
return 'dict comprehension'
|
return 'dict comprehension'
|
||||||
else:
|
else:
|
||||||
@@ -460,17 +461,13 @@ class _YieldFromCheck(SyntaxRule):
|
|||||||
@ErrorFinder.register_rule(type='name')
|
@ErrorFinder.register_rule(type='name')
|
||||||
class _NameChecks(SyntaxRule):
|
class _NameChecks(SyntaxRule):
|
||||||
message = 'cannot assign to __debug__'
|
message = 'cannot assign to __debug__'
|
||||||
message_keyword = 'assignment to keyword'
|
|
||||||
message_none = 'cannot assign to None'
|
message_none = 'cannot assign to None'
|
||||||
|
|
||||||
def is_issue(self, leaf):
|
def is_issue(self, leaf):
|
||||||
self._normalizer.context.add_name(leaf)
|
self._normalizer.context.add_name(leaf)
|
||||||
|
|
||||||
if leaf.value == '__debug__' and leaf.is_definition():
|
if leaf.value == '__debug__' and leaf.is_definition():
|
||||||
if self._normalizer.version < (3, 0):
|
return True
|
||||||
return True
|
|
||||||
else:
|
|
||||||
self.add_issue(leaf, message=self.message_keyword)
|
|
||||||
if leaf.value == 'None' and self._normalizer.version < (3, 0) \
|
if leaf.value == 'None' and self._normalizer.version < (3, 0) \
|
||||||
and leaf.is_definition():
|
and leaf.is_definition():
|
||||||
self.add_issue(leaf, message=self.message_none)
|
self.add_issue(leaf, message=self.message_none)
|
||||||
@@ -538,7 +535,7 @@ class _StarStarCheck(SyntaxRule):
|
|||||||
def is_issue(self, leaf):
|
def is_issue(self, leaf):
|
||||||
if leaf.parent.type == 'dictorsetmaker':
|
if leaf.parent.type == 'dictorsetmaker':
|
||||||
comp_for = leaf.get_next_sibling().get_next_sibling()
|
comp_for = leaf.get_next_sibling().get_next_sibling()
|
||||||
return comp_for is not None and comp_for.type == 'comp_for'
|
return comp_for is not None and comp_for.type in _COMP_FOR_TYPES
|
||||||
|
|
||||||
|
|
||||||
@ErrorFinder.register_rule(value='yield')
|
@ErrorFinder.register_rule(value='yield')
|
||||||
@@ -637,7 +634,7 @@ class _StarExprRule(SyntaxRule):
|
|||||||
return True
|
return True
|
||||||
if node.parent.type == 'testlist_comp':
|
if node.parent.type == 'testlist_comp':
|
||||||
# [*[] for a in [1]]
|
# [*[] for a in [1]]
|
||||||
if node.parent.children[1].type == 'comp_for':
|
if node.parent.children[1].type in _COMP_FOR_TYPES:
|
||||||
self.add_issue(node, message=self.message_iterable_unpacking)
|
self.add_issue(node, message=self.message_iterable_unpacking)
|
||||||
if self._normalizer.version <= (3, 4):
|
if self._normalizer.version <= (3, 4):
|
||||||
n = search_ancestor(node, 'for_stmt', 'expr_stmt')
|
n = search_ancestor(node, 'for_stmt', 'expr_stmt')
|
||||||
@@ -730,10 +727,16 @@ class _ArgumentRule(SyntaxRule):
|
|||||||
if node.children[1] == '=' and first.type != 'name':
|
if node.children[1] == '=' and first.type != 'name':
|
||||||
if first.type == 'lambdef':
|
if first.type == 'lambdef':
|
||||||
# f(lambda: 1=1)
|
# f(lambda: 1=1)
|
||||||
message = "lambda cannot contain assignment"
|
if self._normalizer.version < (3, 8):
|
||||||
|
message = "lambda cannot contain assignment"
|
||||||
|
else:
|
||||||
|
message = 'expression cannot contain assignment, perhaps you meant "=="?'
|
||||||
else:
|
else:
|
||||||
# f(+x=1)
|
# f(+x=1)
|
||||||
message = "keyword can't be an expression"
|
if self._normalizer.version < (3, 8):
|
||||||
|
message = "keyword can't be an expression"
|
||||||
|
else:
|
||||||
|
message = 'expression cannot contain assignment, perhaps you meant "=="?'
|
||||||
self.add_issue(first, message=message)
|
self.add_issue(first, message=message)
|
||||||
|
|
||||||
|
|
||||||
@@ -757,7 +760,7 @@ class _ArglistRule(SyntaxRule):
|
|||||||
def is_issue(self, node):
|
def is_issue(self, node):
|
||||||
first_arg = node.children[0]
|
first_arg = node.children[0]
|
||||||
if first_arg.type == 'argument' \
|
if first_arg.type == 'argument' \
|
||||||
and first_arg.children[1].type in ('comp_for', 'sync_comp_for'):
|
and first_arg.children[1].type in _COMP_FOR_TYPES:
|
||||||
# e.g. foo(x for x in [], b)
|
# e.g. foo(x for x in [], b)
|
||||||
return len(node.children) >= 2
|
return len(node.children) >= 2
|
||||||
else:
|
else:
|
||||||
@@ -890,7 +893,13 @@ class _CheckAssignmentRule(SyntaxRule):
|
|||||||
error = _get_comprehension_type(node)
|
error = _get_comprehension_type(node)
|
||||||
if error is None:
|
if error is None:
|
||||||
if second.type == 'dictorsetmaker':
|
if second.type == 'dictorsetmaker':
|
||||||
error = 'literal'
|
if self._normalizer.version < (3, 8):
|
||||||
|
error = 'literal'
|
||||||
|
else:
|
||||||
|
if second.children[1] == ':':
|
||||||
|
error = 'dict display'
|
||||||
|
else:
|
||||||
|
error = 'set display'
|
||||||
elif first in ('(', '['):
|
elif first in ('(', '['):
|
||||||
if second.type == 'yield_expr':
|
if second.type == 'yield_expr':
|
||||||
error = 'yield expression'
|
error = 'yield expression'
|
||||||
@@ -902,7 +911,10 @@ class _CheckAssignmentRule(SyntaxRule):
|
|||||||
else: # Everything handled, must be useless brackets.
|
else: # Everything handled, must be useless brackets.
|
||||||
self._check_assignment(second, is_deletion)
|
self._check_assignment(second, is_deletion)
|
||||||
elif type_ == 'keyword':
|
elif type_ == 'keyword':
|
||||||
error = 'keyword'
|
if self._normalizer.version < (3, 8):
|
||||||
|
error = 'keyword'
|
||||||
|
else:
|
||||||
|
error = str(node.value)
|
||||||
elif type_ == 'operator':
|
elif type_ == 'operator':
|
||||||
if node.value == '...':
|
if node.value == '...':
|
||||||
error = 'Ellipsis'
|
error = 'Ellipsis'
|
||||||
@@ -936,19 +948,23 @@ class _CheckAssignmentRule(SyntaxRule):
|
|||||||
error = 'operator'
|
error = 'operator'
|
||||||
|
|
||||||
if error is not None:
|
if error is not None:
|
||||||
message = "can't %s %s" % ("delete" if is_deletion else "assign to", error)
|
cannot = "can't" if self._normalizer.version < (3, 8) else "cannot"
|
||||||
|
message = ' '.join([cannot, "delete" if is_deletion else "assign to", error])
|
||||||
self.add_issue(node, message=message)
|
self.add_issue(node, message=message)
|
||||||
|
|
||||||
|
|
||||||
@ErrorFinder.register_rule(type='comp_for')
|
@ErrorFinder.register_rule(type='comp_for')
|
||||||
|
@ErrorFinder.register_rule(type='sync_comp_for')
|
||||||
class _CompForRule(_CheckAssignmentRule):
|
class _CompForRule(_CheckAssignmentRule):
|
||||||
message = "asynchronous comprehension outside of an asynchronous function"
|
message = "asynchronous comprehension outside of an asynchronous function"
|
||||||
|
|
||||||
def is_issue(self, node):
|
def is_issue(self, node):
|
||||||
# Some of the nodes here are already used, so no else if
|
# Some of the nodes here are already used, so no else if
|
||||||
expr_list = node.children[1 + int(node.children[0] == 'async')]
|
if node.type != 'comp_for' or self._normalizer.version < (3, 8):
|
||||||
if expr_list.type != 'expr_list': # Already handled.
|
# comp_for was replaced by sync_comp_for in Python 3.8.
|
||||||
self._check_assignment(expr_list)
|
expr_list = node.children[1 + int(node.children[0] == 'async')]
|
||||||
|
if expr_list.type != 'expr_list': # Already handled.
|
||||||
|
self._check_assignment(expr_list)
|
||||||
|
|
||||||
return node.children[0] == 'async' \
|
return node.children[0] == 'async' \
|
||||||
and not self._normalizer.context.is_async_funcdef()
|
and not self._normalizer.context.is_async_funcdef()
|
||||||
|
|||||||
@@ -251,10 +251,6 @@ GLOBAL_NONLOCAL_ERROR = [
|
|||||||
|
|
||||||
if sys.version_info >= (3, 6):
|
if sys.version_info >= (3, 6):
|
||||||
FAILING_EXAMPLES += GLOBAL_NONLOCAL_ERROR
|
FAILING_EXAMPLES += GLOBAL_NONLOCAL_ERROR
|
||||||
FAILING_EXAMPLES += [
|
|
||||||
# Raises multiple errors in previous versions.
|
|
||||||
'async def foo():\n def nofoo():[x async for x in []]',
|
|
||||||
]
|
|
||||||
if sys.version_info >= (3, 5):
|
if sys.version_info >= (3, 5):
|
||||||
FAILING_EXAMPLES += [
|
FAILING_EXAMPLES += [
|
||||||
# Raises different errors so just ignore them for now.
|
# Raises different errors so just ignore them for now.
|
||||||
|
|||||||
@@ -41,6 +41,29 @@ def test_python_exception_matches(code):
|
|||||||
assert line_nr is None or line_nr == error.start_pos[0]
|
assert line_nr is None or line_nr == error.start_pos[0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_non_async_in_async():
|
||||||
|
"""
|
||||||
|
This example doesn't work with FAILING_EXAMPLES, because the line numbers
|
||||||
|
are not always the same / incorrect in Python 3.8.
|
||||||
|
"""
|
||||||
|
if sys.version_info[:2] < (3, 5):
|
||||||
|
pytest.skip()
|
||||||
|
|
||||||
|
# Raises multiple errors in previous versions.
|
||||||
|
code = 'async def foo():\n def nofoo():[x async for x in []]'
|
||||||
|
wanted, line_nr = _get_actual_exception(code)
|
||||||
|
|
||||||
|
errors = _get_error_list(code)
|
||||||
|
if errors:
|
||||||
|
error, = errors
|
||||||
|
actual = error.message
|
||||||
|
assert actual in wanted
|
||||||
|
if sys.version_info[:2] < (3, 8):
|
||||||
|
assert line_nr == error.start_pos[0]
|
||||||
|
else:
|
||||||
|
assert line_nr == 0 # For whatever reason this is zero in Python 3.8+
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
('code', 'positions'), [
|
('code', 'positions'), [
|
||||||
('1 +', [(1, 3)]),
|
('1 +', [(1, 3)]),
|
||||||
@@ -103,7 +126,8 @@ def _get_actual_exception(code):
|
|||||||
# The python 3.5+ way, a bit nicer.
|
# The python 3.5+ way, a bit nicer.
|
||||||
wanted = 'SyntaxError: positional argument follows keyword argument'
|
wanted = 'SyntaxError: positional argument follows keyword argument'
|
||||||
elif wanted == 'SyntaxError: assignment to keyword':
|
elif wanted == 'SyntaxError: assignment to keyword':
|
||||||
return [wanted, "SyntaxError: can't assign to keyword"], line_nr
|
return [wanted, "SyntaxError: can't assign to keyword",
|
||||||
|
'SyntaxError: cannot assign to __debug__'], line_nr
|
||||||
elif wanted == 'SyntaxError: assignment to None':
|
elif wanted == 'SyntaxError: assignment to None':
|
||||||
# Python 2.6 does has a slightly different error.
|
# Python 2.6 does has a slightly different error.
|
||||||
wanted = 'SyntaxError: cannot assign to None'
|
wanted = 'SyntaxError: cannot assign to None'
|
||||||
|
|||||||
Reference in New Issue
Block a user