Skip to content

Commit beb98da

Browse files
authored
[flake8-use-pathlib] Add autofixes for PTH100, PTH106, PTH107, PTH108, PTH110, PTH111, PTH112, PTH113, PTH114, PTH115, PTH117, PTH119, PTH120 (#19213)
## Summary Part of #2331 ## Test Plan update snapshots for preview mode
1 parent 05b1b78 commit beb98da

34 files changed

+3401
-757
lines changed

crates/ruff_linter/src/checkers/ast/analyze/expression.rs

Lines changed: 59 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,27 +1039,14 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
10391039
flake8_simplify::rules::zip_dict_keys_and_values(checker, call);
10401040
}
10411041
if checker.any_rule_enabled(&[
1042-
Rule::OsPathAbspath,
10431042
Rule::OsChmod,
10441043
Rule::OsMkdir,
10451044
Rule::OsMakedirs,
10461045
Rule::OsRename,
10471046
Rule::OsReplace,
1048-
Rule::OsRmdir,
1049-
Rule::OsRemove,
1050-
Rule::OsUnlink,
10511047
Rule::OsGetcwd,
1052-
Rule::OsPathExists,
1053-
Rule::OsPathExpanduser,
1054-
Rule::OsPathIsdir,
1055-
Rule::OsPathIsfile,
1056-
Rule::OsPathIslink,
1057-
Rule::OsReadlink,
10581048
Rule::OsStat,
1059-
Rule::OsPathIsabs,
10601049
Rule::OsPathJoin,
1061-
Rule::OsPathBasename,
1062-
Rule::OsPathDirname,
10631050
Rule::OsPathSamefile,
10641051
Rule::OsPathSplitext,
10651052
Rule::BuiltinOpen,
@@ -1070,21 +1057,66 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
10701057
]) {
10711058
flake8_use_pathlib::rules::replaceable_by_pathlib(checker, call);
10721059
}
1073-
if checker.is_rule_enabled(Rule::OsPathGetsize) {
1074-
flake8_use_pathlib::rules::os_path_getsize(checker, call);
1075-
}
1076-
if checker.is_rule_enabled(Rule::OsPathGetatime) {
1077-
flake8_use_pathlib::rules::os_path_getatime(checker, call);
1078-
}
1079-
if checker.is_rule_enabled(Rule::OsPathGetctime) {
1080-
flake8_use_pathlib::rules::os_path_getctime(checker, call);
1081-
}
1082-
if checker.is_rule_enabled(Rule::OsPathGetmtime) {
1083-
flake8_use_pathlib::rules::os_path_getmtime(checker, call);
1084-
}
1085-
if checker.is_rule_enabled(Rule::PathConstructorCurrentDirectory) {
1086-
flake8_use_pathlib::rules::path_constructor_current_directory(checker, call);
1060+
if let Some(qualified_name) = checker.semantic().resolve_qualified_name(&call.func) {
1061+
let segments = qualified_name.segments();
1062+
if checker.is_rule_enabled(Rule::OsPathGetsize) {
1063+
flake8_use_pathlib::rules::os_path_getsize(checker, call, segments);
1064+
}
1065+
if checker.is_rule_enabled(Rule::OsPathGetatime) {
1066+
flake8_use_pathlib::rules::os_path_getatime(checker, call, segments);
1067+
}
1068+
if checker.is_rule_enabled(Rule::OsPathGetctime) {
1069+
flake8_use_pathlib::rules::os_path_getctime(checker, call, segments);
1070+
}
1071+
if checker.is_rule_enabled(Rule::OsPathGetmtime) {
1072+
flake8_use_pathlib::rules::os_path_getmtime(checker, call, segments);
1073+
}
1074+
if checker.is_rule_enabled(Rule::OsPathAbspath) {
1075+
flake8_use_pathlib::rules::os_path_abspath(checker, call, segments);
1076+
}
1077+
if checker.is_rule_enabled(Rule::OsRmdir) {
1078+
flake8_use_pathlib::rules::os_rmdir(checker, call, segments);
1079+
}
1080+
if checker.is_rule_enabled(Rule::OsRemove) {
1081+
flake8_use_pathlib::rules::os_remove(checker, call, segments);
1082+
}
1083+
if checker.is_rule_enabled(Rule::OsUnlink) {
1084+
flake8_use_pathlib::rules::os_unlink(checker, call, segments);
1085+
}
1086+
if checker.is_rule_enabled(Rule::OsPathExists) {
1087+
flake8_use_pathlib::rules::os_path_exists(checker, call, segments);
1088+
}
1089+
if checker.is_rule_enabled(Rule::OsPathExpanduser) {
1090+
flake8_use_pathlib::rules::os_path_expanduser(checker, call, segments);
1091+
}
1092+
if checker.is_rule_enabled(Rule::OsPathBasename) {
1093+
flake8_use_pathlib::rules::os_path_basename(checker, call, segments);
1094+
}
1095+
if checker.is_rule_enabled(Rule::OsPathDirname) {
1096+
flake8_use_pathlib::rules::os_path_dirname(checker, call, segments);
1097+
}
1098+
if checker.is_rule_enabled(Rule::OsPathIsabs) {
1099+
flake8_use_pathlib::rules::os_path_isabs(checker, call, segments);
1100+
}
1101+
if checker.is_rule_enabled(Rule::OsPathIsdir) {
1102+
flake8_use_pathlib::rules::os_path_isdir(checker, call, segments);
1103+
}
1104+
if checker.is_rule_enabled(Rule::OsPathIsfile) {
1105+
flake8_use_pathlib::rules::os_path_isfile(checker, call, segments);
1106+
}
1107+
if checker.is_rule_enabled(Rule::OsPathIslink) {
1108+
flake8_use_pathlib::rules::os_path_islink(checker, call, segments);
1109+
}
1110+
if checker.is_rule_enabled(Rule::OsReadlink) {
1111+
flake8_use_pathlib::rules::os_readlink(checker, call, segments);
1112+
}
1113+
if checker.is_rule_enabled(Rule::PathConstructorCurrentDirectory) {
1114+
flake8_use_pathlib::rules::path_constructor_current_directory(
1115+
checker, call, segments,
1116+
);
1117+
}
10871118
}
1119+
10881120
if checker.is_rule_enabled(Rule::OsSepSplit) {
10891121
flake8_use_pathlib::rules::os_sep_split(checker, call);
10901122
}

crates/ruff_linter/src/codes.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -919,27 +919,27 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
919919
(Tryceratops, "401") => (RuleGroup::Stable, rules::tryceratops::rules::VerboseLogMessage),
920920

921921
// flake8-use-pathlib
922-
(Flake8UsePathlib, "100") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathAbspath),
922+
(Flake8UsePathlib, "100") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathAbspath),
923923
(Flake8UsePathlib, "101") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsChmod),
924924
(Flake8UsePathlib, "102") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsMkdir),
925925
(Flake8UsePathlib, "103") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsMakedirs),
926926
(Flake8UsePathlib, "104") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsRename),
927927
(Flake8UsePathlib, "105") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsReplace),
928-
(Flake8UsePathlib, "106") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsRmdir),
929-
(Flake8UsePathlib, "107") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsRemove),
930-
(Flake8UsePathlib, "108") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsUnlink),
928+
(Flake8UsePathlib, "106") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsRmdir),
929+
(Flake8UsePathlib, "107") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsRemove),
930+
(Flake8UsePathlib, "108") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsUnlink),
931931
(Flake8UsePathlib, "109") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsGetcwd),
932-
(Flake8UsePathlib, "110") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathExists),
933-
(Flake8UsePathlib, "111") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathExpanduser),
934-
(Flake8UsePathlib, "112") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathIsdir),
935-
(Flake8UsePathlib, "113") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathIsfile),
936-
(Flake8UsePathlib, "114") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathIslink),
937-
(Flake8UsePathlib, "115") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsReadlink),
932+
(Flake8UsePathlib, "110") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathExists),
933+
(Flake8UsePathlib, "111") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathExpanduser),
934+
(Flake8UsePathlib, "112") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathIsdir),
935+
(Flake8UsePathlib, "113") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathIsfile),
936+
(Flake8UsePathlib, "114") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathIslink),
937+
(Flake8UsePathlib, "115") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsReadlink),
938938
(Flake8UsePathlib, "116") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsStat),
939-
(Flake8UsePathlib, "117") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathIsabs),
939+
(Flake8UsePathlib, "117") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathIsabs),
940940
(Flake8UsePathlib, "118") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathJoin),
941-
(Flake8UsePathlib, "119") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathBasename),
942-
(Flake8UsePathlib, "120") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathDirname),
941+
(Flake8UsePathlib, "119") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathBasename),
942+
(Flake8UsePathlib, "120") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathDirname),
943943
(Flake8UsePathlib, "121") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathSamefile),
944944
(Flake8UsePathlib, "122") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathSplitext),
945945
(Flake8UsePathlib, "123") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::BuiltinOpen),

crates/ruff_linter/src/preview.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,71 @@ pub(crate) const fn is_fix_os_path_getctime_enabled(settings: &LinterSettings) -
6969
settings.preview.is_enabled()
7070
}
7171

72+
// https://github.com/astral-sh/ruff/pull/19213
73+
pub(crate) const fn is_fix_os_path_abspath_enabled(settings: &LinterSettings) -> bool {
74+
settings.preview.is_enabled()
75+
}
76+
77+
// https://github.com/astral-sh/ruff/pull/19213
78+
pub(crate) const fn is_fix_os_rmdir_enabled(settings: &LinterSettings) -> bool {
79+
settings.preview.is_enabled()
80+
}
81+
82+
// https://github.com/astral-sh/ruff/pull/19213
83+
pub(crate) const fn is_fix_os_unlink_enabled(settings: &LinterSettings) -> bool {
84+
settings.preview.is_enabled()
85+
}
86+
87+
// https://github.com/astral-sh/ruff/pull/19213
88+
pub(crate) const fn is_fix_os_remove_enabled(settings: &LinterSettings) -> bool {
89+
settings.preview.is_enabled()
90+
}
91+
92+
// https://github.com/astral-sh/ruff/pull/19213
93+
pub(crate) const fn is_fix_os_path_exists_enabled(settings: &LinterSettings) -> bool {
94+
settings.preview.is_enabled()
95+
}
96+
97+
// https://github.com/astral-sh/ruff/pull/19213
98+
pub(crate) const fn is_fix_os_path_expanduser_enabled(settings: &LinterSettings) -> bool {
99+
settings.preview.is_enabled()
100+
}
101+
102+
// https://github.com/astral-sh/ruff/pull/19213
103+
pub(crate) const fn is_fix_os_path_isdir_enabled(settings: &LinterSettings) -> bool {
104+
settings.preview.is_enabled()
105+
}
106+
107+
// https://github.com/astral-sh/ruff/pull/19213
108+
pub(crate) const fn is_fix_os_path_isfile_enabled(settings: &LinterSettings) -> bool {
109+
settings.preview.is_enabled()
110+
}
111+
112+
// https://github.com/astral-sh/ruff/pull/19213
113+
pub(crate) const fn is_fix_os_path_islink_enabled(settings: &LinterSettings) -> bool {
114+
settings.preview.is_enabled()
115+
}
116+
117+
// https://github.com/astral-sh/ruff/pull/19213
118+
pub(crate) const fn is_fix_os_path_isabs_enabled(settings: &LinterSettings) -> bool {
119+
settings.preview.is_enabled()
120+
}
121+
122+
// https://github.com/astral-sh/ruff/pull/19213
123+
pub(crate) const fn is_fix_os_readlink_enabled(settings: &LinterSettings) -> bool {
124+
settings.preview.is_enabled()
125+
}
126+
127+
// https://github.com/astral-sh/ruff/pull/19213
128+
pub(crate) const fn is_fix_os_path_basename_enabled(settings: &LinterSettings) -> bool {
129+
settings.preview.is_enabled()
130+
}
131+
132+
// https://github.com/astral-sh/ruff/pull/19213
133+
pub(crate) const fn is_fix_os_path_dirname_enabled(settings: &LinterSettings) -> bool {
134+
settings.preview.is_enabled()
135+
}
136+
72137
// https://github.com/astral-sh/ruff/pull/11436
73138
// https://github.com/astral-sh/ruff/pull/11168
74139
pub(crate) const fn is_dunder_init_fix_unused_import_enabled(settings: &LinterSettings) -> bool {

crates/ruff_linter/src/rules/flake8_use_pathlib/helpers.rs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
use crate::checkers::ast::Checker;
22
use crate::importer::ImportRequest;
33
use crate::{Applicability, Edit, Fix, Violation};
4+
use ruff_python_ast::{self as ast};
45
use ruff_python_ast::{Expr, ExprCall};
56
use ruff_text_size::Ranged;
67

7-
pub(crate) fn is_path_call(checker: &Checker, expr: &Expr) -> bool {
8+
pub(crate) fn is_keyword_only_argument_non_default(arguments: &ast::Arguments, name: &str) -> bool {
9+
arguments
10+
.find_keyword(name)
11+
.is_some_and(|keyword| !keyword.value.is_none_literal_expr())
12+
}
13+
14+
pub(crate) fn is_pathlib_path_call(checker: &Checker, expr: &Expr) -> bool {
815
expr.as_call_expr().is_some_and(|expr_call| {
916
checker
1017
.semantic()
@@ -13,27 +20,22 @@ pub(crate) fn is_path_call(checker: &Checker, expr: &Expr) -> bool {
1320
})
1421
}
1522

16-
pub(crate) fn check_os_path_get_calls(
23+
/// We check functions that take only 1 argument, this does not apply to functions
24+
/// with `dir_fd` argument, because `dir_fd` is not supported by pathlib,
25+
/// so check if it's set to non-default values
26+
pub(crate) fn check_os_pathlib_single_arg_calls(
1727
checker: &Checker,
1828
call: &ExprCall,
19-
fn_name: &str,
2029
attr: &str,
30+
fn_argument: &str,
2131
fix_enabled: bool,
2232
violation: impl Violation,
2333
) {
24-
if checker
25-
.semantic()
26-
.resolve_qualified_name(&call.func)
27-
.is_none_or(|qualified_name| qualified_name.segments() != ["os", "path", fn_name])
28-
{
29-
return;
30-
}
31-
3234
if call.arguments.len() != 1 {
3335
return;
3436
}
3537

36-
let Some(arg) = call.arguments.find_argument_value("filename", 0) else {
38+
let Some(arg) = call.arguments.find_argument_value(fn_argument, 0) else {
3739
return;
3840
};
3941

@@ -56,10 +58,10 @@ pub(crate) fn check_os_path_get_calls(
5658
Applicability::Safe
5759
};
5860

59-
let replacement = if is_path_call(checker, arg) {
60-
format!("{arg_code}.stat().{attr}")
61+
let replacement = if is_pathlib_path_call(checker, arg) {
62+
format!("{arg_code}.{attr}")
6163
} else {
62-
format!("{binding}({arg_code}).stat().{attr}")
64+
format!("{binding}({arg_code}).{attr}")
6365
};
6466

6567
Ok(Fix::applicable_edits(

crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,48 @@ mod tests {
8080
Ok(())
8181
}
8282

83+
#[test_case(Path::new("full_name.py"))]
84+
#[test_case(Path::new("import_as.py"))]
85+
#[test_case(Path::new("import_from_as.py"))]
86+
#[test_case(Path::new("import_from.py"))]
87+
fn preview_rules(path: &Path) -> Result<()> {
88+
let snapshot = format!("preview_{}", path.to_string_lossy());
89+
let diagnostics = test_path(
90+
Path::new("flake8_use_pathlib").join(path).as_path(),
91+
&settings::LinterSettings {
92+
preview: PreviewMode::Enabled,
93+
..settings::LinterSettings::for_rules(vec![
94+
Rule::OsPathAbspath,
95+
Rule::OsChmod,
96+
Rule::OsMkdir,
97+
Rule::OsMakedirs,
98+
Rule::OsRename,
99+
Rule::OsReplace,
100+
Rule::OsRmdir,
101+
Rule::OsRemove,
102+
Rule::OsUnlink,
103+
Rule::OsGetcwd,
104+
Rule::OsPathExists,
105+
Rule::OsPathExpanduser,
106+
Rule::OsPathIsdir,
107+
Rule::OsPathIsfile,
108+
Rule::OsPathIslink,
109+
Rule::OsReadlink,
110+
Rule::OsStat,
111+
Rule::OsPathIsabs,
112+
Rule::OsPathJoin,
113+
Rule::OsPathBasename,
114+
Rule::OsPathDirname,
115+
Rule::OsPathSamefile,
116+
Rule::OsPathSplitext,
117+
Rule::BuiltinOpen,
118+
])
119+
},
120+
)?;
121+
assert_diagnostics!(snapshot, diagnostics);
122+
Ok(())
123+
}
124+
83125
#[test_case(Rule::OsPathGetsize, Path::new("PTH202.py"))]
84126
#[test_case(Rule::OsPathGetsize, Path::new("PTH202_2.py"))]
85127
#[test_case(Rule::OsPathGetatime, Path::new("PTH203.py"))]
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,45 @@
11
pub(crate) use glob_rule::*;
22
pub(crate) use invalid_pathlib_with_suffix::*;
3+
pub(crate) use os_path_abspath::*;
4+
pub(crate) use os_path_basename::*;
5+
pub(crate) use os_path_dirname::*;
6+
pub(crate) use os_path_exists::*;
7+
pub(crate) use os_path_expanduser::*;
38
pub(crate) use os_path_getatime::*;
49
pub(crate) use os_path_getctime::*;
510
pub(crate) use os_path_getmtime::*;
611
pub(crate) use os_path_getsize::*;
12+
pub(crate) use os_path_isabs::*;
13+
pub(crate) use os_path_isdir::*;
14+
pub(crate) use os_path_isfile::*;
15+
pub(crate) use os_path_islink::*;
16+
pub(crate) use os_readlink::*;
17+
pub(crate) use os_remove::*;
18+
pub(crate) use os_rmdir::*;
719
pub(crate) use os_sep_split::*;
20+
pub(crate) use os_unlink::*;
821
pub(crate) use path_constructor_current_directory::*;
922
pub(crate) use replaceable_by_pathlib::*;
1023

1124
mod glob_rule;
1225
mod invalid_pathlib_with_suffix;
26+
mod os_path_abspath;
27+
mod os_path_basename;
28+
mod os_path_dirname;
29+
mod os_path_exists;
30+
mod os_path_expanduser;
1331
mod os_path_getatime;
1432
mod os_path_getctime;
1533
mod os_path_getmtime;
1634
mod os_path_getsize;
35+
mod os_path_isabs;
36+
mod os_path_isdir;
37+
mod os_path_isfile;
38+
mod os_path_islink;
39+
mod os_readlink;
40+
mod os_remove;
41+
mod os_rmdir;
1742
mod os_sep_split;
43+
mod os_unlink;
1844
mod path_constructor_current_directory;
1945
mod replaceable_by_pathlib;

0 commit comments

Comments
 (0)