Bug
matches_gitignore (mypy/modulefinder.py) fails to exclude a directory matched by a directory-only pattern (e.g. build/, node_modules/) when the pattern lives in a nested .gitignore (one in a scanned subdirectory), under --exclude-gitignore.
The trailing-slash decision uses the gitignore-relative path with a cwd-relative stat:
relative_path = os.path.relpath(subpath, gi_path)
if fscache.isdir(relative_path): # relative_path is relative to gi_path, but isdir resolves from cwd
relative_path = relative_path + "/"
When gi_path != cwd, relative_path doesn't resolve on disk, so isdir returns False, the / is never appended, and the directory-only pattern doesn't match — mypy then type-checks files the user intended to exclude.
The sibling matches_exclude in the same file does it correctly: it builds the match string with os.path.relpath(subpath) but calls fscache.isdir(subpath) on the original path.
To Reproduce
With --exclude-gitignore, a package dir containing a nested .gitignore whose content is sub/, where sub/ is a real subdirectory: mypy still checks files under sub/.
Expected Behavior
sub/ is excluded, matching git's own behavior for directory-only patterns.
Fix
Stat the original subpath (keep relative_path only for the spec match) — proposed in #21752.
Bug
matches_gitignore(mypy/modulefinder.py) fails to exclude a directory matched by a directory-only pattern (e.g.build/,node_modules/) when the pattern lives in a nested.gitignore(one in a scanned subdirectory), under--exclude-gitignore.The trailing-slash decision uses the gitignore-relative path with a cwd-relative stat:
When
gi_path != cwd,relative_pathdoesn't resolve on disk, soisdirreturnsFalse, the/is never appended, and the directory-only pattern doesn't match — mypy then type-checks files the user intended to exclude.The sibling
matches_excludein the same file does it correctly: it builds the match string withos.path.relpath(subpath)but callsfscache.isdir(subpath)on the original path.To Reproduce
With
--exclude-gitignore, a package dir containing a nested.gitignorewhose content issub/, wheresub/is a real subdirectory: mypy still checks files undersub/.Expected Behavior
sub/is excluded, matching git's own behavior for directory-only patterns.Fix
Stat the original
subpath(keeprelative_pathonly for the spec match) — proposed in #21752.