From e8389c1419e7587948b07d64a1f3440a502e00dd Mon Sep 17 00:00:00 2001 From: shan3520 Date: Fri, 2 Oct 2026 17:42:07 +0530 Subject: [PATCH] Interpret `\t` in the `indent` setting as a tab A config file value of `indent=\t` was used literally, so wrapped imports were written with a backslash followed by `t`, producing a SyntaxError. Also document the accepted values for `indent` and that it only applies to the hanging indent wrap modes. Refs #2693 --- docs/configuration/options.md | 2 +- isort/main.py | 5 ++++- isort/settings.py | 2 ++ tests/unit/test_settings.py | 20 ++++++++++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index 076d576b6..670a948a7 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -524,7 +524,7 @@ forced_separate = ["glob_exp1", "glob_exp2"] ## Indent -String to place for indents defaults to " " (4 spaces). +String to place for indents defaults to " " (4 spaces). Accepts a number of spaces, `tab` (or `\t`) for a tab, or a literal string. Only used by the hanging indent multi_line_output modes; the grid and vertical modes align wrapped imports with the opening parenthesis instead. **Type:** String **Default:** ` ` diff --git a/isort/main.py b/isort/main.py index cbb3f4ba7..16242a1df 100644 --- a/isort/main.py +++ b/isort/main.py @@ -507,7 +507,10 @@ def _build_arg_parser() -> argparse.ArgumentParser: output_group.add_argument( "-i", "--indent", - help='String to place for indents defaults to " " (4 spaces).', + help='String to place for indents defaults to " " (4 spaces). ' + "Accepts a number of spaces, `tab` (or `\\t`) for a tab, or a literal string. " + "Only used by the hanging indent multi_line_output modes; the grid and vertical " + "modes align wrapped imports with the opening parenthesis instead.", dest="indent", type=str, ) diff --git a/isort/settings.py b/isort/settings.py index 48b83eca2..b06f58c97 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -360,6 +360,8 @@ def __init__( indent = indent.strip("'").strip('"') if indent.lower() == "tab": indent = "\t" + else: + indent = indent.replace("\\t", "\t") combined_config["indent"] = indent known_other = {} diff --git a/tests/unit/test_settings.py b/tests/unit/test_settings.py index aa1b3d85f..e0cee7bbd 100644 --- a/tests/unit/test_settings.py +++ b/tests/unit/test_settings.py @@ -41,6 +41,26 @@ def test_invalid_profile(self) -> None: with pytest.raises(exceptions.ProfileDoesNotExist): Config(profile="blackandwhitestylemixedwithpep8") + @pytest.mark.parametrize( + ("indent", "expected"), + [ + ("tab", "\t"), + ("Tab", "\t"), + ('"tab"', "\t"), + ("\t", "\t"), + ("\\t", "\t"), + ("\\t\\t", "\t\t"), + ("2", " "), + ("' '", " "), + ], + ) + def test_indent(self, indent: str, expected: str) -> None: + assert Config(indent=indent).indent == expected + + def test_indent_escaped_tab_in_config_file(self, tmp_path: Path) -> None: + (tmp_path / ".isort.cfg").write_text("[settings]\nindent=\\t\n", encoding="utf8") + assert Config(settings_path=str(tmp_path)).indent == "\t" + def test_is_skipped(self) -> None: assert Config().is_skipped(Path("C:\\path\\isort.py")) assert Config(skip=["/path/isort.py"]).is_skipped(Path("C:\\path\\isort.py"))