[`refurb`] Fix `FURB103` autofix (#21454)

This commit is contained in:
chiri 2025-11-16 11:32:41 +03:00 committed by GitHub
parent 3065f8dbbc
commit 7a546809c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 90 additions and 27 deletions

View File

@ -153,3 +153,12 @@ data = {"price": 100}
with open("test.json", "wb") as f: with open("test.json", "wb") as f:
f.write(json.dumps(data, indent=4).encode("utf-8")) f.write(json.dumps(data, indent=4).encode("utf-8"))
# See: https://github.com/astral-sh/ruff/issues/21381
with open("tmp_path/pyproject.toml", "w") as f:
f.write(dedent(
"""
[project]
other = 1.234
""",
))

View File

@ -2,17 +2,15 @@ use ruff_diagnostics::{Applicability, Edit, Fix};
use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{ use ruff_python_ast::{
self as ast, Expr, Stmt, self as ast, Expr, Stmt,
relocate::relocate_expr,
visitor::{self, Visitor}, visitor::{self, Visitor},
}; };
use ruff_python_codegen::Generator; use ruff_text_size::Ranged;
use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::snippet::SourceCodeSnippet; use crate::fix::snippet::SourceCodeSnippet;
use crate::importer::ImportRequest; use crate::importer::ImportRequest;
use crate::rules::refurb::helpers::{FileOpen, find_file_opens}; use crate::rules::refurb::helpers::{FileOpen, find_file_opens};
use crate::{FixAvailability, Violation}; use crate::{FixAvailability, Locator, Violation};
/// ## What it does /// ## What it does
/// Checks for uses of `open` and `write` that can be replaced by `pathlib` /// Checks for uses of `open` and `write` that can be replaced by `pathlib`
@ -129,7 +127,7 @@ impl<'a> Visitor<'a> for WriteMatcher<'a, '_> {
let open = self.candidates.remove(open); let open = self.candidates.remove(open);
if self.loop_counter == 0 { if self.loop_counter == 0 {
let suggestion = make_suggestion(&open, content, self.checker.generator()); let suggestion = make_suggestion(&open, content, self.checker.locator());
let mut diagnostic = self.checker.report_diagnostic( let mut diagnostic = self.checker.report_diagnostic(
WriteWholeFile { WriteWholeFile {
@ -172,27 +170,21 @@ fn match_write_call(expr: &Expr) -> Option<(&Expr, &Expr)> {
Some((&*attr.value, call.arguments.args.first()?)) Some((&*attr.value, call.arguments.args.first()?))
} }
fn make_suggestion(open: &FileOpen<'_>, arg: &Expr, generator: Generator) -> String { fn make_suggestion(open: &FileOpen<'_>, arg: &Expr, locator: &Locator) -> String {
let name = ast::ExprName { let method_name = open.mode.pathlib_method();
id: open.mode.pathlib_method(), let arg_code = locator.slice(arg.range());
ctx: ast::ExprContext::Load,
range: TextRange::default(), if open.keywords.is_empty() {
node_index: ruff_python_ast::AtomicNodeIndex::NONE, format!("{method_name}({arg_code})")
}; } else {
let mut arg = arg.clone(); format!(
relocate_expr(&mut arg, TextRange::default()); "{method_name}({arg_code}, {})",
let call = ast::ExprCall { itertools::join(
func: Box::new(name.into()), open.keywords.iter().map(|kw| locator.slice(kw.range())),
arguments: ast::Arguments { ", "
args: Box::new([arg]), )
keywords: open.keywords.iter().copied().cloned().collect(), )
range: TextRange::default(), }
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
},
range: TextRange::default(),
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
};
generator.expr(&call.into())
} }
fn generate_fix( fn generate_fix(

View File

@ -279,3 +279,34 @@ help: Replace with `Path("test.json")....`
- with open("test.json", "wb") as f: - with open("test.json", "wb") as f:
- f.write(json.dumps(data, indent=4).encode("utf-8")) - f.write(json.dumps(data, indent=4).encode("utf-8"))
155 + pathlib.Path("test.json").write_bytes(json.dumps(data, indent=4).encode("utf-8")) 155 + pathlib.Path("test.json").write_bytes(json.dumps(data, indent=4).encode("utf-8"))
156 |
157 | # See: https://github.com/astral-sh/ruff/issues/21381
158 | with open("tmp_path/pyproject.toml", "w") as f:
FURB103 [*] `open` and `write` should be replaced by `Path("tmp_path/pyproject.toml")....`
--> FURB103.py:158:6
|
157 | # See: https://github.com/astral-sh/ruff/issues/21381
158 | with open("tmp_path/pyproject.toml", "w") as f:
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
159 | f.write(dedent(
160 | """
|
help: Replace with `Path("tmp_path/pyproject.toml")....`
148 |
149 | # See: https://github.com/astral-sh/ruff/issues/20785
150 | import json
151 + import pathlib
152 |
153 | data = {"price": 100}
154 |
--------------------------------------------------------------------------------
156 | f.write(json.dumps(data, indent=4).encode("utf-8"))
157 |
158 | # See: https://github.com/astral-sh/ruff/issues/21381
- with open("tmp_path/pyproject.toml", "w") as f:
- f.write(dedent(
159 + pathlib.Path("tmp_path/pyproject.toml").write_text(dedent(
160 | """
161 | [project]
162 | other = 1.234

View File

@ -209,3 +209,34 @@ help: Replace with `Path("test.json")....`
- with open("test.json", "wb") as f: - with open("test.json", "wb") as f:
- f.write(json.dumps(data, indent=4).encode("utf-8")) - f.write(json.dumps(data, indent=4).encode("utf-8"))
155 + pathlib.Path("test.json").write_bytes(json.dumps(data, indent=4).encode("utf-8")) 155 + pathlib.Path("test.json").write_bytes(json.dumps(data, indent=4).encode("utf-8"))
156 |
157 | # See: https://github.com/astral-sh/ruff/issues/21381
158 | with open("tmp_path/pyproject.toml", "w") as f:
FURB103 [*] `open` and `write` should be replaced by `Path("tmp_path/pyproject.toml")....`
--> FURB103.py:158:6
|
157 | # See: https://github.com/astral-sh/ruff/issues/21381
158 | with open("tmp_path/pyproject.toml", "w") as f:
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
159 | f.write(dedent(
160 | """
|
help: Replace with `Path("tmp_path/pyproject.toml")....`
148 |
149 | # See: https://github.com/astral-sh/ruff/issues/20785
150 | import json
151 + import pathlib
152 |
153 | data = {"price": 100}
154 |
--------------------------------------------------------------------------------
156 | f.write(json.dumps(data, indent=4).encode("utf-8"))
157 |
158 | # See: https://github.com/astral-sh/ruff/issues/21381
- with open("tmp_path/pyproject.toml", "w") as f:
- f.write(dedent(
159 + pathlib.Path("tmp_path/pyproject.toml").write_text(dedent(
160 | """
161 | [project]
162 | other = 1.234