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 fca823bcbc..db89cbf4e2 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/unspecified_encoding.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/unspecified_encoding.py @@ -94,3 +94,6 @@ Path("foo.txt").write_text(text, **kwargs) # Violation but not detectable x = Path("foo.txt") x.open() + +# https://github.com/astral-sh/ruff/issues/18107 +codecs.open("plw1514.py", "r", "utf-8").close() # this is fine 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 66a44e0fd7..9f49c5beae 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/unspecified_encoding.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/unspecified_encoding.rs @@ -215,8 +215,18 @@ fn is_violation(call: &ast::ExprCall, qualified_name: &Callee) -> bool { return false; } } + + let encoding_param_pos = match qualified_name.segments() { + // The `encoding` parameter position for `codecs.open` + ["codecs", _] => 2, + // The `encoding` parameter position for `_io.open` and the builtin `open` + _ => 3, + }; + // else mode not specified, defaults to text mode - call.arguments.find_argument_value("encoding", 3).is_none() + call.arguments + .find_argument_value("encoding", encoding_param_pos) + .is_none() } ["tempfile", tempfile_class @ ("TemporaryFile" | "NamedTemporaryFile" | "SpooledTemporaryFile")] => {