Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@

if "file" in os.listdir("dir"):
...

os.listdir(1)
os.listdir(path=1)
Original file line number Diff line number Diff line change
Expand Up @@ -18,153 +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
// 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
let Some(qualified_name) = checker.semantic().resolve_qualified_name(&call.func) else {
return;
};

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_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(expr, checker.semantic()))
{
return None;
}
Some(BuiltinOpen.into())
.find_positional(0)
.is_some_and(|expr| is_file_descriptor(expr, checker.semantic()))
{
return;
}
// 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())
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;
}
// PTH208,
["os", "listdir"] => Some(OsListdir.into()),
_ => None,
})
{
let diagnostic = Diagnostic::new::<DiagnosticKind>(diagnostic_kind, call.func.range());

if checker.enabled(diagnostic.kind.rule()) {
checker.report_diagnostic(diagnostic);
OsListdir.into()
}
_ => return,
};

if checker.enabled(diagnostic_kind.rule()) {
checker.report_diagnostic(Diagnostic::new(diagnostic_kind, call.func.range()));
}
}

Expand Down