diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/unspecified_encoding.py b/crates/ruff_linter/resources/test/fixtures/pylint/unspecified_encoding.py index e6a9a65269d94..d89bdf06dcab4 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/unspecified_encoding.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/unspecified_encoding.py @@ -45,3 +45,19 @@ def func(*args, **kwargs): open("test.txt",) open() +open( + "test.txt", # comment +) +open( + "test.txt", + # comment +) +open(("test.txt"),) +open() +open( + ("test.txt"), # comment +) +open( + ("test.txt"), + # comment +) diff --git a/crates/ruff_linter/src/fix/edits.rs b/crates/ruff_linter/src/fix/edits.rs index a471042948a8a..bfde9f971cf4c 100644 --- a/crates/ruff_linter/src/fix/edits.rs +++ b/crates/ruff_linter/src/fix/edits.rs @@ -1,11 +1,12 @@ //! Interface for generating fix edits from higher-level actions (e.g., "remove an argument"). +use std::ops::Sub; + use anyhow::{Context, Result}; -use itertools::{Either, Itertools}; use ruff_diagnostics::Edit; +use ruff_python_ast::AnyNodeRef; use ruff_python_ast::{self as ast, Arguments, ExceptHandler, Stmt}; -use ruff_python_ast::{AnyNodeRef, ArgOrKeyword}; use ruff_python_codegen::Stylist; use ruff_python_index::Indexer; use ruff_python_trivia::{ @@ -139,66 +140,30 @@ pub(crate) fn remove_argument( } } -#[derive(Debug, Copy, Clone)] -pub(crate) enum ArgumentType { - Keyword, -} +/// Generic function to add arguments or keyword arguments to function calls. +pub(crate) fn add_argument(argument: &str, arguments: &Arguments, source: &str) -> Edit { + if let Some(last) = arguments.arguments_source_order().last() { + // Case 1: existing arguments, so append after the last argument. + let tokenizer = SimpleTokenizer::new( + source, + TextRange::new(last.end(), arguments.end().sub(TextSize::from(1))), + ); -/// Generic function to add arguments or keyword arguments to function -/// calls and class definitions. (For classes `args` should be considered -/// `bases`) -pub(crate) fn add_argument( - argument: &str, - arguments: &Arguments, - arg_type: ArgumentType, - source: &str, -) -> Result { - // Partition into arguments before and after the argument to remove. - let (positional, keyword): (Vec<_>, Vec<_>) = - arguments - .arguments_source_order() - .partition_map(|arg| match arg { - ArgOrKeyword::Arg(_) => Either::Left(arg), - ArgOrKeyword::Keyword(_) => Either::Right(arg), - }); - - if positional.is_empty() && keyword.is_empty() { - // Case 1: no arguments. Add argument to call without comma - // TODO: Check no parentheses case - Ok(Edit::insertion( - argument.to_string(), - arguments.start() + TextSize::from(1), - )) - } else { - match arg_type { - ArgumentType::Keyword => { - // Case 3: Keyword arg passed. Can be added at the end of call - // Look ahead back from last argument for a trailing comma - let Some(last_arg) = arguments.args.last() else { - panic!("No last argument found") - }; - let mut tokenizer = SimpleTokenizer::starts_at(last_arg.range().end(), source); - - // Find the next non-whitespace token. - let next = tokenizer - .find(|token| { - token.kind != SimpleTokenKind::Whitespace - && token.kind != SimpleTokenKind::Newline - }) - .context("Unable to find next token")?; - - match next.kind { - SimpleTokenKind::Comma => Ok(Edit::insertion( - argument.to_string(), - arguments.end() - TextSize::from(1), - )), - _ => Ok(Edit::insertion( - format!(", {argument}"), - arguments.end() - TextSize::from(1), - )), - } - } + // Skip any parentheses. + if let Some(token) = tokenizer + .skip_while(|token| token.kind.is_trivia()) + .next() + .filter(|token| token.kind == SimpleTokenKind::RParen) + { + // Ex) Insert after `func(x=(1))`. + Edit::insertion(format!(", {argument}"), token.end()) + } else { + // Ex) Insert after `func(x=1)`. + Edit::insertion(format!(", {argument}"), last.end()) } + } else { + // Case 2: no arguments. Add argument, without any trailing comma. + Edit::insertion(argument.to_string(), arguments.start() + TextSize::from(1)) } } diff --git a/crates/ruff_linter/src/rules/pylint/rules/unspecified_encoding.rs b/crates/ruff_linter/src/rules/pylint/rules/unspecified_encoding.rs index b0e925896c35a..21f1a12db910c 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/unspecified_encoding.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/unspecified_encoding.rs @@ -1,14 +1,15 @@ use anyhow::Result; + use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast as ast; use ruff_python_ast::call_path::{format_call_path, CallPath}; -use ruff_python_ast::imports::{AnyImport, Import}; -use ruff_text_size::{Ranged, TextSize}; +use ruff_python_ast::Expr; +use ruff_text_size::{Ranged, TextRange}; use crate::checkers::ast::Checker; use crate::fix::edits::add_argument; -use crate::fix::edits::ArgumentType::Keyword; +use crate::importer::ImportRequest; use crate::settings::types::PythonVersion; /// ## What it does @@ -20,7 +21,9 @@ use crate::settings::types::PythonVersion; /// non-portable code, with differing behavior across platforms. /// /// Instead, consider using the `encoding` parameter to enforce a specific -/// encoding. +/// encoding. [PEP 597] recommends using `locale.getpreferredencoding(False)` +/// as the default encoding on versions earlier than Python 3.10, and +/// `encoding="locale"` on Python 3.10 and later. /// /// ## Example /// ```python @@ -34,6 +37,8 @@ use crate::settings::types::PythonVersion; /// /// ## References /// - [Python documentation: `open`](https://docs.python.org/3/library/functions.html#open) +/// +/// [PEP 597]: https://peps.python.org/pep-0597/ #[violation] pub struct UnspecifiedEncoding { function_name: String, @@ -88,40 +93,52 @@ pub(crate) fn unspecified_encoding(checker: &mut Checker, call: &ast::ExprCall) ); if checker.settings.target_version >= PythonVersion::Py310 { - diagnostic.try_set_fix(|| { - add_argument( - "encoding=\"locale\"", - &call.arguments, - Keyword, - checker.locator().contents(), - ) - .map(Fix::unsafe_edit) - }); + diagnostic.set_fix(generate_keyword_fix(checker, call)); } else { - diagnostic.try_set_fix(|| generate_fix(checker, call)); + diagnostic.try_set_fix(|| generate_import_fix(checker, call)); } checker.diagnostics.push(diagnostic); } -/// Generate a [`Edit`] for Python39 and older. -fn generate_fix(checker: &Checker, call: &ast::ExprCall) -> Result { - Ok(Fix::unsafe_edits( - checker.importer().add_import( - &AnyImport::Import(Import::module("locale")), - TextSize::default(), +/// Generate an [`Edit`] for Python 3.10 and later. +fn generate_keyword_fix(checker: &Checker, call: &ast::ExprCall) -> Fix { + Fix::unsafe_edit(add_argument( + &format!( + "encoding={}", + checker + .generator() + .expr(&Expr::StringLiteral(ast::ExprStringLiteral { + value: ast::StringLiteralValue::single(ast::StringLiteral { + value: "locale".to_string(), + unicode: false, + range: TextRange::default(), + }), + range: TextRange::default(), + })) ), - [add_argument( - "encoding=locale.getpreferredencoding(False)", - &call.arguments, - Keyword, - checker.locator().contents(), - )?], + &call.arguments, + checker.locator().contents(), )) } +/// Generate an [`Edit`] for Python 3.9 and earlier. +fn generate_import_fix(checker: &Checker, call: &ast::ExprCall) -> Result { + let (import_edit, binding) = checker.importer().get_or_import_symbol( + &ImportRequest::import("locale", "getpreferredencoding"), + call.start(), + checker.semantic(), + )?; + let argument_edit = add_argument( + &format!("encoding={binding}(False)"), + &call.arguments, + checker.locator().contents(), + ); + Ok(Fix::unsafe_edits(import_edit, [argument_edit])) +} + /// Returns `true` if the given expression is a string literal containing a `b` character. -fn is_binary_mode(expr: &ast::Expr) -> Option { +fn is_binary_mode(expr: &Expr) -> Option { Some( expr.as_string_literal_expr()? .value @@ -133,12 +150,7 @@ fn is_binary_mode(expr: &ast::Expr) -> Option { /// Returns `true` if the given call lacks an explicit `encoding`. fn is_violation(call: &ast::ExprCall, call_path: &CallPath) -> bool { // If we have something like `*args`, which might contain the encoding argument, abort. - if call - .arguments - .args - .iter() - .any(ruff_python_ast::Expr::is_starred_expr) - { + if call.arguments.args.iter().any(Expr::is_starred_expr) { return false; } // If we have something like `**kwargs`, which might contain the encoding argument, abort. diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW1514_unspecified_encoding.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW1514_unspecified_encoding.py.snap index 20399c78210bd..3e32704e769a1 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW1514_unspecified_encoding.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLW1514_unspecified_encoding.py.snap @@ -153,6 +153,7 @@ unspecified_encoding.py:46:1: PLW1514 [*] `open` in text mode without explicit ` 46 | open("test.txt",) | ^^^^ PLW1514 47 | open() +48 | open( | = help: Add explicit `encoding` argument @@ -161,14 +162,18 @@ unspecified_encoding.py:46:1: PLW1514 [*] `open` in text mode without explicit ` 44 44 | tempfile.SpooledTemporaryFile(0, ) 45 45 | 46 |-open("test.txt",) - 46 |+open("test.txt",encoding="locale") + 46 |+open("test.txt", encoding="locale",) 47 47 | open() +48 48 | open( +49 49 | "test.txt", # comment unspecified_encoding.py:47:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument | 46 | open("test.txt",) 47 | open() | ^^^^ PLW1514 +48 | open( +49 | "test.txt", # comment | = help: Add explicit `encoding` argument @@ -178,5 +183,133 @@ unspecified_encoding.py:47:1: PLW1514 [*] `open` in text mode without explicit ` 46 46 | open("test.txt",) 47 |-open() 47 |+open(encoding="locale") +48 48 | open( +49 49 | "test.txt", # comment +50 50 | ) + +unspecified_encoding.py:48:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument + | +46 | open("test.txt",) +47 | open() +48 | open( + | ^^^^ PLW1514 +49 | "test.txt", # comment +50 | ) + | + = help: Add explicit `encoding` argument + +ℹ Unsafe fix +46 46 | open("test.txt",) +47 47 | open() +48 48 | open( +49 |- "test.txt", # comment + 49 |+ "test.txt", encoding="locale", # comment +50 50 | ) +51 51 | open( +52 52 | "test.txt", + +unspecified_encoding.py:51:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument + | +49 | "test.txt", # comment +50 | ) +51 | open( + | ^^^^ PLW1514 +52 | "test.txt", +53 | # comment + | + = help: Add explicit `encoding` argument + +ℹ Unsafe fix +49 49 | "test.txt", # comment +50 50 | ) +51 51 | open( +52 |- "test.txt", + 52 |+ "test.txt", encoding="locale", +53 53 | # comment +54 54 | ) +55 55 | open(("test.txt"),) + +unspecified_encoding.py:55:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument + | +53 | # comment +54 | ) +55 | open(("test.txt"),) + | ^^^^ PLW1514 +56 | open() +57 | open( + | + = help: Add explicit `encoding` argument + +ℹ Unsafe fix +52 52 | "test.txt", +53 53 | # comment +54 54 | ) +55 |-open(("test.txt"),) + 55 |+open(("test.txt"), encoding="locale",) +56 56 | open() +57 57 | open( +58 58 | ("test.txt"), # comment + +unspecified_encoding.py:56:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument + | +54 | ) +55 | open(("test.txt"),) +56 | open() + | ^^^^ PLW1514 +57 | open( +58 | ("test.txt"), # comment + | + = help: Add explicit `encoding` argument + +ℹ Unsafe fix +53 53 | # comment +54 54 | ) +55 55 | open(("test.txt"),) +56 |-open() + 56 |+open(encoding="locale") +57 57 | open( +58 58 | ("test.txt"), # comment +59 59 | ) + +unspecified_encoding.py:57:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument + | +55 | open(("test.txt"),) +56 | open() +57 | open( + | ^^^^ PLW1514 +58 | ("test.txt"), # comment +59 | ) + | + = help: Add explicit `encoding` argument + +ℹ Unsafe fix +55 55 | open(("test.txt"),) +56 56 | open() +57 57 | open( +58 |- ("test.txt"), # comment + 58 |+ ("test.txt"), encoding="locale", # comment +59 59 | ) +60 60 | open( +61 61 | ("test.txt"), + +unspecified_encoding.py:60:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument + | +58 | ("test.txt"), # comment +59 | ) +60 | open( + | ^^^^ PLW1514 +61 | ("test.txt"), +62 | # comment + | + = help: Add explicit `encoding` argument + +ℹ Unsafe fix +58 58 | ("test.txt"), # comment +59 59 | ) +60 60 | open( +61 |- ("test.txt"), + 61 |+ ("test.txt"), encoding="locale", +62 62 | # comment +63 63 | ) diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__unspecified_encoding_python39_or_lower.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__unspecified_encoding_python39_or_lower.snap index ec231166bfde0..d0e5d1c0ebcd1 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__unspecified_encoding_python39_or_lower.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__unspecified_encoding_python39_or_lower.snap @@ -12,12 +12,10 @@ unspecified_encoding.py:8:1: PLW1514 [*] `open` in text mode without explicit `e = help: Add explicit `encoding` argument ℹ Unsafe fix - 1 |+import locale -1 2 | import io -2 3 | import sys -3 4 | import tempfile --------------------------------------------------------------------------------- -5 6 | import codecs +3 3 | import tempfile +4 4 | import io as hugo +5 5 | import codecs + 6 |+import locale 6 7 | 7 8 | # Errors. 8 |-open("test.txt") @@ -38,11 +36,10 @@ unspecified_encoding.py:9:1: PLW1514 [*] `io.TextIOWrapper` without explicit `en = help: Add explicit `encoding` argument ℹ Unsafe fix - 1 |+import locale -1 2 | import io -2 3 | import sys -3 4 | import tempfile --------------------------------------------------------------------------------- +3 3 | import tempfile +4 4 | import io as hugo +5 5 | import codecs + 6 |+import locale 6 7 | 7 8 | # Errors. 8 9 | open("test.txt") @@ -64,11 +61,11 @@ unspecified_encoding.py:10:1: PLW1514 [*] `io.TextIOWrapper` without explicit `e = help: Add explicit `encoding` argument ℹ Unsafe fix - 1 |+import locale -1 2 | import io -2 3 | import sys -3 4 | import tempfile --------------------------------------------------------------------------------- +3 3 | import tempfile +4 4 | import io as hugo +5 5 | import codecs + 6 |+import locale +6 7 | 7 8 | # Errors. 8 9 | open("test.txt") 9 10 | io.TextIOWrapper(io.FileIO("test.txt")) @@ -90,11 +87,12 @@ unspecified_encoding.py:11:1: PLW1514 [*] `tempfile.NamedTemporaryFile` in text = help: Add explicit `encoding` argument ℹ Unsafe fix - 1 |+import locale -1 2 | import io -2 3 | import sys -3 4 | import tempfile --------------------------------------------------------------------------------- +3 3 | import tempfile +4 4 | import io as hugo +5 5 | import codecs + 6 |+import locale +6 7 | +7 8 | # Errors. 8 9 | open("test.txt") 9 10 | io.TextIOWrapper(io.FileIO("test.txt")) 10 11 | hugo.TextIOWrapper(hugo.FileIO("test.txt")) @@ -116,11 +114,13 @@ unspecified_encoding.py:12:1: PLW1514 [*] `tempfile.TemporaryFile` in text mode = help: Add explicit `encoding` argument ℹ Unsafe fix - 1 |+import locale -1 2 | import io -2 3 | import sys -3 4 | import tempfile --------------------------------------------------------------------------------- +3 3 | import tempfile +4 4 | import io as hugo +5 5 | import codecs + 6 |+import locale +6 7 | +7 8 | # Errors. +8 9 | open("test.txt") 9 10 | io.TextIOWrapper(io.FileIO("test.txt")) 10 11 | hugo.TextIOWrapper(hugo.FileIO("test.txt")) 11 12 | tempfile.NamedTemporaryFile("w") @@ -141,10 +141,13 @@ unspecified_encoding.py:13:1: PLW1514 [*] `codecs.open` in text mode without exp = help: Add explicit `encoding` argument ℹ Unsafe fix - 1 |+import locale -1 2 | import io -2 3 | import sys -3 4 | import tempfile +3 3 | import tempfile +4 4 | import io as hugo +5 5 | import codecs + 6 |+import locale +6 7 | +7 8 | # Errors. +8 9 | open("test.txt") -------------------------------------------------------------------------------- 10 11 | hugo.TextIOWrapper(hugo.FileIO("test.txt")) 11 12 | tempfile.NamedTemporaryFile("w") @@ -167,10 +170,13 @@ unspecified_encoding.py:14:1: PLW1514 [*] `tempfile.SpooledTemporaryFile` in tex = help: Add explicit `encoding` argument ℹ Unsafe fix - 1 |+import locale -1 2 | import io -2 3 | import sys -3 4 | import tempfile +3 3 | import tempfile +4 4 | import io as hugo +5 5 | import codecs + 6 |+import locale +6 7 | +7 8 | # Errors. +8 9 | open("test.txt") -------------------------------------------------------------------------------- 11 12 | tempfile.NamedTemporaryFile("w") 12 13 | tempfile.TemporaryFile("w") @@ -188,40 +194,227 @@ unspecified_encoding.py:46:1: PLW1514 [*] `open` in text mode without explicit ` 46 | open("test.txt",) | ^^^^ PLW1514 47 | open() +48 | open( | = help: Add explicit `encoding` argument ℹ Unsafe fix - 1 |+import locale -1 2 | import io -2 3 | import sys -3 4 | import tempfile +3 3 | import tempfile +4 4 | import io as hugo +5 5 | import codecs + 6 |+import locale +6 7 | +7 8 | # Errors. +8 9 | open("test.txt") -------------------------------------------------------------------------------- 43 44 | tempfile.SpooledTemporaryFile(0, "wb") 44 45 | tempfile.SpooledTemporaryFile(0, ) 45 46 | 46 |-open("test.txt",) - 47 |+open("test.txt",encoding=locale.getpreferredencoding(False)) + 47 |+open("test.txt", encoding=locale.getpreferredencoding(False),) 47 48 | open() +48 49 | open( +49 50 | "test.txt", # comment unspecified_encoding.py:47:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument | 46 | open("test.txt",) 47 | open() | ^^^^ PLW1514 +48 | open( +49 | "test.txt", # comment | = help: Add explicit `encoding` argument ℹ Unsafe fix - 1 |+import locale -1 2 | import io -2 3 | import sys -3 4 | import tempfile +3 3 | import tempfile +4 4 | import io as hugo +5 5 | import codecs + 6 |+import locale +6 7 | +7 8 | # Errors. +8 9 | open("test.txt") -------------------------------------------------------------------------------- 44 45 | tempfile.SpooledTemporaryFile(0, ) 45 46 | 46 47 | open("test.txt",) 47 |-open() 48 |+open(encoding=locale.getpreferredencoding(False)) +48 49 | open( +49 50 | "test.txt", # comment +50 51 | ) + +unspecified_encoding.py:48:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument + | +46 | open("test.txt",) +47 | open() +48 | open( + | ^^^^ PLW1514 +49 | "test.txt", # comment +50 | ) + | + = help: Add explicit `encoding` argument + +ℹ Unsafe fix +3 3 | import tempfile +4 4 | import io as hugo +5 5 | import codecs + 6 |+import locale +6 7 | +7 8 | # Errors. +8 9 | open("test.txt") +-------------------------------------------------------------------------------- +46 47 | open("test.txt",) +47 48 | open() +48 49 | open( +49 |- "test.txt", # comment + 50 |+ "test.txt", encoding=locale.getpreferredencoding(False), # comment +50 51 | ) +51 52 | open( +52 53 | "test.txt", + +unspecified_encoding.py:51:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument + | +49 | "test.txt", # comment +50 | ) +51 | open( + | ^^^^ PLW1514 +52 | "test.txt", +53 | # comment + | + = help: Add explicit `encoding` argument + +ℹ Unsafe fix +3 3 | import tempfile +4 4 | import io as hugo +5 5 | import codecs + 6 |+import locale +6 7 | +7 8 | # Errors. +8 9 | open("test.txt") +-------------------------------------------------------------------------------- +49 50 | "test.txt", # comment +50 51 | ) +51 52 | open( +52 |- "test.txt", + 53 |+ "test.txt", encoding=locale.getpreferredencoding(False), +53 54 | # comment +54 55 | ) +55 56 | open(("test.txt"),) + +unspecified_encoding.py:55:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument + | +53 | # comment +54 | ) +55 | open(("test.txt"),) + | ^^^^ PLW1514 +56 | open() +57 | open( + | + = help: Add explicit `encoding` argument + +ℹ Unsafe fix +3 3 | import tempfile +4 4 | import io as hugo +5 5 | import codecs + 6 |+import locale +6 7 | +7 8 | # Errors. +8 9 | open("test.txt") +-------------------------------------------------------------------------------- +52 53 | "test.txt", +53 54 | # comment +54 55 | ) +55 |-open(("test.txt"),) + 56 |+open(("test.txt"), encoding=locale.getpreferredencoding(False),) +56 57 | open() +57 58 | open( +58 59 | ("test.txt"), # comment + +unspecified_encoding.py:56:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument + | +54 | ) +55 | open(("test.txt"),) +56 | open() + | ^^^^ PLW1514 +57 | open( +58 | ("test.txt"), # comment + | + = help: Add explicit `encoding` argument + +ℹ Unsafe fix +3 3 | import tempfile +4 4 | import io as hugo +5 5 | import codecs + 6 |+import locale +6 7 | +7 8 | # Errors. +8 9 | open("test.txt") +-------------------------------------------------------------------------------- +53 54 | # comment +54 55 | ) +55 56 | open(("test.txt"),) +56 |-open() + 57 |+open(encoding=locale.getpreferredencoding(False)) +57 58 | open( +58 59 | ("test.txt"), # comment +59 60 | ) + +unspecified_encoding.py:57:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument + | +55 | open(("test.txt"),) +56 | open() +57 | open( + | ^^^^ PLW1514 +58 | ("test.txt"), # comment +59 | ) + | + = help: Add explicit `encoding` argument + +ℹ Unsafe fix +3 3 | import tempfile +4 4 | import io as hugo +5 5 | import codecs + 6 |+import locale +6 7 | +7 8 | # Errors. +8 9 | open("test.txt") +-------------------------------------------------------------------------------- +55 56 | open(("test.txt"),) +56 57 | open() +57 58 | open( +58 |- ("test.txt"), # comment + 59 |+ ("test.txt"), encoding=locale.getpreferredencoding(False), # comment +59 60 | ) +60 61 | open( +61 62 | ("test.txt"), + +unspecified_encoding.py:60:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument + | +58 | ("test.txt"), # comment +59 | ) +60 | open( + | ^^^^ PLW1514 +61 | ("test.txt"), +62 | # comment + | + = help: Add explicit `encoding` argument + +ℹ Unsafe fix +3 3 | import tempfile +4 4 | import io as hugo +5 5 | import codecs + 6 |+import locale +6 7 | +7 8 | # Errors. +8 9 | open("test.txt") +-------------------------------------------------------------------------------- +58 59 | ("test.txt"), # comment +59 60 | ) +60 61 | open( +61 |- ("test.txt"), + 62 |+ ("test.txt"), encoding=locale.getpreferredencoding(False), +62 63 | # comment +63 64 | )