Summary
Summary
With an explicit --config <path>, exclude / extend-exclude patterns are
matched against the target path relative to the current working directory,
not relative to the configuration file's directory. So the same config excludes
a directory when ruff runs from the project root, but formats files inside
that excluded directory when ruff runs from a subdirectory. A leading **/
does not help — the parent path components are absent from the cwd-relative path.
This breaks editors and build tools that invoke ruff from a subdirectory with an
explicit config.
This is the same root cause as #12058, but for exclude/extend-exclude rather
than per-file-ignores.
Reproduction
docker run --rm alchemmist/ruff-exclude-repro
It builds the tree, prints the config and the exact commands, and shows ruff's
own diff output proving the leak, for ruff 0.14.1 and 0.15.19.
Manual reproduction & full details
ruff.toml at the project root:
force-exclude = true
extend-exclude = ["**/libra/tests/py2_legacy/**"]
Tree (both files badly formatted, so ruff format wants to rewrite them):
user_sessions/
ruff.toml
libra/src/good.py # normal, should be formatted
libra/tests/py2_legacy/legacy.py # python2 legacy, should be EXCLUDED
# from the project root — correct: only good.py is touched
$ cd user_sessions && ruff format . --config ruff.toml --diff
--- libra/src/good.py
1 file would be reformatted
# from a subdirectory — WRONG: the excluded file is reformatted too
$ cd user_sessions/libra && ruff format . --config ../ruff.toml --diff
--- src/good.py
--- tests/py2_legacy/legacy.py # <-- excluded file leaks
2 files would be reformatted
Expected: the excluded directory is excluded regardless of cwd — ideally
exclude/extend-exclude anchored to the config file's directory.
Actual: the exclude is anchored to the cwd; from a subdirectory, patterns
with parent path components silently stop matching and excluded files get
formatted. Only a bare leaf basename (py2_legacy, no slash) is stable, but it
matches such a directory anywhere and cannot target a specific path.
Note: with config auto-discovery (no --config) ruff anchors excludes to
the discovered config directory and the bug does not appear; passing
--config <path> is what triggers it.
Versions: reproduced on ruff 0.14.1 and 0.15.19 (native Linux, amd64 + arm64).
Related
Version
0.14.1 & 0.15.19
Summary
Summary
With an explicit
--config <path>,exclude/extend-excludepatterns arematched against the target path relative to the current working directory,
not relative to the configuration file's directory. So the same config excludes
a directory when ruff runs from the project root, but formats files inside
that excluded directory when ruff runs from a subdirectory. A leading
**/does not help — the parent path components are absent from the cwd-relative path.
This breaks editors and build tools that invoke ruff from a subdirectory with an
explicit config.
This is the same root cause as #12058, but for
exclude/extend-excluderatherthan
per-file-ignores.Reproduction
It builds the tree, prints the config and the exact commands, and shows ruff's
own diff output proving the leak, for ruff 0.14.1 and 0.15.19.
Manual reproduction & full details
ruff.tomlat the project root:Tree (both files badly formatted, so
ruff formatwants to rewrite them):Expected: the excluded directory is excluded regardless of cwd — ideally
exclude/extend-excludeanchored to the config file's directory.Actual: the exclude is anchored to the cwd; from a subdirectory, patterns
with parent path components silently stop matching and excluded files get
formatted. Only a bare leaf basename (
py2_legacy, no slash) is stable, but itmatches such a directory anywhere and cannot target a specific path.
Note: with config auto-discovery (no
--config) ruff anchors excludes tothe discovered config directory and the bug does not appear; passing
--config <path>is what triggers it.Versions: reproduced on ruff 0.14.1 and 0.15.19 (native Linux, amd64 + arm64).
Related
per-file-ignoresnot respected when runningruffoutside working directory using--configwithpyproject.toml#12058 — relative paths resolved against cwd with--config(same root cause,per-file-ignores) — openforce-excludedoesn't work in a subdirectory — openruff format— closedVersion
0.14.1 & 0.15.19