From 3746602ab1f49b261cd95600694898603942a54d Mon Sep 17 00:00:00 2001 From: ANSHUL SINGH <72524975+ekanshul@users.noreply.github.com> Date: Mon, 13 Jul 2026 23:39:22 +0530 Subject: [PATCH] Add --min-word-length option to ignore short words Words shorter than the given length are dropped from the misspelling dictionary up front, so they are ignored both in file contents and with --check-filenames. This avoids having to list every short variable name in --ignore-words-list. Closing: #2211 --- codespell_lib/_codespell.py | 20 ++++++++++++++++++++ codespell_lib/tests/test_basic.py | 24 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/codespell_lib/_codespell.py b/codespell_lib/_codespell.py index 8ebb19de43..280972a0e1 100644 --- a/codespell_lib/_codespell.py +++ b/codespell_lib/_codespell.py @@ -513,6 +513,17 @@ def convert_arg_line_to_args(self, arg_line: str) -> list[str]: "by codespell. Words are case sensitive based on " "how they are written in the dictionary file.", ) + parser.add_argument( + "--min-word-length", + action="store", + type=int, + default=0, + metavar="LENGTH", + help="only check words at least LENGTH characters long, " + "e.g., use 4 to not check words of 3 or fewer characters " + "(such as short variable names). " + "The default is %(default)s (check all words).", + ) parser.add_argument( "--uri-ignore-words-list", action="append", @@ -1433,6 +1444,15 @@ def main(*args: str) -> int: misspellings: dict[str, Misspelling] = {} for dictionary in use_dictionaries: build_dict(dictionary, misspellings, ignore_words) + if options.min_word_length > 0: + # Words shorter than the requested length can never match a + # misspelling, so dropping them from the dictionary up front ignores + # them everywhere (file contents and --check-filenames alike). + misspellings = { + key: value + for key, value in misspellings.items() + if len(key) >= options.min_word_length + } colors = TermColors() if not options.colors: colors.disable() diff --git a/codespell_lib/tests/test_basic.py b/codespell_lib/tests/test_basic.py index d1c66a18a5..502524a8b3 100644 --- a/codespell_lib/tests/test_basic.py +++ b/codespell_lib/tests/test_basic.py @@ -438,6 +438,30 @@ def test_ignore_word_list( assert cs.main("-Labandonned,someword", "-Labilty", tmp_path) == 1 +def test_min_word_length( + tmp_path: Path, + capsys: pytest.CaptureFixture[str], +) -> None: + """Test that --min-word-length ignores short words.""" + bad_name = tmp_path / "bad.txt" + bad_name.write_text("nwe abilty abandonned\n") + assert cs.main(bad_name) == 3 + assert cs.main("--min-word-length", 0, bad_name) == 3, "0 checks all words" + assert cs.main("--min-word-length", 3, bad_name) == 3, "'nwe' is 3 chars long" + assert cs.main("--min-word-length", 4, bad_name) == 2 + assert cs.main("--min-word-length", 7, bad_name) == 1 + assert cs.main("--min-word-length", 11, bad_name) == 0 + # case-insensitive: the dictionary key length is what matters + bad_name.write_text("NWE\n") + assert cs.main(bad_name) == 1 + assert cs.main("--min-word-length", 4, bad_name) == 0 + # also applies to --check-filenames + fname = tmp_path / "nwe.txt" + fname.write_text("good contents\n") + assert cs.main("-f", fname) == 1 + assert cs.main("-f", "--min-word-length", 4, fname) == 0 + + @pytest.mark.parametrize( ("content", "expected_error_count"), [