diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b1028ea9f52c3..576c929c26b9b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -342,11 +342,6 @@ repos: (?x) ^(asv_bench|pandas/tests|doc)/ |scripts/validate_min_versions_in_sync\.py$ - - id: unwanted-patterns-strings-to-concatenate - name: Check for use of not concatenated strings - language: python - entry: python scripts/validate_unwanted_patterns.py --validation-type="strings_to_concatenate" - types_or: [python, cython] - id: unwanted-patterns-strings-with-misplaced-whitespace name: Check for strings with misplaced spaces language: python diff --git a/asv_bench/benchmarks/io/csv.py b/asv_bench/benchmarks/io/csv.py index f5aa421951a1d..7245f6ebe7736 100644 --- a/asv_bench/benchmarks/io/csv.py +++ b/asv_bench/benchmarks/io/csv.py @@ -318,7 +318,7 @@ def setup(self, sep, decimal, float_precision): "".join([random.choice(string.digits) for _ in range(28)]) for _ in range(15) ] - rows = sep.join([f"0{decimal}" + "{}"] * 3) + "\n" + rows = sep.join([f"0{decimal}{{}}"] * 3) + "\n" data = rows * 5 data = data.format(*floats) * 200 # 1000 x 3 strings csv self.StringIO_input = StringIO(data) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index eaa916e9ef21d..8ee73e77f5b11 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -3658,7 +3658,7 @@ def css(rgba, text_only) -> str: text_color = "#f1f1f1" if dark else "#000000" return ( f"background-color: {_matplotlib.colors.rgb2hex(rgba)};" - + f"color: {text_color};" + f"color: {text_color};" ) else: return f"color: {_matplotlib.colors.rgb2hex(rgba)};" diff --git a/pandas/io/sas/sas_constants.py b/pandas/io/sas/sas_constants.py index a090b8a1acb3c..62c17bd03927e 100644 --- a/pandas/io/sas/sas_constants.py +++ b/pandas/io/sas/sas_constants.py @@ -4,9 +4,9 @@ magic: Final = ( b"\x00\x00\x00\x00\x00\x00\x00\x00" - + b"\x00\x00\x00\x00\xc2\xea\x81\x60" - + b"\xb3\x14\x11\xcf\xbd\x92\x08\x00" - + b"\x09\xc7\x31\x8c\x18\x1f\x10\x11" + b"\x00\x00\x00\x00\xc2\xea\x81\x60" + b"\xb3\x14\x11\xcf\xbd\x92\x08\x00" + b"\x09\xc7\x31\x8c\x18\x1f\x10\x11" ) align_1_checker_value: Final = b"3" diff --git a/pandas/tests/groupby/test_raises.py b/pandas/tests/groupby/test_raises.py index 8851b7669932d..0dd845bc35482 100644 --- a/pandas/tests/groupby/test_raises.py +++ b/pandas/tests/groupby/test_raises.py @@ -342,25 +342,25 @@ def test_groupby_raises_category( "cummax": ( (NotImplementedError, TypeError), "(category type does not support cummax operations|" - + "category dtype not supported|" - + "cummax is not supported for category dtype)", + "category dtype not supported|" + "cummax is not supported for category dtype)", ), "cummin": ( (NotImplementedError, TypeError), "(category type does not support cummin operations|" - + "category dtype not supported|" + "category dtype not supported|" "cummin is not supported for category dtype)", ), "cumprod": ( (NotImplementedError, TypeError), "(category type does not support cumprod operations|" - + "category dtype not supported|" + "category dtype not supported|" "cumprod is not supported for category dtype)", ), "cumsum": ( (NotImplementedError, TypeError), "(category type does not support cumsum operations|" - + "category dtype not supported|" + "category dtype not supported|" "cumsum is not supported for category dtype)", ), "diff": ( @@ -371,7 +371,7 @@ def test_groupby_raises_category( "fillna": ( TypeError, r"Cannot setitem on a Categorical with a new category \(0\), " - + "set the categories first", + "set the categories first", ) if not using_copy_on_write else (None, ""), # no-op with CoW @@ -539,33 +539,33 @@ def test_groupby_raises_category_on_category( "cummax": ( (NotImplementedError, TypeError), "(cummax is not supported for category dtype|" - + "category dtype not supported|" - + "category type does not support cummax operations)", + "category dtype not supported|" + "category type does not support cummax operations)", ), "cummin": ( (NotImplementedError, TypeError), "(cummin is not supported for category dtype|" - + "category dtype not supported|" + "category dtype not supported|" "category type does not support cummin operations)", ), "cumprod": ( (NotImplementedError, TypeError), "(cumprod is not supported for category dtype|" - + "category dtype not supported|" + "category dtype not supported|" "category type does not support cumprod operations)", ), "cumsum": ( (NotImplementedError, TypeError), "(cumsum is not supported for category dtype|" - + "category dtype not supported|" - + "category type does not support cumsum operations)", + "category dtype not supported|" + "category type does not support cumsum operations)", ), "diff": (TypeError, "unsupported operand type"), "ffill": (None, ""), "fillna": ( TypeError, r"Cannot setitem on a Categorical with a new category \(0\), " - + "set the categories first", + "set the categories first", ) if not using_copy_on_write else (None, ""), # no-op with CoW diff --git a/pandas/tests/io/excel/test_readers.py b/pandas/tests/io/excel/test_readers.py index b36f3fcee16ca..66e55fe86d964 100644 --- a/pandas/tests/io/excel/test_readers.py +++ b/pandas/tests/io/excel/test_readers.py @@ -341,7 +341,8 @@ def test_index_col_with_unnamed(self, read_ext, index_col): def test_usecols_pass_non_existent_column(self, read_ext): msg = ( "Usecols do not match columns, " - "columns expected but not found: " + r"\['E'\]" + "columns expected but not found: " + r"\['E'\]" ) with pytest.raises(ValueError, match=msg): diff --git a/pandas/tests/io/formats/test_css.py b/pandas/tests/io/formats/test_css.py index 70c91dd02751a..db436d8283b99 100644 --- a/pandas/tests/io/formats/test_css.py +++ b/pandas/tests/io/formats/test_css.py @@ -193,7 +193,7 @@ def test_css_border_shorthands(prop, expected): "margin: 1px; margin-top: 2px", "", "margin-left: 1px; margin-right: 1px; " - + "margin-bottom: 1px; margin-top: 2px", + "margin-bottom: 1px; margin-top: 2px", ), ("margin-top: 2px", "margin: 1px", "margin: 1px; margin-top: 2px"), ("margin: 1px", "margin-top: 2px", "margin: 1px"), diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index 3321df72db91f..fccb053e73d4b 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -2160,18 +2160,18 @@ def test_freq_name_separation(self): def test_to_string_mixed(self): s = Series(["foo", np.nan, -1.23, 4.56]) result = s.to_string() - expected = "0 foo\n" + "1 NaN\n" + "2 -1.23\n" + "3 4.56" + expected = "".join(["0 foo\n", "1 NaN\n", "2 -1.23\n", "3 4.56"]) assert result == expected # but don't count NAs as floats s = Series(["foo", np.nan, "bar", "baz"]) result = s.to_string() - expected = "0 foo\n" + "1 NaN\n" + "2 bar\n" + "3 baz" + expected = "".join(["0 foo\n", "1 NaN\n", "2 bar\n", "3 baz"]) assert result == expected s = Series(["foo", 5, "bar", "baz"]) result = s.to_string() - expected = "0 foo\n" + "1 5\n" + "2 bar\n" + "3 baz" + expected = "".join(["0 foo\n", "1 5\n", "2 bar\n", "3 baz"]) assert result == expected def test_to_string_float_na_spacing(self): @@ -2181,10 +2181,10 @@ def test_to_string_float_na_spacing(self): result = s.to_string() expected = ( "0 NaN\n" - + "1 1.5678\n" - + "2 NaN\n" - + "3 -3.0000\n" - + "4 NaN" + "1 1.5678\n" + "2 NaN\n" + "3 -3.0000\n" + "4 NaN" ) assert result == expected @@ -2192,7 +2192,7 @@ def test_to_string_without_index(self): # GH 11729 Test index=False option s = Series([1, 2, 3, 4]) result = s.to_string(index=False) - expected = "1\n" + "2\n" + "3\n" + "4" + expected = "\n".join(["1", "2", "3", "4"]) assert result == expected def test_unicode_name_in_footer(self): @@ -2823,7 +2823,9 @@ def dtype(self): series = Series(ExtTypeStub()) res = repr(series) # This line crashed before #33770 was fixed. - expected = "0 [False True]\n" + "1 [ True False]\n" + "dtype: DtypeStub" + expected = "\n".join( + ["0 [False True]", "1 [ True False]", "dtype: DtypeStub"] + ) assert res == expected diff --git a/pandas/tests/io/json/test_normalize.py b/pandas/tests/io/json/test_normalize.py index 4f025e84e2bd3..78181fe2c4729 100644 --- a/pandas/tests/io/json/test_normalize.py +++ b/pandas/tests/io/json/test_normalize.py @@ -397,7 +397,7 @@ def test_record_prefix(self, state_data): def test_non_ascii_key(self): testjson = ( b'[{"\xc3\x9cnic\xc3\xb8de":0,"sub":{"A":1, "B":2}},' - + b'{"\xc3\x9cnic\xc3\xb8de":1,"sub":{"A":3, "B":4}}]' + b'{"\xc3\x9cnic\xc3\xb8de":1,"sub":{"A":3, "B":4}}]' ).decode("utf8") testdata = { diff --git a/pandas/tests/io/parser/conftest.py b/pandas/tests/io/parser/conftest.py index 0462d1fe6da0b..3ab40ff846cb6 100644 --- a/pandas/tests/io/parser/conftest.py +++ b/pandas/tests/io/parser/conftest.py @@ -189,7 +189,7 @@ def all_parsers_all_precisions(request): _encoding_prefixes = ["utf", "UTF"] _encoding_fmts = [ - f"{prefix}{sep}" + "{0}" for sep in _encoding_seps for prefix in _encoding_prefixes + f"{prefix}{sep}{{0}}" for sep in _encoding_seps for prefix in _encoding_prefixes ] diff --git a/pandas/tests/io/parser/test_network.py b/pandas/tests/io/parser/test_network.py index 3768f37b65546..4a6443e22d690 100644 --- a/pandas/tests/io/parser/test_network.py +++ b/pandas/tests/io/parser/test_network.py @@ -66,7 +66,7 @@ def test_url_encoding_csv(): """ path = ( "https://raw.githubusercontent.com/pandas-dev/pandas/main/" - + "pandas/tests/io/parser/data/unicode_series.csv" + "pandas/tests/io/parser/data/unicode_series.csv" ) df = read_csv(path, encoding="latin-1", header=None) assert df.loc[15, 1] == "Á köldum klaka (Cold Fever) (1994)" diff --git a/pandas/tests/io/test_sql.py b/pandas/tests/io/test_sql.py index 7139971f5f897..b6f88746d53ea 100644 --- a/pandas/tests/io/test_sql.py +++ b/pandas/tests/io/test_sql.py @@ -2921,7 +2921,7 @@ def test_datetime_time(self, tz_aware): def _get_index_columns(self, tbl_name): ixs = sql.read_sql_query( "SELECT * FROM sqlite_master WHERE type = 'index' " - + f"AND tbl_name = '{tbl_name}'", + f"AND tbl_name = '{tbl_name}'", self.conn, ) ix_cols = [] diff --git a/pandas/tests/series/test_repr.py b/pandas/tests/series/test_repr.py index 04dcabd6b832c..cccc4953bc3c7 100644 --- a/pandas/tests/series/test_repr.py +++ b/pandas/tests/series/test_repr.py @@ -290,7 +290,7 @@ def test_categorical_repr(self): a = Series(Categorical([1, 2, 3, 4])) exp = ( "0 1\n1 2\n2 3\n3 4\n" - + "dtype: category\nCategories (4, int64): [1, 2, 3, 4]" + "dtype: category\nCategories (4, int64): [1, 2, 3, 4]" ) assert exp == a.__str__() @@ -298,9 +298,9 @@ def test_categorical_repr(self): a = Series(Categorical(["a", "b"] * 25)) exp = ( "0 a\n1 b\n" - + " ..\n" - + "48 a\n49 b\n" - + "Length: 50, dtype: category\nCategories (2, object): ['a', 'b']" + " ..\n" + "48 a\n49 b\n" + "Length: 50, dtype: category\nCategories (2, object): ['a', 'b']" ) with option_context("display.max_rows", 5): assert exp == repr(a) @@ -308,7 +308,8 @@ def test_categorical_repr(self): levs = list("abcdefghijklmnopqrstuvwxyz") a = Series(Categorical(["a", "b"], categories=levs, ordered=True)) exp = ( - "0 a\n1 b\n" + "dtype: category\n" + "0 a\n1 b\n" + "dtype: category\n" "Categories (26, object): ['a' < 'b' < 'c' < 'd' ... 'w' < 'x' < 'y' < 'z']" ) assert exp == a.__str__() diff --git a/pyproject.toml b/pyproject.toml index 2461eab5c7565..baffbd18329ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -216,6 +216,8 @@ select = [ "PIE", # tidy imports "TID", + # implicit string concatenation + "ISC", ] ignore = [ diff --git a/scripts/tests/test_validate_unwanted_patterns.py b/scripts/tests/test_validate_unwanted_patterns.py index 81e06f758d700..90eca13b21628 100644 --- a/scripts/tests/test_validate_unwanted_patterns.py +++ b/scripts/tests/test_validate_unwanted_patterns.py @@ -153,48 +153,6 @@ def test_pytest_raises_raises(self, data, expected): assert result == expected -@pytest.mark.parametrize( - "data, expected", - [ - ( - 'msg = ("bar " "baz")', - [ - ( - 1, - ( - "String unnecessarily split in two by black. " - "Please merge them manually." - ), - ) - ], - ), - ( - 'msg = ("foo " "bar " "baz")', - [ - ( - 1, - ( - "String unnecessarily split in two by black. " - "Please merge them manually." - ), - ), - ( - 1, - ( - "String unnecessarily split in two by black. " - "Please merge them manually." - ), - ), - ], - ), - ], -) -def test_strings_to_concatenate(data, expected): - fd = io.StringIO(data.strip()) - result = list(validate_unwanted_patterns.strings_to_concatenate(fd)) - assert result == expected - - class TestStringsWithWrongPlacedWhitespace: @pytest.mark.parametrize( "data", diff --git a/scripts/validate_unwanted_patterns.py b/scripts/validate_unwanted_patterns.py index 8d4aecd596328..dbccb91f809d1 100755 --- a/scripts/validate_unwanted_patterns.py +++ b/scripts/validate_unwanted_patterns.py @@ -232,55 +232,6 @@ def private_import_across_module(file_obj: IO[str]) -> Iterable[Tuple[int, str]] yield (node.lineno, f"Import of internal function {repr(module_name)}") -def strings_to_concatenate(file_obj: IO[str]) -> Iterable[Tuple[int, str]]: - """ - This test case is necessary after 'Black' (https://github.com/psf/black), - is formatting strings over multiple lines. - - For example, when this: - - >>> foo = ( - ... "bar " - ... "baz" - ... ) - - Is becoming this: - - >>> foo = ("bar " "baz") - - 'Black' is not considering this as an - issue (see https://github.com/psf/black/issues/1051), - so we are checking it here instead. - - Parameters - ---------- - file_obj : IO - File-like object containing the Python code to validate. - - Yields - ------ - line_number : int - Line number of unconcatenated string. - msg : str - Explanation of the error. - - Notes - ----- - GH #30454 - """ - tokens: List = list(tokenize.generate_tokens(file_obj.readline)) - - for current_token, next_token in zip(tokens, tokens[1:]): - if current_token.type == next_token.type == token.STRING: - yield ( - current_token.start[0], - ( - "String unnecessarily split in two by black. " - "Please merge them manually." - ), - ) - - def strings_with_wrong_placed_whitespace( file_obj: IO[str], ) -> Iterable[Tuple[int, str]]: @@ -457,7 +408,6 @@ def main( "bare_pytest_raises", "private_function_across_module", "private_import_across_module", - "strings_to_concatenate", "strings_with_wrong_placed_whitespace", ]