Skip to content

Commit 2569d89

Browse files
Options that expect a file, should accept lists of files too (codespell-project#2767)
1 parent 742729a commit 2569d89

File tree

3 files changed

+56
-30
lines changed

3 files changed

+56
-30
lines changed

codespell_lib/_codespell.py

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -390,10 +390,9 @@ def parse_options(
390390
"-D",
391391
"--dictionary",
392392
action="append",
393-
help="custom dictionary file that contains spelling "
394-
"corrections. If this flag is not specified or "
395-
'equals "-" then the default dictionary is used. '
396-
"This option can be specified multiple times.",
393+
help="comma-separated list of custom dictionary files that "
394+
"contain spelling corrections. If this flag is not specified "
395+
'or equals "-" then the default dictionary is used.',
397396
)
398397
builtin_opts = "\n- ".join(
399398
[""] + [f"{d[0]!r} {d[1]}" for d in _builtin_dictionaries]
@@ -423,26 +422,26 @@ def parse_options(
423422
"-I",
424423
"--ignore-words",
425424
action="append",
426-
metavar="FILE",
427-
help="file that contains words that will be ignored "
428-
"by codespell. File must contain 1 word per line."
429-
" Words are case sensitive based on how they are "
430-
"written in the dictionary file",
425+
metavar="FILES",
426+
help="comma-separated list of files that contain "
427+
"words to be ignored by codespell. Files must contain "
428+
"1 word per line. Words are case sensitive based on "
429+
"how they are written in the dictionary file.",
431430
)
432431
parser.add_argument(
433432
"-L",
434433
"--ignore-words-list",
435434
action="append",
436435
metavar="WORDS",
437-
help="comma separated list of words to be ignored "
436+
help="comma-separated list of words to be ignored "
438437
"by codespell. Words are case sensitive based on "
439-
"how they are written in the dictionary file",
438+
"how they are written in the dictionary file.",
440439
)
441440
parser.add_argument(
442441
"--uri-ignore-words-list",
443442
action="append",
444443
metavar="WORDS",
445-
help="comma separated list of words to be ignored "
444+
help="comma-separated list of words to be ignored "
446445
"by codespell in URIs and emails only. Words are "
447446
"case sensitive based on how they are written in "
448447
'the dictionary file. If set to "*", all '
@@ -494,11 +493,13 @@ def parse_options(
494493
parser.add_argument(
495494
"-x",
496495
"--exclude-file",
496+
action="append",
497497
type=str,
498-
metavar="FILE",
499-
help="ignore whole lines that match those "
500-
"in the file FILE. The lines in FILE "
501-
"should match the to-be-excluded lines exactly",
498+
metavar="FILES",
499+
help="ignore whole lines that match those in "
500+
"the comma-separated list of files EXCLUDE. "
501+
"The lines in these files should match the "
502+
"to-be-excluded lines exactly",
502503
)
503504

504505
parser.add_argument(
@@ -1166,20 +1167,22 @@ def main(*args: str) -> int:
11661167
else:
11671168
ignore_word_regex = None
11681169

1169-
ignore_words_files = options.ignore_words or []
11701170
ignore_words, ignore_words_cased = parse_ignore_words_option(
11711171
options.ignore_words_list
11721172
)
1173-
1174-
for ignore_words_file in ignore_words_files:
1175-
if not os.path.isfile(ignore_words_file):
1176-
print(
1177-
f"ERROR: cannot find ignore-words file: {ignore_words_file}",
1178-
file=sys.stderr,
1179-
)
1180-
parser.print_help()
1181-
return EX_USAGE
1182-
build_ignore_words(ignore_words_file, ignore_words, ignore_words_cased)
1173+
if options.ignore_words:
1174+
ignore_words_files = flatten_clean_comma_separated_arguments(
1175+
options.ignore_words
1176+
)
1177+
for ignore_words_file in ignore_words_files:
1178+
if not os.path.isfile(ignore_words_file):
1179+
print(
1180+
f"ERROR: cannot find ignore-words file: {ignore_words_file}",
1181+
file=sys.stderr,
1182+
)
1183+
parser.print_help()
1184+
return EX_USAGE
1185+
build_ignore_words(ignore_words_file, ignore_words, ignore_words_cased)
11831186

11841187
uri_regex = options.uri_regex or uri_regex_def
11851188
try:
@@ -1196,7 +1199,7 @@ def main(*args: str) -> int:
11961199
itertools.chain(*parse_ignore_words_option(options.uri_ignore_words_list))
11971200
)
11981201

1199-
dictionaries = options.dictionary or ["-"]
1202+
dictionaries = flatten_clean_comma_separated_arguments(options.dictionary or ["-"])
12001203

12011204
use_dictionaries = []
12021205
for dictionary in dictionaries:
@@ -1258,7 +1261,9 @@ def main(*args: str) -> int:
12581261

12591262
exclude_lines: Set[str] = set()
12601263
if options.exclude_file:
1261-
build_exclude_hashes(options.exclude_file, exclude_lines)
1264+
exclude_files = flatten_clean_comma_separated_arguments(options.exclude_file)
1265+
for exclude_file in exclude_files:
1266+
build_exclude_hashes(exclude_file, exclude_lines)
12621267

12631268
file_opener = FileOpener(options.hard_encoding_detection, options.quiet_level)
12641269

codespell_lib/tests/test_basic.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,20 @@ def test_ignore_dictionary(
342342
fname = tmp_path / "ignore.txt"
343343
fname.write_text("abandonned\nabilty\r\nackward")
344344
assert cs.main("-I", fname, bad_name) == 1
345+
# missing file in ignore list
346+
fname_missing = tmp_path / "missing.txt"
347+
result = cs.main("-I", fname_missing, bad_name, std=True)
348+
assert isinstance(result, tuple)
349+
code, _, stderr = result
350+
assert code == EX_USAGE
351+
assert "ERROR:" in stderr
352+
# comma-separated list of files
353+
fname_dummy1 = tmp_path / "dummy1.txt"
354+
fname_dummy1.touch()
355+
fname_dummy2 = tmp_path / "dummy2.txt"
356+
fname_dummy2.touch()
357+
assert cs.main("-I", fname_dummy1, "-I", fname, "-I", fname_dummy2, bad_name) == 1
358+
assert cs.main("-I", f"{fname_dummy1},{fname},{fname_dummy2}", bad_name) == 1
345359

346360

347361
def test_ignore_words_with_cases(
@@ -495,6 +509,13 @@ def test_exclude_file(
495509
)
496510
assert cs.main(bad_name) == 18
497511
assert cs.main("-x", fname, bad_name) == 1
512+
# comma-separated list of files
513+
fname_dummy1 = tmp_path / "dummy1.txt"
514+
fname_dummy1.touch()
515+
fname_dummy2 = tmp_path / "dummy2.txt"
516+
fname_dummy2.touch()
517+
assert cs.main("-x", fname_dummy1, "-x", fname, "-x", fname_dummy2, bad_name) == 1
518+
assert cs.main("-x", f"{fname_dummy1},{fname},{fname_dummy2}", bad_name) == 1
498519

499520

500521
def test_encoding(

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,6 @@ max-complexity = 45
169169
[tool.ruff.lint.pylint]
170170
allow-magic-value-types = ["bytes", "int", "str",]
171171
max-args = 13
172-
max-branches = 49
172+
max-branches = 51
173173
max-returns = 11
174174
max-statements = 119

0 commit comments

Comments
 (0)