mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 22:31:47 +00:00
[ty] Add --config-file CLI arg (#18083)
This commit is contained in:
parent
6453ac9ea1
commit
8d5655a7ba
12 changed files with 300 additions and 63 deletions
|
@ -1,5 +1,5 @@
|
|||
use crate::db::{Db, ProjectDatabase};
|
||||
use crate::metadata::options::Options;
|
||||
use crate::metadata::options::ProjectOptionsOverrides;
|
||||
use crate::watch::{ChangeEvent, CreatedKind, DeletedKind};
|
||||
use crate::{Project, ProjectMetadata};
|
||||
use std::collections::BTreeSet;
|
||||
|
@ -12,10 +12,18 @@ use rustc_hash::FxHashSet;
|
|||
use ty_python_semantic::Program;
|
||||
|
||||
impl ProjectDatabase {
|
||||
#[tracing::instrument(level = "debug", skip(self, changes, cli_options))]
|
||||
pub fn apply_changes(&mut self, changes: Vec<ChangeEvent>, cli_options: Option<&Options>) {
|
||||
#[tracing::instrument(level = "debug", skip(self, changes, project_options_overrides))]
|
||||
pub fn apply_changes(
|
||||
&mut self,
|
||||
changes: Vec<ChangeEvent>,
|
||||
project_options_overrides: Option<&ProjectOptionsOverrides>,
|
||||
) {
|
||||
let mut project = self.project();
|
||||
let project_root = project.root(self).to_path_buf();
|
||||
let config_file_override =
|
||||
project_options_overrides.and_then(|options| options.config_file_override.clone());
|
||||
let options =
|
||||
project_options_overrides.map(|project_options| project_options.options.clone());
|
||||
let program = Program::get(self);
|
||||
let custom_stdlib_versions_path = program
|
||||
.custom_stdlib_search_path(self)
|
||||
|
@ -42,6 +50,14 @@ impl ProjectDatabase {
|
|||
tracing::trace!("Handle change: {:?}", change);
|
||||
|
||||
if let Some(path) = change.system_path() {
|
||||
if let Some(config_file) = &config_file_override {
|
||||
if config_file.as_path() == path {
|
||||
project_changed = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(
|
||||
path.file_name(),
|
||||
Some(".gitignore" | ".ignore" | "ty.toml" | "pyproject.toml")
|
||||
|
@ -170,10 +186,14 @@ impl ProjectDatabase {
|
|||
}
|
||||
|
||||
if project_changed {
|
||||
match ProjectMetadata::discover(&project_root, self.system()) {
|
||||
let new_project_metadata = match config_file_override {
|
||||
Some(config_file) => ProjectMetadata::from_config_file(config_file, self.system()),
|
||||
None => ProjectMetadata::discover(&project_root, self.system()),
|
||||
};
|
||||
match new_project_metadata {
|
||||
Ok(mut metadata) => {
|
||||
if let Some(cli_options) = cli_options {
|
||||
metadata.apply_cli_options(cli_options.clone());
|
||||
if let Some(cli_options) = options {
|
||||
metadata.apply_options(cli_options);
|
||||
}
|
||||
|
||||
if let Err(error) = metadata.apply_configuration_files(self.system()) {
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::walk::{ProjectFilesFilter, ProjectFilesWalker};
|
|||
pub use db::{Db, ProjectDatabase};
|
||||
use files::{Index, Indexed, IndexedFiles};
|
||||
use metadata::settings::Settings;
|
||||
pub use metadata::{ProjectDiscoveryError, ProjectMetadata};
|
||||
pub use metadata::{ProjectMetadata, ProjectMetadataError};
|
||||
use ruff_db::diagnostic::{
|
||||
Annotation, Diagnostic, DiagnosticId, Severity, Span, SubDiagnostic, create_parse_diagnostic,
|
||||
create_unsupported_syntax_diagnostic,
|
||||
|
|
|
@ -48,6 +48,29 @@ impl ProjectMetadata {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_config_file(
|
||||
path: SystemPathBuf,
|
||||
system: &dyn System,
|
||||
) -> Result<Self, ProjectMetadataError> {
|
||||
tracing::debug!("Using overridden configuration file at '{path}'");
|
||||
|
||||
let config_file = ConfigurationFile::from_path(path.clone(), system).map_err(|error| {
|
||||
ProjectMetadataError::ConfigurationFileError {
|
||||
source: Box::new(error),
|
||||
path: path.clone(),
|
||||
}
|
||||
})?;
|
||||
|
||||
let options = config_file.into_options();
|
||||
|
||||
Ok(Self {
|
||||
name: Name::new(system.current_directory().file_name().unwrap_or("root")),
|
||||
root: system.current_directory().to_path_buf(),
|
||||
options,
|
||||
extra_configuration_paths: vec![path],
|
||||
})
|
||||
}
|
||||
|
||||
/// Loads a project from a `pyproject.toml` file.
|
||||
pub(crate) fn from_pyproject(
|
||||
pyproject: PyProject,
|
||||
|
@ -106,11 +129,11 @@ impl ProjectMetadata {
|
|||
pub fn discover(
|
||||
path: &SystemPath,
|
||||
system: &dyn System,
|
||||
) -> Result<ProjectMetadata, ProjectDiscoveryError> {
|
||||
) -> Result<ProjectMetadata, ProjectMetadataError> {
|
||||
tracing::debug!("Searching for a project in '{path}'");
|
||||
|
||||
if !system.is_directory(path) {
|
||||
return Err(ProjectDiscoveryError::NotADirectory(path.to_path_buf()));
|
||||
return Err(ProjectMetadataError::NotADirectory(path.to_path_buf()));
|
||||
}
|
||||
|
||||
let mut closest_project: Option<ProjectMetadata> = None;
|
||||
|
@ -125,7 +148,7 @@ impl ProjectMetadata {
|
|||
) {
|
||||
Ok(pyproject) => Some(pyproject),
|
||||
Err(error) => {
|
||||
return Err(ProjectDiscoveryError::InvalidPyProject {
|
||||
return Err(ProjectMetadataError::InvalidPyProject {
|
||||
path: pyproject_path,
|
||||
source: Box::new(error),
|
||||
});
|
||||
|
@ -144,7 +167,7 @@ impl ProjectMetadata {
|
|||
) {
|
||||
Ok(options) => options,
|
||||
Err(error) => {
|
||||
return Err(ProjectDiscoveryError::InvalidTyToml {
|
||||
return Err(ProjectMetadataError::InvalidTyToml {
|
||||
path: ty_toml_path,
|
||||
source: Box::new(error),
|
||||
});
|
||||
|
@ -171,7 +194,7 @@ impl ProjectMetadata {
|
|||
.and_then(|pyproject| pyproject.project.as_ref()),
|
||||
)
|
||||
.map_err(|err| {
|
||||
ProjectDiscoveryError::InvalidRequiresPythonConstraint {
|
||||
ProjectMetadataError::InvalidRequiresPythonConstraint {
|
||||
source: err,
|
||||
path: pyproject_path,
|
||||
}
|
||||
|
@ -185,7 +208,7 @@ impl ProjectMetadata {
|
|||
let metadata =
|
||||
ProjectMetadata::from_pyproject(pyproject, project_root.to_path_buf())
|
||||
.map_err(
|
||||
|err| ProjectDiscoveryError::InvalidRequiresPythonConstraint {
|
||||
|err| ProjectMetadataError::InvalidRequiresPythonConstraint {
|
||||
source: err,
|
||||
path: pyproject_path,
|
||||
},
|
||||
|
@ -249,7 +272,7 @@ impl ProjectMetadata {
|
|||
}
|
||||
|
||||
/// Combine the project options with the CLI options where the CLI options take precedence.
|
||||
pub fn apply_cli_options(&mut self, options: Options) {
|
||||
pub fn apply_options(&mut self, options: Options) {
|
||||
self.options = options.combine(std::mem::take(&mut self.options));
|
||||
}
|
||||
|
||||
|
@ -282,7 +305,7 @@ impl ProjectMetadata {
|
|||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ProjectDiscoveryError {
|
||||
pub enum ProjectMetadataError {
|
||||
#[error("project path '{0}' is not a directory")]
|
||||
NotADirectory(SystemPathBuf),
|
||||
|
||||
|
@ -303,6 +326,12 @@ pub enum ProjectDiscoveryError {
|
|||
source: ResolveRequiresPythonError,
|
||||
path: SystemPathBuf,
|
||||
},
|
||||
|
||||
#[error("Error loading configuration file at {path}: {source}")]
|
||||
ConfigurationFileError {
|
||||
source: Box<ConfigurationFileError>,
|
||||
path: SystemPathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -314,7 +343,7 @@ mod tests {
|
|||
use ruff_db::system::{SystemPathBuf, TestSystem};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
|
||||
use crate::{ProjectDiscoveryError, ProjectMetadata};
|
||||
use crate::{ProjectMetadata, ProjectMetadataError};
|
||||
|
||||
#[test]
|
||||
fn project_without_pyproject() -> anyhow::Result<()> {
|
||||
|
@ -1076,7 +1105,7 @@ expected `.`, `]`
|
|||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_error_eq(error: &ProjectDiscoveryError, message: &str) {
|
||||
fn assert_error_eq(error: &ProjectMetadataError, message: &str) {
|
||||
assert_eq!(error.to_string().replace('\\', "/"), message);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,25 @@ pub(crate) struct ConfigurationFile {
|
|||
}
|
||||
|
||||
impl ConfigurationFile {
|
||||
pub(crate) fn from_path(
|
||||
path: SystemPathBuf,
|
||||
system: &dyn System,
|
||||
) -> Result<Self, ConfigurationFileError> {
|
||||
let ty_toml_str = system.read_to_string(&path).map_err(|source| {
|
||||
ConfigurationFileError::FileReadError {
|
||||
source,
|
||||
path: path.clone(),
|
||||
}
|
||||
})?;
|
||||
|
||||
match Options::from_toml_str(&ty_toml_str, ValueSource::File(Arc::new(path.clone()))) {
|
||||
Ok(options) => Ok(Self { path, options }),
|
||||
Err(error) => Err(ConfigurationFileError::InvalidTyToml {
|
||||
source: Box::new(error),
|
||||
path,
|
||||
}),
|
||||
}
|
||||
}
|
||||
/// Loads the user-level configuration file if it exists.
|
||||
///
|
||||
/// Returns `None` if the file does not exist or if the concept of user-level configurations
|
||||
|
@ -66,4 +85,10 @@ pub enum ConfigurationFileError {
|
|||
source: Box<TyTomlError>,
|
||||
path: SystemPathBuf,
|
||||
},
|
||||
#[error("Failed to read `{path}`: {source}")]
|
||||
FileReadError {
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
path: SystemPathBuf,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::Db;
|
|||
use crate::metadata::value::{RangedValue, RelativePathBuf, ValueSource, ValueSourceGuard};
|
||||
use ruff_db::diagnostic::{Annotation, Diagnostic, DiagnosticFormat, DiagnosticId, Severity, Span};
|
||||
use ruff_db::files::system_path_to_file;
|
||||
use ruff_db::system::{System, SystemPath};
|
||||
use ruff_db::system::{System, SystemPath, SystemPathBuf};
|
||||
use ruff_macros::{Combine, OptionsMetadata};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
@ -575,3 +575,20 @@ impl OptionDiagnostic {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a wrapper for options that actually get loaded from configuration files
|
||||
/// and the CLI, which also includes a `config_file_override` option that overrides
|
||||
/// default configuration discovery with an explicitly-provided path to a configuration file
|
||||
pub struct ProjectOptionsOverrides {
|
||||
pub config_file_override: Option<SystemPathBuf>,
|
||||
pub options: Options,
|
||||
}
|
||||
|
||||
impl ProjectOptionsOverrides {
|
||||
pub fn new(config_file_override: Option<SystemPathBuf>, options: Options) -> Self {
|
||||
Self {
|
||||
config_file_override,
|
||||
options,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue