mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-18 16:20:22 +00:00
Show settings path in --show-settings
output (#4199)
This commit is contained in:
parent
37aae666c7
commit
59d40f9f81
9 changed files with 152 additions and 102 deletions
|
@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::resolver::{PyprojectDiscovery, Resolver};
|
use crate::resolver::{PyprojectConfig, Resolver};
|
||||||
|
|
||||||
// If we have a Python package layout like:
|
// If we have a Python package layout like:
|
||||||
// - root/
|
// - root/
|
||||||
|
@ -82,7 +82,7 @@ fn detect_package_root_with_cache<'a>(
|
||||||
pub fn detect_package_roots<'a>(
|
pub fn detect_package_roots<'a>(
|
||||||
files: &[&'a Path],
|
files: &[&'a Path],
|
||||||
resolver: &'a Resolver,
|
resolver: &'a Resolver,
|
||||||
pyproject_strategy: &'a PyprojectDiscovery,
|
pyproject_config: &'a PyprojectConfig,
|
||||||
) -> FxHashMap<&'a Path, Option<&'a Path>> {
|
) -> FxHashMap<&'a Path, Option<&'a Path>> {
|
||||||
// Pre-populate the module cache, since the list of files could (but isn't
|
// Pre-populate the module cache, since the list of files could (but isn't
|
||||||
// required to) contain some `__init__.py` files.
|
// required to) contain some `__init__.py` files.
|
||||||
|
@ -98,9 +98,7 @@ pub fn detect_package_roots<'a>(
|
||||||
// Search for the package root for each file.
|
// Search for the package root for each file.
|
||||||
let mut package_roots: FxHashMap<&Path, Option<&Path>> = FxHashMap::default();
|
let mut package_roots: FxHashMap<&Path, Option<&Path>> = FxHashMap::default();
|
||||||
for file in files {
|
for file in files {
|
||||||
let namespace_packages = &resolver
|
let namespace_packages = &resolver.resolve(file, pyproject_config).namespace_packages;
|
||||||
.resolve(file, pyproject_strategy)
|
|
||||||
.namespace_packages;
|
|
||||||
if let Some(package) = file.parent() {
|
if let Some(package) = file.parent() {
|
||||||
if package_roots.contains_key(package) {
|
if package_roots.contains_key(package) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -17,25 +17,42 @@ use crate::settings::configuration::Configuration;
|
||||||
use crate::settings::pyproject::settings_toml;
|
use crate::settings::pyproject::settings_toml;
|
||||||
use crate::settings::{pyproject, AllSettings, Settings};
|
use crate::settings::{pyproject, AllSettings, Settings};
|
||||||
|
|
||||||
|
/// The configuration information from a `pyproject.toml` file.
|
||||||
|
pub struct PyprojectConfig {
|
||||||
|
/// The strategy used to discover the relevant `pyproject.toml` file for
|
||||||
|
/// each Python file.
|
||||||
|
pub strategy: PyprojectDiscoveryStrategy,
|
||||||
|
/// All settings from the `pyproject.toml` file.
|
||||||
|
pub settings: AllSettings,
|
||||||
|
/// Absolute path to the `pyproject.toml` file. This would be `None` when
|
||||||
|
/// either using the default settings or the `--isolated` flag is set.
|
||||||
|
pub path: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PyprojectConfig {
|
||||||
|
pub fn new(
|
||||||
|
strategy: PyprojectDiscoveryStrategy,
|
||||||
|
settings: AllSettings,
|
||||||
|
path: Option<PathBuf>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
strategy,
|
||||||
|
settings,
|
||||||
|
path: path.map(fs::normalize_path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The strategy used to discover the relevant `pyproject.toml` file for each
|
/// The strategy used to discover the relevant `pyproject.toml` file for each
|
||||||
/// Python file.
|
/// Python file.
|
||||||
#[derive(Debug, is_macro::Is)]
|
#[derive(Debug, is_macro::Is)]
|
||||||
pub enum PyprojectDiscovery {
|
pub enum PyprojectDiscoveryStrategy {
|
||||||
/// Use a fixed `pyproject.toml` file for all Python files (i.e., one
|
/// Use a fixed `pyproject.toml` file for all Python files (i.e., one
|
||||||
/// provided on the command-line).
|
/// provided on the command-line).
|
||||||
Fixed(AllSettings),
|
Fixed,
|
||||||
/// Use the closest `pyproject.toml` file in the filesystem hierarchy, or
|
/// Use the closest `pyproject.toml` file in the filesystem hierarchy, or
|
||||||
/// the default settings.
|
/// the default settings.
|
||||||
Hierarchical(AllSettings),
|
Hierarchical,
|
||||||
}
|
|
||||||
|
|
||||||
impl PyprojectDiscovery {
|
|
||||||
pub fn top_level_settings(&self) -> &AllSettings {
|
|
||||||
match self {
|
|
||||||
PyprojectDiscovery::Fixed(settings) => settings,
|
|
||||||
PyprojectDiscovery::Hierarchical(settings) => settings,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The strategy for resolving file paths in a `pyproject.toml`.
|
/// The strategy for resolving file paths in a `pyproject.toml`.
|
||||||
|
@ -75,21 +92,25 @@ impl Resolver {
|
||||||
pub fn resolve_all<'a>(
|
pub fn resolve_all<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
strategy: &'a PyprojectDiscovery,
|
pyproject_config: &'a PyprojectConfig,
|
||||||
) -> &'a AllSettings {
|
) -> &'a AllSettings {
|
||||||
match strategy {
|
match pyproject_config.strategy {
|
||||||
PyprojectDiscovery::Fixed(settings) => settings,
|
PyprojectDiscoveryStrategy::Fixed => &pyproject_config.settings,
|
||||||
PyprojectDiscovery::Hierarchical(default) => self
|
PyprojectDiscoveryStrategy::Hierarchical => self
|
||||||
.settings
|
.settings
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|(root, settings)| path.starts_with(root).then_some(settings))
|
.find_map(|(root, settings)| path.starts_with(root).then_some(settings))
|
||||||
.unwrap_or(default),
|
.unwrap_or(&pyproject_config.settings),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve<'a>(&'a self, path: &Path, strategy: &'a PyprojectDiscovery) -> &'a Settings {
|
pub fn resolve<'a>(
|
||||||
&self.resolve_all(path, strategy).lib
|
&'a self,
|
||||||
|
path: &Path,
|
||||||
|
pyproject_config: &'a PyprojectConfig,
|
||||||
|
) -> &'a Settings {
|
||||||
|
&self.resolve_all(path, pyproject_config).lib
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return an iterator over the resolved [`Settings`] in this [`Resolver`].
|
/// Return an iterator over the resolved [`Settings`] in this [`Resolver`].
|
||||||
|
@ -200,7 +221,7 @@ fn match_exclusion<P: AsRef<Path>, R: AsRef<Path>>(
|
||||||
/// Find all Python (`.py`, `.pyi` and `.ipynb` files) in a set of paths.
|
/// Find all Python (`.py`, `.pyi` and `.ipynb` files) in a set of paths.
|
||||||
pub fn python_files_in_path(
|
pub fn python_files_in_path(
|
||||||
paths: &[PathBuf],
|
paths: &[PathBuf],
|
||||||
pyproject_strategy: &PyprojectDiscovery,
|
pyproject_config: &PyprojectConfig,
|
||||||
processor: impl ConfigProcessor,
|
processor: impl ConfigProcessor,
|
||||||
) -> Result<(Vec<Result<DirEntry, ignore::Error>>, Resolver)> {
|
) -> Result<(Vec<Result<DirEntry, ignore::Error>>, Resolver)> {
|
||||||
// Normalize every path (e.g., convert from relative to absolute).
|
// Normalize every path (e.g., convert from relative to absolute).
|
||||||
|
@ -209,7 +230,7 @@ pub fn python_files_in_path(
|
||||||
// Search for `pyproject.toml` files in all parent directories.
|
// Search for `pyproject.toml` files in all parent directories.
|
||||||
let mut resolver = Resolver::default();
|
let mut resolver = Resolver::default();
|
||||||
let mut seen = FxHashSet::default();
|
let mut seen = FxHashSet::default();
|
||||||
if pyproject_strategy.is_hierarchical() {
|
if pyproject_config.strategy.is_hierarchical() {
|
||||||
for path in &paths {
|
for path in &paths {
|
||||||
for ancestor in path.ancestors() {
|
for ancestor in path.ancestors() {
|
||||||
if seen.insert(ancestor) {
|
if seen.insert(ancestor) {
|
||||||
|
@ -224,8 +245,8 @@ pub fn python_files_in_path(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the paths themselves are excluded.
|
// Check if the paths themselves are excluded.
|
||||||
if pyproject_strategy.top_level_settings().lib.force_exclude {
|
if pyproject_config.settings.lib.force_exclude {
|
||||||
paths.retain(|path| !is_file_excluded(path, &resolver, pyproject_strategy));
|
paths.retain(|path| !is_file_excluded(path, &resolver, pyproject_config));
|
||||||
if paths.is_empty() {
|
if paths.is_empty() {
|
||||||
return Ok((vec![], resolver));
|
return Ok((vec![], resolver));
|
||||||
}
|
}
|
||||||
|
@ -240,12 +261,7 @@ pub fn python_files_in_path(
|
||||||
for path in &paths[1..] {
|
for path in &paths[1..] {
|
||||||
builder.add(path);
|
builder.add(path);
|
||||||
}
|
}
|
||||||
builder.standard_filters(
|
builder.standard_filters(pyproject_config.settings.lib.respect_gitignore);
|
||||||
pyproject_strategy
|
|
||||||
.top_level_settings()
|
|
||||||
.lib
|
|
||||||
.respect_gitignore,
|
|
||||||
);
|
|
||||||
builder.hidden(false);
|
builder.hidden(false);
|
||||||
let walker = builder.build_parallel();
|
let walker = builder.build_parallel();
|
||||||
|
|
||||||
|
@ -261,7 +277,7 @@ pub fn python_files_in_path(
|
||||||
if entry.depth() > 0 {
|
if entry.depth() > 0 {
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
let resolver = resolver.read().unwrap();
|
let resolver = resolver.read().unwrap();
|
||||||
let settings = resolver.resolve(path, pyproject_strategy);
|
let settings = resolver.resolve(path, pyproject_config);
|
||||||
if let Some(file_name) = path.file_name() {
|
if let Some(file_name) = path.file_name() {
|
||||||
if !settings.exclude.is_empty()
|
if !settings.exclude.is_empty()
|
||||||
&& match_exclusion(path, file_name, &settings.exclude)
|
&& match_exclusion(path, file_name, &settings.exclude)
|
||||||
|
@ -283,7 +299,7 @@ pub fn python_files_in_path(
|
||||||
|
|
||||||
// Search for the `pyproject.toml` file in this directory, before we visit any
|
// Search for the `pyproject.toml` file in this directory, before we visit any
|
||||||
// of its contents.
|
// of its contents.
|
||||||
if pyproject_strategy.is_hierarchical() {
|
if pyproject_config.strategy.is_hierarchical() {
|
||||||
if let Ok(entry) = &result {
|
if let Ok(entry) = &result {
|
||||||
if entry
|
if entry
|
||||||
.file_type()
|
.file_type()
|
||||||
|
@ -321,7 +337,7 @@ pub fn python_files_in_path(
|
||||||
// Otherwise, check if the file is included.
|
// Otherwise, check if the file is included.
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
let resolver = resolver.read().unwrap();
|
let resolver = resolver.read().unwrap();
|
||||||
let settings = resolver.resolve(path, pyproject_strategy);
|
let settings = resolver.resolve(path, pyproject_config);
|
||||||
if settings.include.is_match(path) {
|
if settings.include.is_match(path) {
|
||||||
debug!("Included path via `include`: {:?}", path);
|
debug!("Included path via `include`: {:?}", path);
|
||||||
true
|
true
|
||||||
|
@ -348,10 +364,10 @@ pub fn python_files_in_path(
|
||||||
/// Return `true` if the Python file at [`Path`] is _not_ excluded.
|
/// Return `true` if the Python file at [`Path`] is _not_ excluded.
|
||||||
pub fn python_file_at_path(
|
pub fn python_file_at_path(
|
||||||
path: &Path,
|
path: &Path,
|
||||||
pyproject_strategy: &PyprojectDiscovery,
|
pyproject_config: &PyprojectConfig,
|
||||||
processor: impl ConfigProcessor,
|
processor: impl ConfigProcessor,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
if !pyproject_strategy.top_level_settings().lib.force_exclude {
|
if !pyproject_config.settings.lib.force_exclude {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,7 +376,7 @@ pub fn python_file_at_path(
|
||||||
|
|
||||||
// Search for `pyproject.toml` files in all parent directories.
|
// Search for `pyproject.toml` files in all parent directories.
|
||||||
let mut resolver = Resolver::default();
|
let mut resolver = Resolver::default();
|
||||||
if pyproject_strategy.is_hierarchical() {
|
if pyproject_config.strategy.is_hierarchical() {
|
||||||
for ancestor in path.ancestors() {
|
for ancestor in path.ancestors() {
|
||||||
if let Some(pyproject) = settings_toml(ancestor)? {
|
if let Some(pyproject) = settings_toml(ancestor)? {
|
||||||
let (root, settings) =
|
let (root, settings) =
|
||||||
|
@ -371,14 +387,14 @@ pub fn python_file_at_path(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check exclusions.
|
// Check exclusions.
|
||||||
Ok(!is_file_excluded(&path, &resolver, pyproject_strategy))
|
Ok(!is_file_excluded(&path, &resolver, pyproject_config))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `true` if the given top-level [`Path`] should be excluded.
|
/// Return `true` if the given top-level [`Path`] should be excluded.
|
||||||
fn is_file_excluded(
|
fn is_file_excluded(
|
||||||
path: &Path,
|
path: &Path,
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
pyproject_strategy: &PyprojectDiscovery,
|
pyproject_strategy: &PyprojectConfig,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// TODO(charlie): Respect gitignore.
|
// TODO(charlie): Respect gitignore.
|
||||||
for path in path.ancestors() {
|
for path in path.ancestors() {
|
||||||
|
@ -419,7 +435,7 @@ mod tests {
|
||||||
|
|
||||||
use crate::resolver::{
|
use crate::resolver::{
|
||||||
is_file_excluded, match_exclusion, resolve_settings_with_processor, NoOpProcessor,
|
is_file_excluded, match_exclusion, resolve_settings_with_processor, NoOpProcessor,
|
||||||
PyprojectDiscovery, Relativity, Resolver,
|
PyprojectConfig, PyprojectDiscoveryStrategy, Relativity, Resolver,
|
||||||
};
|
};
|
||||||
use crate::settings::pyproject::find_settings_toml;
|
use crate::settings::pyproject::find_settings_toml;
|
||||||
use crate::settings::types::FilePattern;
|
use crate::settings::types::FilePattern;
|
||||||
|
@ -560,25 +576,29 @@ mod tests {
|
||||||
fn rooted_exclusion() -> Result<()> {
|
fn rooted_exclusion() -> Result<()> {
|
||||||
let package_root = test_resource_path("package");
|
let package_root = test_resource_path("package");
|
||||||
let resolver = Resolver::default();
|
let resolver = Resolver::default();
|
||||||
let ppd = PyprojectDiscovery::Hierarchical(resolve_settings_with_processor(
|
let pyproject_config = PyprojectConfig::new(
|
||||||
&find_settings_toml(&package_root)?.unwrap(),
|
PyprojectDiscoveryStrategy::Hierarchical,
|
||||||
&Relativity::Parent,
|
resolve_settings_with_processor(
|
||||||
&NoOpProcessor,
|
&find_settings_toml(&package_root)?.unwrap(),
|
||||||
)?);
|
&Relativity::Parent,
|
||||||
|
&NoOpProcessor,
|
||||||
|
)?,
|
||||||
|
None,
|
||||||
|
);
|
||||||
// src/app.py should not be excluded even if it lives in a hierarchy that should
|
// src/app.py should not be excluded even if it lives in a hierarchy that should
|
||||||
// be excluded by virtue of the pyproject.toml having `resources/*` in
|
// be excluded by virtue of the pyproject.toml having `resources/*` in
|
||||||
// it.
|
// it.
|
||||||
assert!(!is_file_excluded(
|
assert!(!is_file_excluded(
|
||||||
&package_root.join("src/app.py"),
|
&package_root.join("src/app.py"),
|
||||||
&resolver,
|
&resolver,
|
||||||
&ppd,
|
&pyproject_config,
|
||||||
));
|
));
|
||||||
// However, resources/ignored.py should be ignored, since that `resources` is
|
// However, resources/ignored.py should be ignored, since that `resources` is
|
||||||
// beneath the package root.
|
// beneath the package root.
|
||||||
assert!(is_file_excluded(
|
assert!(is_file_excluded(
|
||||||
&package_root.join("resources/ignored.py"),
|
&package_root.join("resources/ignored.py"),
|
||||||
&resolver,
|
&resolver,
|
||||||
&ppd,
|
&pyproject_config,
|
||||||
));
|
));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use log::{debug, error};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
|
||||||
use ruff::linter::add_noqa_to_path;
|
use ruff::linter::add_noqa_to_path;
|
||||||
use ruff::resolver::PyprojectDiscovery;
|
use ruff::resolver::PyprojectConfig;
|
||||||
use ruff::{packaging, resolver, warn_user_once};
|
use ruff::{packaging, resolver, warn_user_once};
|
||||||
|
|
||||||
use crate::args::Overrides;
|
use crate::args::Overrides;
|
||||||
|
@ -15,12 +15,12 @@ use crate::args::Overrides;
|
||||||
/// Add `noqa` directives to a collection of files.
|
/// Add `noqa` directives to a collection of files.
|
||||||
pub fn add_noqa(
|
pub fn add_noqa(
|
||||||
files: &[PathBuf],
|
files: &[PathBuf],
|
||||||
pyproject_strategy: &PyprojectDiscovery,
|
pyproject_config: &PyprojectConfig,
|
||||||
overrides: &Overrides,
|
overrides: &Overrides,
|
||||||
) -> Result<usize> {
|
) -> Result<usize> {
|
||||||
// Collect all the files to check.
|
// Collect all the files to check.
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let (paths, resolver) = resolver::python_files_in_path(files, pyproject_strategy, overrides)?;
|
let (paths, resolver) = resolver::python_files_in_path(files, pyproject_config, overrides)?;
|
||||||
let duration = start.elapsed();
|
let duration = start.elapsed();
|
||||||
debug!("Identified files to lint in: {:?}", duration);
|
debug!("Identified files to lint in: {:?}", duration);
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ pub fn add_noqa(
|
||||||
.map(ignore::DirEntry::path)
|
.map(ignore::DirEntry::path)
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
&resolver,
|
&resolver,
|
||||||
pyproject_strategy,
|
pyproject_config,
|
||||||
);
|
);
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
@ -50,7 +50,7 @@ pub fn add_noqa(
|
||||||
.parent()
|
.parent()
|
||||||
.and_then(|parent| package_roots.get(parent))
|
.and_then(|parent| package_roots.get(parent))
|
||||||
.and_then(|package| *package);
|
.and_then(|package| *package);
|
||||||
let settings = resolver.resolve(path, pyproject_strategy);
|
let settings = resolver.resolve(path, pyproject_config);
|
||||||
match add_noqa_to_path(path, package, settings) {
|
match add_noqa_to_path(path, package, settings) {
|
||||||
Ok(count) => Some(count),
|
Ok(count) => Some(count),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
|
@ -12,7 +12,7 @@ use ruff_text_size::{TextRange, TextSize};
|
||||||
|
|
||||||
use ruff::message::Message;
|
use ruff::message::Message;
|
||||||
use ruff::registry::Rule;
|
use ruff::registry::Rule;
|
||||||
use ruff::resolver::PyprojectDiscovery;
|
use ruff::resolver::{PyprojectConfig, PyprojectDiscoveryStrategy};
|
||||||
use ruff::settings::{flags, AllSettings};
|
use ruff::settings::{flags, AllSettings};
|
||||||
use ruff::{fs, packaging, resolver, warn_user_once, IOError};
|
use ruff::{fs, packaging, resolver, warn_user_once, IOError};
|
||||||
use ruff_diagnostics::Diagnostic;
|
use ruff_diagnostics::Diagnostic;
|
||||||
|
@ -27,7 +27,7 @@ use crate::panic::catch_unwind;
|
||||||
/// Run the linter over a collection of files.
|
/// Run the linter over a collection of files.
|
||||||
pub fn run(
|
pub fn run(
|
||||||
files: &[PathBuf],
|
files: &[PathBuf],
|
||||||
pyproject_strategy: &PyprojectDiscovery,
|
pyproject_config: &PyprojectConfig,
|
||||||
overrides: &Overrides,
|
overrides: &Overrides,
|
||||||
cache: flags::Cache,
|
cache: flags::Cache,
|
||||||
noqa: flags::Noqa,
|
noqa: flags::Noqa,
|
||||||
|
@ -35,7 +35,7 @@ pub fn run(
|
||||||
) -> Result<Diagnostics> {
|
) -> Result<Diagnostics> {
|
||||||
// Collect all the Python files to check.
|
// Collect all the Python files to check.
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let (paths, resolver) = resolver::python_files_in_path(files, pyproject_strategy, overrides)?;
|
let (paths, resolver) = resolver::python_files_in_path(files, pyproject_config, overrides)?;
|
||||||
let duration = start.elapsed();
|
let duration = start.elapsed();
|
||||||
debug!("Identified files to lint in: {:?}", duration);
|
debug!("Identified files to lint in: {:?}", duration);
|
||||||
|
|
||||||
|
@ -52,12 +52,12 @@ pub fn run(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match &pyproject_strategy {
|
match pyproject_config.strategy {
|
||||||
PyprojectDiscovery::Fixed(settings) => {
|
PyprojectDiscoveryStrategy::Fixed => {
|
||||||
init_cache(&settings.cli.cache_dir);
|
init_cache(&pyproject_config.settings.cli.cache_dir);
|
||||||
}
|
}
|
||||||
PyprojectDiscovery::Hierarchical(default) => {
|
PyprojectDiscoveryStrategy::Hierarchical => {
|
||||||
for settings in std::iter::once(default).chain(resolver.iter()) {
|
for settings in std::iter::once(&pyproject_config.settings).chain(resolver.iter()) {
|
||||||
init_cache(&settings.cli.cache_dir);
|
init_cache(&settings.cli.cache_dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ pub fn run(
|
||||||
.map(ignore::DirEntry::path)
|
.map(ignore::DirEntry::path)
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
&resolver,
|
&resolver,
|
||||||
pyproject_strategy,
|
pyproject_config,
|
||||||
);
|
);
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
@ -86,7 +86,7 @@ pub fn run(
|
||||||
.parent()
|
.parent()
|
||||||
.and_then(|parent| package_roots.get(parent))
|
.and_then(|parent| package_roots.get(parent))
|
||||||
.and_then(|package| *package);
|
.and_then(|package| *package);
|
||||||
let settings = resolver.resolve_all(path, pyproject_strategy);
|
let settings = resolver.resolve_all(path, pyproject_config);
|
||||||
|
|
||||||
lint_path(path, package, settings, cache, noqa, autofix).map_err(|e| {
|
lint_path(path, package, settings, cache, noqa, autofix).map_err(|e| {
|
||||||
(Some(path.to_owned()), {
|
(Some(path.to_owned()), {
|
||||||
|
@ -116,7 +116,7 @@ pub fn run(
|
||||||
fs::relativize_path(path).bold(),
|
fs::relativize_path(path).bold(),
|
||||||
":".bold()
|
":".bold()
|
||||||
);
|
);
|
||||||
let settings = resolver.resolve(path, pyproject_strategy);
|
let settings = resolver.resolve(path, pyproject_config);
|
||||||
if settings.rules.enabled(Rule::IOError) {
|
if settings.rules.enabled(Rule::IOError) {
|
||||||
let file =
|
let file =
|
||||||
SourceFileBuilder::new(path.to_string_lossy().as_ref(), "").finish();
|
SourceFileBuilder::new(path.to_string_lossy().as_ref(), "").finish();
|
||||||
|
@ -196,7 +196,7 @@ mod test {
|
||||||
use path_absolutize::Absolutize;
|
use path_absolutize::Absolutize;
|
||||||
|
|
||||||
use ruff::logging::LogLevel;
|
use ruff::logging::LogLevel;
|
||||||
use ruff::resolver::PyprojectDiscovery;
|
use ruff::resolver::{PyprojectConfig, PyprojectDiscoveryStrategy};
|
||||||
use ruff::settings::configuration::{Configuration, RuleSelection};
|
use ruff::settings::configuration::{Configuration, RuleSelection};
|
||||||
use ruff::settings::flags::FixMode;
|
use ruff::settings::flags::FixMode;
|
||||||
use ruff::settings::flags::{Cache, Noqa};
|
use ruff::settings::flags::{Cache, Noqa};
|
||||||
|
@ -238,7 +238,11 @@ mod test {
|
||||||
|
|
||||||
let diagnostics = run(
|
let diagnostics = run(
|
||||||
&[root_path.join("valid.ipynb")],
|
&[root_path.join("valid.ipynb")],
|
||||||
&PyprojectDiscovery::Fixed(AllSettings::from_configuration(configuration, &root_path)?),
|
&PyprojectConfig::new(
|
||||||
|
PyprojectDiscoveryStrategy::Fixed,
|
||||||
|
AllSettings::from_configuration(configuration, &root_path)?,
|
||||||
|
None,
|
||||||
|
),
|
||||||
&overrides,
|
&overrides,
|
||||||
Cache::Disabled,
|
Cache::Disabled,
|
||||||
Noqa::Enabled,
|
Noqa::Enabled,
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::path::Path;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use ruff::resolver::PyprojectDiscovery;
|
use ruff::resolver::PyprojectConfig;
|
||||||
use ruff::settings::flags;
|
use ruff::settings::flags;
|
||||||
use ruff::{packaging, resolver};
|
use ruff::{packaging, resolver};
|
||||||
|
|
||||||
|
@ -20,22 +20,28 @@ fn read_from_stdin() -> Result<String> {
|
||||||
/// Run the linter over a single file, read from `stdin`.
|
/// Run the linter over a single file, read from `stdin`.
|
||||||
pub fn run_stdin(
|
pub fn run_stdin(
|
||||||
filename: Option<&Path>,
|
filename: Option<&Path>,
|
||||||
pyproject_strategy: &PyprojectDiscovery,
|
pyproject_config: &PyprojectConfig,
|
||||||
overrides: &Overrides,
|
overrides: &Overrides,
|
||||||
noqa: flags::Noqa,
|
noqa: flags::Noqa,
|
||||||
autofix: flags::FixMode,
|
autofix: flags::FixMode,
|
||||||
) -> Result<Diagnostics> {
|
) -> Result<Diagnostics> {
|
||||||
if let Some(filename) = filename {
|
if let Some(filename) = filename {
|
||||||
if !resolver::python_file_at_path(filename, pyproject_strategy, overrides)? {
|
if !resolver::python_file_at_path(filename, pyproject_config, overrides)? {
|
||||||
return Ok(Diagnostics::default());
|
return Ok(Diagnostics::default());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let settings = pyproject_strategy.top_level_settings();
|
let package_root = filename.and_then(Path::parent).and_then(|path| {
|
||||||
let package_root = filename
|
packaging::detect_package_root(path, &pyproject_config.settings.lib.namespace_packages)
|
||||||
.and_then(Path::parent)
|
});
|
||||||
.and_then(|path| packaging::detect_package_root(path, &settings.lib.namespace_packages));
|
|
||||||
let stdin = read_from_stdin()?;
|
let stdin = read_from_stdin()?;
|
||||||
let mut diagnostics = lint_stdin(filename, package_root, &stdin, &settings.lib, noqa, autofix)?;
|
let mut diagnostics = lint_stdin(
|
||||||
|
filename,
|
||||||
|
package_root,
|
||||||
|
&stdin,
|
||||||
|
&pyproject_config.settings.lib,
|
||||||
|
noqa,
|
||||||
|
autofix,
|
||||||
|
)?;
|
||||||
diagnostics.messages.sort_unstable();
|
diagnostics.messages.sort_unstable();
|
||||||
Ok(diagnostics)
|
Ok(diagnostics)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::path::PathBuf;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use ruff::resolver::PyprojectDiscovery;
|
use ruff::resolver::PyprojectConfig;
|
||||||
use ruff::{resolver, warn_user_once};
|
use ruff::{resolver, warn_user_once};
|
||||||
|
|
||||||
use crate::args::Overrides;
|
use crate::args::Overrides;
|
||||||
|
@ -12,11 +12,11 @@ use crate::args::Overrides;
|
||||||
/// Show the list of files to be checked based on current settings.
|
/// Show the list of files to be checked based on current settings.
|
||||||
pub fn show_files(
|
pub fn show_files(
|
||||||
files: &[PathBuf],
|
files: &[PathBuf],
|
||||||
pyproject_strategy: &PyprojectDiscovery,
|
pyproject_config: &PyprojectConfig,
|
||||||
overrides: &Overrides,
|
overrides: &Overrides,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Collect all files in the hierarchy.
|
// Collect all files in the hierarchy.
|
||||||
let (paths, _resolver) = resolver::python_files_in_path(files, pyproject_strategy, overrides)?;
|
let (paths, _resolver) = resolver::python_files_in_path(files, pyproject_config, overrides)?;
|
||||||
|
|
||||||
if paths.is_empty() {
|
if paths.is_empty() {
|
||||||
warn_user_once!("No Python files found under the given path(s)");
|
warn_user_once!("No Python files found under the given path(s)");
|
||||||
|
|
|
@ -5,18 +5,18 @@ use anyhow::{bail, Result};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use ruff::resolver;
|
use ruff::resolver;
|
||||||
use ruff::resolver::PyprojectDiscovery;
|
use ruff::resolver::PyprojectConfig;
|
||||||
|
|
||||||
use crate::args::Overrides;
|
use crate::args::Overrides;
|
||||||
|
|
||||||
/// Print the user-facing configuration settings.
|
/// Print the user-facing configuration settings.
|
||||||
pub fn show_settings(
|
pub fn show_settings(
|
||||||
files: &[PathBuf],
|
files: &[PathBuf],
|
||||||
pyproject_strategy: &PyprojectDiscovery,
|
pyproject_config: &PyprojectConfig,
|
||||||
overrides: &Overrides,
|
overrides: &Overrides,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Collect all files in the hierarchy.
|
// Collect all files in the hierarchy.
|
||||||
let (paths, resolver) = resolver::python_files_in_path(files, pyproject_strategy, overrides)?;
|
let (paths, resolver) = resolver::python_files_in_path(files, pyproject_config, overrides)?;
|
||||||
|
|
||||||
// Print the list of files.
|
// Print the list of files.
|
||||||
let Some(entry) = paths
|
let Some(entry) = paths
|
||||||
|
@ -26,10 +26,13 @@ pub fn show_settings(
|
||||||
bail!("No files found under the given path");
|
bail!("No files found under the given path");
|
||||||
};
|
};
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
let settings = resolver.resolve(path, pyproject_strategy);
|
let settings = resolver.resolve(path, pyproject_config);
|
||||||
|
|
||||||
let mut stdout = BufWriter::new(io::stdout().lock());
|
let mut stdout = BufWriter::new(io::stdout().lock());
|
||||||
writeln!(stdout, "Resolved settings for: {path:?}")?;
|
writeln!(stdout, "Resolved settings for: {path:?}")?;
|
||||||
|
if let Some(settings_path) = pyproject_config.path.as_ref() {
|
||||||
|
writeln!(stdout, "Settings path: {settings_path:?}")?;
|
||||||
|
}
|
||||||
writeln!(stdout, "{settings:#?}")?;
|
writeln!(stdout, "{settings:#?}")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -97,7 +97,7 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
|
|
||||||
// Construct the "default" settings. These are used when no `pyproject.toml`
|
// Construct the "default" settings. These are used when no `pyproject.toml`
|
||||||
// files are present, or files are injected from outside of the hierarchy.
|
// files are present, or files are injected from outside of the hierarchy.
|
||||||
let pyproject_strategy = resolve::resolve(
|
let pyproject_config = resolve::resolve(
|
||||||
cli.isolated,
|
cli.isolated,
|
||||||
cli.config.as_deref(),
|
cli.config.as_deref(),
|
||||||
&overrides,
|
&overrides,
|
||||||
|
@ -105,16 +105,14 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if cli.show_settings {
|
if cli.show_settings {
|
||||||
commands::show_settings::show_settings(&cli.files, &pyproject_strategy, &overrides)?;
|
commands::show_settings::show_settings(&cli.files, &pyproject_config, &overrides)?;
|
||||||
return Ok(ExitStatus::Success);
|
return Ok(ExitStatus::Success);
|
||||||
}
|
}
|
||||||
if cli.show_files {
|
if cli.show_files {
|
||||||
commands::show_files::show_files(&cli.files, &pyproject_strategy, &overrides)?;
|
commands::show_files::show_files(&cli.files, &pyproject_config, &overrides)?;
|
||||||
return Ok(ExitStatus::Success);
|
return Ok(ExitStatus::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
let top_level_settings = pyproject_strategy.top_level_settings();
|
|
||||||
|
|
||||||
// Extract options that are included in `Settings`, but only apply at the top
|
// Extract options that are included in `Settings`, but only apply at the top
|
||||||
// level.
|
// level.
|
||||||
let CliSettings {
|
let CliSettings {
|
||||||
|
@ -124,7 +122,7 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
show_fixes,
|
show_fixes,
|
||||||
update_check,
|
update_check,
|
||||||
..
|
..
|
||||||
} = top_level_settings.cli.clone();
|
} = pyproject_config.settings.cli;
|
||||||
|
|
||||||
// Autofix rules are as follows:
|
// Autofix rules are as follows:
|
||||||
// - If `--fix` or `--fix-only` is set, always apply fixes to the filesystem (or
|
// - If `--fix` or `--fix-only` is set, always apply fixes to the filesystem (or
|
||||||
|
@ -155,7 +153,7 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
printer_flags |= PrinterFlags::SHOW_FIXES;
|
printer_flags |= PrinterFlags::SHOW_FIXES;
|
||||||
}
|
}
|
||||||
|
|
||||||
if top_level_settings.lib.show_source {
|
if pyproject_config.settings.lib.show_source {
|
||||||
printer_flags |= PrinterFlags::SHOW_SOURCE;
|
printer_flags |= PrinterFlags::SHOW_SOURCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +169,7 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
warn_user_once!("--fix is incompatible with --add-noqa.");
|
warn_user_once!("--fix is incompatible with --add-noqa.");
|
||||||
}
|
}
|
||||||
let modifications =
|
let modifications =
|
||||||
commands::add_noqa::add_noqa(&cli.files, &pyproject_strategy, &overrides)?;
|
commands::add_noqa::add_noqa(&cli.files, &pyproject_config, &overrides)?;
|
||||||
if modifications > 0 && log_level >= LogLevel::Default {
|
if modifications > 0 && log_level >= LogLevel::Default {
|
||||||
let s = if modifications == 1 { "" } else { "s" };
|
let s = if modifications == 1 { "" } else { "s" };
|
||||||
#[allow(clippy::print_stderr)]
|
#[allow(clippy::print_stderr)]
|
||||||
|
@ -195,7 +193,7 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
|
|
||||||
let messages = commands::run::run(
|
let messages = commands::run::run(
|
||||||
&cli.files,
|
&cli.files,
|
||||||
&pyproject_strategy,
|
&pyproject_config,
|
||||||
&overrides,
|
&overrides,
|
||||||
cache.into(),
|
cache.into(),
|
||||||
noqa.into(),
|
noqa.into(),
|
||||||
|
@ -225,7 +223,7 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
|
|
||||||
let messages = commands::run::run(
|
let messages = commands::run::run(
|
||||||
&cli.files,
|
&cli.files,
|
||||||
&pyproject_strategy,
|
&pyproject_config,
|
||||||
&overrides,
|
&overrides,
|
||||||
cache.into(),
|
cache.into(),
|
||||||
noqa.into(),
|
noqa.into(),
|
||||||
|
@ -244,7 +242,7 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
let diagnostics = if is_stdin {
|
let diagnostics = if is_stdin {
|
||||||
commands::run_stdin::run_stdin(
|
commands::run_stdin::run_stdin(
|
||||||
cli.stdin_filename.map(fs::normalize_path).as_deref(),
|
cli.stdin_filename.map(fs::normalize_path).as_deref(),
|
||||||
&pyproject_strategy,
|
&pyproject_config,
|
||||||
&overrides,
|
&overrides,
|
||||||
noqa.into(),
|
noqa.into(),
|
||||||
autofix,
|
autofix,
|
||||||
|
@ -252,7 +250,7 @@ fn check(args: CheckArgs, log_level: LogLevel) -> Result<ExitStatus> {
|
||||||
} else {
|
} else {
|
||||||
commands::run::run(
|
commands::run::run(
|
||||||
&cli.files,
|
&cli.files,
|
||||||
&pyproject_strategy,
|
&pyproject_config,
|
||||||
&overrides,
|
&overrides,
|
||||||
cache.into(),
|
cache.into(),
|
||||||
noqa.into(),
|
noqa.into(),
|
||||||
|
|
|
@ -4,7 +4,8 @@ use anyhow::Result;
|
||||||
use path_absolutize::path_dedot;
|
use path_absolutize::path_dedot;
|
||||||
|
|
||||||
use ruff::resolver::{
|
use ruff::resolver::{
|
||||||
resolve_settings_with_processor, ConfigProcessor, PyprojectDiscovery, Relativity,
|
resolve_settings_with_processor, ConfigProcessor, PyprojectConfig, PyprojectDiscoveryStrategy,
|
||||||
|
Relativity,
|
||||||
};
|
};
|
||||||
use ruff::settings::configuration::Configuration;
|
use ruff::settings::configuration::Configuration;
|
||||||
use ruff::settings::{pyproject, AllSettings};
|
use ruff::settings::{pyproject, AllSettings};
|
||||||
|
@ -18,13 +19,17 @@ pub fn resolve(
|
||||||
config: Option<&Path>,
|
config: Option<&Path>,
|
||||||
overrides: &Overrides,
|
overrides: &Overrides,
|
||||||
stdin_filename: Option<&Path>,
|
stdin_filename: Option<&Path>,
|
||||||
) -> Result<PyprojectDiscovery> {
|
) -> Result<PyprojectConfig> {
|
||||||
// First priority: if we're running in isolated mode, use the default settings.
|
// First priority: if we're running in isolated mode, use the default settings.
|
||||||
if isolated {
|
if isolated {
|
||||||
let mut config = Configuration::default();
|
let mut config = Configuration::default();
|
||||||
overrides.process_config(&mut config);
|
overrides.process_config(&mut config);
|
||||||
let settings = AllSettings::from_configuration(config, &path_dedot::CWD)?;
|
let settings = AllSettings::from_configuration(config, &path_dedot::CWD)?;
|
||||||
return Ok(PyprojectDiscovery::Fixed(settings));
|
return Ok(PyprojectConfig::new(
|
||||||
|
PyprojectDiscoveryStrategy::Fixed,
|
||||||
|
settings,
|
||||||
|
None,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second priority: the user specified a `pyproject.toml` file. Use that
|
// Second priority: the user specified a `pyproject.toml` file. Use that
|
||||||
|
@ -36,7 +41,11 @@ pub fn resolve(
|
||||||
.transpose()?
|
.transpose()?
|
||||||
{
|
{
|
||||||
let settings = resolve_settings_with_processor(&pyproject, &Relativity::Cwd, overrides)?;
|
let settings = resolve_settings_with_processor(&pyproject, &Relativity::Cwd, overrides)?;
|
||||||
return Ok(PyprojectDiscovery::Fixed(settings));
|
return Ok(PyprojectConfig::new(
|
||||||
|
PyprojectDiscoveryStrategy::Fixed,
|
||||||
|
settings,
|
||||||
|
Some(pyproject),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Third priority: find a `pyproject.toml` file in either an ancestor of
|
// Third priority: find a `pyproject.toml` file in either an ancestor of
|
||||||
|
@ -50,7 +59,11 @@ pub fn resolve(
|
||||||
.unwrap_or(&path_dedot::CWD.as_path()),
|
.unwrap_or(&path_dedot::CWD.as_path()),
|
||||||
)? {
|
)? {
|
||||||
let settings = resolve_settings_with_processor(&pyproject, &Relativity::Parent, overrides)?;
|
let settings = resolve_settings_with_processor(&pyproject, &Relativity::Parent, overrides)?;
|
||||||
return Ok(PyprojectDiscovery::Hierarchical(settings));
|
return Ok(PyprojectConfig::new(
|
||||||
|
PyprojectDiscoveryStrategy::Hierarchical,
|
||||||
|
settings,
|
||||||
|
Some(pyproject),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fourth priority: find a user-specific `pyproject.toml`, but resolve all paths
|
// Fourth priority: find a user-specific `pyproject.toml`, but resolve all paths
|
||||||
|
@ -59,7 +72,11 @@ pub fn resolve(
|
||||||
// these act as the "default" settings.)
|
// these act as the "default" settings.)
|
||||||
if let Some(pyproject) = pyproject::find_user_settings_toml() {
|
if let Some(pyproject) = pyproject::find_user_settings_toml() {
|
||||||
let settings = resolve_settings_with_processor(&pyproject, &Relativity::Cwd, overrides)?;
|
let settings = resolve_settings_with_processor(&pyproject, &Relativity::Cwd, overrides)?;
|
||||||
return Ok(PyprojectDiscovery::Hierarchical(settings));
|
return Ok(PyprojectConfig::new(
|
||||||
|
PyprojectDiscoveryStrategy::Hierarchical,
|
||||||
|
settings,
|
||||||
|
Some(pyproject),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: load Ruff's default settings, and resolve all paths relative to the
|
// Fallback: load Ruff's default settings, and resolve all paths relative to the
|
||||||
|
@ -69,5 +86,9 @@ pub fn resolve(
|
||||||
let mut config = Configuration::default();
|
let mut config = Configuration::default();
|
||||||
overrides.process_config(&mut config);
|
overrides.process_config(&mut config);
|
||||||
let settings = AllSettings::from_configuration(config, &path_dedot::CWD)?;
|
let settings = AllSettings::from_configuration(config, &path_dedot::CWD)?;
|
||||||
Ok(PyprojectDiscovery::Hierarchical(settings))
|
Ok(PyprojectConfig::new(
|
||||||
|
PyprojectDiscoveryStrategy::Hierarchical,
|
||||||
|
settings,
|
||||||
|
None,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue