Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hidden --parser option to override the input format, rather than inferring it from the file extension #7835

Closed
wants to merge 8 commits into from
10 changes: 8 additions & 2 deletions crates/ruff_cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use ruff_linter::line_width::LineLength;
use ruff_linter::logging::LogLevel;
use ruff_linter::registry::Rule;
use ruff_linter::settings::types::{
FilePattern, PatternPrefixPair, PerFileIgnore, PreviewMode, PythonVersion, SerializationFormat,
UnsafeFixes,
FilePattern, ParserType, PatternPrefixPair, PerFileIgnore, PreviewMode, PythonVersion,
SerializationFormat, UnsafeFixes,
};
use ruff_linter::{RuleParser, RuleSelector, RuleSelectorParser};
use ruff_workspace::configuration::{Configuration, RuleSelection};
Expand Down Expand Up @@ -347,6 +347,10 @@ pub struct CheckCommand {
/// Dev-only argument to show fixes
#[arg(long, hide = true)]
pub ecosystem_ci: bool,

/// Override file format
#[arg(long, hide = true)]
pub parser: Option<ParserType>,
}

#[derive(Clone, Debug, clap::Parser)]
Expand Down Expand Up @@ -496,6 +500,7 @@ impl CheckCommand {
statistics: self.statistics,
stdin_filename: self.stdin_filename,
watch: self.watch,
parser: self.parser,
},
CliOverrides {
dummy_variable_rgx: self.dummy_variable_rgx,
Expand Down Expand Up @@ -594,6 +599,7 @@ pub struct CheckArguments {
pub statistics: bool,
pub stdin_filename: Option<PathBuf>,
pub watch: bool,
pub parser: Option<ParserType>,
}

/// CLI settings that are distinct from configuration (commands, lists of files,
Expand Down
3 changes: 3 additions & 0 deletions crates/ruff_cli/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,7 @@ mod tests {
flags::Noqa::Enabled,
flags::FixMode::Generate,
UnsafeFixes::Enabled,
flags::Parser::Auto,
)
.unwrap();
if diagnostics
Expand Down Expand Up @@ -676,6 +677,7 @@ mod tests {
flags::Noqa::Enabled,
flags::FixMode::Generate,
UnsafeFixes::Enabled,
flags::Parser::Auto,
)
.unwrap();
}
Expand Down Expand Up @@ -1049,6 +1051,7 @@ mod tests {
flags::Noqa::Enabled,
flags::FixMode::Generate,
UnsafeFixes::Enabled,
flags::Parser::Auto,
)
}

Expand Down
17 changes: 16 additions & 1 deletion crates/ruff_cli/src/commands/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use crate::diagnostics::Diagnostics;
use crate::panic::catch_unwind;

/// Run the linter over a collection of files.
#[allow(clippy::too_many_arguments)]
pub(crate) fn check(
files: &[PathBuf],
pyproject_config: &PyprojectConfig,
Expand All @@ -38,6 +39,7 @@ pub(crate) fn check(
noqa: flags::Noqa,
fix_mode: flags::FixMode,
unsafe_fixes: UnsafeFixes,
parser: flags::Parser,
) -> Result<Diagnostics> {
// Collect all the Python files to check.
let start = Instant::now();
Expand Down Expand Up @@ -103,6 +105,7 @@ pub(crate) fn check(
noqa,
fix_mode,
unsafe_fixes,
parser,
)
.map_err(|e| {
(Some(path.to_path_buf()), {
Expand Down Expand Up @@ -184,6 +187,7 @@ pub(crate) fn check(

/// Wraps [`lint_path`](crate::diagnostics::lint_path) in a [`catch_unwind`](std::panic::catch_unwind) and emits
/// a diagnostic if the linting the file panics.
#[allow(clippy::too_many_arguments)]
fn lint_path(
path: &Path,
package: Option<&Path>,
Expand All @@ -192,9 +196,19 @@ fn lint_path(
noqa: flags::Noqa,
fix_mode: flags::FixMode,
unsafe_fixes: UnsafeFixes,
parser: flags::Parser,
) -> Result<Diagnostics> {
let result = catch_unwind(|| {
crate::diagnostics::lint_path(path, package, settings, cache, noqa, fix_mode, unsafe_fixes)
crate::diagnostics::lint_path(
path,
package,
settings,
cache,
noqa,
fix_mode,
unsafe_fixes,
parser,
)
});

match result {
Expand Down Expand Up @@ -280,6 +294,7 @@ mod test {
flags::Noqa::Disabled,
flags::FixMode::Generate,
UnsafeFixes::Enabled,
flags::Parser::Auto,
)
.unwrap();
let mut output = Vec::new();
Expand Down
2 changes: 2 additions & 0 deletions crates/ruff_cli/src/commands/check_stdin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub(crate) fn check_stdin(
overrides: &CliOverrides,
noqa: flags::Noqa,
fix_mode: flags::FixMode,
parser: flags::Parser,
) -> Result<Diagnostics> {
if let Some(filename) = filename {
if !python_file_at_path(filename, pyproject_config, overrides)? {
Expand All @@ -42,6 +43,7 @@ pub(crate) fn check_stdin(
&pyproject_config.settings,
noqa,
fix_mode,
parser,
)?;
diagnostics.messages.sort_unstable();
Ok(diagnostics)
Expand Down
21 changes: 19 additions & 2 deletions crates/ruff_cli/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,21 @@ impl AddAssign for FixMap {
}
}

fn parser_source_type(
parser: flags::Parser,
source_type: ruff_python_ast::PySourceType,
) -> ruff_python_ast::PySourceType {
match (parser, source_type) {
(flags::Parser::Auto, _) => source_type,
(flags::Parser::Ipynb, _) => ruff_python_ast::PySourceType::Ipynb,
(flags::Parser::Python, ruff_python_ast::PySourceType::Stub) => {
ruff_python_ast::PySourceType::Stub
}
(flags::Parser::Python, _) => ruff_python_ast::PySourceType::Python,
}
}
/// Lint the source code at the given `Path`.
#[allow(clippy::too_many_arguments)]
pub(crate) fn lint_path(
path: &Path,
package: Option<&Path>,
Expand All @@ -186,6 +200,7 @@ pub(crate) fn lint_path(
noqa: flags::Noqa,
fix_mode: flags::FixMode,
unsafe_fixes: UnsafeFixes,
parser: flags::Parser,
) -> Result<Diagnostics> {
// Check the cache.
// TODO(charlie): `fixer::Mode::Apply` and `fixer::Mode::Diff` both have
Expand All @@ -194,7 +209,7 @@ pub(crate) fn lint_path(
// write the fixes to disk, thus invalidating the cache. But it's a bit hard
// to reason about. We need to come up with a better solution here.)
let caching = match cache {
Some(cache) if noqa.into() && fix_mode.is_generate() => {
Some(cache) if noqa.into() && fix_mode.is_generate() && parser.is_auto() => {
let relative_path = cache
.relative_path(path)
.expect("wrong package cache for file");
Expand Down Expand Up @@ -240,7 +255,7 @@ pub(crate) fn lint_path(
});
}
SourceType::Toml(_) => return Ok(Diagnostics::default()),
SourceType::Python(source_type) => source_type,
SourceType::Python(source_type) => parser_source_type(parser, source_type),
};

// Extract the sources from the file.
Expand Down Expand Up @@ -353,11 +368,13 @@ pub(crate) fn lint_stdin(
settings: &Settings,
noqa: flags::Noqa,
fix_mode: flags::FixMode,
parser: flags::Parser,
) -> Result<Diagnostics> {
// TODO(charlie): Support `pyproject.toml`.
let SourceType::Python(source_type) = path.map(SourceType::from).unwrap_or_default() else {
return Ok(Diagnostics::default());
};
let source_type = parser_source_type(parser, source_type);

// Extract the sources from the file.
let source_kind = match SourceKind::from_source_code(contents, source_type) {
Expand Down
14 changes: 12 additions & 2 deletions crates/ruff_cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use log::warn;
use notify::{recommended_watcher, RecursiveMode, Watcher};

use ruff_linter::logging::{set_up_logging, LogLevel};
use ruff_linter::settings::flags::FixMode;
use ruff_linter::settings::types::SerializationFormat;
use ruff_linter::settings::flags::{FixMode, Parser};
use ruff_linter::settings::types::{ParserType, SerializationFormat};
use ruff_linter::{fs, warn_user, warn_user_once};
use ruff_workspace::Settings;

Expand Down Expand Up @@ -259,6 +259,12 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
if show_source {
printer_flags |= PrinterFlags::SHOW_SOURCE;
}
let parser = match cli.parser {
Some(ParserType::Ipynb) => Parser::Ipynb,
Some(ParserType::Py) => Parser::Python,
None => Parser::Auto,
};

if cli.ecosystem_ci {
warn_user!(
"The formatting of fixes emitted by this option is a work-in-progress, subject to \
Expand Down Expand Up @@ -325,6 +331,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
noqa.into(),
fix_mode,
unsafe_fixes,
parser,
)?;
printer.write_continuously(&mut writer, &messages)?;

Expand Down Expand Up @@ -358,6 +365,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
noqa.into(),
fix_mode,
unsafe_fixes,
Parser::Auto,
)?;
printer.write_continuously(&mut writer, &messages)?;
}
Expand All @@ -375,6 +383,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
&overrides,
noqa.into(),
fix_mode,
parser,
)?
} else {
commands::check::check(
Expand All @@ -385,6 +394,7 @@ pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
noqa.into(),
fix_mode,
unsafe_fixes,
parser,
)?
};

Expand Down
114 changes: 114 additions & 0 deletions crates/ruff_cli/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,120 @@ fn stdin_fix_jupyter() {
"###);
}

#[test]
fn stdin_override_parser_py() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--stdin-filename", "F401.ipynb", "--parser", "py"])
.pass_stdin("import os\n"), @r###"
success: false
exit_code: 1
----- stdout -----
F401.ipynb:1:8: F401 [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.

----- stderr -----
"###);
}

#[test]
fn stdin_override_parser_ipynb() {
let args = ["--parser", "ipynb", "--stdin-filename", "Jupyter.py"];
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(args)
.pass_stdin(r#"{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "dccc687c-96e2-4604-b957-a8a89b5bec06",
"metadata": {},
"outputs": [],
"source": [
"import os"
]
},
{
"cell_type": "markdown",
"id": "19e1b029-f516-4662-a9b9-623b93edac1a",
"metadata": {},
"source": [
"Foo"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "cdce7b92-b0fb-4c02-86f6-e233b26fa84f",
"metadata": {},
"outputs": [],
"source": [
"import sys"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "e40b33d2-7fe4-46c5-bdf0-8802f3052565",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n"
]
}
],
"source": [
"print(1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a1899bc8-d46f-4ec0-b1d1-e1ca0f04bf60",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}"#), @r###"
success: false
exit_code: 1
----- stdout -----
Jupyter.py:cell 1:1:8: F401 [*] `os` imported but unused
Jupyter.py:cell 3:1:8: F401 [*] `sys` imported but unused
Found 2 errors.
[*] 2 fixable with the `--fix` option.

----- stderr -----
"###);
}

#[test]
fn stdin_fix_when_not_fixable_should_still_print_contents() {
let args = ["--fix"];
Expand Down
13 changes: 13 additions & 0 deletions crates/ruff_linter/src/settings/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,16 @@ pub enum Cache {
Enabled,
Disabled,
}

#[derive(Debug, Copy, Clone, Hash, is_macro::Is)]
pub enum Parser {
Auto,
Python,
Ipynb,
}

impl Default for Parser {
fn default() -> Self {
Self::Auto
}
}
Loading
Loading