Compare commits
9 Commits
micha/test
...
0.7.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e6de4e0c6 | ||
|
|
70e5c4a8ba | ||
|
|
9218d6bedc | ||
|
|
1b79ae9817 | ||
|
|
2b87587ac2 | ||
|
|
d1e15f6246 | ||
|
|
89a82158a1 | ||
|
|
202c6a6d75 | ||
|
|
5c3c0c4705 |
47
CHANGELOG.md
47
CHANGELOG.md
@@ -1,5 +1,52 @@
|
||||
# Changelog
|
||||
|
||||
## 0.7.0
|
||||
|
||||
Check out the [blog post](https://astral.sh/blog/ruff-v0.7.0) for a migration guide and overview of the changes!
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- The pytest rules `PT001` and `PT023` now default to omitting the decorator parentheses when there are no arguments
|
||||
([#12838](https://github.com/astral-sh/ruff/pull/12838), [#13292](https://github.com/astral-sh/ruff/pull/13292)).
|
||||
This was a change that we attempted to make in Ruff v0.6.0, but only partially made due to an error on our part.
|
||||
See the [blog post](https://astral.sh/blog/ruff-v0.7.0) for more details.
|
||||
- The `useless-try-except` rule (in our `tryceratops` category) has been recoded from `TRY302` to
|
||||
`TRY203` ([#13502](https://github.com/astral-sh/ruff/pull/13502)). This ensures Ruff's code is consistent with
|
||||
the same rule in the [`tryceratops`](https://github.com/guilatrova/tryceratops) linter.
|
||||
- The `lint.allow-unused-imports` setting has been removed ([#13677](https://github.com/astral-sh/ruff/pull/13677)). Use
|
||||
[`lint.pyflakes.allow-unused-imports`](https://docs.astral.sh/ruff/settings/#lint_pyflakes_allowed-unused-imports)
|
||||
instead.
|
||||
|
||||
### Formatter preview style
|
||||
|
||||
- Normalize implicit concatenated f-string quotes per part ([#13539](https://github.com/astral-sh/ruff/pull/13539))
|
||||
|
||||
### Preview linter features
|
||||
|
||||
- \[`refurb`\] implement `hardcoded-string-charset` (FURB156) ([#13530](https://github.com/astral-sh/ruff/pull/13530))
|
||||
- \[`refurb`\] Count codepoints not bytes for `slice-to-remove-prefix-or-suffix (FURB188)` ([#13631](https://github.com/astral-sh/ruff/pull/13631))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`pylint`\] Mark `PLE1141` fix as unsafe ([#13629](https://github.com/astral-sh/ruff/pull/13629))
|
||||
- \[`flake8-async`\] Consider async generators to be "checkpoints" for `cancel-scope-no-checkpoint` (`ASYNC100`) ([#13639](https://github.com/astral-sh/ruff/pull/13639))
|
||||
- \[`flake8-bugbear`\] Do not suggest setting parameter `strict=` to `False` in `B905` diagnostic message ([#13656](https://github.com/astral-sh/ruff/pull/13656))
|
||||
- \[`flake8-todos`\] Only flag the word "TODO", not words starting with "todo" (`TD006`) ([#13640](https://github.com/astral-sh/ruff/pull/13640))
|
||||
- \[`pycodestyle`\] Fix whitespace-related false positives and false negatives inside type-parameter lists (`E231`, `E251`) ([#13704](https://github.com/astral-sh/ruff/pull/13704))
|
||||
- \[`flake8-simplify`\] Stabilize preview behavior for `SIM115` so that the rule can detect files
|
||||
being opened from a wider range of standard-library functions ([#12959](https://github.com/astral-sh/ruff/pull/12959)).
|
||||
|
||||
### CLI
|
||||
|
||||
- Add explanation of fixable in `--statistics` command ([#13774](https://github.com/astral-sh/ruff/pull/13774))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- \[`pyflakes`\] Allow `ipytest` cell magic (`F401`) ([#13745](https://github.com/astral-sh/ruff/pull/13745))
|
||||
- \[`flake8-use-pathlib`\] Fix `PTH123` false positive when `open` is passed a file descriptor ([#13616](https://github.com/astral-sh/ruff/pull/13616))
|
||||
- \[`flake8-bandit`\] Detect patterns from multi line SQL statements (`S608`) ([#13574](https://github.com/astral-sh/ruff/pull/13574))
|
||||
- \[`flake8-pyi`\] - Fix dropped expressions in `PYI030` autofix ([#13727](https://github.com/astral-sh/ruff/pull/13727))
|
||||
|
||||
## 0.6.9
|
||||
|
||||
### Preview features
|
||||
|
||||
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -2320,7 +2320,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.6.9"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argfile",
|
||||
@@ -2539,7 +2539,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.6.9"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"annotate-snippets 0.9.2",
|
||||
@@ -2859,7 +2859,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_wasm"
|
||||
version = "0.6.9"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
|
||||
@@ -136,8 +136,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
|
||||
|
||||
# For a specific version.
|
||||
curl -LsSf https://astral.sh/ruff/0.6.9/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.6.9/install.ps1 | iex"
|
||||
curl -LsSf https://astral.sh/ruff/0.7.0/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.7.0/install.ps1 | iex"
|
||||
```
|
||||
|
||||
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
|
||||
@@ -170,7 +170,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.6.9
|
||||
rev: v0.7.0
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# Comparison: Unions
|
||||
|
||||
## Union on one side of the comparison
|
||||
|
||||
Comparisons on union types need to consider all possible cases:
|
||||
|
||||
```py
|
||||
one_or_two = 1 if flag else 2
|
||||
|
||||
reveal_type(one_or_two <= 2) # revealed: Literal[True]
|
||||
reveal_type(one_or_two <= 1) # revealed: bool
|
||||
reveal_type(one_or_two <= 0) # revealed: Literal[False]
|
||||
|
||||
reveal_type(2 >= one_or_two) # revealed: Literal[True]
|
||||
reveal_type(1 >= one_or_two) # revealed: bool
|
||||
reveal_type(0 >= one_or_two) # revealed: Literal[False]
|
||||
|
||||
reveal_type(one_or_two < 1) # revealed: Literal[False]
|
||||
reveal_type(one_or_two < 2) # revealed: bool
|
||||
reveal_type(one_or_two < 3) # revealed: Literal[True]
|
||||
|
||||
reveal_type(one_or_two > 0) # revealed: Literal[True]
|
||||
reveal_type(one_or_two > 1) # revealed: bool
|
||||
reveal_type(one_or_two > 2) # revealed: Literal[False]
|
||||
|
||||
reveal_type(one_or_two == 3) # revealed: Literal[False]
|
||||
reveal_type(one_or_two == 1) # revealed: bool
|
||||
|
||||
reveal_type(one_or_two != 3) # revealed: Literal[True]
|
||||
reveal_type(one_or_two != 1) # revealed: bool
|
||||
|
||||
a_or_ab = "a" if flag else "ab"
|
||||
|
||||
reveal_type(a_or_ab in "ab") # revealed: Literal[True]
|
||||
reveal_type("a" in a_or_ab) # revealed: Literal[True]
|
||||
|
||||
reveal_type("c" not in a_or_ab) # revealed: Literal[True]
|
||||
reveal_type("a" not in a_or_ab) # revealed: Literal[False]
|
||||
|
||||
reveal_type("b" in a_or_ab) # revealed: bool
|
||||
reveal_type("b" not in a_or_ab) # revealed: bool
|
||||
|
||||
one_or_none = 1 if flag else None
|
||||
|
||||
reveal_type(one_or_none is None) # revealed: bool
|
||||
reveal_type(one_or_none is not None) # revealed: bool
|
||||
```
|
||||
|
||||
## Union on both sides of the comparison
|
||||
|
||||
With unions on both sides, we need to consider the full cross product of
|
||||
options when building the resulting (union) type:
|
||||
|
||||
```py
|
||||
small = 1 if flag_s else 2
|
||||
large = 2 if flag_l else 3
|
||||
|
||||
reveal_type(small <= large) # revealed: Literal[True]
|
||||
reveal_type(small >= large) # revealed: bool
|
||||
|
||||
reveal_type(small < large) # revealed: bool
|
||||
reveal_type(small > large) # revealed: Literal[False]
|
||||
```
|
||||
|
||||
## Unsupported operations
|
||||
|
||||
Make sure we emit a diagnostic if *any* of the possible comparisons is
|
||||
unsupported. For now, we fall back to `bool` for the result type instead of
|
||||
trying to infer something more precise from the other (supported) variants:
|
||||
|
||||
```py
|
||||
x = [1, 2] if flag else 1
|
||||
|
||||
result = 1 in x # error: "Operator `in` is not supported"
|
||||
reveal_type(result) # revealed: bool
|
||||
```
|
||||
@@ -415,6 +415,11 @@ impl<'db> Type<'db> {
|
||||
(_, Type::Unknown | Type::Any | Type::Todo) => false,
|
||||
(Type::Never, _) => true,
|
||||
(_, Type::Never) => false,
|
||||
(Type::BooleanLiteral(_), Type::Instance(class))
|
||||
if class.is_known(db, KnownClass::Bool) =>
|
||||
{
|
||||
true
|
||||
}
|
||||
(Type::IntLiteral(_), Type::Instance(class)) if class.is_known(db, KnownClass::Int) => {
|
||||
true
|
||||
}
|
||||
|
||||
@@ -374,6 +374,12 @@ mod tests {
|
||||
|
||||
let union = UnionType::from_elements(&db, [t0, t1, t2, t3]).expect_union();
|
||||
assert_eq!(union.elements(&db), &[bool_instance_ty, t3]);
|
||||
|
||||
let result_ty = UnionType::from_elements(&db, [bool_instance_ty, t0]);
|
||||
assert_eq!(result_ty, bool_instance_ty);
|
||||
|
||||
let result_ty = UnionType::from_elements(&db, [t0, bool_instance_ty]);
|
||||
assert_eq!(result_ty, bool_instance_ty);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -57,7 +57,7 @@ use crate::types::{
|
||||
};
|
||||
use crate::Db;
|
||||
|
||||
use super::KnownClass;
|
||||
use super::{KnownClass, UnionBuilder};
|
||||
|
||||
/// Infer all types for a [`ScopeId`], including all definitions and expressions in that scope.
|
||||
/// Use when checking a scope, or needing to provide a type for an arbitrary expression in the
|
||||
@@ -2711,6 +2711,21 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
// - `[ast::CompOp::Is]`: return `false` if unequal, `bool` if equal
|
||||
// - `[ast::CompOp::IsNot]`: return `true` if unequal, `bool` if equal
|
||||
match (left, right) {
|
||||
(Type::Union(union), other) => {
|
||||
let mut builder = UnionBuilder::new(self.db);
|
||||
for element in union.elements(self.db) {
|
||||
builder = builder.add(self.infer_binary_type_comparison(*element, op, other)?);
|
||||
}
|
||||
Some(builder.build())
|
||||
}
|
||||
(other, Type::Union(union)) => {
|
||||
let mut builder = UnionBuilder::new(self.db);
|
||||
for element in union.elements(self.db) {
|
||||
builder = builder.add(self.infer_binary_type_comparison(other, op, *element)?);
|
||||
}
|
||||
Some(builder.build())
|
||||
}
|
||||
|
||||
(Type::IntLiteral(n), Type::IntLiteral(m)) => match op {
|
||||
ast::CmpOp::Eq => Some(Type::BooleanLiteral(n == m)),
|
||||
ast::CmpOp::NotEq => Some(Type::BooleanLiteral(n != m)),
|
||||
@@ -2902,6 +2917,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup the rich comparison `__dunder__` methods on instances
|
||||
(Type::Instance(left_class_ty), Type::Instance(right_class_ty)) => match op {
|
||||
ast::CmpOp::Lt => {
|
||||
@@ -2911,7 +2927,10 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
_ => Some(Type::Todo),
|
||||
},
|
||||
// TODO: handle more types
|
||||
_ => Some(Type::Todo),
|
||||
_ => match op {
|
||||
ast::CmpOp::Is | ast::CmpOp::IsNot => Some(KnownClass::Bool.to_instance(self.db)),
|
||||
_ => Some(Type::Todo),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.6.9"
|
||||
version = "0.7.0"
|
||||
publish = true
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, bail};
|
||||
use anyhow::bail;
|
||||
use clap::builder::{TypedValueParser, ValueParserFactory};
|
||||
use clap::{command, Parser, Subcommand};
|
||||
use colored::Colorize;
|
||||
@@ -729,7 +729,7 @@ impl CheckCommand {
|
||||
unsafe_fixes: resolve_bool_arg(self.unsafe_fixes, self.no_unsafe_fixes)
|
||||
.map(UnsafeFixes::from),
|
||||
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
|
||||
output_format: resolve_output_format(self.output_format)?,
|
||||
output_format: self.output_format,
|
||||
show_fixes: resolve_bool_arg(self.show_fixes, self.no_show_fixes),
|
||||
extension: self.extension,
|
||||
..ExplicitConfigOverrides::default()
|
||||
@@ -984,17 +984,6 @@ The path `{value}` does not point to a configuration file"
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn resolve_output_format(
|
||||
output_format: Option<OutputFormat>,
|
||||
) -> anyhow::Result<Option<OutputFormat>> {
|
||||
if let Some(OutputFormat::Text) = output_format {
|
||||
Err(anyhow!("`--output-format=text` is no longer supported. Use `--output-format=full` or `--output-format=concise` instead."))
|
||||
} else {
|
||||
Ok(output_format)
|
||||
}
|
||||
}
|
||||
|
||||
/// CLI settings that are distinct from configuration (commands, lists of files,
|
||||
/// etc.).
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
use std::io::Write;
|
||||
use std::process::ExitCode;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use clap::Parser;
|
||||
use colored::Colorize;
|
||||
use log::error;
|
||||
use std::io::Write;
|
||||
|
||||
use ruff::args::{Args, Command};
|
||||
use ruff::args::Args;
|
||||
use ruff::{run, ExitStatus};
|
||||
use ruff_linter::logging::{set_up_logging, LogLevel};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[global_allocator]
|
||||
@@ -41,47 +39,6 @@ pub fn main() -> ExitCode {
|
||||
let args = wild::args_os();
|
||||
let args = argfile::expand_args_from(args, argfile::parse_fromfile, argfile::PREFIX).unwrap();
|
||||
|
||||
// We can't use `warn_user` here because logging isn't set up at this point
|
||||
// and we also don't know if the user runs ruff with quiet.
|
||||
// Keep the message and pass it to `run` that is responsible for emitting the warning.
|
||||
let deprecated_alias_error = match args.get(1).and_then(|arg| arg.to_str()) {
|
||||
// Deprecated aliases that are handled by clap
|
||||
Some("--explain") => {
|
||||
Some("`ruff --explain <RULE>` has been removed. Use `ruff rule <RULE>` instead.")
|
||||
}
|
||||
Some("--clean") => {
|
||||
Some("`ruff --clean` has been removed. Use `ruff clean` instead.")
|
||||
}
|
||||
Some("--generate-shell-completion") => {
|
||||
Some("`ruff --generate-shell-completion <SHELL>` has been removed. Use `ruff generate-shell-completion <SHELL>` instead.")
|
||||
}
|
||||
// Deprecated `ruff` alias to `ruff check`
|
||||
// Clap doesn't support default subcommands but we want to run `check` by
|
||||
// default for convenience and backwards-compatibility, so we just
|
||||
// preprocess the arguments accordingly before passing them to Clap.
|
||||
Some(arg) if !Command::has_subcommand(arg)
|
||||
&& arg != "-h"
|
||||
&& arg != "--help"
|
||||
&& arg != "-V"
|
||||
&& arg != "--version"
|
||||
&& arg != "help" => {
|
||||
{
|
||||
Some("`ruff <path>` has been removed. Use `ruff check <path>` instead.")
|
||||
}
|
||||
},
|
||||
_ => None
|
||||
};
|
||||
|
||||
if let Some(error) = deprecated_alias_error {
|
||||
#[allow(clippy::print_stderr)]
|
||||
if set_up_logging(LogLevel::Default).is_ok() {
|
||||
error!("{}", error);
|
||||
} else {
|
||||
eprintln!("{}", error.red().bold());
|
||||
}
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
|
||||
let args = Args::parse_from(args);
|
||||
|
||||
match run(args) {
|
||||
|
||||
@@ -244,10 +244,7 @@ impl Printer {
|
||||
#[allow(deprecated)]
|
||||
if matches!(
|
||||
self.format,
|
||||
OutputFormat::Text
|
||||
| OutputFormat::Full
|
||||
| OutputFormat::Concise
|
||||
| OutputFormat::Grouped
|
||||
OutputFormat::Full | OutputFormat::Concise | OutputFormat::Grouped
|
||||
) {
|
||||
if self.flags.intersects(Flags::SHOW_FIX_SUMMARY) {
|
||||
if !diagnostics.fixed.is_empty() {
|
||||
@@ -325,8 +322,6 @@ impl Printer {
|
||||
OutputFormat::Sarif => {
|
||||
SarifEmitter.emit(writer, &diagnostics.messages, &context)?;
|
||||
}
|
||||
#[allow(deprecated)]
|
||||
OutputFormat::Text => unreachable!("Text is deprecated and should have been automatically converted to the default serialization format")
|
||||
}
|
||||
|
||||
writer.flush()?;
|
||||
@@ -368,8 +363,7 @@ impl Printer {
|
||||
}
|
||||
|
||||
match self.format {
|
||||
#[allow(deprecated)]
|
||||
OutputFormat::Text | OutputFormat::Full | OutputFormat::Concise => {
|
||||
OutputFormat::Full | OutputFormat::Concise => {
|
||||
// Compute the maximum number of digits in the count and code, for all messages,
|
||||
// to enable pretty-printing.
|
||||
let count_width = num_digits(
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
//! A test suite that ensures deprecated command line options have appropriate warnings / behaviors
|
||||
|
||||
use ruff_linter::settings::types::OutputFormat;
|
||||
use std::process::Command;
|
||||
|
||||
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
|
||||
|
||||
const BIN_NAME: &str = "ruff";
|
||||
|
||||
const STDIN: &str = "l = 1";
|
||||
|
||||
fn ruff_check(output_format: OutputFormat) -> Command {
|
||||
let mut cmd = Command::new(get_cargo_bin(BIN_NAME));
|
||||
let output_format = output_format.to_string();
|
||||
cmd.arg("check")
|
||||
.arg("--output-format")
|
||||
.arg(output_format)
|
||||
.arg("--no-cache");
|
||||
cmd.arg("-");
|
||||
|
||||
cmd
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn ensure_output_format_is_deprecated() {
|
||||
assert_cmd_snapshot!(ruff_check(OutputFormat::Text).pass_stdin(STDIN), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
ruff failed
|
||||
Cause: `--output-format=text` is no longer supported. Use `--output-format=full` or `--output-format=concise` instead.
|
||||
"###);
|
||||
}
|
||||
@@ -818,7 +818,13 @@ if True:
|
||||
|
||||
----- stderr -----
|
||||
ruff failed
|
||||
Cause: The `tab-size` option has been renamed to `indent-width` to emphasize that it configures the indentation used by the formatter as well as the tab width. Please update `[RUFF-TOML-PATH]` to use `indent-width = <value>` instead.
|
||||
Cause: Failed to parse [RUFF-TOML-PATH]
|
||||
Cause: TOML parse error at line 1, column 1
|
||||
|
|
||||
1 |
|
||||
| ^
|
||||
unknown field `tab-size`
|
||||
|
||||
"###);
|
||||
});
|
||||
Ok(())
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.6.9"
|
||||
version = "0.7.0"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -855,9 +855,9 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Tryceratops, "004") => (RuleGroup::Stable, rules::tryceratops::rules::TypeCheckWithoutTypeError),
|
||||
(Tryceratops, "200") => (RuleGroup::Removed, rules::tryceratops::rules::ReraiseNoCause),
|
||||
(Tryceratops, "201") => (RuleGroup::Stable, rules::tryceratops::rules::VerboseRaise),
|
||||
(Tryceratops, "203") => (RuleGroup::Stable, rules::tryceratops::rules::UselessTryExcept),
|
||||
(Tryceratops, "300") => (RuleGroup::Stable, rules::tryceratops::rules::TryConsiderElse),
|
||||
(Tryceratops, "301") => (RuleGroup::Stable, rules::tryceratops::rules::RaiseWithinTry),
|
||||
(Tryceratops, "302") => (RuleGroup::Stable, rules::tryceratops::rules::UselessTryExcept),
|
||||
(Tryceratops, "400") => (RuleGroup::Stable, rules::tryceratops::rules::ErrorInsteadOfException),
|
||||
(Tryceratops, "401") => (RuleGroup::Stable, rules::tryceratops::rules::VerboseLogMessage),
|
||||
|
||||
|
||||
@@ -125,5 +125,7 @@ static REDIRECTS: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||
("PLW0117", "PLW0177"),
|
||||
// See: https://github.com/astral-sh/ruff/issues/12110
|
||||
("RUF025", "C420"),
|
||||
// See: https://github.com/astral-sh/ruff/issues/13492
|
||||
("TRY302", "TRY203"),
|
||||
])
|
||||
});
|
||||
|
||||
@@ -58,7 +58,6 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test_case(Rule::IfElseBlockInsteadOfIfExp, Path::new("SIM108.py"))]
|
||||
#[test_case(Rule::OpenFileWithContextHandler, Path::new("SIM115.py"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
"preview__{}_{}",
|
||||
|
||||
@@ -14,13 +14,9 @@ use crate::checkers::ast::Checker;
|
||||
/// ## Why is this bad?
|
||||
/// If a file is opened without a context manager, it is not guaranteed that
|
||||
/// the file will be closed (e.g., if an exception is raised), which can cause
|
||||
/// resource leaks.
|
||||
///
|
||||
/// ## Preview-mode behavior
|
||||
/// If [preview] mode is enabled, this rule will detect a wide array of IO calls where
|
||||
/// context managers could be used, such as `tempfile.TemporaryFile()` or
|
||||
/// `tarfile.TarFile(...).gzopen()`. If preview mode is not enabled, only `open()`,
|
||||
/// `builtins.open()` and `pathlib.Path(...).open()` are detected.
|
||||
/// resource leaks. The rule detects a wide array of IO calls where context managers
|
||||
/// could be used, such as `open`, `pathlib.Path(...).open()`, `tempfile.TemporaryFile()`
|
||||
/// or`tarfile.TarFile(...).gzopen()`.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
@@ -118,36 +114,9 @@ fn match_exit_stack(semantic: &SemanticModel) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Return `true` if `func` is the builtin `open` or `pathlib.Path(...).open`.
|
||||
fn is_open(semantic: &SemanticModel, call: &ast::ExprCall) -> bool {
|
||||
// Ex) `open(...)`
|
||||
if semantic.match_builtin_expr(&call.func, "open") {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ex) `pathlib.Path(...).open()`
|
||||
let Expr::Attribute(ast::ExprAttribute { attr, value, .. }) = &*call.func else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if attr != "open" {
|
||||
return false;
|
||||
}
|
||||
|
||||
let Expr::Call(ast::ExprCall {
|
||||
func: value_func, ..
|
||||
}) = &**value
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
|
||||
semantic
|
||||
.resolve_qualified_name(value_func)
|
||||
.is_some_and(|qualified_name| matches!(qualified_name.segments(), ["pathlib", "Path"]))
|
||||
}
|
||||
|
||||
/// Return `true` if the expression is an `open` call or temporary file constructor.
|
||||
fn is_open_preview(semantic: &SemanticModel, call: &ast::ExprCall) -> bool {
|
||||
/// Return `true` if the expression is a call to `open()`,
|
||||
/// or a call to some other standard-library function that opens a file.
|
||||
fn is_open_call(semantic: &SemanticModel, call: &ast::ExprCall) -> bool {
|
||||
let func = &*call.func;
|
||||
|
||||
// Ex) `open(...)`
|
||||
@@ -203,8 +172,8 @@ fn is_open_preview(semantic: &SemanticModel, call: &ast::ExprCall) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
/// Return `true` if the current expression is followed by a `close` call.
|
||||
fn is_closed(semantic: &SemanticModel) -> bool {
|
||||
/// Return `true` if the current expression is immediately followed by a `.close()` call.
|
||||
fn is_immediately_closed(semantic: &SemanticModel) -> bool {
|
||||
let Some(expr) = semantic.current_expression_grandparent() else {
|
||||
return false;
|
||||
};
|
||||
@@ -231,18 +200,12 @@ fn is_closed(semantic: &SemanticModel) -> bool {
|
||||
pub(crate) fn open_file_with_context_handler(checker: &mut Checker, call: &ast::ExprCall) {
|
||||
let semantic = checker.semantic();
|
||||
|
||||
if checker.settings.preview.is_disabled() {
|
||||
if !is_open(semantic, call) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if !is_open_preview(semantic, call) {
|
||||
return;
|
||||
}
|
||||
if !is_open_call(semantic, call) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ex) `open("foo.txt").close()`
|
||||
if is_closed(semantic) {
|
||||
if is_immediately_closed(semantic) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,3 +59,276 @@ SIM115.py:39:9: SIM115 Use a context manager for opening files
|
||||
40 |
|
||||
41 | # OK
|
||||
|
|
||||
|
||||
SIM115.py:80:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
78 | import fileinput
|
||||
79 |
|
||||
80 | f = tempfile.NamedTemporaryFile()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
81 | f = tempfile.TemporaryFile()
|
||||
82 | f = tempfile.SpooledTemporaryFile()
|
||||
|
|
||||
|
||||
SIM115.py:81:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
80 | f = tempfile.NamedTemporaryFile()
|
||||
81 | f = tempfile.TemporaryFile()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
82 | f = tempfile.SpooledTemporaryFile()
|
||||
83 | f = tarfile.open("foo.tar")
|
||||
|
|
||||
|
||||
SIM115.py:82:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
80 | f = tempfile.NamedTemporaryFile()
|
||||
81 | f = tempfile.TemporaryFile()
|
||||
82 | f = tempfile.SpooledTemporaryFile()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
83 | f = tarfile.open("foo.tar")
|
||||
84 | f = TarFile("foo.tar").open()
|
||||
|
|
||||
|
||||
SIM115.py:83:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
81 | f = tempfile.TemporaryFile()
|
||||
82 | f = tempfile.SpooledTemporaryFile()
|
||||
83 | f = tarfile.open("foo.tar")
|
||||
| ^^^^^^^^^^^^ SIM115
|
||||
84 | f = TarFile("foo.tar").open()
|
||||
85 | f = tarfile.TarFile("foo.tar").open()
|
||||
|
|
||||
|
||||
SIM115.py:84:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
82 | f = tempfile.SpooledTemporaryFile()
|
||||
83 | f = tarfile.open("foo.tar")
|
||||
84 | f = TarFile("foo.tar").open()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
85 | f = tarfile.TarFile("foo.tar").open()
|
||||
86 | f = tarfile.TarFile().open()
|
||||
|
|
||||
|
||||
SIM115.py:85:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
83 | f = tarfile.open("foo.tar")
|
||||
84 | f = TarFile("foo.tar").open()
|
||||
85 | f = tarfile.TarFile("foo.tar").open()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
86 | f = tarfile.TarFile().open()
|
||||
87 | f = zipfile.ZipFile("foo.zip").open("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:86:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
84 | f = TarFile("foo.tar").open()
|
||||
85 | f = tarfile.TarFile("foo.tar").open()
|
||||
86 | f = tarfile.TarFile().open()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
87 | f = zipfile.ZipFile("foo.zip").open("foo.txt")
|
||||
88 | f = io.open("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:87:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
85 | f = tarfile.TarFile("foo.tar").open()
|
||||
86 | f = tarfile.TarFile().open()
|
||||
87 | f = zipfile.ZipFile("foo.zip").open("foo.txt")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
88 | f = io.open("foo.txt")
|
||||
89 | f = io.open_code("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:88:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
86 | f = tarfile.TarFile().open()
|
||||
87 | f = zipfile.ZipFile("foo.zip").open("foo.txt")
|
||||
88 | f = io.open("foo.txt")
|
||||
| ^^^^^^^ SIM115
|
||||
89 | f = io.open_code("foo.txt")
|
||||
90 | f = codecs.open("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:89:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
87 | f = zipfile.ZipFile("foo.zip").open("foo.txt")
|
||||
88 | f = io.open("foo.txt")
|
||||
89 | f = io.open_code("foo.txt")
|
||||
| ^^^^^^^^^^^^ SIM115
|
||||
90 | f = codecs.open("foo.txt")
|
||||
91 | f = bz2.open("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:90:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
88 | f = io.open("foo.txt")
|
||||
89 | f = io.open_code("foo.txt")
|
||||
90 | f = codecs.open("foo.txt")
|
||||
| ^^^^^^^^^^^ SIM115
|
||||
91 | f = bz2.open("foo.txt")
|
||||
92 | f = gzip.open("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:91:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
89 | f = io.open_code("foo.txt")
|
||||
90 | f = codecs.open("foo.txt")
|
||||
91 | f = bz2.open("foo.txt")
|
||||
| ^^^^^^^^ SIM115
|
||||
92 | f = gzip.open("foo.txt")
|
||||
93 | f = dbm.open("foo.db")
|
||||
|
|
||||
|
||||
SIM115.py:92:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
90 | f = codecs.open("foo.txt")
|
||||
91 | f = bz2.open("foo.txt")
|
||||
92 | f = gzip.open("foo.txt")
|
||||
| ^^^^^^^^^ SIM115
|
||||
93 | f = dbm.open("foo.db")
|
||||
94 | f = dbm.gnu.open("foo.db")
|
||||
|
|
||||
|
||||
SIM115.py:93:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
91 | f = bz2.open("foo.txt")
|
||||
92 | f = gzip.open("foo.txt")
|
||||
93 | f = dbm.open("foo.db")
|
||||
| ^^^^^^^^ SIM115
|
||||
94 | f = dbm.gnu.open("foo.db")
|
||||
95 | f = dbm.ndbm.open("foo.db")
|
||||
|
|
||||
|
||||
SIM115.py:94:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
92 | f = gzip.open("foo.txt")
|
||||
93 | f = dbm.open("foo.db")
|
||||
94 | f = dbm.gnu.open("foo.db")
|
||||
| ^^^^^^^^^^^^ SIM115
|
||||
95 | f = dbm.ndbm.open("foo.db")
|
||||
96 | f = dbm.dumb.open("foo.db")
|
||||
|
|
||||
|
||||
SIM115.py:95:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
93 | f = dbm.open("foo.db")
|
||||
94 | f = dbm.gnu.open("foo.db")
|
||||
95 | f = dbm.ndbm.open("foo.db")
|
||||
| ^^^^^^^^^^^^^ SIM115
|
||||
96 | f = dbm.dumb.open("foo.db")
|
||||
97 | f = lzma.open("foo.xz")
|
||||
|
|
||||
|
||||
SIM115.py:96:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
94 | f = dbm.gnu.open("foo.db")
|
||||
95 | f = dbm.ndbm.open("foo.db")
|
||||
96 | f = dbm.dumb.open("foo.db")
|
||||
| ^^^^^^^^^^^^^ SIM115
|
||||
97 | f = lzma.open("foo.xz")
|
||||
98 | f = lzma.LZMAFile("foo.xz")
|
||||
|
|
||||
|
||||
SIM115.py:97:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
95 | f = dbm.ndbm.open("foo.db")
|
||||
96 | f = dbm.dumb.open("foo.db")
|
||||
97 | f = lzma.open("foo.xz")
|
||||
| ^^^^^^^^^ SIM115
|
||||
98 | f = lzma.LZMAFile("foo.xz")
|
||||
99 | f = shelve.open("foo.db")
|
||||
|
|
||||
|
||||
SIM115.py:98:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
96 | f = dbm.dumb.open("foo.db")
|
||||
97 | f = lzma.open("foo.xz")
|
||||
98 | f = lzma.LZMAFile("foo.xz")
|
||||
| ^^^^^^^^^^^^^ SIM115
|
||||
99 | f = shelve.open("foo.db")
|
||||
100 | f = tokenize.open("foo.py")
|
||||
|
|
||||
|
||||
SIM115.py:99:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
97 | f = lzma.open("foo.xz")
|
||||
98 | f = lzma.LZMAFile("foo.xz")
|
||||
99 | f = shelve.open("foo.db")
|
||||
| ^^^^^^^^^^^ SIM115
|
||||
100 | f = tokenize.open("foo.py")
|
||||
101 | f = wave.open("foo.wav")
|
||||
|
|
||||
|
||||
SIM115.py:100:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
98 | f = lzma.LZMAFile("foo.xz")
|
||||
99 | f = shelve.open("foo.db")
|
||||
100 | f = tokenize.open("foo.py")
|
||||
| ^^^^^^^^^^^^^ SIM115
|
||||
101 | f = wave.open("foo.wav")
|
||||
102 | f = tarfile.TarFile.taropen("foo.tar")
|
||||
|
|
||||
|
||||
SIM115.py:101:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
99 | f = shelve.open("foo.db")
|
||||
100 | f = tokenize.open("foo.py")
|
||||
101 | f = wave.open("foo.wav")
|
||||
| ^^^^^^^^^ SIM115
|
||||
102 | f = tarfile.TarFile.taropen("foo.tar")
|
||||
103 | f = fileinput.input("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:102:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
100 | f = tokenize.open("foo.py")
|
||||
101 | f = wave.open("foo.wav")
|
||||
102 | f = tarfile.TarFile.taropen("foo.tar")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
103 | f = fileinput.input("foo.txt")
|
||||
104 | f = fileinput.FileInput("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:103:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
101 | f = wave.open("foo.wav")
|
||||
102 | f = tarfile.TarFile.taropen("foo.tar")
|
||||
103 | f = fileinput.input("foo.txt")
|
||||
| ^^^^^^^^^^^^^^^ SIM115
|
||||
104 | f = fileinput.FileInput("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:104:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
102 | f = tarfile.TarFile.taropen("foo.tar")
|
||||
103 | f = fileinput.input("foo.txt")
|
||||
104 | f = fileinput.FileInput("foo.txt")
|
||||
| ^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
105 |
|
||||
106 | with contextlib.suppress(Exception):
|
||||
|
|
||||
|
||||
SIM115.py:240:9: SIM115 Use a context manager for opening files
|
||||
|
|
||||
238 | def aliased():
|
||||
239 | from shelve import open as open_shelf
|
||||
240 | x = open_shelf("foo.dbm")
|
||||
| ^^^^^^^^^^ SIM115
|
||||
241 | x.close()
|
||||
|
|
||||
|
||||
SIM115.py:244:9: SIM115 Use a context manager for opening files
|
||||
|
|
||||
243 | from tarfile import TarFile as TF
|
||||
244 | f = TF("foo").open()
|
||||
| ^^^^^^^^^^^^^^ SIM115
|
||||
245 | f.close()
|
||||
|
|
||||
|
||||
SIM115.py:257:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
256 | # SIM115
|
||||
257 | f = dbm.sqlite3.open("foo.db")
|
||||
| ^^^^^^^^^^^^^^^^ SIM115
|
||||
258 | f.close()
|
||||
|
|
||||
|
||||
@@ -1,334 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_simplify/mod.rs
|
||||
---
|
||||
SIM115.py:8:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
7 | # SIM115
|
||||
8 | f = open("foo.txt")
|
||||
| ^^^^ SIM115
|
||||
9 | f = Path("foo.txt").open()
|
||||
10 | f = pathlib.Path("foo.txt").open()
|
||||
|
|
||||
|
||||
SIM115.py:9:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
7 | # SIM115
|
||||
8 | f = open("foo.txt")
|
||||
9 | f = Path("foo.txt").open()
|
||||
| ^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
10 | f = pathlib.Path("foo.txt").open()
|
||||
11 | f = pl.Path("foo.txt").open()
|
||||
|
|
||||
|
||||
SIM115.py:10:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
8 | f = open("foo.txt")
|
||||
9 | f = Path("foo.txt").open()
|
||||
10 | f = pathlib.Path("foo.txt").open()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
11 | f = pl.Path("foo.txt").open()
|
||||
12 | f = P("foo.txt").open()
|
||||
|
|
||||
|
||||
SIM115.py:11:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
9 | f = Path("foo.txt").open()
|
||||
10 | f = pathlib.Path("foo.txt").open()
|
||||
11 | f = pl.Path("foo.txt").open()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
12 | f = P("foo.txt").open()
|
||||
13 | data = f.read()
|
||||
|
|
||||
|
||||
SIM115.py:12:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
10 | f = pathlib.Path("foo.txt").open()
|
||||
11 | f = pl.Path("foo.txt").open()
|
||||
12 | f = P("foo.txt").open()
|
||||
| ^^^^^^^^^^^^^^^^^ SIM115
|
||||
13 | data = f.read()
|
||||
14 | f.close()
|
||||
|
|
||||
|
||||
SIM115.py:39:9: SIM115 Use a context manager for opening files
|
||||
|
|
||||
37 | # SIM115
|
||||
38 | with contextlib.ExitStack():
|
||||
39 | f = open("filename")
|
||||
| ^^^^ SIM115
|
||||
40 |
|
||||
41 | # OK
|
||||
|
|
||||
|
||||
SIM115.py:80:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
78 | import fileinput
|
||||
79 |
|
||||
80 | f = tempfile.NamedTemporaryFile()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
81 | f = tempfile.TemporaryFile()
|
||||
82 | f = tempfile.SpooledTemporaryFile()
|
||||
|
|
||||
|
||||
SIM115.py:81:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
80 | f = tempfile.NamedTemporaryFile()
|
||||
81 | f = tempfile.TemporaryFile()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
82 | f = tempfile.SpooledTemporaryFile()
|
||||
83 | f = tarfile.open("foo.tar")
|
||||
|
|
||||
|
||||
SIM115.py:82:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
80 | f = tempfile.NamedTemporaryFile()
|
||||
81 | f = tempfile.TemporaryFile()
|
||||
82 | f = tempfile.SpooledTemporaryFile()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
83 | f = tarfile.open("foo.tar")
|
||||
84 | f = TarFile("foo.tar").open()
|
||||
|
|
||||
|
||||
SIM115.py:83:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
81 | f = tempfile.TemporaryFile()
|
||||
82 | f = tempfile.SpooledTemporaryFile()
|
||||
83 | f = tarfile.open("foo.tar")
|
||||
| ^^^^^^^^^^^^ SIM115
|
||||
84 | f = TarFile("foo.tar").open()
|
||||
85 | f = tarfile.TarFile("foo.tar").open()
|
||||
|
|
||||
|
||||
SIM115.py:84:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
82 | f = tempfile.SpooledTemporaryFile()
|
||||
83 | f = tarfile.open("foo.tar")
|
||||
84 | f = TarFile("foo.tar").open()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
85 | f = tarfile.TarFile("foo.tar").open()
|
||||
86 | f = tarfile.TarFile().open()
|
||||
|
|
||||
|
||||
SIM115.py:85:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
83 | f = tarfile.open("foo.tar")
|
||||
84 | f = TarFile("foo.tar").open()
|
||||
85 | f = tarfile.TarFile("foo.tar").open()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
86 | f = tarfile.TarFile().open()
|
||||
87 | f = zipfile.ZipFile("foo.zip").open("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:86:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
84 | f = TarFile("foo.tar").open()
|
||||
85 | f = tarfile.TarFile("foo.tar").open()
|
||||
86 | f = tarfile.TarFile().open()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
87 | f = zipfile.ZipFile("foo.zip").open("foo.txt")
|
||||
88 | f = io.open("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:87:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
85 | f = tarfile.TarFile("foo.tar").open()
|
||||
86 | f = tarfile.TarFile().open()
|
||||
87 | f = zipfile.ZipFile("foo.zip").open("foo.txt")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
88 | f = io.open("foo.txt")
|
||||
89 | f = io.open_code("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:88:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
86 | f = tarfile.TarFile().open()
|
||||
87 | f = zipfile.ZipFile("foo.zip").open("foo.txt")
|
||||
88 | f = io.open("foo.txt")
|
||||
| ^^^^^^^ SIM115
|
||||
89 | f = io.open_code("foo.txt")
|
||||
90 | f = codecs.open("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:89:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
87 | f = zipfile.ZipFile("foo.zip").open("foo.txt")
|
||||
88 | f = io.open("foo.txt")
|
||||
89 | f = io.open_code("foo.txt")
|
||||
| ^^^^^^^^^^^^ SIM115
|
||||
90 | f = codecs.open("foo.txt")
|
||||
91 | f = bz2.open("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:90:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
88 | f = io.open("foo.txt")
|
||||
89 | f = io.open_code("foo.txt")
|
||||
90 | f = codecs.open("foo.txt")
|
||||
| ^^^^^^^^^^^ SIM115
|
||||
91 | f = bz2.open("foo.txt")
|
||||
92 | f = gzip.open("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:91:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
89 | f = io.open_code("foo.txt")
|
||||
90 | f = codecs.open("foo.txt")
|
||||
91 | f = bz2.open("foo.txt")
|
||||
| ^^^^^^^^ SIM115
|
||||
92 | f = gzip.open("foo.txt")
|
||||
93 | f = dbm.open("foo.db")
|
||||
|
|
||||
|
||||
SIM115.py:92:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
90 | f = codecs.open("foo.txt")
|
||||
91 | f = bz2.open("foo.txt")
|
||||
92 | f = gzip.open("foo.txt")
|
||||
| ^^^^^^^^^ SIM115
|
||||
93 | f = dbm.open("foo.db")
|
||||
94 | f = dbm.gnu.open("foo.db")
|
||||
|
|
||||
|
||||
SIM115.py:93:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
91 | f = bz2.open("foo.txt")
|
||||
92 | f = gzip.open("foo.txt")
|
||||
93 | f = dbm.open("foo.db")
|
||||
| ^^^^^^^^ SIM115
|
||||
94 | f = dbm.gnu.open("foo.db")
|
||||
95 | f = dbm.ndbm.open("foo.db")
|
||||
|
|
||||
|
||||
SIM115.py:94:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
92 | f = gzip.open("foo.txt")
|
||||
93 | f = dbm.open("foo.db")
|
||||
94 | f = dbm.gnu.open("foo.db")
|
||||
| ^^^^^^^^^^^^ SIM115
|
||||
95 | f = dbm.ndbm.open("foo.db")
|
||||
96 | f = dbm.dumb.open("foo.db")
|
||||
|
|
||||
|
||||
SIM115.py:95:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
93 | f = dbm.open("foo.db")
|
||||
94 | f = dbm.gnu.open("foo.db")
|
||||
95 | f = dbm.ndbm.open("foo.db")
|
||||
| ^^^^^^^^^^^^^ SIM115
|
||||
96 | f = dbm.dumb.open("foo.db")
|
||||
97 | f = lzma.open("foo.xz")
|
||||
|
|
||||
|
||||
SIM115.py:96:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
94 | f = dbm.gnu.open("foo.db")
|
||||
95 | f = dbm.ndbm.open("foo.db")
|
||||
96 | f = dbm.dumb.open("foo.db")
|
||||
| ^^^^^^^^^^^^^ SIM115
|
||||
97 | f = lzma.open("foo.xz")
|
||||
98 | f = lzma.LZMAFile("foo.xz")
|
||||
|
|
||||
|
||||
SIM115.py:97:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
95 | f = dbm.ndbm.open("foo.db")
|
||||
96 | f = dbm.dumb.open("foo.db")
|
||||
97 | f = lzma.open("foo.xz")
|
||||
| ^^^^^^^^^ SIM115
|
||||
98 | f = lzma.LZMAFile("foo.xz")
|
||||
99 | f = shelve.open("foo.db")
|
||||
|
|
||||
|
||||
SIM115.py:98:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
96 | f = dbm.dumb.open("foo.db")
|
||||
97 | f = lzma.open("foo.xz")
|
||||
98 | f = lzma.LZMAFile("foo.xz")
|
||||
| ^^^^^^^^^^^^^ SIM115
|
||||
99 | f = shelve.open("foo.db")
|
||||
100 | f = tokenize.open("foo.py")
|
||||
|
|
||||
|
||||
SIM115.py:99:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
97 | f = lzma.open("foo.xz")
|
||||
98 | f = lzma.LZMAFile("foo.xz")
|
||||
99 | f = shelve.open("foo.db")
|
||||
| ^^^^^^^^^^^ SIM115
|
||||
100 | f = tokenize.open("foo.py")
|
||||
101 | f = wave.open("foo.wav")
|
||||
|
|
||||
|
||||
SIM115.py:100:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
98 | f = lzma.LZMAFile("foo.xz")
|
||||
99 | f = shelve.open("foo.db")
|
||||
100 | f = tokenize.open("foo.py")
|
||||
| ^^^^^^^^^^^^^ SIM115
|
||||
101 | f = wave.open("foo.wav")
|
||||
102 | f = tarfile.TarFile.taropen("foo.tar")
|
||||
|
|
||||
|
||||
SIM115.py:101:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
99 | f = shelve.open("foo.db")
|
||||
100 | f = tokenize.open("foo.py")
|
||||
101 | f = wave.open("foo.wav")
|
||||
| ^^^^^^^^^ SIM115
|
||||
102 | f = tarfile.TarFile.taropen("foo.tar")
|
||||
103 | f = fileinput.input("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:102:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
100 | f = tokenize.open("foo.py")
|
||||
101 | f = wave.open("foo.wav")
|
||||
102 | f = tarfile.TarFile.taropen("foo.tar")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
103 | f = fileinput.input("foo.txt")
|
||||
104 | f = fileinput.FileInput("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:103:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
101 | f = wave.open("foo.wav")
|
||||
102 | f = tarfile.TarFile.taropen("foo.tar")
|
||||
103 | f = fileinput.input("foo.txt")
|
||||
| ^^^^^^^^^^^^^^^ SIM115
|
||||
104 | f = fileinput.FileInput("foo.txt")
|
||||
|
|
||||
|
||||
SIM115.py:104:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
102 | f = tarfile.TarFile.taropen("foo.tar")
|
||||
103 | f = fileinput.input("foo.txt")
|
||||
104 | f = fileinput.FileInput("foo.txt")
|
||||
| ^^^^^^^^^^^^^^^^^^^ SIM115
|
||||
105 |
|
||||
106 | with contextlib.suppress(Exception):
|
||||
|
|
||||
|
||||
SIM115.py:240:9: SIM115 Use a context manager for opening files
|
||||
|
|
||||
238 | def aliased():
|
||||
239 | from shelve import open as open_shelf
|
||||
240 | x = open_shelf("foo.dbm")
|
||||
| ^^^^^^^^^^ SIM115
|
||||
241 | x.close()
|
||||
|
|
||||
|
||||
SIM115.py:244:9: SIM115 Use a context manager for opening files
|
||||
|
|
||||
243 | from tarfile import TarFile as TF
|
||||
244 | f = TF("foo").open()
|
||||
| ^^^^^^^^^^^^^^ SIM115
|
||||
245 | f.close()
|
||||
|
|
||||
|
||||
SIM115.py:257:5: SIM115 Use a context manager for opening files
|
||||
|
|
||||
256 | # SIM115
|
||||
257 | f = dbm.sqlite3.open("foo.db")
|
||||
| ^^^^^^^^^^^^^^^^ SIM115
|
||||
258 | f.close()
|
||||
|
|
||||
@@ -19,9 +19,9 @@ mod tests {
|
||||
#[test_case(Rule::RaiseVanillaArgs, Path::new("TRY003.py"))]
|
||||
#[test_case(Rule::TypeCheckWithoutTypeError, Path::new("TRY004.py"))]
|
||||
#[test_case(Rule::VerboseRaise, Path::new("TRY201.py"))]
|
||||
#[test_case(Rule::UselessTryExcept, Path::new("TRY203.py"))]
|
||||
#[test_case(Rule::TryConsiderElse, Path::new("TRY300.py"))]
|
||||
#[test_case(Rule::RaiseWithinTry, Path::new("TRY301.py"))]
|
||||
#[test_case(Rule::UselessTryExcept, Path::new("TRY302.py"))]
|
||||
#[test_case(Rule::ErrorInsteadOfException, Path::new("TRY400.py"))]
|
||||
#[test_case(Rule::VerboseLogMessage, Path::new("TRY401.py"))]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
|
||||
@@ -38,7 +38,7 @@ impl Violation for UselessTryExcept {
|
||||
}
|
||||
}
|
||||
|
||||
/// TRY302
|
||||
/// TRY203 (previously TRY302)
|
||||
pub(crate) fn useless_try_except(checker: &mut Checker, handlers: &[ExceptHandler]) {
|
||||
if let Some(diagnostics) = handlers
|
||||
.iter()
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/tryceratops/mod.rs
|
||||
---
|
||||
TRY302.py:12:5: TRY302 Remove exception handler; error is immediately re-raised
|
||||
TRY203.py:12:5: TRY203 Remove exception handler; error is immediately re-raised
|
||||
|
|
||||
10 | try:
|
||||
11 | process()
|
||||
12 | except Exception:
|
||||
| _____^
|
||||
13 | | raise
|
||||
| |_____________^ TRY302
|
||||
| |_____________^ TRY203
|
||||
14 |
|
||||
15 | def bad():
|
||||
|
|
||||
|
||||
TRY302.py:18:5: TRY302 Remove exception handler; error is immediately re-raised
|
||||
TRY203.py:18:5: TRY203 Remove exception handler; error is immediately re-raised
|
||||
|
|
||||
16 | try:
|
||||
17 | process()
|
||||
@@ -21,12 +21,12 @@ TRY302.py:18:5: TRY302 Remove exception handler; error is immediately re-raised
|
||||
| _____^
|
||||
19 | | raise
|
||||
20 | | print("this code is pointless!")
|
||||
| |________________________________________^ TRY302
|
||||
| |________________________________________^ TRY203
|
||||
21 |
|
||||
22 | def bad():
|
||||
|
|
||||
|
||||
TRY302.py:25:5: TRY302 Remove exception handler; error is immediately re-raised
|
||||
TRY203.py:25:5: TRY203 Remove exception handler; error is immediately re-raised
|
||||
|
|
||||
23 | try:
|
||||
24 | process()
|
||||
@@ -34,117 +34,115 @@ TRY302.py:25:5: TRY302 Remove exception handler; error is immediately re-raised
|
||||
| _____^
|
||||
26 | | # I am a comment, not a statement!
|
||||
27 | | raise
|
||||
| |_____________^ TRY302
|
||||
| |_____________^ TRY203
|
||||
28 |
|
||||
29 | def bad():
|
||||
|
|
||||
|
||||
TRY302.py:32:5: TRY302 Remove exception handler; error is immediately re-raised
|
||||
TRY203.py:32:5: TRY203 Remove exception handler; error is immediately re-raised
|
||||
|
|
||||
30 | try:
|
||||
31 | process()
|
||||
32 | except Exception:
|
||||
| _____^
|
||||
33 | | raise
|
||||
| |_____________^ TRY302
|
||||
| |_____________^ TRY203
|
||||
34 |
|
||||
35 | def bad():
|
||||
|
|
||||
|
||||
TRY302.py:38:5: TRY302 Remove exception handler; error is immediately re-raised
|
||||
TRY203.py:38:5: TRY203 Remove exception handler; error is immediately re-raised
|
||||
|
|
||||
36 | try:
|
||||
37 | process()
|
||||
38 | except Exception as e:
|
||||
| _____^
|
||||
39 | | raise
|
||||
| |_____________^ TRY302
|
||||
| |_____________^ TRY203
|
||||
40 |
|
||||
41 | def bad():
|
||||
|
|
||||
|
||||
TRY302.py:44:5: TRY302 Remove exception handler; error is immediately re-raised
|
||||
TRY203.py:44:5: TRY203 Remove exception handler; error is immediately re-raised
|
||||
|
|
||||
42 | try:
|
||||
43 | process()
|
||||
44 | except Exception as e:
|
||||
| _____^
|
||||
45 | | raise e
|
||||
| |_______________^ TRY302
|
||||
| |_______________^ TRY203
|
||||
46 |
|
||||
47 | def bad():
|
||||
|
|
||||
|
||||
TRY302.py:50:5: TRY302 Remove exception handler; error is immediately re-raised
|
||||
TRY203.py:50:5: TRY203 Remove exception handler; error is immediately re-raised
|
||||
|
|
||||
48 | try:
|
||||
49 | process()
|
||||
50 | except MyException:
|
||||
| _____^
|
||||
51 | | raise
|
||||
| |_____________^ TRY302
|
||||
| |_____________^ TRY203
|
||||
52 | except Exception:
|
||||
53 | raise
|
||||
|
|
||||
|
||||
TRY302.py:52:5: TRY302 Remove exception handler; error is immediately re-raised
|
||||
TRY203.py:52:5: TRY203 Remove exception handler; error is immediately re-raised
|
||||
|
|
||||
50 | except MyException:
|
||||
51 | raise
|
||||
52 | except Exception:
|
||||
| _____^
|
||||
53 | | raise
|
||||
| |_____________^ TRY302
|
||||
| |_____________^ TRY203
|
||||
54 |
|
||||
55 | def bad():
|
||||
|
|
||||
|
||||
TRY302.py:58:5: TRY302 Remove exception handler; error is immediately re-raised
|
||||
TRY203.py:58:5: TRY203 Remove exception handler; error is immediately re-raised
|
||||
|
|
||||
56 | try:
|
||||
57 | process()
|
||||
58 | except MyException as e:
|
||||
| _____^
|
||||
59 | | raise e
|
||||
| |_______________^ TRY302
|
||||
| |_______________^ TRY203
|
||||
60 | except Exception as e:
|
||||
61 | raise e
|
||||
|
|
||||
|
||||
TRY302.py:60:5: TRY302 Remove exception handler; error is immediately re-raised
|
||||
TRY203.py:60:5: TRY203 Remove exception handler; error is immediately re-raised
|
||||
|
|
||||
58 | except MyException as e:
|
||||
59 | raise e
|
||||
60 | except Exception as e:
|
||||
| _____^
|
||||
61 | | raise e
|
||||
| |_______________^ TRY302
|
||||
| |_______________^ TRY203
|
||||
62 |
|
||||
63 | def bad():
|
||||
|
|
||||
|
||||
TRY302.py:66:5: TRY302 Remove exception handler; error is immediately re-raised
|
||||
TRY203.py:66:5: TRY203 Remove exception handler; error is immediately re-raised
|
||||
|
|
||||
64 | try:
|
||||
65 | process()
|
||||
66 | except MyException as ex:
|
||||
| _____^
|
||||
67 | | raise ex
|
||||
| |________________^ TRY302
|
||||
| |________________^ TRY203
|
||||
68 | except Exception as e:
|
||||
69 | raise e
|
||||
|
|
||||
|
||||
TRY302.py:68:5: TRY302 Remove exception handler; error is immediately re-raised
|
||||
TRY203.py:68:5: TRY203 Remove exception handler; error is immediately re-raised
|
||||
|
|
||||
66 | except MyException as ex:
|
||||
67 | raise ex
|
||||
68 | except Exception as e:
|
||||
| _____^
|
||||
69 | | raise e
|
||||
| |_______________^ TRY302
|
||||
| |_______________^ TRY203
|
||||
70 |
|
||||
71 | def fine():
|
||||
|
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
@@ -511,12 +509,6 @@ impl FromIterator<ExtensionPair> for ExtensionMapping {
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum OutputFormat {
|
||||
// Remove the module level `#![allow(deprecated)` when removing the text variant.
|
||||
// Adding the `#[deprecated]` attribute to text creates clippy warnings about
|
||||
// using a deprecated item in the derived code and there seems to be no way to suppress the clippy error
|
||||
// other than disabling the warning for the entire module and/or moving `OutputFormat` to another module.
|
||||
#[deprecated(note = "Use `concise` or `full` instead")]
|
||||
Text,
|
||||
Concise,
|
||||
#[default]
|
||||
Full,
|
||||
@@ -535,7 +527,6 @@ pub enum OutputFormat {
|
||||
impl Display for OutputFormat {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Text => write!(f, "text"),
|
||||
Self::Concise => write!(f, "concise"),
|
||||
Self::Full => write!(f, "full"),
|
||||
Self::Json => write!(f, "json"),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_wasm"
|
||||
version = "0.6.9"
|
||||
version = "0.7.0"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -347,9 +347,7 @@ impl Configuration {
|
||||
.unwrap_or_default(),
|
||||
flake8_pytest_style: lint
|
||||
.flake8_pytest_style
|
||||
.map(|options| {
|
||||
Flake8PytestStyleOptions::try_into_settings(options, lint_preview)
|
||||
})
|
||||
.map(Flake8PytestStyleOptions::try_into_settings)
|
||||
.transpose()?
|
||||
.unwrap_or_default(),
|
||||
flake8_quotes: lint
|
||||
@@ -445,26 +443,6 @@ impl Configuration {
|
||||
}
|
||||
};
|
||||
|
||||
#[allow(deprecated)]
|
||||
if options.tab_size.is_some() {
|
||||
let config_to_update = path.map_or_else(
|
||||
|| String::from("your `--config` CLI arguments"),
|
||||
|path| format!("`{}`", fs::relativize_path(path)),
|
||||
);
|
||||
return Err(anyhow!("The `tab-size` option has been renamed to `indent-width` to emphasize that it configures the indentation used by the formatter as well as the tab width. Please update {config_to_update} to use `indent-width = <value>` instead."));
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
if options.output_format == Some(OutputFormat::Text) {
|
||||
let config_to_update = path.map_or_else(
|
||||
|| String::from("your `--config` CLI arguments"),
|
||||
|path| format!("`{}`", fs::relativize_path(path)),
|
||||
);
|
||||
return Err(anyhow!(
|
||||
r#"The option `output_format=text` is no longer supported. Update {config_to_update} to use `output-format="concise"` or `output-format="full"` instead."#
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
builtins: options.builtins,
|
||||
cache_dir: options
|
||||
@@ -626,7 +604,6 @@ pub struct LintConfiguration {
|
||||
pub logger_objects: Option<Vec<String>>,
|
||||
pub task_tags: Option<Vec<String>>,
|
||||
pub typing_modules: Option<Vec<String>>,
|
||||
pub allowed_unused_imports: Option<Vec<String>>,
|
||||
|
||||
// Plugins
|
||||
pub flake8_annotations: Option<Flake8AnnotationsOptions>,
|
||||
@@ -739,7 +716,7 @@ impl LintConfiguration {
|
||||
task_tags: options.common.task_tags,
|
||||
logger_objects: options.common.logger_objects,
|
||||
typing_modules: options.common.typing_modules,
|
||||
allowed_unused_imports: options.common.allowed_unused_imports,
|
||||
|
||||
// Plugins
|
||||
flake8_annotations: options.common.flake8_annotations,
|
||||
flake8_bandit: options.common.flake8_bandit,
|
||||
@@ -1108,9 +1085,7 @@ impl LintConfiguration {
|
||||
.or(config.explicit_preview_rules),
|
||||
task_tags: self.task_tags.or(config.task_tags),
|
||||
typing_modules: self.typing_modules.or(config.typing_modules),
|
||||
allowed_unused_imports: self
|
||||
.allowed_unused_imports
|
||||
.or(config.allowed_unused_imports),
|
||||
|
||||
// Plugins
|
||||
flake8_annotations: self.flake8_annotations.combine(config.flake8_annotations),
|
||||
flake8_bandit: self.flake8_bandit.combine(config.flake8_bandit),
|
||||
@@ -1332,7 +1307,6 @@ fn warn_about_deprecated_top_level_lint_options(
|
||||
explicit_preview_rules,
|
||||
task_tags,
|
||||
typing_modules,
|
||||
allowed_unused_imports,
|
||||
unfixable,
|
||||
flake8_annotations,
|
||||
flake8_bandit,
|
||||
@@ -1431,9 +1405,6 @@ fn warn_about_deprecated_top_level_lint_options(
|
||||
if typing_modules.is_some() {
|
||||
used_options.push("typing-modules");
|
||||
}
|
||||
if allowed_unused_imports.is_some() {
|
||||
used_options.push("allowed-unused-imports");
|
||||
}
|
||||
|
||||
if unfixable.is_some() {
|
||||
used_options.push("unfixable");
|
||||
|
||||
@@ -27,7 +27,7 @@ use ruff_linter::rules::{
|
||||
pycodestyle, pydocstyle, pyflakes, pylint, pyupgrade, ruff,
|
||||
};
|
||||
use ruff_linter::settings::types::{
|
||||
IdentifierPattern, OutputFormat, PreviewMode, PythonVersion, RequiredVersion,
|
||||
IdentifierPattern, OutputFormat, PythonVersion, RequiredVersion,
|
||||
};
|
||||
use ruff_linter::{warn_user_once, RuleSelector};
|
||||
use ruff_macros::{CombineOptions, OptionsMetadata};
|
||||
@@ -415,17 +415,6 @@ pub struct Options {
|
||||
)]
|
||||
pub indent_width: Option<IndentWidth>,
|
||||
|
||||
/// The number of spaces a tab is equal to when enforcing long-line violations (like `E501`)
|
||||
/// or formatting code with the formatter.
|
||||
///
|
||||
/// This option changes the number of spaces inserted by the formatter when
|
||||
/// using soft-tabs (`indent-style = space`).
|
||||
#[deprecated(
|
||||
since = "0.1.2",
|
||||
note = "The `tab-size` option has been renamed to `indent-width` to emphasize that it configures the indentation used by the formatter as well as the tab width. Please update your configuration to use `indent-width = <value>` instead."
|
||||
)]
|
||||
pub tab_size: Option<IndentWidth>,
|
||||
|
||||
#[option_group]
|
||||
pub lint: Option<LintOptions>,
|
||||
|
||||
@@ -796,16 +785,6 @@ pub struct LintCommonOptions {
|
||||
)]
|
||||
pub typing_modules: Option<Vec<String>>,
|
||||
|
||||
/// A list of modules which is allowed even though they are not used
|
||||
/// in the code.
|
||||
///
|
||||
/// This is useful when a module has a side effect when imported.
|
||||
#[option(
|
||||
default = r#"[]"#,
|
||||
value_type = "list[str]",
|
||||
example = r#"allowed-unused-imports = ["hvplot.pandas"]"#
|
||||
)]
|
||||
pub allowed_unused_imports: Option<Vec<String>>,
|
||||
/// A list of rule codes or prefixes to consider non-fixable.
|
||||
#[option(
|
||||
default = "[]",
|
||||
@@ -1511,12 +1490,9 @@ pub struct Flake8PytestStyleOptions {
|
||||
}
|
||||
|
||||
impl Flake8PytestStyleOptions {
|
||||
pub fn try_into_settings(
|
||||
self,
|
||||
preview: PreviewMode,
|
||||
) -> anyhow::Result<flake8_pytest_style::settings::Settings> {
|
||||
pub fn try_into_settings(self) -> anyhow::Result<flake8_pytest_style::settings::Settings> {
|
||||
Ok(flake8_pytest_style::settings::Settings {
|
||||
fixture_parentheses: self.fixture_parentheses.unwrap_or(preview.is_disabled()),
|
||||
fixture_parentheses: self.fixture_parentheses.unwrap_or_default(),
|
||||
parametrize_names_type: self.parametrize_names_type.unwrap_or_default(),
|
||||
parametrize_values_type: self.parametrize_values_type.unwrap_or_default(),
|
||||
parametrize_values_row_type: self.parametrize_values_row_type.unwrap_or_default(),
|
||||
@@ -1542,7 +1518,7 @@ impl Flake8PytestStyleOptions {
|
||||
.transpose()
|
||||
.map_err(SettingsError::InvalidRaisesExtendRequireMatchFor)?
|
||||
.unwrap_or_default(),
|
||||
mark_parentheses: self.mark_parentheses.unwrap_or(preview.is_disabled()),
|
||||
mark_parentheses: self.mark_parentheses.unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,7 +589,7 @@ Options:
|
||||
Ignore any `# noqa` comments
|
||||
--output-format <OUTPUT_FORMAT>
|
||||
Output serialization format for violations. The default serialization
|
||||
format is "full" [env: RUFF_OUTPUT_FORMAT=] [possible values: text,
|
||||
format is "full" [env: RUFF_OUTPUT_FORMAT=] [possible values:
|
||||
concise, full, json, json-lines, junit, grouped, github, gitlab,
|
||||
pylint, rdjson, azure, sarif]
|
||||
-o, --output-file <OUTPUT_FILE>
|
||||
|
||||
@@ -78,7 +78,7 @@ Ruff can be used as a [pre-commit](https://pre-commit.com) hook via [`ruff-pre-c
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.6.9
|
||||
rev: v0.7.0
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
@@ -91,7 +91,7 @@ To enable lint fixes, add the `--fix` argument to the lint hook:
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.6.9
|
||||
rev: v0.7.0
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
@@ -105,7 +105,7 @@ To run the hooks over Jupyter Notebooks too, add `jupyter` to the list of allowe
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.6.9
|
||||
rev: v0.7.0
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "ruff"
|
||||
version = "0.6.9"
|
||||
version = "0.7.0"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
authors = [{ name = "Astral Software Inc.", email = "hey@astral.sh" }]
|
||||
readme = "README.md"
|
||||
|
||||
74
ruff.schema.json
generated
74
ruff.schema.json
generated
@@ -16,17 +16,6 @@
|
||||
"minLength": 1
|
||||
}
|
||||
},
|
||||
"allowed-unused-imports": {
|
||||
"description": "A list of modules which is allowed even though they are not used in the code.\n\nThis is useful when a module has a side effect when imported.",
|
||||
"deprecated": true,
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"analyze": {
|
||||
"description": "Options to configure import map generation.",
|
||||
"anyOf": [
|
||||
@@ -702,18 +691,6 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"tab-size": {
|
||||
"description": "The number of spaces a tab is equal to when enforcing long-line violations (like `E501`) or formatting code with the formatter.\n\nThis option changes the number of spaces inserted by the formatter when using soft-tabs (`indent-style = space`).",
|
||||
"deprecated": true,
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/IndentWidth"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"target-version": {
|
||||
"description": "The minimum Python version to target, e.g., when considering automatic code upgrades, like rewriting type annotations. Ruff will not propose changes using features that are not available in the given version.\n\nFor example, to represent supporting Python >=3.10 or ==3.10 specify `target-version = \"py310\"`.\n\nIf you're already using a `pyproject.toml` file, we recommend `project.requires-python` instead, as it's based on Python packaging standards, and will be respected by other tools. For example, Ruff treats the following as identical to `target-version = \"py38\"`:\n\n```toml [project] requires-python = \">=3.8\" ```\n\nIf both are specified, `target-version` takes precedence over `requires-python`.\n\nNote that a stub file can [sometimes make use of a typing feature](https://typing.readthedocs.io/en/latest/spec/distributing.html#syntax) before it is available at runtime, as long as the stub does not make use of new *syntax*. For example, a type checker will understand `int | str` in a stub as being a `Union` type annotation, even if the type checker is run using Python 3.9, despite the fact that the `|` operator can only be used to create union types at runtime on Python 3.10+. As such, Ruff will often recommend newer features in a stub file than it would for an equivalent runtime file with the same target version.",
|
||||
"anyOf": [
|
||||
@@ -1898,16 +1875,6 @@
|
||||
"minLength": 1
|
||||
}
|
||||
},
|
||||
"allowed-unused-imports": {
|
||||
"description": "A list of modules which is allowed even though they are not used in the code.\n\nThis is useful when a module has a side effect when imported.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"dummy-variable-rgx": {
|
||||
"description": "A regular expression used to identify \"dummy\" variables, or those which should be ignored when enforcing (e.g.) unused-variable rules. The default expression matches `_`, `__`, and `_var`, but not `_var_`.",
|
||||
"type": [
|
||||
@@ -2423,31 +2390,20 @@
|
||||
"type": "string"
|
||||
},
|
||||
"OutputFormat": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"concise",
|
||||
"full",
|
||||
"json",
|
||||
"json-lines",
|
||||
"junit",
|
||||
"grouped",
|
||||
"github",
|
||||
"gitlab",
|
||||
"pylint",
|
||||
"rdjson",
|
||||
"azure",
|
||||
"sarif"
|
||||
]
|
||||
},
|
||||
{
|
||||
"deprecated": true,
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"text"
|
||||
]
|
||||
}
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"concise",
|
||||
"full",
|
||||
"json",
|
||||
"json-lines",
|
||||
"junit",
|
||||
"grouped",
|
||||
"github",
|
||||
"gitlab",
|
||||
"pylint",
|
||||
"rdjson",
|
||||
"azure",
|
||||
"sarif"
|
||||
]
|
||||
},
|
||||
"ParametrizeNameType": {
|
||||
@@ -4053,11 +4009,11 @@
|
||||
"TRY2",
|
||||
"TRY20",
|
||||
"TRY201",
|
||||
"TRY203",
|
||||
"TRY3",
|
||||
"TRY30",
|
||||
"TRY300",
|
||||
"TRY301",
|
||||
"TRY302",
|
||||
"TRY4",
|
||||
"TRY40",
|
||||
"TRY400",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "scripts"
|
||||
version = "0.6.9"
|
||||
version = "0.7.0"
|
||||
description = ""
|
||||
authors = ["Charles Marsh <charlie.r.marsh@gmail.com>"]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user