fix: fix whitespace stripping inconsistencies in wcmatch

This commit is contained in:
Travis Abendshien
2026-09-14 00:23:20 -07:00
parent 795be0ac4e
commit 3ae9dd39a3
2 changed files with 39 additions and 2 deletions
+13 -1
View File
@@ -113,6 +113,18 @@ def migrate_ext_list(exts: list[str], is_exclude_list: bool) -> str:
return out
def _strip(line: str) -> str:
"""Strip a line ending and unescaped trailing whitespace from an ignore file line.
Leading whitespace and a backslash-escaped trailing space are left intact, matching
.gitignore's rule that trailing spaces are ignored unless escaped.
"""
line = line.rstrip("\r\n")
while line and line[-1].isspace() and line[-2:-1] != "\\":
line = line[:-1]
return line
class Ignore(metaclass=Singleton):
"""Class for processing and managing glob-like file ignore file patterns."""
@@ -210,7 +222,7 @@ class Ignore(metaclass=Singleton):
if path.exists():
with open(path, encoding="utf8") as f:
for line_raw in f.readlines():
line = line_raw.strip()
line = _strip(line_raw)
# Ignore blank lines and comments
if not line or line.startswith("#"):
continue
+26 -1
View File
@@ -1,9 +1,13 @@
# SPDX-FileCopyrightText: (c) TagStudio Contributors
# SPDX-License-Identifier: GPL-3.0-only
# pyright: reportPrivateUsage=false
from pathlib import Path
from wcmatch import glob
from tagstudio.core.library.ignore import PATH_GLOB_FLAGS, ignore_to_glob
from tagstudio.core.library.ignore import PATH_GLOB_FLAGS, Ignore, ignore_to_glob
def matches(patterns: list[str], path: str) -> bool:
@@ -102,3 +106,24 @@ def test_negation_does_not_extend_to_deeper_subfolder():
assert matches(patterns, "a.jpg") is True
assert matches(patterns, "Photos/a.jpg") is False
assert matches(patterns, "Photos/Private/a.jpg") is True
def test_ignore_file_preserves_escaped_trailing_space(tmp_path: Path):
"""An escaped trailing space must not be stripped."""
ts_ignore = tmp_path / ".ts_ignore"
ts_ignore.write_bytes(b"foo\\ \nbar \n")
assert Ignore._load_ignore_file(ts_ignore) == ["foo\\ ", "bar"]
def test_ignore_file_preserves_leading_whitespace(tmp_path: Path):
"""Leading whitespace must not be stripped."""
ts_ignore = tmp_path / ".ts_ignore"
ts_ignore.write_bytes(b" baz\n")
assert Ignore._load_ignore_file(ts_ignore) == [" baz"]
def test_ignore_file_strips_crlf_line_ending(tmp_path: Path):
"""A Windows CRLF line ending must not become part of the pattern."""
ts_ignore = tmp_path / ".ts_ignore"
ts_ignore.write_bytes(b"qux\r\n")
assert Ignore._load_ignore_file(ts_ignore) == ["qux"]