mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:24 +00:00
Escape template filenames in glob patterns (#16407)
## Summary Fixes #9381. This PR fixes errors like ``` Cause: error parsing glob '/Users/me/project/{{cookiecutter.project_dirname}}/__pycache__': nested alternate groups are not allowed ``` caused by glob special characters in filenames like `{{cookiecutter.project_dirname}}`. When the user is matching that directory exactly, they can use the workaround given by https://github.com/astral-sh/ruff/issues/7959#issuecomment-1764751734, but that doesn't work for a nested config file with relative paths. For example, the directory tree in the reproduction repo linked [here](https://github.com/astral-sh/ruff/issues/9381#issuecomment-2677696408): ``` . ├── README.md ├── hello.py ├── pyproject.toml ├── uv.lock └── {{cookiecutter.repo_name}} ├── main.py ├── pyproject.toml └── tests └── maintest.py ``` where the inner `pyproject.toml` contains a relative glob: ```toml [tool.ruff.lint.per-file-ignores] "tests/*" = ["F811"] ``` ## Test Plan A new CLI test in both the linter and formatter. The formatter test may not be necessary because I didn't have to modify any additional code to pass it, but the original report mentioned both `check` and `format`, so I wanted to be sure both were fixed.
This commit is contained in:
parent
4d92e20e81
commit
d93ed293eb
8 changed files with 195 additions and 70 deletions
|
@ -30,7 +30,7 @@ use ruff_linter::settings::fix_safety_table::FixSafetyTable;
|
|||
use ruff_linter::settings::rule_table::RuleTable;
|
||||
use ruff_linter::settings::types::{
|
||||
CompiledPerFileIgnoreList, CompiledPerFileTargetVersionList, ExtensionMapping, FilePattern,
|
||||
FilePatternSet, OutputFormat, PerFileIgnore, PerFileTargetVersion, PreviewMode,
|
||||
FilePatternSet, GlobPath, OutputFormat, PerFileIgnore, PerFileTargetVersion, PreviewMode,
|
||||
RequiredVersion, UnsafeFixes,
|
||||
};
|
||||
use ruff_linter::settings::{LinterSettings, DEFAULT_SELECTORS, DUMMY_VARIABLE_RGX, TASK_TAGS};
|
||||
|
@ -476,7 +476,7 @@ impl Configuration {
|
|||
paths
|
||||
.into_iter()
|
||||
.map(|pattern| {
|
||||
let absolute = fs::normalize_path_to(&pattern, project_root);
|
||||
let absolute = GlobPath::normalize(&pattern, project_root);
|
||||
FilePattern::User(pattern, absolute)
|
||||
})
|
||||
.collect()
|
||||
|
@ -495,7 +495,7 @@ impl Configuration {
|
|||
paths
|
||||
.into_iter()
|
||||
.map(|pattern| {
|
||||
let absolute = fs::normalize_path_to(&pattern, project_root);
|
||||
let absolute = GlobPath::normalize(&pattern, project_root);
|
||||
FilePattern::User(pattern, absolute)
|
||||
})
|
||||
.collect()
|
||||
|
@ -507,7 +507,7 @@ impl Configuration {
|
|||
paths
|
||||
.into_iter()
|
||||
.map(|pattern| {
|
||||
let absolute = fs::normalize_path_to(&pattern, project_root);
|
||||
let absolute = GlobPath::normalize(&pattern, project_root);
|
||||
FilePattern::User(pattern, absolute)
|
||||
})
|
||||
.collect()
|
||||
|
@ -517,7 +517,7 @@ impl Configuration {
|
|||
paths
|
||||
.into_iter()
|
||||
.map(|pattern| {
|
||||
let absolute = fs::normalize_path_to(&pattern, project_root);
|
||||
let absolute = GlobPath::normalize(&pattern, project_root);
|
||||
FilePattern::User(pattern, absolute)
|
||||
})
|
||||
.collect()
|
||||
|
@ -700,7 +700,7 @@ impl LintConfiguration {
|
|||
paths
|
||||
.into_iter()
|
||||
.map(|pattern| {
|
||||
let absolute = fs::normalize_path_to(&pattern, project_root);
|
||||
let absolute = GlobPath::normalize(&pattern, project_root);
|
||||
FilePattern::User(pattern, absolute)
|
||||
})
|
||||
.collect()
|
||||
|
@ -1203,7 +1203,7 @@ impl FormatConfiguration {
|
|||
paths
|
||||
.into_iter()
|
||||
.map(|pattern| {
|
||||
let absolute = fs::normalize_path_to(&pattern, project_root);
|
||||
let absolute = GlobPath::normalize(&pattern, project_root);
|
||||
FilePattern::User(pattern, absolute)
|
||||
})
|
||||
.collect()
|
||||
|
@ -1267,7 +1267,7 @@ impl AnalyzeConfiguration {
|
|||
paths
|
||||
.into_iter()
|
||||
.map(|pattern| {
|
||||
let absolute = fs::normalize_path_to(&pattern, project_root);
|
||||
let absolute = GlobPath::normalize(&pattern, project_root);
|
||||
FilePattern::User(pattern, absolute)
|
||||
})
|
||||
.collect()
|
||||
|
|
|
@ -877,7 +877,7 @@ mod tests {
|
|||
use path_absolutize::Absolutize;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use ruff_linter::settings::types::FilePattern;
|
||||
use ruff_linter::settings::types::{FilePattern, GlobPath};
|
||||
|
||||
use crate::configuration::Configuration;
|
||||
use crate::pyproject::find_settings_toml;
|
||||
|
@ -972,13 +972,8 @@ mod tests {
|
|||
let project_root = Path::new("/tmp/");
|
||||
|
||||
let path = Path::new("foo").absolutize_from(project_root).unwrap();
|
||||
let exclude = FilePattern::User(
|
||||
"foo".to_string(),
|
||||
Path::new("foo")
|
||||
.absolutize_from(project_root)
|
||||
.unwrap()
|
||||
.to_path_buf(),
|
||||
);
|
||||
let exclude =
|
||||
FilePattern::User("foo".to_string(), GlobPath::normalize("foo", project_root));
|
||||
let file_path = &path;
|
||||
let file_basename = path.file_name().unwrap();
|
||||
assert!(match_exclusion(
|
||||
|
@ -988,13 +983,8 @@ mod tests {
|
|||
));
|
||||
|
||||
let path = Path::new("foo/bar").absolutize_from(project_root).unwrap();
|
||||
let exclude = FilePattern::User(
|
||||
"bar".to_string(),
|
||||
Path::new("bar")
|
||||
.absolutize_from(project_root)
|
||||
.unwrap()
|
||||
.to_path_buf(),
|
||||
);
|
||||
let exclude =
|
||||
FilePattern::User("bar".to_string(), GlobPath::normalize("bar", project_root));
|
||||
let file_path = &path;
|
||||
let file_basename = path.file_name().unwrap();
|
||||
assert!(match_exclusion(
|
||||
|
@ -1008,10 +998,7 @@ mod tests {
|
|||
.unwrap();
|
||||
let exclude = FilePattern::User(
|
||||
"baz.py".to_string(),
|
||||
Path::new("baz.py")
|
||||
.absolutize_from(project_root)
|
||||
.unwrap()
|
||||
.to_path_buf(),
|
||||
GlobPath::normalize("baz.py", project_root),
|
||||
);
|
||||
let file_path = &path;
|
||||
let file_basename = path.file_name().unwrap();
|
||||
|
@ -1024,10 +1011,7 @@ mod tests {
|
|||
let path = Path::new("foo/bar").absolutize_from(project_root).unwrap();
|
||||
let exclude = FilePattern::User(
|
||||
"foo/bar".to_string(),
|
||||
Path::new("foo/bar")
|
||||
.absolutize_from(project_root)
|
||||
.unwrap()
|
||||
.to_path_buf(),
|
||||
GlobPath::normalize("foo/bar", project_root),
|
||||
);
|
||||
let file_path = &path;
|
||||
let file_basename = path.file_name().unwrap();
|
||||
|
@ -1042,10 +1026,7 @@ mod tests {
|
|||
.unwrap();
|
||||
let exclude = FilePattern::User(
|
||||
"foo/bar/baz.py".to_string(),
|
||||
Path::new("foo/bar/baz.py")
|
||||
.absolutize_from(project_root)
|
||||
.unwrap()
|
||||
.to_path_buf(),
|
||||
GlobPath::normalize("foo/bar/baz.py", project_root),
|
||||
);
|
||||
let file_path = &path;
|
||||
let file_basename = path.file_name().unwrap();
|
||||
|
@ -1060,10 +1041,7 @@ mod tests {
|
|||
.unwrap();
|
||||
let exclude = FilePattern::User(
|
||||
"foo/bar/*.py".to_string(),
|
||||
Path::new("foo/bar/*.py")
|
||||
.absolutize_from(project_root)
|
||||
.unwrap()
|
||||
.to_path_buf(),
|
||||
GlobPath::normalize("foo/bar/*.py", project_root),
|
||||
);
|
||||
let file_path = &path;
|
||||
let file_basename = path.file_name().unwrap();
|
||||
|
@ -1076,13 +1054,8 @@ mod tests {
|
|||
let path = Path::new("foo/bar/baz.py")
|
||||
.absolutize_from(project_root)
|
||||
.unwrap();
|
||||
let exclude = FilePattern::User(
|
||||
"baz".to_string(),
|
||||
Path::new("baz")
|
||||
.absolutize_from(project_root)
|
||||
.unwrap()
|
||||
.to_path_buf(),
|
||||
);
|
||||
let exclude =
|
||||
FilePattern::User("baz".to_string(), GlobPath::normalize("baz", project_root));
|
||||
let file_path = &path;
|
||||
let file_basename = path.file_name().unwrap();
|
||||
assert!(!match_exclusion(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue