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"):
|
||||
...
|
||||
|
||||
os.listdir(1)
|
||||
os.listdir(path=1)
|
||||
|
|
|
|||
|
|
@ -18,152 +18,150 @@ use crate::rules::flake8_use_pathlib::violations::{
|
|||
use ruff_python_ast::PythonVersion;
|
||||
|
||||
pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) {
|
||||
if let Some(diagnostic_kind) = checker
|
||||
.semantic()
|
||||
.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());
|
||||
let Some(qualified_name) = checker.semantic().resolve_qualified_name(&call.func) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if checker.enabled(diagnostic.kind.rule()) {
|
||||
checker.report_diagnostic(diagnostic);
|
||||
let diagnostic_kind: DiagnosticKind = match qualified_name.segments() {
|
||||
// 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