mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-07 21:25:08 +00:00
[ty] Add --config CLI arg (#17697)
This commit is contained in:
parent
6c177e2bbe
commit
f46ed8d410
3 changed files with 186 additions and 4 deletions
|
@ -1,9 +1,11 @@
|
|||
use crate::logging::Verbosity;
|
||||
use crate::python_version::PythonVersion;
|
||||
use clap::error::ErrorKind;
|
||||
use clap::{ArgAction, ArgMatches, Error, Parser};
|
||||
use ruff_db::system::SystemPathBuf;
|
||||
use ty_project::combine::Combine;
|
||||
use ty_project::metadata::options::{EnvironmentOptions, Options, TerminalOptions};
|
||||
use ty_project::metadata::value::{RangedValue, RelativePathBuf};
|
||||
use ty_project::metadata::value::{RangedValue, RelativePathBuf, ValueSource};
|
||||
use ty_python_semantic::lint;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
|
@ -14,6 +16,7 @@ pub(crate) struct Args {
|
|||
pub(crate) command: Command,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
pub(crate) enum Command {
|
||||
/// Check a project for type errors.
|
||||
|
@ -86,6 +89,9 @@ pub(crate) struct CheckCommand {
|
|||
#[clap(flatten)]
|
||||
pub(crate) rules: RulesArg,
|
||||
|
||||
#[clap(flatten)]
|
||||
pub(crate) config: ConfigsArg,
|
||||
|
||||
/// The format to use for printing diagnostic messages.
|
||||
#[arg(long)]
|
||||
pub(crate) output_format: Option<OutputFormat>,
|
||||
|
@ -140,7 +146,7 @@ impl CheckCommand {
|
|||
.no_respect_ignore_files
|
||||
.then_some(false)
|
||||
.or(self.respect_ignore_files);
|
||||
Options {
|
||||
let options = Options {
|
||||
environment: Some(EnvironmentOptions {
|
||||
python_version: self
|
||||
.python_version
|
||||
|
@ -166,7 +172,9 @@ impl CheckCommand {
|
|||
rules,
|
||||
respect_ignore_files,
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
// Merge with options passed in via --config
|
||||
options.combine(self.config.into_options().unwrap_or_default())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -299,3 +307,55 @@ pub(crate) enum TerminalColor {
|
|||
/// Never display colors.
|
||||
Never,
|
||||
}
|
||||
/// A TOML `<KEY> = <VALUE>` pair
|
||||
/// (such as you might find in a `ty.toml` configuration file)
|
||||
/// overriding a specific configuration option.
|
||||
/// Overrides of individual settings using this option always take precedence
|
||||
/// over all configuration files.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ConfigsArg(Option<Options>);
|
||||
|
||||
impl clap::FromArgMatches for ConfigsArg {
|
||||
fn from_arg_matches(matches: &ArgMatches) -> Result<Self, Error> {
|
||||
let combined = matches
|
||||
.get_many::<String>("config")
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|s| {
|
||||
Options::from_toml_str(s, ValueSource::Cli)
|
||||
.map_err(|err| Error::raw(ErrorKind::InvalidValue, err.to_string()))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
.into_iter()
|
||||
.reduce(|acc, item| item.combine(acc));
|
||||
Ok(Self(combined))
|
||||
}
|
||||
|
||||
fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), Error> {
|
||||
self.0 = Self::from_arg_matches(matches)?.0;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl clap::Args for ConfigsArg {
|
||||
fn augment_args(cmd: clap::Command) -> clap::Command {
|
||||
cmd.arg(
|
||||
clap::Arg::new("config")
|
||||
.short('c')
|
||||
.long("config")
|
||||
.value_name("CONFIG_OPTION")
|
||||
.help("A TOML `<KEY> = <VALUE>` pair")
|
||||
.action(ArgAction::Append),
|
||||
)
|
||||
}
|
||||
|
||||
fn augment_args_for_update(cmd: clap::Command) -> clap::Command {
|
||||
Self::augment_args(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigsArg {
|
||||
pub(crate) fn into_options(self) -> Option<Options> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1338,6 +1338,128 @@ fn defaults_to_a_new_python_version() -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_config_args_toml_string_basic() -> anyhow::Result<()> {
|
||||
let case = TestCase::with_file("test.py", r"print(x) # [unresolved-reference]")?;
|
||||
|
||||
// Long flag
|
||||
assert_cmd_snapshot!(case.command().arg("--warn").arg("unresolved-reference").arg("--config").arg("terminal.error-on-warning=true"), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
warning: lint:unresolved-reference: Name `x` used when not defined
|
||||
--> test.py:1:7
|
||||
|
|
||||
1 | print(x) # [unresolved-reference]
|
||||
| ^
|
||||
|
|
||||
info: `lint:unresolved-reference` was selected on the command line
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
// Short flag
|
||||
assert_cmd_snapshot!(case.command().arg("-c").arg("terminal.error-on-warning=true"), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error: lint:unresolved-reference: Name `x` used when not defined
|
||||
--> test.py:1:7
|
||||
|
|
||||
1 | print(x) # [unresolved-reference]
|
||||
| ^
|
||||
|
|
||||
info: `lint:unresolved-reference` is enabled by default
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_config_args_overrides_knot_toml() -> anyhow::Result<()> {
|
||||
let case = TestCase::with_files(vec![
|
||||
(
|
||||
"knot.toml",
|
||||
r#"
|
||||
[terminal]
|
||||
error-on-warning = true
|
||||
"#,
|
||||
),
|
||||
("test.py", r"print(x) # [unresolved-reference]"),
|
||||
])?;
|
||||
assert_cmd_snapshot!(case.command().arg("--warn").arg("unresolved-reference").arg("--config").arg("terminal.error-on-warning=false"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
warning: lint:unresolved-reference: Name `x` used when not defined
|
||||
--> test.py:1:7
|
||||
|
|
||||
1 | print(x) # [unresolved-reference]
|
||||
| ^
|
||||
|
|
||||
info: `lint:unresolved-reference` was selected on the command line
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_config_args_later_overrides_earlier() -> anyhow::Result<()> {
|
||||
let case = TestCase::with_file("test.py", r"print(x) # [unresolved-reference]")?;
|
||||
assert_cmd_snapshot!(case.command().arg("--warn").arg("unresolved-reference").arg("--config").arg("terminal.error-on-warning=true").arg("--config").arg("terminal.error-on-warning=false"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
warning: lint:unresolved-reference: Name `x` used when not defined
|
||||
--> test.py:1:7
|
||||
|
|
||||
1 | print(x) # [unresolved-reference]
|
||||
| ^
|
||||
|
|
||||
info: `lint:unresolved-reference` was selected on the command line
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_config_args_invalid_option() -> anyhow::Result<()> {
|
||||
let case = TestCase::with_file("test.py", r"print(1)")?;
|
||||
assert_cmd_snapshot!(case.command().arg("--config").arg("bad-option=true"), @r"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: TOML parse error at line 1, column 1
|
||||
|
|
||||
1 | bad-option=true
|
||||
| ^^^^^^^^^^
|
||||
unknown field `bad-option`, expected one of `environment`, `src`, `rules`, `terminal`, `respect-ignore-files`
|
||||
|
||||
|
||||
Usage: ty <COMMAND>
|
||||
|
||||
For more information, try '--help'.
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct TestCase {
|
||||
_temp_dir: TempDir,
|
||||
_settings_scope: SettingsBindDropGuard,
|
||||
|
|
|
@ -38,7 +38,7 @@ pub struct Options {
|
|||
}
|
||||
|
||||
impl Options {
|
||||
pub(crate) fn from_toml_str(content: &str, source: ValueSource) -> Result<Self, TyTomlError> {
|
||||
pub fn from_toml_str(content: &str, source: ValueSource) -> Result<Self, TyTomlError> {
|
||||
let _guard = ValueSourceGuard::new(source, true);
|
||||
let options = toml::from_str(content)?;
|
||||
Ok(options)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue