diff --git a/black.py b/black.py index 669520ef35b..21c1c7e1f3b 100644 --- a/black.py +++ b/black.py @@ -60,6 +60,7 @@ DEFAULT_EXCLUDES = r"/(\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|\.svn|_build|buck-out|build|dist)/" # noqa: B950 DEFAULT_INCLUDES = r"\.pyi?$" CACHE_DIR = Path(user_cache_dir("black", version=__version__)) +PREFIX_CHARS = "furbFURB" # All possible string prefix characters. # types @@ -413,8 +414,8 @@ def main( versions = set(target_version) elif py36: err( - "--py36 is deprecated and will be removed in a future version. " - "Use --target-version py36 instead." + "--py36 is deprecated and will be removed in a future version. Use " + "--target-version py36 instead." ) versions = PY36_VERSIONS else: @@ -2411,6 +2412,122 @@ def split_line( yield line return + leaves = [leaf for leaf in line.leaves] + + num_of_inline_string_comments = 0 + set_of_quotes = set() + set_of_prefixes = set() + for leaf in leaves: + if leaf.type == token.STRING: + set_of_quotes.add(leaf.value[-1]) + + tmp_prefix_idx = 0 + tmp_prefix = "" + while leaf.value[tmp_prefix_idx] in PREFIX_CHARS: + tmp_prefix += leaf.value[tmp_prefix_idx] + tmp_prefix_idx += 1 + + if tmp_prefix: + set_of_prefixes.add(tmp_prefix) + + if id(leaf) in line.comments: + num_of_inline_string_comments += 1 + + if ( + num_of_inline_string_comments <= 1 + and len(set_of_quotes) == 1 + and len(set_of_prefixes) <= 1 + ): + for i, leaf in enumerate(leaves): + if ( + i + 1 < len(line.leaves) + and leaf.type == token.STRING + and leaves[i + 1].type == token.STRING + ): + atom_node = leaf.parent + first_str_idx = next_str_idx = i + string_value = "" + prefix = "" + prefix_idx = 0 + QUOTE = line.leaves[next_str_idx].value[-1] + num_of_strings = 0 + while line.leaves[next_str_idx].type == token.STRING: + num_of_strings += 1 + + naked_last_string_value = string_value[prefix_idx:].strip(QUOTE) + + next_string_value = line.leaves[next_str_idx].value + if not prefix: + while next_string_value[prefix_idx] in PREFIX_CHARS: + prefix += next_string_value[prefix_idx] + prefix_idx += 1 + + naked_next_string_value = next_string_value[prefix_idx:].strip( + QUOTE + ) + + string_value = ( + prefix + + QUOTE + + naked_last_string_value + + naked_next_string_value + + QUOTE + ) + next_str_idx += 1 + + string_leaf = Leaf(token.STRING, string_value) + if atom_node: + grandparent = atom_node.parent + if grandparent is not None: + child_idx = atom_node.remove() + if child_idx is not None: + grandparent.insert_child(child_idx, string_leaf) + else: + raise RuntimeError( + "Something is wrong here. Atom node has parent but " + "can't be removed from it?" + ) + + old_comments = line.comments + new_comments = {} + + line = Line( + depth=line.depth, + bracket_tracker=line.bracket_tracker, + inside_brackets=line.inside_brackets, + should_explode=line.should_explode, + ) + + for j, old_leaf in enumerate(leaves): + if j == first_str_idx: + line.append(string_leaf) + + if first_str_idx <= j < first_str_idx + num_of_strings: + if id(old_leaf) in old_comments: + new_comments[id(string_leaf)] = old_comments[id(old_leaf)] + continue + + new_leaf = Leaf(old_leaf.type, old_leaf.value) + + if id(old_leaf) in old_comments: + new_comments[id(new_leaf)] = old_comments[id(old_leaf)] + + old_parent = old_leaf.parent + if old_parent: + child_idx = old_leaf.remove() + if child_idx is not None: + old_parent.insert_child(child_idx, new_leaf) + else: + raise RuntimeError( + "Something is wrong here. Old leaf has parent but " + "can't be removed from it?" + ) + + line.append(new_leaf) + + line.comments = new_comments + break + line_str = str(line).strip("\n") if ( @@ -2483,36 +2600,49 @@ def string_split( """Split long strings.""" tokens = tuple([leaf.type for leaf in line.leaves]) - def validate_string(str_idx: int) -> None: + def validate_string() -> None: if is_line_short_enough(line, line_length=line_length): raise CannotSplit("Line is already short enough. No reason to split.") - if re.search("^[a-z]?[\"']{3}", line.leaves[str_idx].value): - raise CannotSplit("This split function does not work on multiline strings.") + if line.comments and len(line.comments.values()) > 1: + raise CannotSplit("Multiple inline comments detected.") + + for leaf in line.leaves: + value = leaf.value.lstrip(PREFIX_CHARS) + if value[:3] in {'"""', "'''"}: + raise CannotSplit( + "This split function does not work on multiline strings." + ) - if line.comments and re.match( - "^# ([A-Za-z_0-9]+: .*|noqa)$", list(line.comments.values())[0][0].value + if ( + line.comments + and list(line.comments.values())[0] + and re.match( + r"^#\s*([a-z_0-9]+:.*|noqa)\s*$", + list(line.comments.values())[0][0].value, + re.IGNORECASE, + ) ): raise CannotSplit( "Line appears to end with an inline pragma comment. Splitting the line " "could modify the pragma's behavior." ) + validate_string() + if tokens[:2] in [ (token.STRING,), (token.STRING, token.COMMA), (token.STRING, token.DOT), (token.STRING, token.PERCENT), ]: - validate_string(0) - yield from string_atomic_split(line, line_length) + for split_line in string_atomic_split(line, line_length): + yield split_line elif (tokens[:2] == (token.NAME, token.EQUAL) or tokens[1] == token.COLON) and ( tokens[2] == token.STRING or tokens[2:4] == (token.LPAR, token.STRING) ): - str_idx = tokens[1:].index(token.STRING) - assert str_idx is not None, "String assignment line contains no string?" - validate_string(str_idx + 1) - yield from string_assignment_split(line, line_length) + for split_line in string_assignment_split(line, line_length): + yield split_line else: raise CannotSplit( f"Unable to match this line's tokens with a valid split function: {tokens}" @@ -2555,47 +2685,63 @@ def insert_child(child: LN) -> None: else: string_depth = line.depth - rest = line.leaves[0].value.replace("\\\n", "") + rest_value = line.leaves[0].value.replace("\\\n", "") prefix = "" - if rest[0] not in ["'", '"']: - prefix = rest[0] + prefix_idx = 0 + while rest_value[prefix_idx] in PREFIX_CHARS: + prefix += rest_value[prefix_idx] + prefix_idx += 1 - rest_length_offset = 0 + drop_pointless_f_prefix = True + if "f" in prefix and not re.search(r"\{.+\}", rest_value): + drop_pointless_f_prefix = False + + rest_length_offset = prefix_idx if ends_with_comma: rest_length_offset += 1 - if prefix: - rest_length_offset += 1 rest_line = Line(depth=line.depth) - rest_leaf = Leaf(token.STRING, rest) + rest_leaf = Leaf(token.STRING, rest_value) rest_line.append(rest_leaf) max_rest_length = line_length - rest_length_offset - QUOTE = rest[-1] + QUOTE = rest_value[-1] while not is_line_short_enough(rest_line, line_length=max_rest_length): - max_next_length = line_length - (2 if prefix else 1) - (string_depth * 4) + max_next_length = line_length - (1 + prefix_idx) - (string_depth * 4) idx = max_next_length - while 0 < idx < len(rest) and rest[idx - 1] != " ": + while 0 < idx < len(rest_value) and rest_value[idx - 1] != " ": idx -= 1 - if rest[idx - 1] != " ": + if rest_value[idx - 1] != " ": raise CannotSplit("Long strings which contain no spaces are not split.") - next_line = Line(depth=string_depth) - next_value = rest[:idx] + QUOTE - - if prefix == "f" and not re.search(r"\{.+\}", next_value): - next_value = next_value[1:] + next_value = rest_value[:idx] + QUOTE + if ( + "f" in prefix + and not re.search(r"\{.+\}", next_value) + and drop_pointless_f_prefix + ): + tmp_prefix = prefix.replace("f", "") + next_value = tmp_prefix + next_value[prefix_idx:] + next_line = Line(depth=string_depth) next_leaf = Leaf(token.STRING, next_value) next_line.append(next_leaf) insert_child(next_leaf) yield next_line - rest = prefix + QUOTE + rest[idx:] + rest_value = prefix + QUOTE + rest_value[idx:] + if ( + "f" in prefix + and not re.search(r"\{.+\}", rest_value) + and drop_pointless_f_prefix + ): + tmp_prefix = prefix.replace("f", "") + rest_value = tmp_prefix + rest_value[prefix_idx:] + rest_line = Line(depth=string_depth) - rest_leaf = Leaf(token.STRING, rest) + rest_leaf = Leaf(token.STRING, rest_value) rest_line.append(rest_leaf) if not ends_with_comma: @@ -2702,19 +2848,19 @@ def append_leaf(line: Line, leaf: Leaf) -> None: next_str_idx = first_str_idx string_value = "" prefix = "" + prefix_idx = 0 QUOTE = line.leaves[next_str_idx].value[-1] while line.leaves[next_str_idx].type == token.STRING: next_string_value = line.leaves[next_str_idx].value - clean_current_string_value = string_value[1:] if prefix else string_value + clean_current_string_value = string_value[prefix_idx:] - if next_string_value[0] == QUOTE: - clean_next_string_value = next_string_value - else: - prefix = next_string_value[0] - clean_next_string_value = next_string_value[1:] + if not prefix: + while next_string_value[prefix_idx] in PREFIX_CHARS: + prefix += next_string_value[prefix_idx] + prefix_idx += 1 clean_current_string_value = clean_current_string_value.strip(QUOTE) - clean_next_string_value = clean_next_string_value.strip(QUOTE) + clean_next_string_value = next_string_value[prefix_idx:].strip(QUOTE) string_value = ( prefix + QUOTE @@ -2863,8 +3009,7 @@ def right_hand_split( raise CannotSplit( "The current optional pair of parentheses is bound to fail to " "satisfy the splitting algorithm because the head or the tail " - "contains multiline strings which by definition never fit one " - "line." + "contains multiline strings which by definition never fit one line." ) ensure_visible(opening_bracket) @@ -2895,8 +3040,8 @@ def bracket_split_succeeded_or_raise(head: Line, body: Line, tail: Line) -> None elif tail_len < 3: raise CannotSplit( - f"Splitting brackets on an empty body to save " - f"{tail_len} characters is not worth it" + f"Splitting brackets on an empty body to save {tail_len} characters " + "is not worth it" ) @@ -3105,7 +3250,7 @@ def normalize_string_prefix(leaf: Leaf, remove_u_prefix: bool = False) -> None: Note: Mutates its argument. """ - match = re.match(r"^([furbFURB]*)(.*)$", leaf.value, re.DOTALL) + match = re.match(r"^([" + PREFIX_CHARS + r"]*)(.*)$", leaf.value, re.DOTALL) assert match is not None, f"failed to match string {leaf.value!r}" orig_prefix = match.group(1) new_prefix = orig_prefix.lower() @@ -3122,7 +3267,7 @@ def normalize_string_quotes(leaf: Leaf) -> None: Note: Mutates its argument. """ - value = leaf.value.lstrip("furbFURB") + value = leaf.value.lstrip(PREFIX_CHARS) if value[:3] == '"""': return @@ -3532,7 +3677,7 @@ def is_vararg(leaf: Leaf, within: Set[NodeType]) -> bool: def is_multiline_string(leaf: Leaf) -> bool: """Return True if `leaf` is a multiline string that actually spans many lines.""" - value = leaf.value.lstrip("furbFURB") + value = leaf.value.lstrip(PREFIX_CHARS) return value[:3] in {'"""', "'''"} and "\n" in value @@ -4052,8 +4197,8 @@ def _v(node: Union[ast.AST, ast3.AST, ast27.AST], depth: int = 0) -> Iterator[st src_ast = parse_ast(src) except Exception as exc: raise AssertionError( - f"cannot use --safe with this file; failed to parse source file. " - f"AST error message: {exc}" + "cannot use --safe with this file; failed to parse source file. AST " + f"error message: {exc}" ) try: @@ -4061,9 +4206,9 @@ def _v(node: Union[ast.AST, ast3.AST, ast27.AST], depth: int = 0) -> Iterator[st except Exception as exc: log = dump_to_file("".join(traceback.format_tb(exc.__traceback__)), dst) raise AssertionError( - f"INTERNAL ERROR: Black produced invalid code: {exc}. " - f"Please report a bug on https://github.com/psf/black/issues. " - f"This invalid output might be helpful: {log}" + f"INTERNAL ERROR: Black produced invalid code: {exc}. Please report a bug " + "on https://github.com/psf/black/issues. This invalid output might be " + f"helpful: {log}" ) from None src_ast_str = "\n".join(_v(src_ast)) @@ -4071,9 +4216,8 @@ def _v(node: Union[ast.AST, ast3.AST, ast27.AST], depth: int = 0) -> Iterator[st if src_ast_str != dst_ast_str: log = dump_to_file(diff(src_ast_str, dst_ast_str, "src", "dst")) raise AssertionError( - f"INTERNAL ERROR: Black produced code that is not equivalent to " - f"the source. " - f"Please report a bug on https://github.com/psf/black/issues. " + "INTERNAL ERROR: Black produced code that is not equivalent to the " + "source. Please report a bug on https://github.com/psf/black/issues. " f"This diff might be helpful: {log}" ) from None @@ -4087,10 +4231,9 @@ def assert_stable(src: str, dst: str, mode: FileMode) -> None: diff(dst, newdst, "first pass", "second pass"), ) raise AssertionError( - f"INTERNAL ERROR: Black produced different code on the second pass " - f"of the formatter. " - f"Please report a bug on https://github.com/psf/black/issues. " - f"This diff might be helpful: {log}" + "INTERNAL ERROR: Black produced different code on the second pass of the " + "formatter. Please report a bug on https://github.com/psf/black/issues. " + f" This diff might be helpful: {log}" ) from None diff --git a/blib2to3/pgen2/pgen.py b/blib2to3/pgen2/pgen.py index 774cffc5e1c..33592e3033d 100644 --- a/blib2to3/pgen2/pgen.py +++ b/blib2to3/pgen2/pgen.py @@ -167,8 +167,8 @@ def calcfirst(self, name: Text) -> None: for symbol in itsfirst: if symbol in inverse: raise ValueError( - "rule %s is ambiguous; %s is in the" - " first sets of %s as well as %s" + "rule %s is ambiguous; %s is in the first sets of %s as well " + "as %s" % (name, symbol, label, inverse[symbol]) ) inverse[symbol] = label diff --git a/tests/data/comments4.py b/tests/data/comments4.py index c0db5b1c850..acd020d31f3 100644 --- a/tests/data/comments4.py +++ b/tests/data/comments4.py @@ -13,49 +13,54 @@ class C: # metadata_version errors. ( {}, - "None is an invalid value for Metadata-Version. " - "Error: This field is required. " - "see " - "https://packaging.python.org/specifications/core-metadata", + ( + "None is an invalid value for Metadata-Version. Error: This field " + "is required. see " + "https://packaging.python.org/specifications/core-metadata" + ), ), ( {"metadata_version": "-1"}, - "'-1' is an invalid value for Metadata-Version. " - "Error: Unknown Metadata Version " - "see " - "https://packaging.python.org/specifications/core-metadata", + ( + "'-1' is an invalid value for Metadata-Version. Error: Unknown " + "Metadata Version see " + "https://packaging.python.org/specifications/core-metadata" + ), ), # name errors. ( {"metadata_version": "1.2"}, - "'' is an invalid value for Name. " - "Error: This field is required. " - "see " - "https://packaging.python.org/specifications/core-metadata", + ( + "'' is an invalid value for Name. Error: This field is required. " + "see https://packaging.python.org/specifications/core-metadata" + ), ), ( {"metadata_version": "1.2", "name": "foo-"}, - "'foo-' is an invalid value for Name. " - "Error: Must start and end with a letter or numeral and " - "contain only ascii numeric and '.', '_' and '-'. " - "see " - "https://packaging.python.org/specifications/core-metadata", + ( + "'foo-' is an invalid value for Name. Error: Must start and end " + "with a letter or numeral and contain only ascii numeric and '.', " + "'_' and '-'. see " + "https://packaging.python.org/specifications/core-metadata" + ), ), # version errors. ( {"metadata_version": "1.2", "name": "example"}, - "'' is an invalid value for Version. " - "Error: This field is required. " - "see " - "https://packaging.python.org/specifications/core-metadata", + ( + "'' is an invalid value for Version. Error: This field is " + "required. see " + "https://packaging.python.org/specifications/core-metadata" + ), ), ( {"metadata_version": "1.2", "name": "example", "version": "dog"}, - "'dog' is an invalid value for Version. " - "Error: Must start and end with a letter or numeral and " - "contain only ascii numeric and '.', '_' and '-'. " - "see " - "https://packaging.python.org/specifications/core-metadata", + ( + "'dog' is an invalid value for Version. Error: Must start and end " + "with a letter or numeral and contain only ascii numeric and '.', " + "'_' and '-'. see " + "https://packaging.python.org/specifications/core-metadata" + ), ), ], ) diff --git a/tests/data/composition.py b/tests/data/composition.py index ac63e46751a..6939a8dd3c9 100644 --- a/tests/data/composition.py +++ b/tests/data/composition.py @@ -10,13 +10,17 @@ def test(self) -> None: ) self.assertEqual( unstyle(str(report)), - "2 files reformatted, 1 file left unchanged, " - "1 file failed to reformat.", + ( + "2 files reformatted, 1 file left unchanged, 1 file failed to " + "reformat." + ), ) self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, " - "2 files failed to reformat.", + ( + "2 files reformatted, 2 files left unchanged, 2 files failed to " + "reformat." + ), ) for i in (a,): if ( @@ -149,10 +153,7 @@ def tricky_asserts(self) -> None: key7: value7, key8: value8, key9: value9, - }, ( - "Not what we expected and the message is too long to fit " - "in one line because it's too long" - ) + }, "Not what we expected and the message is too long to fit in one line because it's too long" dis_c_instance_method = """\ %3d 0 LOAD_FAST 1 (x) diff --git a/tests/data/long_strings.py b/tests/data/long_strings.py index b6dff051733..78b18256cab 100644 --- a/tests/data/long_strings.py +++ b/tests/data/long_strings.py @@ -12,13 +12,13 @@ func_with_keywords(my_arg, my_kwarg="Long keyword strings also need to be wrapped, but they will probably need to be handled a little bit differently.") -bad_split = ( +bad_split1 = ( "But what should happen when code has already been formatted but in the wrong way?" " Like with a space at the beginning instead of the end." " Or what about when it is split too soon?" ) -bad_split = ( +bad_split2 = ( "But what should happen when code has already " "been formatted but in the wrong way? Like " "with a space at the beginning instead of the " @@ -26,6 +26,30 @@ "soon?" ) +bad_split3 = ( + "What if we have inline comments on " # First Comment + "each line of a bad split? In that " # Second Comment + "case, we should just leave it alone." # Third Comment +) + +bad_split_func1( + "But what should happen when code has already " + "been formatted but in the wrong way? Like " + "with a space at the beginning instead of the " + "end. Or what about when it is split too " + "soon?", + xxx, yyy, zzz +) + +bad_split_func2( + xxx, yyy, zzz, + long_string_kwarg="But what should happen when code has already " + "been formatted but in the wrong way? Like " + "with a space at the beginning instead of the " + "end. Or what about when it is split too " + "soon?", +) + raw_string = r"This is a long raw string. When re-formatting this string, black needs to make sure it prepends the 'r' onto the new string." fmt_string1 = "We also need to be sure to preserve any and all {} which may or may not be attached to the string in question.".format("method calls") @@ -45,7 +69,9 @@ arg_comment_string = print("Long lines with inline comments which are apart of (and not the only member of) an argument list should have their comments appended to the reformatted string's enclosing left parentheses.", # This comment gets thrown to the top. "Arg #2", "Arg #3", "Arg #4", "Arg #5") -pragma_comment_string = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa: E501 +pragma_comment_string1 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa: E501 + +pragma_comment_string2 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa """This is a really really really long triple quote string and it should not be touched.""" @@ -121,18 +147,46 @@ ), ) -bad_split = ( +bad_split1 = ( "But what should happen when code has already been formatted but in the wrong way? " "Like with a space at the beginning instead of the end. Or what about when it is " "split too soon?" ) -bad_split = ( +bad_split2 = ( "But what should happen when code has already been formatted but in the wrong way? " "Like with a space at the beginning instead of the end. Or what about when it is " "split too soon?" ) +bad_split3 = ( + "What if we have inline comments on " # First Comment + "each line of a bad split? In that " # Second Comment + "case, we should just leave it alone." # Third Comment +) + +bad_split_func1( + ( + "But what should happen when code has already been formatted but in the wrong " + "way? Like with a space at the beginning instead of the end. Or what about " + "when it is split too soon?" + ), + xxx, + yyy, + zzz, +) + +bad_split_func2( + xxx, + yyy, + zzz, + long_string_kwarg=( + "But what should happen when code has already been formatted but in the wrong " + "way? Like with a space at the beginning instead of the end. Or what about " + "when it is split too soon?" + ), +) + raw_string = ( r"This is a long raw string. When re-formatting this string, black needs to make " r"sure it prepends the 'r' onto the new string." @@ -193,7 +247,9 @@ "Arg #5", ) -pragma_comment_string = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa: E501 +pragma_comment_string1 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa: E501 + +pragma_comment_string2 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa """This is a really really really long triple quote string and it should not be touched.""" diff --git a/tests/test_black.py b/tests/test_black.py index 19eb1fd82f1..ce0b81f1203 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -243,8 +243,8 @@ def test_piping(self) -> None: def test_piping_diff(self) -> None: diff_header = re.compile( - rf"(STDIN|STDOUT)\t\d\d\d\d-\d\d-\d\d " - rf"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d" + rf"(STDIN|STDOUT)\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d\d\d\d " + rf"\+\d\d\d\d" ) source, _ = read_data("expression.py") expected, _ = read_data("expression.diff") @@ -333,7 +333,7 @@ def test_expression_diff(self) -> None: tmp_file = Path(black.dump_to_file(source)) diff_header = re.compile( rf"{re.escape(str(tmp_file))}\t\d\d\d\d-\d\d-\d\d " - rf"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d" + r"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d" ) try: result = BlackRunner().invoke(black.main, ["--diff", str(tmp_file)]) @@ -736,8 +736,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(err_lines[-1], "error: cannot format e1: boom") self.assertEqual( unstyle(str(report)), - "1 file reformatted, 2 files left unchanged, " - "1 file failed to reformat.", + ( + "1 file reformatted, 2 files left unchanged, 1 file failed to " + "reformat." + ), ) self.assertEqual(report.return_code, 123) report.done(Path("f3"), black.Changed.YES) @@ -746,8 +748,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(out_lines[-1], "reformatted f3") self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, " - "1 file failed to reformat.", + ( + "2 files reformatted, 2 files left unchanged, 1 file failed to " + "reformat." + ), ) self.assertEqual(report.return_code, 123) report.failed(Path("e2"), "boom") @@ -756,8 +760,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(err_lines[-1], "error: cannot format e2: boom") self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, " - "2 files failed to reformat.", + ( + "2 files reformatted, 2 files left unchanged, 2 files failed to " + "reformat." + ), ) self.assertEqual(report.return_code, 123) report.path_ignored(Path("wat"), "no match") @@ -766,8 +772,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(out_lines[-1], "wat ignored: no match") self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, " - "2 files failed to reformat.", + ( + "2 files reformatted, 2 files left unchanged, 2 files failed to " + "reformat." + ), ) self.assertEqual(report.return_code, 123) report.done(Path("f4"), black.Changed.NO) @@ -776,15 +784,19 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(out_lines[-1], "f4 already well formatted, good job.") self.assertEqual( unstyle(str(report)), - "2 files reformatted, 3 files left unchanged, " - "2 files failed to reformat.", + ( + "2 files reformatted, 3 files left unchanged, 2 files failed to " + "reformat." + ), ) self.assertEqual(report.return_code, 123) report.check = True self.assertEqual( unstyle(str(report)), - "2 files would be reformatted, 3 files would be left unchanged, " - "2 files would fail to reformat.", + ( + "2 files would be reformatted, 3 files would be left unchanged, 2 " + "files would fail to reformat." + ), ) def test_report_quiet(self) -> None: @@ -826,8 +838,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(err_lines[-1], "error: cannot format e1: boom") self.assertEqual( unstyle(str(report)), - "1 file reformatted, 2 files left unchanged, " - "1 file failed to reformat.", + ( + "1 file reformatted, 2 files left unchanged, 1 file failed to " + "reformat." + ), ) self.assertEqual(report.return_code, 123) report.done(Path("f3"), black.Changed.YES) @@ -835,8 +849,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(len(err_lines), 1) self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, " - "1 file failed to reformat.", + ( + "2 files reformatted, 2 files left unchanged, 1 file failed to " + "reformat." + ), ) self.assertEqual(report.return_code, 123) report.failed(Path("e2"), "boom") @@ -845,8 +861,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(err_lines[-1], "error: cannot format e2: boom") self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, " - "2 files failed to reformat.", + ( + "2 files reformatted, 2 files left unchanged, 2 files failed to " + "reformat." + ), ) self.assertEqual(report.return_code, 123) report.path_ignored(Path("wat"), "no match") @@ -854,8 +872,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(len(err_lines), 2) self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, " - "2 files failed to reformat.", + ( + "2 files reformatted, 2 files left unchanged, 2 files failed to " + "reformat." + ), ) self.assertEqual(report.return_code, 123) report.done(Path("f4"), black.Changed.NO) @@ -863,15 +883,19 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(len(err_lines), 2) self.assertEqual( unstyle(str(report)), - "2 files reformatted, 3 files left unchanged, " - "2 files failed to reformat.", + ( + "2 files reformatted, 3 files left unchanged, 2 files failed to " + "reformat." + ), ) self.assertEqual(report.return_code, 123) report.check = True self.assertEqual( unstyle(str(report)), - "2 files would be reformatted, 3 files would be left unchanged, " - "2 files would fail to reformat.", + ( + "2 files would be reformatted, 3 files would be left unchanged, 2 " + "files would fail to reformat." + ), ) def test_report_normal(self) -> None: @@ -915,8 +939,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(err_lines[-1], "error: cannot format e1: boom") self.assertEqual( unstyle(str(report)), - "1 file reformatted, 2 files left unchanged, " - "1 file failed to reformat.", + ( + "1 file reformatted, 2 files left unchanged, 1 file failed to " + "reformat." + ), ) self.assertEqual(report.return_code, 123) report.done(Path("f3"), black.Changed.YES) @@ -925,8 +951,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(out_lines[-1], "reformatted f3") self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, " - "1 file failed to reformat.", + ( + "2 files reformatted, 2 files left unchanged, 1 file failed to " + "reformat." + ), ) self.assertEqual(report.return_code, 123) report.failed(Path("e2"), "boom") @@ -935,8 +963,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(err_lines[-1], "error: cannot format e2: boom") self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, " - "2 files failed to reformat.", + ( + "2 files reformatted, 2 files left unchanged, 2 files failed to " + "reformat." + ), ) self.assertEqual(report.return_code, 123) report.path_ignored(Path("wat"), "no match") @@ -944,8 +974,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(len(err_lines), 2) self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, " - "2 files failed to reformat.", + ( + "2 files reformatted, 2 files left unchanged, 2 files failed to " + "reformat." + ), ) self.assertEqual(report.return_code, 123) report.done(Path("f4"), black.Changed.NO) @@ -953,15 +985,19 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(len(err_lines), 2) self.assertEqual( unstyle(str(report)), - "2 files reformatted, 3 files left unchanged, " - "2 files failed to reformat.", + ( + "2 files reformatted, 3 files left unchanged, 2 files failed to " + "reformat." + ), ) self.assertEqual(report.return_code, 123) report.check = True self.assertEqual( unstyle(str(report)), - "2 files would be reformatted, 3 files would be left unchanged, " - "2 files would fail to reformat.", + ( + "2 files would be reformatted, 3 files would be left unchanged, 2 " + "files would fail to reformat." + ), ) def test_lib2to3_parse(self) -> None: @@ -1712,8 +1748,7 @@ async def test_blackd_pyi(self) -> None: @unittest_run_loop async def test_blackd_diff(self) -> None: diff_header = re.compile( - rf"(In|Out)\t\d\d\d\d-\d\d-\d\d " - rf"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d" + rf"(In|Out)\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d" ) source, _ = read_data("blackd_diff.py")