mirror of https://github.com/astral-sh/ruff
[`flake8-use-pathlib`] Avoid suggesting `Path.iterdir()` for `os.listdir` with file descriptor (`PTH208`) (#17715)
## Summary Fixes: #17695 --------- Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
This commit is contained in:
parent
7825975972
commit
0e85cbdd91
|
|
@ -21,3 +21,6 @@ if os.listdir("dir"):
|
||||||
|
|
||||||
if "file" in os.listdir("dir"):
|
if "file" in os.listdir("dir"):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
os.listdir(1)
|
||||||
|
os.listdir(path=1)
|
||||||
|
|
|
||||||
|
|
@ -18,152 +18,150 @@ use crate::rules::flake8_use_pathlib::violations::{
|
||||||
use ruff_python_ast::PythonVersion;
|
use ruff_python_ast::PythonVersion;
|
||||||
|
|
||||||
pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) {
|
pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) {
|
||||||
if let Some(diagnostic_kind) = checker
|
let Some(qualified_name) = checker.semantic().resolve_qualified_name(&call.func) else {
|
||||||
.semantic()
|
return;
|
||||||
.resolve_qualified_name(&call.func)
|
};
|
||||||
.and_then(|qualified_name| match qualified_name.segments() {
|
|
||||||
// PTH100
|
|
||||||
["os", "path", "abspath"] => Some(OsPathAbspath.into()),
|
|
||||||
// PTH101
|
|
||||||
["os", "chmod"] => Some(OsChmod.into()),
|
|
||||||
// PTH102
|
|
||||||
["os", "makedirs"] => Some(OsMakedirs.into()),
|
|
||||||
// PTH103
|
|
||||||
["os", "mkdir"] => Some(OsMkdir.into()),
|
|
||||||
// PTH104
|
|
||||||
["os", "rename"] => Some(OsRename.into()),
|
|
||||||
// PTH105
|
|
||||||
["os", "replace"] => Some(OsReplace.into()),
|
|
||||||
// PTH106
|
|
||||||
["os", "rmdir"] => Some(OsRmdir.into()),
|
|
||||||
// PTH107
|
|
||||||
["os", "remove"] => Some(OsRemove.into()),
|
|
||||||
// PTH108
|
|
||||||
["os", "unlink"] => Some(OsUnlink.into()),
|
|
||||||
// PTH109
|
|
||||||
["os", "getcwd"] => Some(OsGetcwd.into()),
|
|
||||||
["os", "getcwdb"] => Some(OsGetcwd.into()),
|
|
||||||
// PTH110
|
|
||||||
["os", "path", "exists"] => Some(OsPathExists.into()),
|
|
||||||
// PTH111
|
|
||||||
["os", "path", "expanduser"] => Some(OsPathExpanduser.into()),
|
|
||||||
// PTH112
|
|
||||||
["os", "path", "isdir"] => Some(OsPathIsdir.into()),
|
|
||||||
// PTH113
|
|
||||||
["os", "path", "isfile"] => Some(OsPathIsfile.into()),
|
|
||||||
// PTH114
|
|
||||||
["os", "path", "islink"] => Some(OsPathIslink.into()),
|
|
||||||
// PTH116
|
|
||||||
["os", "stat"] => Some(OsStat.into()),
|
|
||||||
// PTH117
|
|
||||||
["os", "path", "isabs"] => Some(OsPathIsabs.into()),
|
|
||||||
// PTH118
|
|
||||||
["os", "path", "join"] => Some(
|
|
||||||
OsPathJoin {
|
|
||||||
module: "path".to_string(),
|
|
||||||
joiner: if call.arguments.args.iter().any(Expr::is_starred_expr) {
|
|
||||||
Joiner::Joinpath
|
|
||||||
} else {
|
|
||||||
Joiner::Slash
|
|
||||||
},
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
),
|
|
||||||
["os", "sep", "join"] => Some(
|
|
||||||
OsPathJoin {
|
|
||||||
module: "sep".to_string(),
|
|
||||||
joiner: if call.arguments.args.iter().any(Expr::is_starred_expr) {
|
|
||||||
Joiner::Joinpath
|
|
||||||
} else {
|
|
||||||
Joiner::Slash
|
|
||||||
},
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
),
|
|
||||||
// PTH119
|
|
||||||
["os", "path", "basename"] => Some(OsPathBasename.into()),
|
|
||||||
// PTH120
|
|
||||||
["os", "path", "dirname"] => Some(OsPathDirname.into()),
|
|
||||||
// PTH121
|
|
||||||
["os", "path", "samefile"] => Some(OsPathSamefile.into()),
|
|
||||||
// PTH122
|
|
||||||
["os", "path", "splitext"] => Some(OsPathSplitext.into()),
|
|
||||||
// PTH202
|
|
||||||
["os", "path", "getsize"] => Some(OsPathGetsize.into()),
|
|
||||||
// PTH203
|
|
||||||
["os", "path", "getatime"] => Some(OsPathGetatime.into()),
|
|
||||||
// PTH204
|
|
||||||
["os", "path", "getmtime"] => Some(OsPathGetmtime.into()),
|
|
||||||
// PTH205
|
|
||||||
["os", "path", "getctime"] => Some(OsPathGetctime.into()),
|
|
||||||
// PTH123
|
|
||||||
["" | "builtins", "open"] => {
|
|
||||||
// `closefd` and `opener` are not supported by pathlib, so check if they are
|
|
||||||
// set to non-default values.
|
|
||||||
// https://github.com/astral-sh/ruff/issues/7620
|
|
||||||
// Signature as of Python 3.11 (https://docs.python.org/3/library/functions.html#open):
|
|
||||||
// ```text
|
|
||||||
// 0 1 2 3 4 5
|
|
||||||
// open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None,
|
|
||||||
// 6 7
|
|
||||||
// closefd=True, opener=None)
|
|
||||||
// ^^^^ ^^^^
|
|
||||||
// ```
|
|
||||||
// For `pathlib` (https://docs.python.org/3/library/pathlib.html#pathlib.Path.open):
|
|
||||||
// ```text
|
|
||||||
// Path.open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)
|
|
||||||
// ```
|
|
||||||
if call
|
|
||||||
.arguments
|
|
||||||
.find_argument_value("closefd", 6)
|
|
||||||
.is_some_and(|expr| {
|
|
||||||
!matches!(
|
|
||||||
expr,
|
|
||||||
Expr::BooleanLiteral(ExprBooleanLiteral { value: true, .. })
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|| call
|
|
||||||
.arguments
|
|
||||||
.find_argument_value("opener", 7)
|
|
||||||
.is_some_and(|expr| !expr.is_none_literal_expr())
|
|
||||||
|| call.arguments.find_positional(0).is_some_and(|expr| {
|
|
||||||
is_file_descriptor_or_bytes_str(expr, checker.semantic())
|
|
||||||
})
|
|
||||||
{
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(BuiltinOpen.into())
|
|
||||||
}
|
|
||||||
// PTH124
|
|
||||||
["py", "path", "local"] => Some(PyPath.into()),
|
|
||||||
// PTH207
|
|
||||||
["glob", "glob"] => Some(
|
|
||||||
Glob {
|
|
||||||
function: "glob".to_string(),
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
),
|
|
||||||
["glob", "iglob"] => Some(
|
|
||||||
Glob {
|
|
||||||
function: "iglob".to_string(),
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
),
|
|
||||||
// PTH115
|
|
||||||
// Python 3.9+
|
|
||||||
["os", "readlink"] if checker.target_version() >= PythonVersion::PY39 => {
|
|
||||||
Some(OsReadlink.into())
|
|
||||||
}
|
|
||||||
// PTH208,
|
|
||||||
["os", "listdir"] => Some(OsListdir.into()),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
{
|
|
||||||
let diagnostic = Diagnostic::new::<DiagnosticKind>(diagnostic_kind, call.func.range());
|
|
||||||
|
|
||||||
if checker.enabled(diagnostic.kind.rule()) {
|
let diagnostic_kind: DiagnosticKind = match qualified_name.segments() {
|
||||||
checker.report_diagnostic(diagnostic);
|
// PTH100
|
||||||
|
["os", "path", "abspath"] => OsPathAbspath.into(),
|
||||||
|
// PTH101
|
||||||
|
["os", "chmod"] => OsChmod.into(),
|
||||||
|
// PTH102
|
||||||
|
["os", "makedirs"] => OsMakedirs.into(),
|
||||||
|
// PTH103
|
||||||
|
["os", "mkdir"] => OsMkdir.into(),
|
||||||
|
// PTH104
|
||||||
|
["os", "rename"] => OsRename.into(),
|
||||||
|
// PTH105
|
||||||
|
["os", "replace"] => OsReplace.into(),
|
||||||
|
// PTH106
|
||||||
|
["os", "rmdir"] => OsRmdir.into(),
|
||||||
|
// PTH107
|
||||||
|
["os", "remove"] => OsRemove.into(),
|
||||||
|
// PTH108
|
||||||
|
["os", "unlink"] => OsUnlink.into(),
|
||||||
|
// PTH109
|
||||||
|
["os", "getcwd"] => OsGetcwd.into(),
|
||||||
|
["os", "getcwdb"] => OsGetcwd.into(),
|
||||||
|
// PTH110
|
||||||
|
["os", "path", "exists"] => OsPathExists.into(),
|
||||||
|
// PTH111
|
||||||
|
["os", "path", "expanduser"] => OsPathExpanduser.into(),
|
||||||
|
// PTH112
|
||||||
|
["os", "path", "isdir"] => OsPathIsdir.into(),
|
||||||
|
// PTH113
|
||||||
|
["os", "path", "isfile"] => OsPathIsfile.into(),
|
||||||
|
// PTH114
|
||||||
|
["os", "path", "islink"] => OsPathIslink.into(),
|
||||||
|
// PTH116
|
||||||
|
["os", "stat"] => OsStat.into(),
|
||||||
|
// PTH117
|
||||||
|
["os", "path", "isabs"] => OsPathIsabs.into(),
|
||||||
|
// PTH118
|
||||||
|
["os", "path", "join"] => OsPathJoin {
|
||||||
|
module: "path".to_string(),
|
||||||
|
joiner: if call.arguments.args.iter().any(Expr::is_starred_expr) {
|
||||||
|
Joiner::Joinpath
|
||||||
|
} else {
|
||||||
|
Joiner::Slash
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
.into(),
|
||||||
|
["os", "sep", "join"] => OsPathJoin {
|
||||||
|
module: "sep".to_string(),
|
||||||
|
joiner: if call.arguments.args.iter().any(Expr::is_starred_expr) {
|
||||||
|
Joiner::Joinpath
|
||||||
|
} else {
|
||||||
|
Joiner::Slash
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
// PTH119
|
||||||
|
["os", "path", "basename"] => OsPathBasename.into(),
|
||||||
|
// PTH120
|
||||||
|
["os", "path", "dirname"] => OsPathDirname.into(),
|
||||||
|
// PTH121
|
||||||
|
["os", "path", "samefile"] => OsPathSamefile.into(),
|
||||||
|
// PTH122
|
||||||
|
["os", "path", "splitext"] => OsPathSplitext.into(),
|
||||||
|
// PTH202
|
||||||
|
["os", "path", "getsize"] => OsPathGetsize.into(),
|
||||||
|
// PTH203
|
||||||
|
["os", "path", "getatime"] => OsPathGetatime.into(),
|
||||||
|
// PTH204
|
||||||
|
["os", "path", "getmtime"] => OsPathGetmtime.into(),
|
||||||
|
// PTH205
|
||||||
|
["os", "path", "getctime"] => OsPathGetctime.into(),
|
||||||
|
// PTH123
|
||||||
|
["" | "builtins", "open"] => {
|
||||||
|
// `closefd` and `opener` are not supported by pathlib, so check if they are
|
||||||
|
// are set to non-default values.
|
||||||
|
// https://github.com/astral-sh/ruff/issues/7620
|
||||||
|
// Signature as of Python 3.11 (https://docs.python.org/3/library/functions.html#open):
|
||||||
|
// ```text
|
||||||
|
// 0 1 2 3 4 5
|
||||||
|
// open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None,
|
||||||
|
// 6 7
|
||||||
|
// closefd=True, opener=None)
|
||||||
|
// ^^^^ ^^^^
|
||||||
|
// ```
|
||||||
|
// For `pathlib` (https://docs.python.org/3/library/pathlib.html#pathlib.Path.open):
|
||||||
|
// ```text
|
||||||
|
// Path.open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)
|
||||||
|
// ```
|
||||||
|
if call
|
||||||
|
.arguments
|
||||||
|
.find_argument_value("closefd", 6)
|
||||||
|
.is_some_and(|expr| {
|
||||||
|
!matches!(
|
||||||
|
expr,
|
||||||
|
Expr::BooleanLiteral(ExprBooleanLiteral { value: true, .. })
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|| call
|
||||||
|
.arguments
|
||||||
|
.find_argument_value("opener", 7)
|
||||||
|
.is_some_and(|expr| !expr.is_none_literal_expr())
|
||||||
|
|| call
|
||||||
|
.arguments
|
||||||
|
.find_positional(0)
|
||||||
|
.is_some_and(|expr| is_file_descriptor_or_bytes_str(expr, checker.semantic()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BuiltinOpen.into()
|
||||||
|
}
|
||||||
|
// PTH124
|
||||||
|
["py", "path", "local"] => PyPath.into(),
|
||||||
|
// PTH207
|
||||||
|
["glob", "glob"] => Glob {
|
||||||
|
function: "glob".to_string(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
["glob", "iglob"] => Glob {
|
||||||
|
function: "iglob".to_string(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
// PTH115
|
||||||
|
// Python 3.9+
|
||||||
|
["os", "readlink"] if checker.target_version() >= PythonVersion::PY39 => OsReadlink.into(),
|
||||||
|
// PTH208
|
||||||
|
["os", "listdir"] => {
|
||||||
|
if call
|
||||||
|
.arguments
|
||||||
|
.find_argument_value("path", 0)
|
||||||
|
.is_some_and(|expr| is_file_descriptor(expr, checker.semantic()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
OsListdir.into()
|
||||||
|
}
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
if checker.enabled(diagnostic_kind.rule()) {
|
||||||
|
checker.report_diagnostic(Diagnostic::new(diagnostic_kind, call.func.range()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue