diff --git a/CHANGELOG.md b/CHANGELOG.md index ccc8726c9..17cc37277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,46 @@ ## (0.41.0) UNRELEASED ### Added +- New `ALIGN_ASSIGNMENT` and `ALIGN_ASSIGNMENT_RESTART_AFTER_COMMENTS` flags. + `ALIGN_ASSIGNMENT` is a new knob that enables alignment of assignment + operators in consecutive lines. + + A blank line or multiline object (function, multiline dictionary, ...) will + always interrupt the alignment block and start a new one. Comments can also + optionally interrupt the block, a behaviour that can be turned on with + `ALIGN_ASSIGNMENT_RESTART_AFTER_COMMENTS`. + + For example, with `ALIGN_ASSIGNMENT` off: + ``` + val_first = 1 + val_second += 2 + # comment + val_third = 3 + ``` + + will be formatted as: + ``` + val_first = 1 + val_second += 2 + # comment + val_third = 3 + ``` + whereas with `ALIGN_ASSIGNMENT` on and + `ALIGN_ASSIGNMENT_RESTART_AFTER_COMMENTS` off we get: + ``` + val_first = 1 + val_second += 2 + # comment + val_third = 3 + ``` + while if they are both on: + ``` + val_first = 1 + val_second += 2 + # comment + val_third = 3 + ``` + - 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. diff --git a/CONTRIBUTORS b/CONTRIBUTORS index fe6f13d3b..29b874c6c 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -15,4 +15,6 @@ Sam Clegg Ɓukasz Langa Oleg Butuzov Mauricio Herrera Cuadra +Xiao Wang +Andrea Oliveri Kyle Gottfried diff --git a/README.md b/README.md index 7bb6b2277..93de3388b 100644 --- a/README.md +++ b/README.md @@ -357,6 +357,64 @@ optional arguments: ## Knobs +#### `ALIGN_ASSIGNMENT` + +> Align assignment or augmented assignment operators. If there is a blank +> line or objects with newline entries in between, it will start new block +> alignment. For exemple: + +```python + val_first = 1 + val_second += 2 + object = { + entry1:1, + entry2:2, + entry3:3, + } + val_third = 3 +``` + +> will be formatted as follows: + +```python + val_first = 1 + val_second += 2 + object = { + entry1: 1, + entry2: 2, + entry3: 3, + } + val_third = 3 +``` + +#### `ALIGN_ASSIGNMENT_RESTART_AFTER_COMMENTS` + +> Allows to choose whether to align assignments before and after a comment +> line independently. For exemple, if off: + +```python + val_first = 1 + val_second += 2 + # comment + val_third = 3 +``` + +will be aligned as: +```python + val_first = 1 + val_second += 2 + # comment + val_third = 3 +``` + +whereas if we turn it on: +```python + val_first = 1 + val_second += 2 + # comment + val_third = 3 +``` + #### `ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT` > Align closing bracket with visual indentation. diff --git a/yapf/yapflib/format_token.py b/yapf/yapflib/format_token.py index 141c2650e..5abcb8c3f 100644 --- a/yapf/yapflib/format_token.py +++ b/yapf/yapflib/format_token.py @@ -327,3 +327,26 @@ def is_pytype_comment(self): def is_copybara_comment(self): return self.is_comment and re.match( r'#.*\bcopybara:\s*(strip|insert|replace)', self.value) + + @property + def is_assign(self): + return subtypes.ASSIGN_OPERATOR in self.subtypes + + @property + @lru_cache() + def is_augassign(self): + return self.value in frozenset({ + '+=', + '-=', + '*=', + '@=', + '/=', + '//=', + '%=', + '<<=', + '>>=', + '|=', + '&=', + '^=', + '**=', + }) diff --git a/yapf/yapflib/reformatter.py b/yapf/yapflib/reformatter.py index ff0952543..eeac49873 100644 --- a/yapf/yapflib/reformatter.py +++ b/yapf/yapflib/reformatter.py @@ -98,6 +98,9 @@ def Reformat(llines, lines=None): final_lines.append(lline) prev_line = lline + if style.Get('ALIGN_ASSIGNMENT'): + _AlignAssignment(final_lines) + _AlignTrailingComments(final_lines) return _FormatFinalLines(final_lines) @@ -266,8 +269,15 @@ def _CanPlaceOnSingleLine(line): not any(tok.is_comment for tok in line.tokens[:last_index])) -def _AlignTrailingComments(final_lines): - """Align trailing comments to the same column.""" +def _AlignGeneric(final_lines, func_is_tok_to_align_current_line, + func_additional_checks_break_align_block, + func_calculate_pre_token_line_lengths, + func_calculate_column_to_align_on, + func_is_tok_to_align_following_lines, + func_get_val_after_align): + """Implements generic alignement logic which needs to be complemented for + needed purpose by passing suitable functions as parameters. + """ final_lines_index = 0 while final_lines_index < len(final_lines): line = final_lines[final_lines_index] @@ -276,123 +286,260 @@ def _AlignTrailingComments(final_lines): processed_content = False for tok in line.tokens: - if (tok.is_comment and isinstance(tok.spaces_required_before, list) and - tok.value.startswith('#')): - # All trailing comments and comments that appear on a line by themselves - # in this block should be indented at the same level. The block is - # terminated by an empty line or EOF. Enumerate through each line in - # the block and calculate the max line length. Once complete, use the - # first col value greater than that value and create the necessary for - # each line accordingly. - all_pc_line_lengths = [] # All pre-comment line lengths + + if func_is_tok_to_align_current_line(tok): + all_pt_line_lengths = [] # All pre-tok line lengths max_line_length = 0 + stop_next_it = False - while True: + while not stop_next_it: # EOF - if final_lines_index + len(all_pc_line_lengths) == len(final_lines): + if final_lines_index + len(all_pt_line_lengths) == len(final_lines): break - this_line = final_lines[final_lines_index + len(all_pc_line_lengths)] + this_line = final_lines[final_lines_index + len(all_pt_line_lengths)] # Blank line - note that content is preformatted so we don't need to # worry about spaces/tabs; a blank line will always be '\n\n'. assert this_line.tokens - if (all_pc_line_lengths and + if (all_pt_line_lengths and this_line.tokens[0].formatted_whitespace_prefix.startswith('\n\n') ): break if this_line.disable: - all_pc_line_lengths.append([]) + all_pt_line_lengths.append([]) continue - # Calculate the length of each line in this logical line. - line_content = '' - pc_line_lengths = [] - - for line_tok in this_line.tokens: - whitespace_prefix = line_tok.formatted_whitespace_prefix - - newline_index = whitespace_prefix.rfind('\n') - if newline_index != -1: - max_line_length = max(max_line_length, len(line_content)) - line_content = '' - - whitespace_prefix = whitespace_prefix[newline_index + 1:] + # Check for additional specific conditions for breaking out of loop. + stop_now, stop_next_it = func_additional_checks_break_align_block( + tok, final_lines, final_lines_index, all_pt_line_lengths) + if stop_now: + break - if line_tok.is_comment: - pc_line_lengths.append(len(line_content)) - else: - line_content += '{}{}'.format(whitespace_prefix, line_tok.value) + # Calculate the length of each line in this logical line. + pt_line_lengths, max_line_length, tmp = func_calculate_pre_token_line_lengths( + this_line, max_line_length) + stop_next_it = stop_next_it or tmp - if pc_line_lengths: - max_line_length = max(max_line_length, max(pc_line_lengths)) + if pt_line_lengths: + max_line_length = max(max_line_length, max(pt_line_lengths)) - all_pc_line_lengths.append(pc_line_lengths) + all_pt_line_lengths.append(pt_line_lengths) # Calculate the aligned column value max_line_length += 2 + aligned_col = func_calculate_column_to_align_on(tok, max_line_length) + assert isinstance(aligned_col, int) - aligned_col = None - for potential_col in tok.spaces_required_before: - if potential_col > max_line_length: - aligned_col = potential_col - break - - if aligned_col is None: - aligned_col = max_line_length - - # Update the comment token values based on the aligned values - for all_pc_line_lengths_index, pc_line_lengths in enumerate( - all_pc_line_lengths): - if not pc_line_lengths: + # Update the token values based on the aligned values + for all_pt_line_lengths_index, pt_line_lengths in enumerate( + all_pt_line_lengths): + if not pt_line_lengths: continue - this_line = final_lines[final_lines_index + all_pc_line_lengths_index] + this_line = final_lines[final_lines_index + all_pt_line_lengths_index] - pc_line_length_index = 0 + pt_line_length_index = 0 for line_tok in this_line.tokens: - if line_tok.is_comment: - assert pc_line_length_index < len(pc_line_lengths) - assert pc_line_lengths[pc_line_length_index] < aligned_col + if func_is_tok_to_align_following_lines(line_tok): + assert pt_line_length_index < len(pt_line_lengths) + assert pt_line_lengths[pt_line_length_index] < aligned_col + line_tok.value = func_get_val_after_align( + line_tok, aligned_col, pt_line_lengths[pt_line_length_index]) + pt_line_length_index += 1 - # Note that there may be newlines embedded in the comments, so - # we need to apply a whitespace prefix to each line. - whitespace = ' ' * ( - aligned_col - pc_line_lengths[pc_line_length_index] - 1) - pc_line_length_index += 1 + assert pt_line_length_index == len(pt_line_lengths) - line_content = [] + final_lines_index += len(all_pt_line_lengths) - for comment_line_index, comment_line in enumerate( - line_tok.value.split('\n')): - line_content.append('{}{}'.format(whitespace, - comment_line.strip())) + processed_content = True + break - if comment_line_index == 0: - whitespace = ' ' * (aligned_col - 1) + if not processed_content: + final_lines_index += 1 - line_content = '\n'.join(line_content) - # Account for initial whitespace already slated for the - # beginning of the line. - existing_whitespace_prefix = \ - line_tok.formatted_whitespace_prefix.lstrip('\n') +def _AlignTrailingComments(final_lines): + """Align trailing comments to the same column.""" - if line_content.startswith(existing_whitespace_prefix): - line_content = line_content[len(existing_whitespace_prefix):] + def _IsTokToAlignCurrentLine(tok): + # All trailing comments and comments that appear on a line by themselves + # in this block should be indented at the same level. The block is + # terminated by an empty line or EOF. Enumerate through each line in + # the block and calculate the max line length. Once complete, use the + # first col value greater than that value and create the necessary for + # each line accordingly. + return tok.is_comment and isinstance(tok.spaces_required_before, + list) and tok.value.startswith('#') + + def _IsTokToAlignFollowingLines(tok): + return tok.is_comment + + def _AdditionalChecksBreakAlignBlock(tok, final_lines, final_lines_index, + all_pt_line_lengths): + return False, False + + def _CalculatePreTokenLineLengths(this_line, max_line_length): + line_content = '' + pt_line_lengths = [] + stop_align_block = False + + for line_tok in this_line.tokens: + whitespace_prefix = line_tok.formatted_whitespace_prefix + + newline_index = whitespace_prefix.rfind('\n') + if newline_index != -1: + max_line_length = max(max_line_length, len(line_content)) + line_content = '' + + whitespace_prefix = whitespace_prefix[newline_index + 1:] + + if line_tok.is_comment: + pt_line_lengths.append(len(line_content)) + else: + line_content += '{}{}'.format(whitespace_prefix, line_tok.value) + + return pt_line_lengths, max_line_length, stop_align_block + + def _CalculateColumnToAlignOn(tok, max_line_length): + aligned_col = None + for potential_col in tok.spaces_required_before: + if potential_col > max_line_length: + aligned_col = potential_col + break - line_tok.value = line_content + if aligned_col is None: + aligned_col = max_line_length + + return aligned_col + + def _GetValueAfterAlign(line_tok, aligned_col, pt_line_length): + # Note that there may be newlines embedded in the comments, so we need to + # apply a whitespace prefix to each line. + whitespace = ' ' * (aligned_col - pt_line_length - 1) + + line_content = [] + + for comment_line_index, comment_line in enumerate( + line_tok.value.split('\n')): + line_content.append('{}{}'.format(whitespace, comment_line.strip())) + + if comment_line_index == 0: + whitespace = ' ' * (aligned_col - 1) + + line_content = '\n'.join(line_content) + + # Account for initial whitespace already slated for beginning of the line. + existing_whitespace_prefix = line_tok.formatted_whitespace_prefix.lstrip( + '\n') + + if line_content.startswith(existing_whitespace_prefix): + line_content = line_content[len(existing_whitespace_prefix):] + + return line_content + + return _AlignGeneric(final_lines, _IsTokToAlignCurrentLine, + _AdditionalChecksBreakAlignBlock, + _CalculatePreTokenLineLengths, _CalculateColumnToAlignOn, + _IsTokToAlignFollowingLines, _GetValueAfterAlign) + + +def _AlignAssignment(final_lines): + """Align assignment operators and augmented assignment operators to the same column""" + + def _IsTokToAlign(tok): + return tok.is_assign or tok.is_augassign + + def _AdditionalChecksBreakAlignBlock(tok, final_lines, final_lines_index, + all_pt_line_lengths): + stop_now = False + stop_next_it = False + + this_line_index = final_lines_index + len(all_pt_line_lengths) + this_line = final_lines[this_line_index] + + if this_line_index < len(final_lines) - 1: + next_line = final_lines[this_line_index + 1] + assert next_line.tokens + + if this_line.depth != next_line.depth: + stop_next_it = True + + # If there is a standalone comment or keyword statement line or other lines + # without assignment in between, break. + if (all_pt_line_lengths and + not any(tok.is_assign or tok.is_augassign for tok in this_line.tokens)): + if this_line.tokens[0].is_comment: + if style.Get('ALIGN_ASSIGNMENT_RESTART_AFTER_COMMENTS'): + stop_now = True + else: + stop_now = True + return stop_now, stop_next_it + + def _CalculatePreTokenLineLengths(this_line, max_line_length): + pt_line_length = [] + variables_content = '' + contain_object = False + line_tokens = this_line.tokens + # Only one assignment expression in on each line + for index in range(len(line_tokens)): + line_tok = line_tokens[index] + + prefix = line_tok.formatted_whitespace_prefix + newline_index = prefix.rfind('\n') + if newline_index != -1: + variables_content = '' + prefix = prefix[newline_index + 1:] + + if line_tok.is_assign or line_tok.is_augassign: + next_toks = [line_tokens[i] for i in range(index + 1, len(line_tokens))] + # if there is object(list/tuple/dict) with newline entries, break, + # update the alignment so far and start to calulate new alignment + for tok in next_toks: + if tok.value in ['(', '[', '{'] and tok.next_token: + if (tok.next_token.formatted_whitespace_prefix.startswith('\n') or + (tok.next_token.is_comment and tok.next_token.next_token + .formatted_whitespace_prefix.startswith('\n'))): + pt_line_length.append(len(variables_content)) + contain_object = True + break + if not contain_object: + if line_tok.is_assign: + pt_line_length.append(len(variables_content)) + # if augassign, add the extra augmented part to the max length caculation + elif line_tok.is_augassign: + pt_line_length.append( + len(variables_content) + len(line_tok.value) - 1) + # don't add the tokens after the assignment operator + break + else: + variables_content += '{}{}'.format(prefix, line_tok.value) + return pt_line_length, max_line_length, contain_object - assert pc_line_length_index == len(pc_line_lengths) + def _CalculateColumnToAlignOn(tok, max_line_length): + return max_line_length - final_lines_index += len(all_pc_line_lengths) + def _GetValueAfterAlign(line_tok, aligned_col, pt_line_length): + whitespace = ' ' * (aligned_col - pt_line_length - 1) - processed_content = True - break + assign_content = '{}{}'.format(whitespace, line_tok.value.strip()) - if not processed_content: - final_lines_index += 1 + existing_whitespace_prefix = line_tok.formatted_whitespace_prefix.lstrip( + '\n') + + # In case the existing spaces are larger than padded spaces + if (len(whitespace) == 1 or len(whitespace) > 1 and + len(existing_whitespace_prefix) > len(whitespace)): + line_tok.whitespace_prefix = '' + elif assign_content.startswith(existing_whitespace_prefix): + assign_content = assign_content[len(existing_whitespace_prefix):] + return assign_content + + return _AlignGeneric(final_lines, _IsTokToAlign, + _AdditionalChecksBreakAlignBlock, + _CalculatePreTokenLineLengths, _CalculateColumnToAlignOn, + _IsTokToAlign, _GetValueAfterAlign) def _FormatFinalLines(final_lines): diff --git a/yapf/yapflib/style.py b/yapf/yapflib/style.py index 7642c01f4..cbe0eb16b 100644 --- a/yapf/yapflib/style.py +++ b/yapf/yapflib/style.py @@ -59,6 +59,54 @@ def SetGlobalStyle(style): _STYLE_HELP = dict( # BASED_ON_STYLE='Which predefined style this style is based on', + ALIGN_ASSIGNMENT=textwrap.dedent("""\ + Align assignment or augmented assignment operators. + If there is a blank line or newline comment or objects with newline entries in between, + it will start new block alignment. For exemple: + + val_first = 1 + val_second += 2 + object = { + entry1:1, + entry2:2, + entry3:3, + } + val_third = 3 + + will be formatted as: + + val_first = 1 + val_second += 2 + object = { + entry1: 1, + entry2: 2, + entry3: 3, + } + val_third = 3 + """), + ALIGN_ASSIGNMENT_RESTART_AFTER_COMMENTS=textwrap.dedent("""\ + Start new assignment alignment block when there is a newline comment in + between. For example if this is off then: + + val_first = 1 + val_second += 2 + # comment + val_third = 3 + + will be formatted as: + + val_first = 1 + val_second += 2 + # comment + val_third = 3 + + and if it is on it will be formatted as: + + val_first = 1 + val_second += 2 + # comment + val_third = 3 + """), ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=textwrap.dedent("""\ Align closing bracket with visual indentation. """), @@ -476,6 +524,8 @@ def method(): def CreatePEP8Style(): """Create the PEP8 formatting style.""" return dict( + ALIGN_ASSIGNMENT=False, + ALIGN_ASSIGNMENT_RESTART_AFTER_COMMENTS=False, ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=True, ALLOW_MULTILINE_DICTIONARY_KEYS=False, ALLOW_MULTILINE_LAMBDAS=False, @@ -665,6 +715,8 @@ def _IntOrIntListConverter(s): # # Note: this dict has to map all the supported style options. _STYLE_OPTION_VALUE_CONVERTER = dict( + ALIGN_ASSIGNMENT=_BoolConverter, + ALIGN_ASSIGNMENT_RESTART_AFTER_COMMENTS=_BoolConverter, ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=_BoolConverter, ALLOW_MULTILINE_DICTIONARY_KEYS=_BoolConverter, ALLOW_MULTILINE_LAMBDAS=_BoolConverter, diff --git a/yapftests/reformatter_basic_test.py b/yapftests/reformatter_basic_test.py index 74b1ba405..9ef9b92ae 100644 --- a/yapftests/reformatter_basic_test.py +++ b/yapftests/reformatter_basic_test.py @@ -3339,6 +3339,227 @@ def testParenthesizedContextManagers(self): llines = yapf_test_helper.ParseAndUnwrap(unformatted_code) self.assertCodeEqual(expected, reformatter.Reformat(llines)) + #------tests for alignment functions-------- + def testAlignAssignBlankLineInbetween(self): + try: + style.SetGlobalStyle( + style.CreateStyleFromConfig('{align_assignment: true}')) + unformatted_code = textwrap.dedent("""\ + val_first = 1 + val_second += 2 + + val_third = 3 + """) + expected_formatted_code = textwrap.dedent("""\ + val_first = 1 + val_second += 2 + + val_third = 3 + """) + llines = yapf_test_helper.ParseAndUnwrap(unformatted_code) + self.assertCodeEqual(expected_formatted_code, + reformatter.Reformat(llines)) + finally: + style.SetGlobalStyle(style.CreateYapfStyle()) + + def testAlignAssignCommentLineInbetween(self): + try: + style.SetGlobalStyle( + style.CreateStyleFromConfig( + '{align_assignment: true,' + 'align_assignment_restart_after_comments: true}')) + unformatted_code = textwrap.dedent("""\ + val_first = 1 + val_second += 2 + # comment + val_third = 3 + """) + expected_formatted_code = textwrap.dedent("""\ + val_first = 1 + val_second += 2 + # comment + val_third = 3 + """) + llines = yapf_test_helper.ParseAndUnwrap(unformatted_code) + self.assertCodeEqual(expected_formatted_code, + reformatter.Reformat(llines)) + finally: + style.SetGlobalStyle(style.CreateYapfStyle()) + + def testAlignAssignContinueWithCommentLineInbetween(self): + try: + style.SetGlobalStyle( + style.CreateStyleFromConfig( + '{align_assignment: true,' + 'align_assignment_restart_after_comments: false}')) + unformatted_code = textwrap.dedent("""\ + val_first = 1 + val_second += 2 + # comment + val_third = 3 + """) + expected_formatted_code = textwrap.dedent("""\ + val_first = 1 + val_second += 2 + # comment + val_third = 3 + """) + llines = yapf_test_helper.ParseAndUnwrap(unformatted_code) + self.assertCodeEqual(expected_formatted_code, + reformatter.Reformat(llines)) + finally: + style.SetGlobalStyle(style.CreateYapfStyle()) + + def testAlignIgnoreAssignInComment(self): + try: + style.SetGlobalStyle( + style.CreateStyleFromConfig('{align_assignment: true}')) + unformatted_code = textwrap.dedent("""\ + val_first = 1 + val_second += 2 + # comments should not be = aligned + val_third = 3 + """) + expected_formatted_code = textwrap.dedent("""\ + val_first = 1 + val_second += 2 + # comments should not be = aligned + val_third = 3 + """) + llines = yapf_test_helper.ParseAndUnwrap(unformatted_code) + self.assertCodeEqual(expected_formatted_code, + reformatter.Reformat(llines)) + finally: + style.SetGlobalStyle(style.CreateYapfStyle()) + + def testAlignConfuseKwargs(self): + try: + style.SetGlobalStyle( + style.CreateStyleFromConfig('{align_assignment: true}')) + unformatted_code = textwrap.dedent("""\ + val_first = 1 + val_second += 2 + def fun(a=1): + a = 'example' + abc = '' + val_third = 3 + """) + expected_formatted_code = textwrap.dedent("""\ + val_first = 1 + val_second += 2 + + + def fun(a=1): + a = 'example' + abc = '' + + + val_third = 3 + """) + llines = yapf_test_helper.ParseAndUnwrap(unformatted_code) + self.assertCodeEqual(expected_formatted_code, + reformatter.Reformat(llines)) + finally: + style.SetGlobalStyle(style.CreateYapfStyle()) + + def testAlignAssignDefLineInbetween(self): + try: + style.SetGlobalStyle( + style.CreateStyleFromConfig('{align_assignment: true}')) + unformatted_code = textwrap.dedent("""\ + val_first = 1 + val_second += 2 + def fun(): + a = 'example' + abc = '' + val_third = 3 + """) + expected_formatted_code = textwrap.dedent("""\ + val_first = 1 + val_second += 2 + + + def fun(): + a = 'example' + abc = '' + + + val_third = 3 + """) + llines = yapf_test_helper.ParseAndUnwrap(unformatted_code) + self.assertCodeEqual(expected_formatted_code, + reformatter.Reformat(llines)) + finally: + style.SetGlobalStyle(style.CreateYapfStyle()) + + def testAlignAssignMultipleDepths(self): + try: + style.SetGlobalStyle( + style.CreateStyleFromConfig('{align_assignment: true}')) + unformatted_code = textwrap.dedent("""\ + if True: + val_first = 1 + val_second += 2 + val_third = 3 + val_fourth = 4 + """) + expected_formatted_code = textwrap.dedent("""\ + if True: + val_first = 1 + val_second += 2 + val_third = 3 + val_fourth = 4 + """) + llines = yapf_test_helper.ParseAndUnwrap(unformatted_code) + self.assertCodeEqual(expected_formatted_code, + reformatter.Reformat(llines)) + finally: + style.SetGlobalStyle(style.CreateYapfStyle()) + + def testAlignAssignObjectWithNewLineInbetween(self): + try: + style.SetGlobalStyle( + style.CreateStyleFromConfig('{align_assignment: true}')) + unformatted_code = textwrap.dedent("""\ + val_first = 1 + val_second += 2 + object = { + entry1:1, + entry2:2, + entry3:3, + } + val_third = 3 + """) + expected_formatted_code = textwrap.dedent("""\ + val_first = 1 + val_second += 2 + object = { + entry1: 1, + entry2: 2, + entry3: 3, + } + val_third = 3 + """) + llines = yapf_test_helper.ParseAndUnwrap(unformatted_code) + self.assertCodeEqual(expected_formatted_code, + reformatter.Reformat(llines)) + finally: + style.SetGlobalStyle(style.CreateYapfStyle()) + + def testAlignAssignWithOnlyOneAssignmentLine(self): + try: + style.SetGlobalStyle( + style.CreateStyleFromConfig('{align_assignment: true}')) + unformatted_code = textwrap.dedent("""\ + val_first = 1 + """) + expected_formatted_code = unformatted_code + llines = yapf_test_helper.ParseAndUnwrap(unformatted_code) + self.assertCodeEqual(expected_formatted_code, + reformatter.Reformat(llines)) + finally: + style.SetGlobalStyle(style.CreateYapfStyle()) + if __name__ == '__main__': unittest.main()