Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix --skip matching #2058

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,14 @@ Comma-separated list of files to skip. It accepts globs as well. Examples:

* to skip .eps & .txt files, invoke ``codespell --skip="*.eps,*.txt"``

* to skip directories, invoke ``codespell --skip="./src/3rd-Party,./src/Test"``
* to skip directories, invoke ``codespell --skip="src/3rd-Party,src/Test"``


Useful commands:

.. code-block:: sh

codespell -d -q 3 --skip="*.po,*.ts,./src/3rdParty,./src/Test"
codespell -d -q 3 --skip="*.po,*.ts,src/3rdParty,src/Test"

List all typos found except translation files and some directories.
Display them without terminal colors and with a quiet level of 3.
Expand Down Expand Up @@ -129,7 +129,7 @@ be specified in this file (without the preceding dashes), for example:
.. code-block:: ini

[codespell]
skip = *.po,*.ts,./src/3rdParty,./src/Test
skip = *.po,*.ts,src/3rdParty,src/Test
count =
quiet-level = 3

Expand All @@ -149,7 +149,7 @@ These are both equivalent to running:

.. code-block:: sh

codespell --quiet-level 3 --count --skip "*.po,*.ts,./src/3rdParty,./src/Test"
codespell --quiet-level 3 --count --skip "*.po,*.ts,src/3rdParty,src/Test"

If several config files are present, they are read in the following order:

Expand Down
50 changes: 38 additions & 12 deletions codespell_lib/_codespell.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,14 @@ class GlobMatch:
def __init__(self, pattern: Optional[str]) -> None:
self.pattern_list: Optional[List[str]]
if pattern:
# Pattern might be a list of comma-delimited strings
self.pattern_list = ",".join(pattern).split(",")
self.pattern_list = [
# We remove trailing path separators from each pattern
# because shell completion might add a trailing separator
# '/' after directory names
p.rstrip(os.path.sep)
# Pattern might be a list of comma-separated patterns
for p in ",".join(pattern).split(",")
]
else:
self.pattern_list = None

Expand Down Expand Up @@ -596,6 +602,10 @@ def parse_options(
# Re-parse command line options to override config.
options = parser.parse_args(list(args), namespace=options)

# Add a phony option than can be changed into a real option when we
# are ready to deprecate old patterns (./foo/bar instead of foo/bar)
options.deprecated = True

if not options.files:
options.files.append(".")

Expand Down Expand Up @@ -1135,28 +1145,44 @@ def main(*args: str) -> int:

bad_count = 0
for filename in options.files:
# ignore hidden files
# ignore hidden file or directory
if is_hidden(filename, options.check_hidden):
continue

if os.path.isdir(filename):
for root, dirs, files in os.walk(filename):
if glob_match.match(root): # skip (absolute) directories
rel_root = os.path.relpath(root, filename)
# skip matching relative and "as is" directory paths
if (
glob_match.match(rel_root)
or glob_match.match(root) and options.deprecated
):
del dirs[:]
continue
if is_hidden(root, options.check_hidden): # dir itself hidden

# ignore hidden directory
if is_hidden(root, options.check_hidden):
continue

# files
for file_ in files:
# ignore hidden files in directories
# ignore hidden files
if is_hidden(file_, options.check_hidden):
continue
if glob_match.match(file_): # skip files
# skip base file name
if glob_match.match(file_):
continue
fname = os.path.join(root, file_)
if glob_match.match(fname): # skip paths
abs_path = os.path.join(root, file_)
rel_path = os.path.join(rel_root, file_)
# skip matching relative and "as is" file paths
if (
glob_match.match(rel_path)
or glob_match.match(abs_path) and options.deprecated
):
continue

bad_count += parse_file(
fname,
abs_path,
colors,
summary,
misspellings,
Expand All @@ -1170,10 +1196,10 @@ def main(*args: str) -> int:
options,
)

# skip (relative) directories
# skip base directory name
dirs[:] = [dir_ for dir_ in dirs if not glob_match.match(dir_)]

elif not glob_match.match(filename): # skip files
elif not glob_match.match(filename): # skip file
bad_count += parse_file(
filename,
colors,
Expand Down
64 changes: 54 additions & 10 deletions codespell_lib/tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ def test_encoding(
assert "WARNING: Binary file" in stderr


<<<<<<< HEAD
def test_unknown_encoding_chardet(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
Expand All @@ -398,26 +399,69 @@ def test_ignore(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
"""Test ignoring of files and directories."""
"""Test ignoring of files and directories.

Test the following file hierarchy:

tmpdir
├── good.txt
├── bad.txt
└── ignoredir
   └── subdir
    └── bad.txt

"""
goodtxt = tmp_path / "good.txt"
goodtxt.write_text("this file is okay")
assert cs.main(tmp_path) == 0
assert cs.main(d) == 0
badtxt = tmp_path / "bad.txt"
badtxt.write_text("abandonned")
assert cs.main(tmp_path) == 1
assert cs.main("--skip=bad*", tmp_path) == 0
assert cs.main("--skip=*bad*", tmp_path) == 0
assert cs.main("--skip=bad.txt", tmp_path) == 0
subdir = tmp_path / "ignoredir"
assert cs.main("--skip=./bad.txt", tmp_path) == 0 # deprecated
subdir = tmp_path / "ignoredir" / "subdir"
subdir.mkdir()
(subdir / "bad.txt").write_text("abandonned")
assert cs.main(tmp_path) == 2
assert cs.main("--skip=bad*", tmp_path) == 0
assert cs.main("--skip=*ignoredir*", tmp_path) == 1
assert cs.main("--skip=ignoredir", tmp_path) == 1
assert cs.main("--skip=*ignoredir/bad*", tmp_path) == 1
badjs = tmp_path / "bad.js"
copyfile(badtxt, badjs)
assert cs.main("--skip=*.js", goodtxt, badtxt, badjs) == 1
assert cs.main(goodtxt, badtxt) == 1
assert cs.main('--skip=*.txt', tmp_path) == 0
assert cs.main('--skip=*.txt', goodtxt, badtxt) == 0
assert cs.main('--skip=bad*', tmp_path) == 0
assert cs.main('--skip=*ignoredir*', tmp_path) == 1
assert cs.main('--skip=*gnoredir*', tmp_path) == 1
assert cs.main('--skip=ignoredir', tmp_path) == 1
assert cs.main('--skip=ignoredir/', tmp_path) == 1
assert cs.main('--skip=*ignoredir/subdir*', tmp_path) == 1
assert cs.main('--skip=*gnoredir/subdi*', tmp_path) == 1
assert cs.main('--skip=ignoredir/subdir', tmp_path) == 1
assert cs.main('--skip=ignoredir/subdir/', tmp_path) == 1
assert cs.main('--skip=*ignoredir/subdir/bad*', tmp_path) == 1
assert cs.main('--skip=ignoredir/subdir/bad.txt', tmp_path) == 1
assert cs.main('--skip=*subdir*', tmp_path) == 1
assert cs.main('--skip=*ubd*', tmp_path) == 1
assert cs.main('--skip=subdir', tmp_path) == 1
assert cs.main('--skip=*subdir/bad*', tmp_path) == 1
assert cs.main('--skip=subdir/bad*', tmp_path) == 2
# test deprecated syntax from outside "tmpdir"
assert cs.main('--skip=./ignoredir', tmp_path) == 2
assert cs.main('--skip=./ignoredir/subdir/', tmp_path) == 2
assert cs.main('--skip=./ignoredir/subdir/bad.txt', tmp_path) == 2
assert cs.main(f'--skip={d}/ignoredir', tmp_path) == 1
assert cs.main(f'--skip={d}/ignoredir/subdir/', tmp_path) == 1
assert cs.main(f'--skip={d}/ignoredir/subdir/bad.txt', tmp_path) == 1
# test deprecated syntax from inside "tmpdir"
cwd = os.getcwd()
os.chdir(tmpdir)
assert cs.main('--skip=./ignoredir') == 1
assert cs.main('--skip=./ignoredir/subdir/') == 1
assert cs.main('--skip=./ignoredir/subdir/bad.txt') == 1
assert cs.main('--skip=./ignoredir', '.') == 1
assert cs.main(f'--skip={d}/ignoredir') == 1
assert cs.main(f'--skip={d}/ignoredir/subdir/') == 1
assert cs.main(f'--skip={d}/ignoredir/subdir/bad.txt') == 1
os.chdir(cwd)


def test_check_filename(
Expand Down