Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

## (0.41.0) UNRELEASED
### Added
- Support Python 3.14 unparenthesized exception lists in `except` and `except*`
clauses (PEP 758).
- New `DISABLE_SPLIT_LIST_WITH_COMMENT` flag.
`DISABLE_SPLIT_LIST_WITH_COMMENT` is a new knob that changes the
behavior of splitting a list when a comment is present inside the list.
Expand Down
3 changes: 2 additions & 1 deletion third_party/yapf_third_party/_ylib2to3/Grammar.txt
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ try_stmt: ('try' ':' suite
with_stmt: 'with' asexpr_test (',' asexpr_test)* ':' suite

# NB compile.c makes sure that the default except clause is last
except_clause: 'except' ['*'] [test [(',' | 'as') test]]
# PEP 758 allows an unparenthesized exception list without an 'as' target.
except_clause: 'except' ['*'] [test ('as' test | (',' test)* [','])]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT

# Backward compatibility cruft to support:
Expand Down
4 changes: 2 additions & 2 deletions yapf/yapflib/logical_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,8 @@ def _SpaceRequiredBetween(left, right, is_line_disabled):
# Spaces between keyword, string, number, and identifier tokens.
return True
if lval == ',' and rval == ':':
# We do want a space between a comma and colon.
return True
# Keep a space in slices, but not after a trailing exception-list comma.
return pytree_utils.NodeName(left.node.parent) != 'except_clause'
if style.Get('SPACE_INSIDE_BRACKETS'):
# Supersede the "no space before a colon or comma" check.
if left.OpensScope() and rval == ':':
Expand Down
9 changes: 9 additions & 0 deletions yapftests/pytree_utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ def testClassNotLocal(self):
with self.assertRaises(SyntaxError):
pytree_utils.ParseCodeToTree('class nonlocal: pass\n')

def testUnparenthesizedExceptClausesWithAs(self):
for keyword in ('except', 'except*'):
for exceptions in ('ValueError, TypeError', 'ValueError,'):
with self.subTest(keyword=keyword, exceptions=exceptions):
code = 'try:\n pass\n{} {} as error:\n pass\n'.format(
keyword, exceptions)
with self.assertRaises(SyntaxError):
pytree_utils.ParseCodeToTree(code)


class InsertNodesBeforeAfterTest(yapf_test_helper.YAPFTest):

Expand Down
48 changes: 48 additions & 0 deletions yapftests/reformatter_python3_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

from yapf.yapflib import reformatter
from yapf.yapflib import style
from yapf.yapflib import yapf_api

from yapftests import yapf_test_helper

Expand All @@ -30,6 +31,53 @@ class TestsForPython3Code(yapf_test_helper.YAPFTest):
def setUpClass(cls): # pylint: disable=g-missing-super-call
style.SetGlobalStyle(style.CreatePEP8Style())

def testUnparenthesizedExceptClauses(self):
# https://github.com/google/yapf/issues/1319
for keyword in ('except', 'except*'):
for exceptions, formatted_exceptions in (
('ZeroDivisionError,ValueError,IndexError',
'ZeroDivisionError, ValueError, IndexError'),
('ValueError,TypeError,IndexError,KeyError',
'ValueError, TypeError, IndexError, KeyError'),
('ValueError,', 'ValueError,'),
('ValueError,TypeError,', 'ValueError, TypeError,'),
):
with self.subTest(keyword=keyword, exceptions=exceptions):
unformatted_code = ('try:\n x=1/0\n{} {}:\n pass\n'.format(
keyword, exceptions))
expected_formatted_code = (
'try:\n x = 1 / 0\n{} {}:\n pass\n'.format(
keyword.replace('*', ' *'), formatted_exceptions))
formatted_code, changed = yapf_api.FormatCode(
unformatted_code, style_config='pep8')
self.assertTrue(changed)
self.assertCodeEqual(expected_formatted_code, formatted_code)
self.assertEqual(
(formatted_code, False),
yapf_api.FormatCode(formatted_code, style_config='pep8'))

def testLongUnparenthesizedExceptClauses(self):
# Without parentheses, splitting the exception list would be invalid.
for keyword in ('except', 'except *'):
with self.subTest(keyword=keyword):
code = ('try:\n pass\n'
'{} module.FirstException, module.SecondException, '
'module.ThirdException, module.FourthException:\n'
' pass\n').format(keyword)
self.assertEqual((code, False),
yapf_api.FormatCode(code, style_config='pep8'))

def testExistingExceptClauses(self):
for keyword in ('except', 'except *'):
for exceptions in ('ValueError', 'ValueError as error',
'(ValueError, TypeError)',
'(ValueError, TypeError) as error'):
with self.subTest(keyword=keyword, exceptions=exceptions):
code = 'try:\n pass\n{} {}:\n pass\n'.format(
keyword, exceptions)
self.assertEqual((code, False),
yapf_api.FormatCode(code, style_config='pep8'))

def testTypedNames(self):
unformatted_code = textwrap.dedent("""\
def x(aaaaaaaaaaaaaaa:int,bbbbbbbbbbbbbbbb:str,ccccccccccccccc:dict,eeeeeeeeeeeeee:set={1, 2, 3})->bool:
Expand Down
Loading