diff --git a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs index dfddaab7a3328..3c4b3ab700837 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs @@ -1037,27 +1037,14 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) { flake8_simplify::rules::zip_dict_keys_and_values(checker, call); } if checker.any_rule_enabled(&[ - Rule::OsPathAbspath, Rule::OsChmod, Rule::OsMkdir, Rule::OsMakedirs, Rule::OsRename, Rule::OsReplace, - Rule::OsRmdir, - Rule::OsRemove, - Rule::OsUnlink, Rule::OsGetcwd, - Rule::OsPathExists, - Rule::OsPathExpanduser, - Rule::OsPathIsdir, - Rule::OsPathIsfile, - Rule::OsPathIslink, - Rule::OsReadlink, Rule::OsStat, - Rule::OsPathIsabs, Rule::OsPathJoin, - Rule::OsPathBasename, - Rule::OsPathDirname, Rule::OsPathSamefile, Rule::OsPathSplitext, Rule::BuiltinOpen, @@ -1068,21 +1055,66 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) { ]) { flake8_use_pathlib::rules::replaceable_by_pathlib(checker, call); } - if checker.is_rule_enabled(Rule::OsPathGetsize) { - flake8_use_pathlib::rules::os_path_getsize(checker, call); - } - if checker.is_rule_enabled(Rule::OsPathGetatime) { - flake8_use_pathlib::rules::os_path_getatime(checker, call); - } - if checker.is_rule_enabled(Rule::OsPathGetctime) { - flake8_use_pathlib::rules::os_path_getctime(checker, call); - } - if checker.is_rule_enabled(Rule::OsPathGetmtime) { - flake8_use_pathlib::rules::os_path_getmtime(checker, call); - } - if checker.is_rule_enabled(Rule::PathConstructorCurrentDirectory) { - flake8_use_pathlib::rules::path_constructor_current_directory(checker, call); + if let Some(qualified_name) = checker.semantic().resolve_qualified_name(&call.func) { + let segments = qualified_name.segments(); + if checker.is_rule_enabled(Rule::OsPathGetsize) { + flake8_use_pathlib::rules::os_path_getsize(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsPathGetatime) { + flake8_use_pathlib::rules::os_path_getatime(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsPathGetctime) { + flake8_use_pathlib::rules::os_path_getctime(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsPathGetmtime) { + flake8_use_pathlib::rules::os_path_getmtime(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsPathAbspath) { + flake8_use_pathlib::rules::os_path_abspath(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsRmdir) { + flake8_use_pathlib::rules::os_rmdir(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsRemove) { + flake8_use_pathlib::rules::os_remove(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsUnlink) { + flake8_use_pathlib::rules::os_unlink(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsPathExists) { + flake8_use_pathlib::rules::os_path_exists(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsPathExpanduser) { + flake8_use_pathlib::rules::os_path_expanduser(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsPathBasename) { + flake8_use_pathlib::rules::os_path_basename(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsPathDirname) { + flake8_use_pathlib::rules::os_path_dirname(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsPathIsabs) { + flake8_use_pathlib::rules::os_path_isabs(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsPathIsdir) { + flake8_use_pathlib::rules::os_path_isdir(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsPathIsfile) { + flake8_use_pathlib::rules::os_path_isfile(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsPathIslink) { + flake8_use_pathlib::rules::os_path_islink(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsReadlink) { + flake8_use_pathlib::rules::os_readlink(checker, call, segments); + } + if checker.is_rule_enabled(Rule::PathConstructorCurrentDirectory) { + flake8_use_pathlib::rules::path_constructor_current_directory( + checker, call, segments, + ); + } } + if checker.is_rule_enabled(Rule::OsSepSplit) { flake8_use_pathlib::rules::os_sep_split(checker, call); } diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 2cbdaead9ae4e..8813c239dfab7 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -919,27 +919,27 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Tryceratops, "401") => (RuleGroup::Stable, rules::tryceratops::rules::VerboseLogMessage), // flake8-use-pathlib - (Flake8UsePathlib, "100") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathAbspath), + (Flake8UsePathlib, "100") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathAbspath), (Flake8UsePathlib, "101") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsChmod), (Flake8UsePathlib, "102") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsMkdir), (Flake8UsePathlib, "103") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsMakedirs), (Flake8UsePathlib, "104") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsRename), (Flake8UsePathlib, "105") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsReplace), - (Flake8UsePathlib, "106") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsRmdir), - (Flake8UsePathlib, "107") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsRemove), - (Flake8UsePathlib, "108") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsUnlink), + (Flake8UsePathlib, "106") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsRmdir), + (Flake8UsePathlib, "107") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsRemove), + (Flake8UsePathlib, "108") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsUnlink), (Flake8UsePathlib, "109") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsGetcwd), - (Flake8UsePathlib, "110") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathExists), - (Flake8UsePathlib, "111") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathExpanduser), - (Flake8UsePathlib, "112") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathIsdir), - (Flake8UsePathlib, "113") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathIsfile), - (Flake8UsePathlib, "114") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathIslink), - (Flake8UsePathlib, "115") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsReadlink), + (Flake8UsePathlib, "110") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathExists), + (Flake8UsePathlib, "111") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathExpanduser), + (Flake8UsePathlib, "112") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathIsdir), + (Flake8UsePathlib, "113") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathIsfile), + (Flake8UsePathlib, "114") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathIslink), + (Flake8UsePathlib, "115") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsReadlink), (Flake8UsePathlib, "116") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsStat), - (Flake8UsePathlib, "117") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathIsabs), + (Flake8UsePathlib, "117") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathIsabs), (Flake8UsePathlib, "118") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathJoin), - (Flake8UsePathlib, "119") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathBasename), - (Flake8UsePathlib, "120") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathDirname), + (Flake8UsePathlib, "119") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathBasename), + (Flake8UsePathlib, "120") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathDirname), (Flake8UsePathlib, "121") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathSamefile), (Flake8UsePathlib, "122") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsPathSplitext), (Flake8UsePathlib, "123") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::BuiltinOpen), diff --git a/crates/ruff_linter/src/preview.rs b/crates/ruff_linter/src/preview.rs index 26aaea5b5342c..e5047bb8e50eb 100644 --- a/crates/ruff_linter/src/preview.rs +++ b/crates/ruff_linter/src/preview.rs @@ -69,6 +69,71 @@ pub(crate) const fn is_fix_os_path_getctime_enabled(settings: &LinterSettings) - settings.preview.is_enabled() } +// https://github.com/astral-sh/ruff/pull/19213 +pub(crate) const fn is_fix_os_path_abspath_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} + +// https://github.com/astral-sh/ruff/pull/19213 +pub(crate) const fn is_fix_os_rmdir_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} + +// https://github.com/astral-sh/ruff/pull/19213 +pub(crate) const fn is_fix_os_unlink_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} + +// https://github.com/astral-sh/ruff/pull/19213 +pub(crate) const fn is_fix_os_remove_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} + +// https://github.com/astral-sh/ruff/pull/19213 +pub(crate) const fn is_fix_os_path_exists_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} + +// https://github.com/astral-sh/ruff/pull/19213 +pub(crate) const fn is_fix_os_path_expanduser_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} + +// https://github.com/astral-sh/ruff/pull/19213 +pub(crate) const fn is_fix_os_path_isdir_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} + +// https://github.com/astral-sh/ruff/pull/19213 +pub(crate) const fn is_fix_os_path_isfile_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} + +// https://github.com/astral-sh/ruff/pull/19213 +pub(crate) const fn is_fix_os_path_islink_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} + +// https://github.com/astral-sh/ruff/pull/19213 +pub(crate) const fn is_fix_os_path_isabs_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} + +// https://github.com/astral-sh/ruff/pull/19213 +pub(crate) const fn is_fix_os_readlink_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} + +// https://github.com/astral-sh/ruff/pull/19213 +pub(crate) const fn is_fix_os_path_basename_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} + +// https://github.com/astral-sh/ruff/pull/19213 +pub(crate) const fn is_fix_os_path_dirname_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} + // https://github.com/astral-sh/ruff/pull/11436 // https://github.com/astral-sh/ruff/pull/11168 pub(crate) const fn is_dunder_init_fix_unused_import_enabled(settings: &LinterSettings) -> bool { diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/helpers.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/helpers.rs index ef71f948e32e7..d8e1fbe0d760c 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/helpers.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/helpers.rs @@ -1,10 +1,17 @@ use crate::checkers::ast::Checker; use crate::importer::ImportRequest; use crate::{Applicability, Edit, Fix, Violation}; +use ruff_python_ast::{self as ast}; use ruff_python_ast::{Expr, ExprCall}; use ruff_text_size::Ranged; -pub(crate) fn is_path_call(checker: &Checker, expr: &Expr) -> bool { +pub(crate) fn is_keyword_only_argument_non_default(arguments: &ast::Arguments, name: &str) -> bool { + arguments + .find_keyword(name) + .is_some_and(|keyword| !keyword.value.is_none_literal_expr()) +} + +pub(crate) fn is_pathlib_path_call(checker: &Checker, expr: &Expr) -> bool { expr.as_call_expr().is_some_and(|expr_call| { checker .semantic() @@ -13,27 +20,22 @@ pub(crate) fn is_path_call(checker: &Checker, expr: &Expr) -> bool { }) } -pub(crate) fn check_os_path_get_calls( +/// We check functions that take only 1 argument, this does not apply to functions +/// with `dir_fd` argument, because `dir_fd` is not supported by pathlib, +/// so check if it's set to non-default values +pub(crate) fn check_os_pathlib_single_arg_calls( checker: &Checker, call: &ExprCall, - fn_name: &str, attr: &str, + fn_argument: &str, fix_enabled: bool, violation: impl Violation, ) { - if checker - .semantic() - .resolve_qualified_name(&call.func) - .is_none_or(|qualified_name| qualified_name.segments() != ["os", "path", fn_name]) - { - return; - } - if call.arguments.len() != 1 { return; } - let Some(arg) = call.arguments.find_argument_value("filename", 0) else { + let Some(arg) = call.arguments.find_argument_value(fn_argument, 0) else { return; }; @@ -56,10 +58,10 @@ pub(crate) fn check_os_path_get_calls( Applicability::Safe }; - let replacement = if is_path_call(checker, arg) { - format!("{arg_code}.stat().{attr}") + let replacement = if is_pathlib_path_call(checker, arg) { + format!("{arg_code}.{attr}") } else { - format!("{binding}({arg_code}).stat().{attr}") + format!("{binding}({arg_code}).{attr}") }; Ok(Fix::applicable_edits( diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs index b22fa584b7f77..91765ce8413cc 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs @@ -80,6 +80,48 @@ mod tests { Ok(()) } + #[test_case(Path::new("full_name.py"))] + #[test_case(Path::new("import_as.py"))] + #[test_case(Path::new("import_from_as.py"))] + #[test_case(Path::new("import_from.py"))] + fn preview_rules(path: &Path) -> Result<()> { + let snapshot = format!("preview_{}", path.to_string_lossy()); + let diagnostics = test_path( + Path::new("flake8_use_pathlib").join(path).as_path(), + &settings::LinterSettings { + preview: PreviewMode::Enabled, + ..settings::LinterSettings::for_rules(vec![ + Rule::OsPathAbspath, + Rule::OsChmod, + Rule::OsMkdir, + Rule::OsMakedirs, + Rule::OsRename, + Rule::OsReplace, + Rule::OsRmdir, + Rule::OsRemove, + Rule::OsUnlink, + Rule::OsGetcwd, + Rule::OsPathExists, + Rule::OsPathExpanduser, + Rule::OsPathIsdir, + Rule::OsPathIsfile, + Rule::OsPathIslink, + Rule::OsReadlink, + Rule::OsStat, + Rule::OsPathIsabs, + Rule::OsPathJoin, + Rule::OsPathBasename, + Rule::OsPathDirname, + Rule::OsPathSamefile, + Rule::OsPathSplitext, + Rule::BuiltinOpen, + ]) + }, + )?; + assert_diagnostics!(snapshot, diagnostics); + Ok(()) + } + #[test_case(Rule::OsPathGetsize, Path::new("PTH202.py"))] #[test_case(Rule::OsPathGetsize, Path::new("PTH202_2.py"))] #[test_case(Rule::OsPathGetatime, Path::new("PTH203.py"))] diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/mod.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/mod.rs index f009855154a44..a5dd3f69f3a76 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/mod.rs @@ -1,19 +1,45 @@ pub(crate) use glob_rule::*; pub(crate) use invalid_pathlib_with_suffix::*; +pub(crate) use os_path_abspath::*; +pub(crate) use os_path_basename::*; +pub(crate) use os_path_dirname::*; +pub(crate) use os_path_exists::*; +pub(crate) use os_path_expanduser::*; pub(crate) use os_path_getatime::*; pub(crate) use os_path_getctime::*; pub(crate) use os_path_getmtime::*; pub(crate) use os_path_getsize::*; +pub(crate) use os_path_isabs::*; +pub(crate) use os_path_isdir::*; +pub(crate) use os_path_isfile::*; +pub(crate) use os_path_islink::*; +pub(crate) use os_readlink::*; +pub(crate) use os_remove::*; +pub(crate) use os_rmdir::*; pub(crate) use os_sep_split::*; +pub(crate) use os_unlink::*; pub(crate) use path_constructor_current_directory::*; pub(crate) use replaceable_by_pathlib::*; mod glob_rule; mod invalid_pathlib_with_suffix; +mod os_path_abspath; +mod os_path_basename; +mod os_path_dirname; +mod os_path_exists; +mod os_path_expanduser; mod os_path_getatime; mod os_path_getctime; mod os_path_getmtime; mod os_path_getsize; +mod os_path_isabs; +mod os_path_isdir; +mod os_path_isfile; +mod os_path_islink; +mod os_readlink; +mod os_remove; +mod os_rmdir; mod os_sep_split; +mod os_unlink; mod path_constructor_current_directory; mod replaceable_by_pathlib; diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_abspath.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_abspath.rs new file mode 100644 index 0000000000000..a8961da959999 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_abspath.rs @@ -0,0 +1,74 @@ +use crate::checkers::ast::Checker; +use crate::preview::is_fix_os_path_abspath_enabled; +use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls; +use crate::{FixAvailability, Violation}; +use ruff_macros::{ViolationMetadata, derive_message_formats}; +use ruff_python_ast::ExprCall; + +/// ## What it does +/// Checks for uses of `os.path.abspath`. +/// +/// ## Why is this bad? +/// `pathlib` offers a high-level API for path manipulation, as compared to +/// the lower-level API offered by `os.path`. When possible, using `Path` object +/// methods such as `Path.resolve()` can improve readability over the `os.path` +/// module's counterparts (e.g., `os.path.abspath()`). +/// +/// ## Examples +/// ```python +/// import os +/// +/// file_path = os.path.abspath("../path/to/file") +/// ``` +/// +/// Use instead: +/// ```python +/// from pathlib import Path +/// +/// file_path = Path("../path/to/file").resolve() +/// ``` +/// +/// ## Known issues +/// While using `pathlib` can improve the readability and type safety of your code, +/// it can be less performant than the lower-level alternatives that work directly with strings, +/// especially on older versions of Python. +/// +/// ## Fix Safety +/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression. +/// +/// ## References +/// - [Python documentation: `Path.resolve`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.resolve) +/// - [Python documentation: `os.path.abspath`](https://docs.python.org/3/library/os.path.html#os.path.abspath) +/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) +/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) +/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) +/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) +#[derive(ViolationMetadata)] +pub(crate) struct OsPathAbspath; + +impl Violation for OsPathAbspath { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] + fn message(&self) -> String { + "`os.path.abspath()` should be replaced by `Path.resolve()`".to_string() + } + + fn fix_title(&self) -> Option { + Some("Replace with `Path(...).resolve()`".to_string()) + } +} + +/// PTH100 +pub(crate) fn os_path_abspath(checker: &Checker, call: &ExprCall, segments: &[&str]) { + if segments != ["os", "path", "abspath"] { + return; + } + check_os_pathlib_single_arg_calls( + checker, + call, + "resolve()", + "path", + is_fix_os_path_abspath_enabled(checker.settings()), + OsPathAbspath, + ); +} diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_basename.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_basename.rs new file mode 100644 index 0000000000000..f69526dd7abe3 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_basename.rs @@ -0,0 +1,73 @@ +use crate::checkers::ast::Checker; +use crate::preview::is_fix_os_path_basename_enabled; +use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls; +use crate::{FixAvailability, Violation}; +use ruff_macros::{ViolationMetadata, derive_message_formats}; +use ruff_python_ast::ExprCall; + +/// ## What it does +/// Checks for uses of `os.path.basename`. +/// +/// ## Why is this bad? +/// `pathlib` offers a high-level API for path manipulation, as compared to +/// the lower-level API offered by `os.path`. When possible, using `Path` object +/// methods such as `Path.name` can improve readability over the `os.path` +/// module's counterparts (e.g., `os.path.basename()`). +/// +/// ## Examples +/// ```python +/// import os +/// +/// os.path.basename(__file__) +/// ``` +/// +/// Use instead: +/// ```python +/// from pathlib import Path +/// +/// Path(__file__).name +/// ``` +/// +/// ## Known issues +/// While using `pathlib` can improve the readability and type safety of your code, +/// it can be less performant than the lower-level alternatives that work directly with strings, +/// especially on older versions of Python. +/// +/// ## Fix Safety +/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression. +/// +/// ## References +/// - [Python documentation: `PurePath.name`](https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.name) +/// - [Python documentation: `os.path.basename`](https://docs.python.org/3/library/os.path.html#os.path.basename) +/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) +/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) +/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) +/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) +#[derive(ViolationMetadata)] +pub(crate) struct OsPathBasename; + +impl Violation for OsPathBasename { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] + fn message(&self) -> String { + "`os.path.basename()` should be replaced by `Path.name`".to_string() + } + fn fix_title(&self) -> Option { + Some("Replace with `Path(...).name`".to_string()) + } +} + +/// PTH119 +pub(crate) fn os_path_basename(checker: &Checker, call: &ExprCall, segments: &[&str]) { + if segments != ["os", "path", "basename"] { + return; + } + check_os_pathlib_single_arg_calls( + checker, + call, + "name", + "p", + is_fix_os_path_basename_enabled(checker.settings()), + OsPathBasename, + ); +} diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_dirname.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_dirname.rs new file mode 100644 index 0000000000000..a39ebc2814a27 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_dirname.rs @@ -0,0 +1,73 @@ +use crate::checkers::ast::Checker; +use crate::preview::is_fix_os_path_dirname_enabled; +use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls; +use crate::{FixAvailability, Violation}; +use ruff_macros::{ViolationMetadata, derive_message_formats}; +use ruff_python_ast::ExprCall; + +/// ## What it does +/// Checks for uses of `os.path.dirname`. +/// +/// ## Why is this bad? +/// `pathlib` offers a high-level API for path manipulation, as compared to +/// the lower-level API offered by `os.path`. When possible, using `Path` object +/// methods such as `Path.parent` can improve readability over the `os.path` +/// module's counterparts (e.g., `os.path.dirname()`). +/// +/// ## Examples +/// ```python +/// import os +/// +/// os.path.dirname(__file__) +/// ``` +/// +/// Use instead: +/// ```python +/// from pathlib import Path +/// +/// Path(__file__).parent +/// ``` +/// +/// ## Fix Safety +/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression. +/// +/// ## Known issues +/// While using `pathlib` can improve the readability and type safety of your code, +/// it can be less performant than the lower-level alternatives that work directly with strings, +/// especially on older versions of Python. +/// +/// ## References +/// - [Python documentation: `PurePath.parent`](https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.parent) +/// - [Python documentation: `os.path.dirname`](https://docs.python.org/3/library/os.path.html#os.path.dirname) +/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) +/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) +/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) +/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) +#[derive(ViolationMetadata)] +pub(crate) struct OsPathDirname; + +impl Violation for OsPathDirname { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] + fn message(&self) -> String { + "`os.path.dirname()` should be replaced by `Path.parent`".to_string() + } + fn fix_title(&self) -> Option { + Some("Replace with `Path(...).parent`".to_string()) + } +} + +/// PTH120 +pub(crate) fn os_path_dirname(checker: &Checker, call: &ExprCall, segments: &[&str]) { + if segments != ["os", "path", "dirname"] { + return; + } + check_os_pathlib_single_arg_calls( + checker, + call, + "parent", + "p", + is_fix_os_path_dirname_enabled(checker.settings()), + OsPathDirname, + ); +} diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_exists.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_exists.rs new file mode 100644 index 0000000000000..d56697b7b1852 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_exists.rs @@ -0,0 +1,73 @@ +use crate::checkers::ast::Checker; +use crate::preview::is_fix_os_path_exists_enabled; +use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls; +use crate::{FixAvailability, Violation}; +use ruff_macros::{ViolationMetadata, derive_message_formats}; +use ruff_python_ast::ExprCall; + +/// ## What it does +/// Checks for uses of `os.path.exists`. +/// +/// ## Why is this bad? +/// `pathlib` offers a high-level API for path manipulation, as compared to +/// the lower-level API offered by `os.path`. When possible, using `Path` object +/// methods such as `Path.exists()` can improve readability over the `os.path` +/// module's counterparts (e.g., `os.path.exists()`). +/// +/// ## Examples +/// ```python +/// import os +/// +/// os.path.exists("file.py") +/// ``` +/// +/// Use instead: +/// ```python +/// from pathlib import Path +/// +/// Path("file.py").exists() +/// ``` +/// +/// ## Known issues +/// While using `pathlib` can improve the readability and type safety of your code, +/// it can be less performant than the lower-level alternatives that work directly with strings, +/// especially on older versions of Python. +/// +/// ## Fix Safety +/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression. +/// +/// ## References +/// - [Python documentation: `Path.exists`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.exists) +/// - [Python documentation: `os.path.exists`](https://docs.python.org/3/library/os.path.html#os.path.exists) +/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) +/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) +/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) +/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) +#[derive(ViolationMetadata)] +pub(crate) struct OsPathExists; + +impl Violation for OsPathExists { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] + fn message(&self) -> String { + "`os.path.exists()` should be replaced by `Path.exists()`".to_string() + } + fn fix_title(&self) -> Option { + Some("Replace with `Path(...).exists()`".to_string()) + } +} + +/// PTH110 +pub(crate) fn os_path_exists(checker: &Checker, call: &ExprCall, segments: &[&str]) { + if segments != ["os", "path", "exists"] { + return; + } + check_os_pathlib_single_arg_calls( + checker, + call, + "exists()", + "path", + is_fix_os_path_exists_enabled(checker.settings()), + OsPathExists, + ); +} diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_expanduser.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_expanduser.rs new file mode 100644 index 0000000000000..6c25e30a5fcd3 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_expanduser.rs @@ -0,0 +1,73 @@ +use crate::checkers::ast::Checker; +use crate::preview::is_fix_os_path_expanduser_enabled; +use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls; +use crate::{FixAvailability, Violation}; +use ruff_macros::{ViolationMetadata, derive_message_formats}; +use ruff_python_ast::ExprCall; + +/// ## What it does +/// Checks for uses of `os.path.expanduser`. +/// +/// ## Why is this bad? +/// `pathlib` offers a high-level API for path manipulation, as compared to +/// the lower-level API offered by `os.path`. When possible, using `Path` object +/// methods such as `Path.expanduser()` can improve readability over the `os.path` +/// module's counterparts (e.g., as `os.path.expanduser()`). +/// +/// ## Examples +/// ```python +/// import os +/// +/// os.path.expanduser("~/films/Monty Python") +/// ``` +/// +/// Use instead: +/// ```python +/// from pathlib import Path +/// +/// Path("~/films/Monty Python").expanduser() +/// ``` +/// +/// ## Known issues +/// While using `pathlib` can improve the readability and type safety of your code, +/// it can be less performant than the lower-level alternatives that work directly with strings, +/// especially on older versions of Python. +/// +/// ## Fix Safety +/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression. +/// +/// ## References +/// - [Python documentation: `Path.expanduser`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.expanduser) +/// - [Python documentation: `os.path.expanduser`](https://docs.python.org/3/library/os.path.html#os.path.expanduser) +/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) +/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) +/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) +/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) +#[derive(ViolationMetadata)] +pub(crate) struct OsPathExpanduser; + +impl Violation for OsPathExpanduser { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] + fn message(&self) -> String { + "`os.path.expanduser()` should be replaced by `Path.expanduser()`".to_string() + } + fn fix_title(&self) -> Option { + Some("Replace with `Path(...).expanduser()`".to_string()) + } +} + +/// PTH111 +pub(crate) fn os_path_expanduser(checker: &Checker, call: &ExprCall, segments: &[&str]) { + if segments != ["os", "path", "expanduser"] { + return; + } + check_os_pathlib_single_arg_calls( + checker, + call, + "expanduser()", + "path", + is_fix_os_path_expanduser_enabled(checker.settings()), + OsPathExpanduser, + ); +} diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getatime.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getatime.rs index 9ec2297844a62..435ec1a9393ad 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getatime.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getatime.rs @@ -1,6 +1,6 @@ use crate::checkers::ast::Checker; use crate::preview::is_fix_os_path_getatime_enabled; -use crate::rules::flake8_use_pathlib::helpers::check_os_path_get_calls; +use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls; use crate::{FixAvailability, Violation}; use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_ast::ExprCall; @@ -61,12 +61,15 @@ impl Violation for OsPathGetatime { } /// PTH203 -pub(crate) fn os_path_getatime(checker: &Checker, call: &ExprCall) { - check_os_path_get_calls( +pub(crate) fn os_path_getatime(checker: &Checker, call: &ExprCall, segments: &[&str]) { + if segments != ["os", "path", "getatime"] { + return; + } + check_os_pathlib_single_arg_calls( checker, call, - "getatime", - "st_atime", + "stat().st_atime", + "filename", is_fix_os_path_getatime_enabled(checker.settings()), OsPathGetatime, ); diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getctime.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getctime.rs index 023d17daeb5e1..7b01da2234802 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getctime.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getctime.rs @@ -1,6 +1,6 @@ use crate::checkers::ast::Checker; use crate::preview::is_fix_os_path_getctime_enabled; -use crate::rules::flake8_use_pathlib::helpers::check_os_path_get_calls; +use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls; use crate::{FixAvailability, Violation}; use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_ast::ExprCall; @@ -62,12 +62,15 @@ impl Violation for OsPathGetctime { } /// PTH205 -pub(crate) fn os_path_getctime(checker: &Checker, call: &ExprCall) { - check_os_path_get_calls( +pub(crate) fn os_path_getctime(checker: &Checker, call: &ExprCall, segments: &[&str]) { + if segments != ["os", "path", "getctime"] { + return; + } + check_os_pathlib_single_arg_calls( checker, call, - "getctime", - "st_ctime", + "stat().st_ctime", + "filename", is_fix_os_path_getctime_enabled(checker.settings()), OsPathGetctime, ); diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getmtime.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getmtime.rs index 40c7d96e8a561..dbcc1d44b9535 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getmtime.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getmtime.rs @@ -1,6 +1,6 @@ use crate::checkers::ast::Checker; use crate::preview::is_fix_os_path_getmtime_enabled; -use crate::rules::flake8_use_pathlib::helpers::check_os_path_get_calls; +use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls; use crate::{FixAvailability, Violation}; use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_ast::ExprCall; @@ -62,12 +62,15 @@ impl Violation for OsPathGetmtime { } /// PTH204 -pub(crate) fn os_path_getmtime(checker: &Checker, call: &ExprCall) { - check_os_path_get_calls( +pub(crate) fn os_path_getmtime(checker: &Checker, call: &ExprCall, segments: &[&str]) { + if segments != ["os", "path", "getmtime"] { + return; + } + check_os_pathlib_single_arg_calls( checker, call, - "getmtime", - "st_mtime", + "stat().st_mtime", + "filename", is_fix_os_path_getmtime_enabled(checker.settings()), OsPathGetmtime, ); diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getsize.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getsize.rs index 6e3da85fac006..9dc2606d7f2a0 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getsize.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_getsize.rs @@ -1,6 +1,6 @@ use crate::checkers::ast::Checker; use crate::preview::is_fix_os_path_getsize_enabled; -use crate::rules::flake8_use_pathlib::helpers::check_os_path_get_calls; +use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls; use crate::{FixAvailability, Violation}; use ruff_macros::{ViolationMetadata, derive_message_formats}; use ruff_python_ast::ExprCall; @@ -62,12 +62,15 @@ impl Violation for OsPathGetsize { } /// PTH202 -pub(crate) fn os_path_getsize(checker: &Checker, call: &ExprCall) { - check_os_path_get_calls( +pub(crate) fn os_path_getsize(checker: &Checker, call: &ExprCall, segments: &[&str]) { + if segments != ["os", "path", "getsize"] { + return; + } + check_os_pathlib_single_arg_calls( checker, call, - "getsize", - "st_size", + "stat().st_size", + "filename", is_fix_os_path_getsize_enabled(checker.settings()), OsPathGetsize, ); diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_isabs.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_isabs.rs new file mode 100644 index 0000000000000..482ec95f24484 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_isabs.rs @@ -0,0 +1,72 @@ +use crate::checkers::ast::Checker; +use crate::preview::is_fix_os_path_isabs_enabled; +use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls; +use crate::{FixAvailability, Violation}; +use ruff_macros::{ViolationMetadata, derive_message_formats}; +use ruff_python_ast::ExprCall; + +/// ## What it does +/// Checks for uses of `os.path.isabs`. +/// +/// ## Why is this bad? +/// `pathlib` offers a high-level API for path manipulation, as compared to +/// the lower-level API offered by `os.path`. When possible, using `Path` object +/// methods such as `Path.is_absolute()` can improve readability over the `os.path` +/// module's counterparts (e.g., as `os.path.isabs()`). +/// +/// ## Examples +/// ```python +/// import os +/// +/// if os.path.isabs(file_name): +/// print("Absolute path!") +/// ``` +/// +/// Use instead: +/// ```python +/// from pathlib import Path +/// +/// if Path(file_name).is_absolute(): +/// print("Absolute path!") +/// ``` +/// +/// ## Known issues +/// While using `pathlib` can improve the readability and type safety of your code, +/// it can be less performant than the lower-level alternatives that work directly with strings, +/// especially on older versions of Python. +/// +/// ## References +/// - [Python documentation: `PurePath.is_absolute`](https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.is_absolute) +/// - [Python documentation: `os.path.isabs`](https://docs.python.org/3/library/os.path.html#os.path.isabs) +/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) +/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) +/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) +/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) +#[derive(ViolationMetadata)] +pub(crate) struct OsPathIsabs; + +impl Violation for OsPathIsabs { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] + fn message(&self) -> String { + "`os.path.isabs()` should be replaced by `Path.is_absolute()`".to_string() + } + fn fix_title(&self) -> Option { + Some("Replace with `Path(...).is_absolute()`".to_string()) + } +} + +/// PTH117 +pub(crate) fn os_path_isabs(checker: &Checker, call: &ExprCall, segments: &[&str]) { + if segments != ["os", "path", "isabs"] { + return; + } + check_os_pathlib_single_arg_calls( + checker, + call, + "is_absolute()", + "s", + is_fix_os_path_isabs_enabled(checker.settings()), + OsPathIsabs, + ); +} diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_isdir.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_isdir.rs new file mode 100644 index 0000000000000..4c33745e521c1 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_isdir.rs @@ -0,0 +1,75 @@ +use crate::checkers::ast::Checker; +use crate::preview::is_fix_os_path_isdir_enabled; +use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls; +use crate::{FixAvailability, Violation}; +use ruff_macros::{ViolationMetadata, derive_message_formats}; +use ruff_python_ast::ExprCall; + +/// ## What it does +/// Checks for uses of `os.path.isdir`. +/// +/// ## Why is this bad? +/// `pathlib` offers a high-level API for path manipulation, as compared to +/// the lower-level API offered by `os.path`. When possible, using `Path` object +/// methods such as `Path.is_dir()` can improve readability over the `os.path` +/// module's counterparts (e.g., `os.path.isdir()`). +/// +/// ## Examples +/// ```python +/// import os +/// +/// os.path.isdir("docs") +/// ``` +/// +/// Use instead: +/// ```python +/// from pathlib import Path +/// +/// Path("docs").is_dir() +/// ``` +/// +/// ## Known issues +/// While using `pathlib` can improve the readability and type safety of your code, +/// it can be less performant than the lower-level alternatives that work directly with strings, +/// especially on older versions of Python. +/// +/// ## Fix Safety +/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression. +/// +/// ## References +/// - [Python documentation: `Path.is_dir`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.is_dir) +/// - [Python documentation: `os.path.isdir`](https://docs.python.org/3/library/os.path.html#os.path.isdir) +/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) +/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) +/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) +/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) +#[derive(ViolationMetadata)] +pub(crate) struct OsPathIsdir; + +impl Violation for OsPathIsdir { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] + fn message(&self) -> String { + "`os.path.isdir()` should be replaced by `Path.is_dir()`".to_string() + } + + fn fix_title(&self) -> Option { + Some("Replace with `Path(...).is_dir()`".to_string()) + } +} + +/// PTH112 +pub(crate) fn os_path_isdir(checker: &Checker, call: &ExprCall, segments: &[&str]) { + if segments != ["os", "path", "isdir"] { + return; + } + + check_os_pathlib_single_arg_calls( + checker, + call, + "is_dir()", + "s", + is_fix_os_path_isdir_enabled(checker.settings()), + OsPathIsdir, + ); +} diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_isfile.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_isfile.rs new file mode 100644 index 0000000000000..0fa2bd4fd8cbb --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_isfile.rs @@ -0,0 +1,75 @@ +use crate::checkers::ast::Checker; +use crate::preview::is_fix_os_path_isfile_enabled; +use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls; +use crate::{FixAvailability, Violation}; +use ruff_macros::{ViolationMetadata, derive_message_formats}; +use ruff_python_ast::ExprCall; + +/// ## What it does +/// Checks for uses of `os.path.isfile`. +/// +/// ## Why is this bad? +/// `pathlib` offers a high-level API for path manipulation, as compared to +/// the lower-level API offered by `os.path`. When possible, using `Path` object +/// methods such as `Path.is_file()` can improve readability over the `os.path` +/// module's counterparts (e.g., `os.path.isfile()`). +/// +/// ## Examples +/// ```python +/// import os +/// +/// os.path.isfile("docs") +/// ``` +/// +/// Use instead: +/// ```python +/// from pathlib import Path +/// +/// Path("docs").is_file() +/// ``` +/// +/// ## Known issues +/// While using `pathlib` can improve the readability and type safety of your code, +/// it can be less performant than the lower-level alternatives that work directly with strings, +/// especially on older versions of Python. +/// +/// ## Fix Safety +/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression. +/// +/// ## References +/// - [Python documentation: `Path.is_file`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.is_file) +/// - [Python documentation: `os.path.isfile`](https://docs.python.org/3/library/os.path.html#os.path.isfile) +/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) +/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) +/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) +/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) +#[derive(ViolationMetadata)] +pub(crate) struct OsPathIsfile; + +impl Violation for OsPathIsfile { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] + fn message(&self) -> String { + "`os.path.isfile()` should be replaced by `Path.is_file()`".to_string() + } + + fn fix_title(&self) -> Option { + Some("Replace with `Path(...).is_file()`".to_string()) + } +} + +/// PTH113 +pub(crate) fn os_path_isfile(checker: &Checker, call: &ExprCall, segments: &[&str]) { + if segments != ["os", "path", "isfile"] { + return; + } + + check_os_pathlib_single_arg_calls( + checker, + call, + "is_file()", + "path", + is_fix_os_path_isfile_enabled(checker.settings()), + OsPathIsfile, + ); +} diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_islink.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_islink.rs new file mode 100644 index 0000000000000..db9aa3d5e93ff --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_path_islink.rs @@ -0,0 +1,75 @@ +use crate::checkers::ast::Checker; +use crate::preview::is_fix_os_path_islink_enabled; +use crate::rules::flake8_use_pathlib::helpers::check_os_pathlib_single_arg_calls; +use crate::{FixAvailability, Violation}; +use ruff_macros::{ViolationMetadata, derive_message_formats}; +use ruff_python_ast::ExprCall; + +/// ## What it does +/// Checks for uses of `os.path.islink`. +/// +/// ## Why is this bad? +/// `pathlib` offers a high-level API for path manipulation, as compared to +/// the lower-level API offered by `os.path`. When possible, using `Path` object +/// methods such as `Path.is_symlink()` can improve readability over the `os.path` +/// module's counterparts (e.g., `os.path.islink()`). +/// +/// ## Examples +/// ```python +/// import os +/// +/// os.path.islink("docs") +/// ``` +/// +/// Use instead: +/// ```python +/// from pathlib import Path +/// +/// Path("docs").is_symlink() +/// ``` +/// +/// ## Known issues +/// While using `pathlib` can improve the readability and type safety of your code, +/// it can be less performant than the lower-level alternatives that work directly with strings, +/// especially on older versions of Python. +/// +/// ## Fix Safety +/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression. +/// +/// ## References +/// - [Python documentation: `Path.is_symlink`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.is_symlink) +/// - [Python documentation: `os.path.islink`](https://docs.python.org/3/library/os.path.html#os.path.islink) +/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) +/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) +/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) +/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) +#[derive(ViolationMetadata)] +pub(crate) struct OsPathIslink; + +impl Violation for OsPathIslink { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] + fn message(&self) -> String { + "`os.path.islink()` should be replaced by `Path.is_symlink()`".to_string() + } + + fn fix_title(&self) -> Option { + Some("Replace with `Path(...).is_symlink()`".to_string()) + } +} + +/// PTH114 +pub(crate) fn os_path_islink(checker: &Checker, call: &ExprCall, segments: &[&str]) { + if segments != ["os", "path", "islink"] { + return; + } + + check_os_pathlib_single_arg_calls( + checker, + call, + "is_symlink()", + "path", + is_fix_os_path_islink_enabled(checker.settings()), + OsPathIslink, + ); +} diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_readlink.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_readlink.rs new file mode 100644 index 0000000000000..62526f6666a8e --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_readlink.rs @@ -0,0 +1,91 @@ +use crate::checkers::ast::Checker; +use crate::preview::is_fix_os_readlink_enabled; +use crate::rules::flake8_use_pathlib::helpers::{ + check_os_pathlib_single_arg_calls, is_keyword_only_argument_non_default, +}; +use crate::{FixAvailability, Violation}; +use ruff_macros::{ViolationMetadata, derive_message_formats}; +use ruff_python_ast::{ExprCall, PythonVersion}; + +/// ## What it does +/// Checks for uses of `os.readlink`. +/// +/// ## Why is this bad? +/// `pathlib` offers a high-level API for path manipulation, as compared to +/// the lower-level API offered by `os`. When possible, using `Path` object +/// methods such as `Path.readlink()` can improve readability over the `os` +/// module's counterparts (e.g., `os.readlink()`). +/// +/// ## Examples +/// ```python +/// import os +/// +/// os.readlink(file_name) +/// ``` +/// +/// Use instead: +/// ```python +/// from pathlib import Path +/// +/// Path(file_name).readlink() +/// ``` +/// +/// ## Known issues +/// While using `pathlib` can improve the readability and type safety of your code, +/// it can be less performant than the lower-level alternatives that work directly with strings, +/// especially on older versions of Python. +/// +/// ## Fix Safety +/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression. +/// +/// ## References +/// - [Python documentation: `Path.readlink`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.readline) +/// - [Python documentation: `os.readlink`](https://docs.python.org/3/library/os.html#os.readlink) +/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) +/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) +/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) +/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) +#[derive(ViolationMetadata)] +pub(crate) struct OsReadlink; + +impl Violation for OsReadlink { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] + fn message(&self) -> String { + "`os.readlink()` should be replaced by `Path.readlink()`".to_string() + } + + fn fix_title(&self) -> Option { + Some("Replace with `Path(...).readlink()`".to_string()) + } +} + +/// PTH115 +pub(crate) fn os_readlink(checker: &Checker, call: &ExprCall, segments: &[&str]) { + // Python 3.9+ + if checker.target_version() < PythonVersion::PY39 { + return; + } + // `dir_fd` is not supported by pathlib, so check if it's set to non-default values. + // Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.readlink) + // ```text + // 0 1 + // os.readlink(path, *, dir_fd=None) + // ``` + if is_keyword_only_argument_non_default(&call.arguments, "dir_fd") { + return; + } + + if segments != ["os", "readlink"] { + return; + } + + check_os_pathlib_single_arg_calls( + checker, + call, + "readlink()", + "path", + is_fix_os_readlink_enabled(checker.settings()), + OsReadlink, + ); +} diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_remove.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_remove.rs new file mode 100644 index 0000000000000..3ec99469eae42 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_remove.rs @@ -0,0 +1,86 @@ +use crate::checkers::ast::Checker; +use crate::preview::is_fix_os_remove_enabled; +use crate::rules::flake8_use_pathlib::helpers::{ + check_os_pathlib_single_arg_calls, is_keyword_only_argument_non_default, +}; +use crate::{FixAvailability, Violation}; +use ruff_macros::{ViolationMetadata, derive_message_formats}; +use ruff_python_ast::ExprCall; + +/// ## What it does +/// Checks for uses of `os.remove`. +/// +/// ## Why is this bad? +/// `pathlib` offers a high-level API for path manipulation, as compared to +/// the lower-level API offered by `os`. When possible, using `Path` object +/// methods such as `Path.unlink()` can improve readability over the `os` +/// module's counterparts (e.g., `os.remove()`). +/// +/// ## Examples +/// ```python +/// import os +/// +/// os.remove("file.py") +/// ``` +/// +/// Use instead: +/// ```python +/// from pathlib import Path +/// +/// Path("file.py").unlink() +/// ``` +/// +/// ## Known issues +/// While using `pathlib` can improve the readability and type safety of your code, +/// it can be less performant than the lower-level alternatives that work directly with strings, +/// especially on older versions of Python. +/// +/// ## Fix Safety +/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression. +/// +/// ## References +/// - [Python documentation: `Path.unlink`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.unlink) +/// - [Python documentation: `os.remove`](https://docs.python.org/3/library/os.html#os.remove) +/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) +/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) +/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) +/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) +#[derive(ViolationMetadata)] +pub(crate) struct OsRemove; + +impl Violation for OsRemove { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] + fn message(&self) -> String { + "`os.remove()` should be replaced by `Path.unlink()`".to_string() + } + fn fix_title(&self) -> Option { + Some("Replace with `Path(...).unlink()`".to_string()) + } +} + +/// PTH107 +pub(crate) fn os_remove(checker: &Checker, call: &ExprCall, segments: &[&str]) { + // `dir_fd` is not supported by pathlib, so check if it's set to non-default values. + // Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.remove) + // ```text + // 0 1 + // os.remove(path, *, dir_fd=None) + // ``` + if is_keyword_only_argument_non_default(&call.arguments, "dir_fd") { + return; + } + + if segments != ["os", "remove"] { + return; + } + + check_os_pathlib_single_arg_calls( + checker, + call, + "unlink()", + "path", + is_fix_os_remove_enabled(checker.settings()), + OsRemove, + ); +} diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_rmdir.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_rmdir.rs new file mode 100644 index 0000000000000..bb85284f4e15f --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_rmdir.rs @@ -0,0 +1,86 @@ +use crate::checkers::ast::Checker; +use crate::preview::is_fix_os_rmdir_enabled; +use crate::rules::flake8_use_pathlib::helpers::{ + check_os_pathlib_single_arg_calls, is_keyword_only_argument_non_default, +}; +use crate::{FixAvailability, Violation}; +use ruff_macros::{ViolationMetadata, derive_message_formats}; +use ruff_python_ast::ExprCall; + +/// ## What it does +/// Checks for uses of `os.rmdir`. +/// +/// ## Why is this bad? +/// `pathlib` offers a high-level API for path manipulation, as compared to +/// the lower-level API offered by `os`. When possible, using `Path` object +/// methods such as `Path.rmdir()` can improve readability over the `os` +/// module's counterparts (e.g., `os.rmdir()`). +/// +/// ## Examples +/// ```python +/// import os +/// +/// os.rmdir("folder/") +/// ``` +/// +/// Use instead: +/// ```python +/// from pathlib import Path +/// +/// Path("folder/").rmdir() +/// ``` +/// +/// ## Known issues +/// While using `pathlib` can improve the readability and type safety of your code, +/// it can be less performant than the lower-level alternatives that work directly with strings, +/// especially on older versions of Python. +/// +/// ## Fix Safety +/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression. +/// +/// ## References +/// - [Python documentation: `Path.rmdir`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.rmdir) +/// - [Python documentation: `os.rmdir`](https://docs.python.org/3/library/os.html#os.rmdir) +/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) +/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) +/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) +/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) +#[derive(ViolationMetadata)] +pub(crate) struct OsRmdir; + +impl Violation for OsRmdir { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] + fn message(&self) -> String { + "`os.rmdir()` should be replaced by `Path.rmdir()`".to_string() + } + fn fix_title(&self) -> Option { + Some("Replace with `Path(...).rmdir()`".to_string()) + } +} + +/// PTH106 +pub(crate) fn os_rmdir(checker: &Checker, call: &ExprCall, segments: &[&str]) { + // `dir_fd` is not supported by pathlib, so check if it's set to non-default values. + // Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.rmdir) + // ```text + // 0 1 + // os.rmdir(path, *, dir_fd=None) + // ``` + if is_keyword_only_argument_non_default(&call.arguments, "dir_fd") { + return; + } + + if segments != ["os", "rmdir"] { + return; + } + + check_os_pathlib_single_arg_calls( + checker, + call, + "rmdir()", + "path", + is_fix_os_rmdir_enabled(checker.settings()), + OsRmdir, + ); +} diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_unlink.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_unlink.rs new file mode 100644 index 0000000000000..5d97338e880d5 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_unlink.rs @@ -0,0 +1,86 @@ +use crate::checkers::ast::Checker; +use crate::preview::is_fix_os_unlink_enabled; +use crate::rules::flake8_use_pathlib::helpers::{ + check_os_pathlib_single_arg_calls, is_keyword_only_argument_non_default, +}; +use crate::{FixAvailability, Violation}; +use ruff_macros::{ViolationMetadata, derive_message_formats}; +use ruff_python_ast::ExprCall; + +/// ## What it does +/// Checks for uses of `os.unlink`. +/// +/// ## Why is this bad? +/// `pathlib` offers a high-level API for path manipulation, as compared to +/// the lower-level API offered by `os`. When possible, using `Path` object +/// methods such as `Path.unlink()` can improve readability over the `os` +/// module's counterparts (e.g., `os.unlink()`). +/// +/// ## Examples +/// ```python +/// import os +/// +/// os.unlink("file.py") +/// ``` +/// +/// Use instead: +/// ```python +/// from pathlib import Path +/// +/// Path("file.py").unlink() +/// ``` +/// +/// ## Known issues +/// While using `pathlib` can improve the readability and type safety of your code, +/// it can be less performant than the lower-level alternatives that work directly with strings, +/// especially on older versions of Python. +/// +/// ## Fix Safety +/// This rule's fix is marked as unsafe if the replacement would remove comments attached to the original expression. +/// +/// ## References +/// - [Python documentation: `Path.unlink`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.unlink) +/// - [Python documentation: `os.unlink`](https://docs.python.org/3/library/os.html#os.unlink) +/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) +/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) +/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) +/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) +#[derive(ViolationMetadata)] +pub(crate) struct OsUnlink; + +impl Violation for OsUnlink { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] + fn message(&self) -> String { + "`os.unlink()` should be replaced by `Path.unlink()`".to_string() + } + fn fix_title(&self) -> Option { + Some("Replace with `Path(...).unlink()`".to_string()) + } +} + +/// PTH108 +pub(crate) fn os_unlink(checker: &Checker, call: &ExprCall, segments: &[&str]) { + // `dir_fd` is not supported by pathlib, so check if it's set to non-default values. + // Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.unlink) + // ```text + // 0 1 + // os.unlink(path, *, dir_fd=None) + // ``` + if is_keyword_only_argument_non_default(&call.arguments, "dir_fd") { + return; + } + + if segments != ["os", "unlink"] { + return; + } + + check_os_pathlib_single_arg_calls( + checker, + call, + "unlink()", + "path", + is_fix_os_unlink_enabled(checker.settings()), + OsUnlink, + ); +} diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/path_constructor_current_directory.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/path_constructor_current_directory.rs index f8d8830a591a7..5b448ebe96e38 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/path_constructor_current_directory.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/path_constructor_current_directory.rs @@ -54,7 +54,11 @@ impl AlwaysFixableViolation for PathConstructorCurrentDirectory { } /// PTH201 -pub(crate) fn path_constructor_current_directory(checker: &Checker, call: &ExprCall) { +pub(crate) fn path_constructor_current_directory( + checker: &Checker, + call: &ExprCall, + segments: &[&str], +) { let applicability = |range| { if checker.comment_ranges().intersects(range) { Applicability::Unsafe @@ -63,15 +67,9 @@ pub(crate) fn path_constructor_current_directory(checker: &Checker, call: &ExprC } }; - let (func, arguments) = (&call.func, &call.arguments); + let arguments = &call.arguments; - if !checker - .semantic() - .resolve_qualified_name(func) - .is_some_and(|qualified_name| { - matches!(qualified_name.segments(), ["pathlib", "Path" | "PurePath"]) - }) - { + if !matches!(segments, ["pathlib", "Path" | "PurePath"]) { return; } diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/replaceable_by_pathlib.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/replaceable_by_pathlib.rs index ab3c7e43ca504..28ce248e2f7a6 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/replaceable_by_pathlib.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/replaceable_by_pathlib.rs @@ -4,14 +4,12 @@ use ruff_python_semantic::analyze::typing; use ruff_text_size::Ranged; use crate::checkers::ast::Checker; +use crate::rules::flake8_use_pathlib::helpers::is_keyword_only_argument_non_default; use crate::rules::flake8_use_pathlib::rules::Glob; use crate::rules::flake8_use_pathlib::violations::{ - BuiltinOpen, Joiner, OsChmod, OsGetcwd, OsListdir, OsMakedirs, OsMkdir, OsPathAbspath, - OsPathBasename, OsPathDirname, OsPathExists, OsPathExpanduser, OsPathIsabs, OsPathIsdir, - OsPathIsfile, OsPathIslink, OsPathJoin, OsPathSamefile, OsPathSplitext, OsReadlink, OsRemove, - OsRename, OsReplace, OsRmdir, OsStat, OsSymlink, OsUnlink, PyPath, + BuiltinOpen, Joiner, OsChmod, OsGetcwd, OsListdir, OsMakedirs, OsMkdir, OsPathJoin, + OsPathSamefile, OsPathSplitext, OsRename, OsReplace, OsStat, OsSymlink, PyPath, }; -use ruff_python_ast::PythonVersion; pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) { let Some(qualified_name) = checker.semantic().resolve_qualified_name(&call.func) else { @@ -20,8 +18,6 @@ pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) { let range = call.func.range(); match qualified_name.segments() { - // PTH100 - ["os", "path", "abspath"] => checker.report_diagnostic_if_enabled(OsPathAbspath, range), // PTH101 ["os", "chmod"] => { // `dir_fd` is not supported by pathlib, so check if it's set to non-default values. @@ -87,60 +83,10 @@ pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) { } checker.report_diagnostic_if_enabled(OsReplace, range) } - // PTH106 - ["os", "rmdir"] => { - // `dir_fd` is not supported by pathlib, so check if it's set to non-default values. - // Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.rmdir) - // ```text - // 0 1 - // os.rmdir(path, *, dir_fd=None) - // ``` - if is_keyword_only_argument_non_default(&call.arguments, "dir_fd") { - return; - } - checker.report_diagnostic_if_enabled(OsRmdir, range) - } - // PTH107 - ["os", "remove"] => { - // `dir_fd` is not supported by pathlib, so check if it's set to non-default values. - // Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.remove) - // ```text - // 0 1 - // os.remove(path, *, dir_fd=None) - // ``` - if is_keyword_only_argument_non_default(&call.arguments, "dir_fd") { - return; - } - checker.report_diagnostic_if_enabled(OsRemove, range) - } - // PTH108 - ["os", "unlink"] => { - // `dir_fd` is not supported by pathlib, so check if it's set to non-default values. - // Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.unlink) - // ```text - // 0 1 - // os.unlink(path, *, dir_fd=None) - // ``` - if is_keyword_only_argument_non_default(&call.arguments, "dir_fd") { - return; - } - checker.report_diagnostic_if_enabled(OsUnlink, range) - } // PTH109 ["os", "getcwd"] => checker.report_diagnostic_if_enabled(OsGetcwd, range), ["os", "getcwdb"] => checker.report_diagnostic_if_enabled(OsGetcwd, range), - // PTH110 - ["os", "path", "exists"] => checker.report_diagnostic_if_enabled(OsPathExists, range), - // PTH111 - ["os", "path", "expanduser"] => { - checker.report_diagnostic_if_enabled(OsPathExpanduser, range) - } - // PTH112 - ["os", "path", "isdir"] => checker.report_diagnostic_if_enabled(OsPathIsdir, range), - // PTH113 - ["os", "path", "isfile"] => checker.report_diagnostic_if_enabled(OsPathIsfile, range), - // PTH114 - ["os", "path", "islink"] => checker.report_diagnostic_if_enabled(OsPathIslink, range), + // PTH116 ["os", "stat"] => { // `dir_fd` is not supported by pathlib, so check if it's set to non-default values. @@ -159,8 +105,6 @@ pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) { } checker.report_diagnostic_if_enabled(OsStat, range) } - // PTH117 - ["os", "path", "isabs"] => checker.report_diagnostic_if_enabled(OsPathIsabs, range), // PTH118 ["os", "path", "join"] => checker.report_diagnostic_if_enabled( OsPathJoin { @@ -184,10 +128,6 @@ pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) { }, range, ), - // PTH119 - ["os", "path", "basename"] => checker.report_diagnostic_if_enabled(OsPathBasename, range), - // PTH120 - ["os", "path", "dirname"] => checker.report_diagnostic_if_enabled(OsPathDirname, range), // PTH121 ["os", "path", "samefile"] => checker.report_diagnostic_if_enabled(OsPathSamefile, range), // PTH122 @@ -208,7 +148,7 @@ pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) { // PTH123 ["" | "builtins", "open"] => { - // `closefd` and `opener` are not supported by pathlib, so check if they are + // `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): @@ -282,20 +222,6 @@ pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) { range, ) } - // PTH115 - // Python 3.9+ - ["os", "readlink"] if checker.target_version() >= PythonVersion::PY39 => { - // `dir_fd` is not supported by pathlib, so check if it's set to non-default values. - // Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.readlink) - // ```text - // 0 1 - // os.readlink(path, *, dir_fd=None) - // ``` - if is_keyword_only_argument_non_default(&call.arguments, "dir_fd") { - return; - } - checker.report_diagnostic_if_enabled(OsReadlink, range) - } // PTH208 ["os", "listdir"] => { if call @@ -338,7 +264,7 @@ fn is_file_descriptor(expr: &Expr, semantic: &SemanticModel) -> bool { fn get_name_expr(expr: &Expr) -> Option<&ast::ExprName> { match expr { Expr::Name(name) => Some(name), - Expr::Call(ast::ExprCall { func, .. }) => get_name_expr(func), + Expr::Call(ExprCall { func, .. }) => get_name_expr(func), _ => None, } } @@ -349,9 +275,3 @@ fn is_argument_non_default(arguments: &ast::Arguments, name: &str, position: usi .find_argument_value(name, position) .is_some_and(|expr| !expr.is_none_literal_expr()) } - -fn is_keyword_only_argument_non_default(arguments: &ast::Arguments, name: &str) -> bool { - arguments - .find_keyword(name) - .is_some_and(|keyword| !keyword.value.is_none_literal_expr()) -} diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__full_name.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__full_name.py.snap index 63d515f0b3ac3..f02d3de37eec6 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__full_name.py.snap +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__full_name.py.snap @@ -10,6 +10,7 @@ full_name.py:7:5: PTH100 `os.path.abspath()` should be replaced by `Path.resolve 8 | aa = os.chmod(p) 9 | aaa = os.mkdir(p) | + = help: Replace with `Path(...).resolve()` full_name.py:8:6: PTH101 `os.chmod()` should be replaced by `Path.chmod()` | @@ -69,6 +70,7 @@ full_name.py:13:1: PTH106 `os.rmdir()` should be replaced by `Path.rmdir()` 14 | os.remove(p) 15 | os.unlink(p) | + = help: Replace with `Path(...).rmdir()` full_name.py:14:1: PTH107 `os.remove()` should be replaced by `Path.unlink()` | @@ -79,6 +81,7 @@ full_name.py:14:1: PTH107 `os.remove()` should be replaced by `Path.unlink()` 15 | os.unlink(p) 16 | os.getcwd(p) | + = help: Replace with `Path(...).unlink()` full_name.py:15:1: PTH108 `os.unlink()` should be replaced by `Path.unlink()` | @@ -89,6 +92,7 @@ full_name.py:15:1: PTH108 `os.unlink()` should be replaced by `Path.unlink()` 16 | os.getcwd(p) 17 | b = os.path.exists(p) | + = help: Replace with `Path(...).unlink()` full_name.py:16:1: PTH109 `os.getcwd()` should be replaced by `Path.cwd()` | @@ -109,6 +113,7 @@ full_name.py:17:5: PTH110 `os.path.exists()` should be replaced by `Path.exists( 18 | bb = os.path.expanduser(p) 19 | bbb = os.path.isdir(p) | + = help: Replace with `Path(...).exists()` full_name.py:18:6: PTH111 `os.path.expanduser()` should be replaced by `Path.expanduser()` | @@ -119,6 +124,7 @@ full_name.py:18:6: PTH111 `os.path.expanduser()` should be replaced by `Path.exp 19 | bbb = os.path.isdir(p) 20 | bbbb = os.path.isfile(p) | + = help: Replace with `Path(...).expanduser()` full_name.py:19:7: PTH112 `os.path.isdir()` should be replaced by `Path.is_dir()` | @@ -129,6 +135,7 @@ full_name.py:19:7: PTH112 `os.path.isdir()` should be replaced by `Path.is_dir() 20 | bbbb = os.path.isfile(p) 21 | bbbbb = os.path.islink(p) | + = help: Replace with `Path(...).is_dir()` full_name.py:20:8: PTH113 `os.path.isfile()` should be replaced by `Path.is_file()` | @@ -139,6 +146,7 @@ full_name.py:20:8: PTH113 `os.path.isfile()` should be replaced by `Path.is_file 21 | bbbbb = os.path.islink(p) 22 | os.readlink(p) | + = help: Replace with `Path(...).is_file()` full_name.py:21:9: PTH114 `os.path.islink()` should be replaced by `Path.is_symlink()` | @@ -149,6 +157,7 @@ full_name.py:21:9: PTH114 `os.path.islink()` should be replaced by `Path.is_syml 22 | os.readlink(p) 23 | os.stat(p) | + = help: Replace with `Path(...).is_symlink()` full_name.py:22:1: PTH115 `os.readlink()` should be replaced by `Path.readlink()` | @@ -159,6 +168,7 @@ full_name.py:22:1: PTH115 `os.readlink()` should be replaced by `Path.readlink() 23 | os.stat(p) 24 | os.path.isabs(p) | + = help: Replace with `Path(...).readlink()` full_name.py:23:1: PTH116 `os.stat()` should be replaced by `Path.stat()`, `Path.owner()`, or `Path.group()` | @@ -179,6 +189,7 @@ full_name.py:24:1: PTH117 `os.path.isabs()` should be replaced by `Path.is_absol 25 | os.path.join(p, q) 26 | os.sep.join([p, q]) | + = help: Replace with `Path(...).is_absolute()` full_name.py:25:1: PTH118 `os.path.join()` should be replaced by `Path` with `/` operator | @@ -219,6 +230,7 @@ full_name.py:28:1: PTH119 `os.path.basename()` should be replaced by `Path.name` 29 | os.path.dirname(p) 30 | os.path.samefile(p) | + = help: Replace with `Path(...).name` full_name.py:29:1: PTH120 `os.path.dirname()` should be replaced by `Path.parent` | @@ -229,6 +241,7 @@ full_name.py:29:1: PTH120 `os.path.dirname()` should be replaced by `Path.parent 30 | os.path.samefile(p) 31 | os.path.splitext(p) | + = help: Replace with `Path(...).parent` full_name.py:30:1: PTH121 `os.path.samefile()` should be replaced by `Path.samefile()` | diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__import_as.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__import_as.py.snap index dcf8d8a659931..aedda0b51deda 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__import_as.py.snap +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__import_as.py.snap @@ -10,6 +10,7 @@ import_as.py:7:5: PTH100 `os.path.abspath()` should be replaced by `Path.resolve 8 | aa = foo.chmod(p) 9 | aaa = foo.mkdir(p) | + = help: Replace with `Path(...).resolve()` import_as.py:8:6: PTH101 `os.chmod()` should be replaced by `Path.chmod()` | @@ -69,6 +70,7 @@ import_as.py:13:1: PTH106 `os.rmdir()` should be replaced by `Path.rmdir()` 14 | foo.remove(p) 15 | foo.unlink(p) | + = help: Replace with `Path(...).rmdir()` import_as.py:14:1: PTH107 `os.remove()` should be replaced by `Path.unlink()` | @@ -79,6 +81,7 @@ import_as.py:14:1: PTH107 `os.remove()` should be replaced by `Path.unlink()` 15 | foo.unlink(p) 16 | foo.getcwd(p) | + = help: Replace with `Path(...).unlink()` import_as.py:15:1: PTH108 `os.unlink()` should be replaced by `Path.unlink()` | @@ -89,6 +92,7 @@ import_as.py:15:1: PTH108 `os.unlink()` should be replaced by `Path.unlink()` 16 | foo.getcwd(p) 17 | b = foo_p.exists(p) | + = help: Replace with `Path(...).unlink()` import_as.py:16:1: PTH109 `os.getcwd()` should be replaced by `Path.cwd()` | @@ -109,6 +113,7 @@ import_as.py:17:5: PTH110 `os.path.exists()` should be replaced by `Path.exists( 18 | bb = foo_p.expanduser(p) 19 | bbb = foo_p.isdir(p) | + = help: Replace with `Path(...).exists()` import_as.py:18:6: PTH111 `os.path.expanduser()` should be replaced by `Path.expanduser()` | @@ -119,6 +124,7 @@ import_as.py:18:6: PTH111 `os.path.expanduser()` should be replaced by `Path.exp 19 | bbb = foo_p.isdir(p) 20 | bbbb = foo_p.isfile(p) | + = help: Replace with `Path(...).expanduser()` import_as.py:19:7: PTH112 `os.path.isdir()` should be replaced by `Path.is_dir()` | @@ -129,6 +135,7 @@ import_as.py:19:7: PTH112 `os.path.isdir()` should be replaced by `Path.is_dir() 20 | bbbb = foo_p.isfile(p) 21 | bbbbb = foo_p.islink(p) | + = help: Replace with `Path(...).is_dir()` import_as.py:20:8: PTH113 `os.path.isfile()` should be replaced by `Path.is_file()` | @@ -139,6 +146,7 @@ import_as.py:20:8: PTH113 `os.path.isfile()` should be replaced by `Path.is_file 21 | bbbbb = foo_p.islink(p) 22 | foo.readlink(p) | + = help: Replace with `Path(...).is_file()` import_as.py:21:9: PTH114 `os.path.islink()` should be replaced by `Path.is_symlink()` | @@ -149,6 +157,7 @@ import_as.py:21:9: PTH114 `os.path.islink()` should be replaced by `Path.is_syml 22 | foo.readlink(p) 23 | foo.stat(p) | + = help: Replace with `Path(...).is_symlink()` import_as.py:22:1: PTH115 `os.readlink()` should be replaced by `Path.readlink()` | @@ -159,6 +168,7 @@ import_as.py:22:1: PTH115 `os.readlink()` should be replaced by `Path.readlink() 23 | foo.stat(p) 24 | foo_p.isabs(p) | + = help: Replace with `Path(...).readlink()` import_as.py:23:1: PTH116 `os.stat()` should be replaced by `Path.stat()`, `Path.owner()`, or `Path.group()` | @@ -179,6 +189,7 @@ import_as.py:24:1: PTH117 `os.path.isabs()` should be replaced by `Path.is_absol 25 | foo_p.join(p, q) 26 | foo.sep.join([p, q]) | + = help: Replace with `Path(...).is_absolute()` import_as.py:25:1: PTH118 `os.path.join()` should be replaced by `Path` with `/` operator | @@ -219,6 +230,7 @@ import_as.py:28:1: PTH119 `os.path.basename()` should be replaced by `Path.name` 29 | foo_p.dirname(p) 30 | foo_p.samefile(p) | + = help: Replace with `Path(...).name` import_as.py:29:1: PTH120 `os.path.dirname()` should be replaced by `Path.parent` | @@ -229,6 +241,7 @@ import_as.py:29:1: PTH120 `os.path.dirname()` should be replaced by `Path.parent 30 | foo_p.samefile(p) 31 | foo_p.splitext(p) | + = help: Replace with `Path(...).parent` import_as.py:30:1: PTH121 `os.path.samefile()` should be replaced by `Path.samefile()` | diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__import_from.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__import_from.py.snap index 0cacb90179cd8..18a2370618df7 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__import_from.py.snap +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__import_from.py.snap @@ -10,6 +10,7 @@ import_from.py:9:5: PTH100 `os.path.abspath()` should be replaced by `Path.resol 10 | aa = chmod(p) 11 | aaa = mkdir(p) | + = help: Replace with `Path(...).resolve()` import_from.py:10:6: PTH101 `os.chmod()` should be replaced by `Path.chmod()` | @@ -69,6 +70,7 @@ import_from.py:15:1: PTH106 `os.rmdir()` should be replaced by `Path.rmdir()` 16 | remove(p) 17 | unlink(p) | + = help: Replace with `Path(...).rmdir()` import_from.py:16:1: PTH107 `os.remove()` should be replaced by `Path.unlink()` | @@ -79,6 +81,7 @@ import_from.py:16:1: PTH107 `os.remove()` should be replaced by `Path.unlink()` 17 | unlink(p) 18 | getcwd(p) | + = help: Replace with `Path(...).unlink()` import_from.py:17:1: PTH108 `os.unlink()` should be replaced by `Path.unlink()` | @@ -89,6 +92,7 @@ import_from.py:17:1: PTH108 `os.unlink()` should be replaced by `Path.unlink()` 18 | getcwd(p) 19 | b = exists(p) | + = help: Replace with `Path(...).unlink()` import_from.py:18:1: PTH109 `os.getcwd()` should be replaced by `Path.cwd()` | @@ -109,6 +113,7 @@ import_from.py:19:5: PTH110 `os.path.exists()` should be replaced by `Path.exist 20 | bb = expanduser(p) 21 | bbb = isdir(p) | + = help: Replace with `Path(...).exists()` import_from.py:20:6: PTH111 `os.path.expanduser()` should be replaced by `Path.expanduser()` | @@ -119,6 +124,7 @@ import_from.py:20:6: PTH111 `os.path.expanduser()` should be replaced by `Path.e 21 | bbb = isdir(p) 22 | bbbb = isfile(p) | + = help: Replace with `Path(...).expanduser()` import_from.py:21:7: PTH112 `os.path.isdir()` should be replaced by `Path.is_dir()` | @@ -129,6 +135,7 @@ import_from.py:21:7: PTH112 `os.path.isdir()` should be replaced by `Path.is_dir 22 | bbbb = isfile(p) 23 | bbbbb = islink(p) | + = help: Replace with `Path(...).is_dir()` import_from.py:22:8: PTH113 `os.path.isfile()` should be replaced by `Path.is_file()` | @@ -139,6 +146,7 @@ import_from.py:22:8: PTH113 `os.path.isfile()` should be replaced by `Path.is_fi 23 | bbbbb = islink(p) 24 | readlink(p) | + = help: Replace with `Path(...).is_file()` import_from.py:23:9: PTH114 `os.path.islink()` should be replaced by `Path.is_symlink()` | @@ -149,6 +157,7 @@ import_from.py:23:9: PTH114 `os.path.islink()` should be replaced by `Path.is_sy 24 | readlink(p) 25 | stat(p) | + = help: Replace with `Path(...).is_symlink()` import_from.py:24:1: PTH115 `os.readlink()` should be replaced by `Path.readlink()` | @@ -159,6 +168,7 @@ import_from.py:24:1: PTH115 `os.readlink()` should be replaced by `Path.readlink 25 | stat(p) 26 | isabs(p) | + = help: Replace with `Path(...).readlink()` import_from.py:25:1: PTH116 `os.stat()` should be replaced by `Path.stat()`, `Path.owner()`, or `Path.group()` | @@ -179,6 +189,7 @@ import_from.py:26:1: PTH117 `os.path.isabs()` should be replaced by `Path.is_abs 27 | join(p, q) 28 | sep.join((p, q)) | + = help: Replace with `Path(...).is_absolute()` import_from.py:27:1: PTH118 `os.path.join()` should be replaced by `Path` with `/` operator | @@ -219,6 +230,7 @@ import_from.py:30:1: PTH119 `os.path.basename()` should be replaced by `Path.nam 31 | dirname(p) 32 | samefile(p) | + = help: Replace with `Path(...).name` import_from.py:31:1: PTH120 `os.path.dirname()` should be replaced by `Path.parent` | @@ -229,6 +241,7 @@ import_from.py:31:1: PTH120 `os.path.dirname()` should be replaced by `Path.pare 32 | samefile(p) 33 | splitext(p) | + = help: Replace with `Path(...).parent` import_from.py:32:1: PTH121 `os.path.samefile()` should be replaced by `Path.samefile()` | diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__import_from_as.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__import_from_as.py.snap index 7f9fc380f4b97..f3b946a72c0d4 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__import_from_as.py.snap +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__import_from_as.py.snap @@ -10,6 +10,7 @@ import_from_as.py:14:5: PTH100 `os.path.abspath()` should be replaced by `Path.r 15 | aa = xchmod(p) 16 | aaa = xmkdir(p) | + = help: Replace with `Path(...).resolve()` import_from_as.py:15:6: PTH101 `os.chmod()` should be replaced by `Path.chmod()` | @@ -69,6 +70,7 @@ import_from_as.py:20:1: PTH106 `os.rmdir()` should be replaced by `Path.rmdir()` 21 | xremove(p) 22 | xunlink(p) | + = help: Replace with `Path(...).rmdir()` import_from_as.py:21:1: PTH107 `os.remove()` should be replaced by `Path.unlink()` | @@ -79,6 +81,7 @@ import_from_as.py:21:1: PTH107 `os.remove()` should be replaced by `Path.unlink( 22 | xunlink(p) 23 | xgetcwd(p) | + = help: Replace with `Path(...).unlink()` import_from_as.py:22:1: PTH108 `os.unlink()` should be replaced by `Path.unlink()` | @@ -89,6 +92,7 @@ import_from_as.py:22:1: PTH108 `os.unlink()` should be replaced by `Path.unlink( 23 | xgetcwd(p) 24 | b = xexists(p) | + = help: Replace with `Path(...).unlink()` import_from_as.py:23:1: PTH109 `os.getcwd()` should be replaced by `Path.cwd()` | @@ -109,6 +113,7 @@ import_from_as.py:24:5: PTH110 `os.path.exists()` should be replaced by `Path.ex 25 | bb = xexpanduser(p) 26 | bbb = xisdir(p) | + = help: Replace with `Path(...).exists()` import_from_as.py:25:6: PTH111 `os.path.expanduser()` should be replaced by `Path.expanduser()` | @@ -119,6 +124,7 @@ import_from_as.py:25:6: PTH111 `os.path.expanduser()` should be replaced by `Pat 26 | bbb = xisdir(p) 27 | bbbb = xisfile(p) | + = help: Replace with `Path(...).expanduser()` import_from_as.py:26:7: PTH112 `os.path.isdir()` should be replaced by `Path.is_dir()` | @@ -129,6 +135,7 @@ import_from_as.py:26:7: PTH112 `os.path.isdir()` should be replaced by `Path.is_ 27 | bbbb = xisfile(p) 28 | bbbbb = xislink(p) | + = help: Replace with `Path(...).is_dir()` import_from_as.py:27:8: PTH113 `os.path.isfile()` should be replaced by `Path.is_file()` | @@ -139,6 +146,7 @@ import_from_as.py:27:8: PTH113 `os.path.isfile()` should be replaced by `Path.is 28 | bbbbb = xislink(p) 29 | xreadlink(p) | + = help: Replace with `Path(...).is_file()` import_from_as.py:28:9: PTH114 `os.path.islink()` should be replaced by `Path.is_symlink()` | @@ -149,6 +157,7 @@ import_from_as.py:28:9: PTH114 `os.path.islink()` should be replaced by `Path.is 29 | xreadlink(p) 30 | xstat(p) | + = help: Replace with `Path(...).is_symlink()` import_from_as.py:29:1: PTH115 `os.readlink()` should be replaced by `Path.readlink()` | @@ -159,6 +168,7 @@ import_from_as.py:29:1: PTH115 `os.readlink()` should be replaced by `Path.readl 30 | xstat(p) 31 | xisabs(p) | + = help: Replace with `Path(...).readlink()` import_from_as.py:30:1: PTH116 `os.stat()` should be replaced by `Path.stat()`, `Path.owner()`, or `Path.group()` | @@ -179,6 +189,7 @@ import_from_as.py:31:1: PTH117 `os.path.isabs()` should be replaced by `Path.is_ 32 | xjoin(p, q) 33 | s.join((p, q)) | + = help: Replace with `Path(...).is_absolute()` import_from_as.py:32:1: PTH118 `os.path.join()` should be replaced by `Path` with `/` operator | @@ -219,6 +230,7 @@ import_from_as.py:35:1: PTH119 `os.path.basename()` should be replaced by `Path. 36 | xdirname(p) 37 | xsamefile(p) | + = help: Replace with `Path(...).name` import_from_as.py:36:1: PTH120 `os.path.dirname()` should be replaced by `Path.parent` | @@ -229,6 +241,7 @@ import_from_as.py:36:1: PTH120 `os.path.dirname()` should be replaced by `Path.p 37 | xsamefile(p) 38 | xsplitext(p) | + = help: Replace with `Path(...).parent` import_from_as.py:37:1: PTH121 `os.path.samefile()` should be replaced by `Path.samefile()` | diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__preview_full_name.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__preview_full_name.py.snap new file mode 100644 index 0000000000000..2018e0bafb44f --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__preview_full_name.py.snap @@ -0,0 +1,580 @@ +--- +source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs +--- +full_name.py:7:5: PTH100 [*] `os.path.abspath()` should be replaced by `Path.resolve()` + | +5 | q = "bar" +6 | +7 | a = os.path.abspath(p) + | ^^^^^^^^^^^^^^^ PTH100 +8 | aa = os.chmod(p) +9 | aaa = os.mkdir(p) + | + = help: Replace with `Path(...).resolve()` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +6 7 | +7 |-a = os.path.abspath(p) + 8 |+a = pathlib.Path(p).resolve() +8 9 | aa = os.chmod(p) +9 10 | aaa = os.mkdir(p) +10 11 | os.makedirs(p) + +full_name.py:8:6: PTH101 `os.chmod()` should be replaced by `Path.chmod()` + | + 7 | a = os.path.abspath(p) + 8 | aa = os.chmod(p) + | ^^^^^^^^ PTH101 + 9 | aaa = os.mkdir(p) +10 | os.makedirs(p) + | + +full_name.py:9:7: PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` + | + 7 | a = os.path.abspath(p) + 8 | aa = os.chmod(p) + 9 | aaa = os.mkdir(p) + | ^^^^^^^^ PTH102 +10 | os.makedirs(p) +11 | os.rename(p) + | + +full_name.py:10:1: PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` + | + 8 | aa = os.chmod(p) + 9 | aaa = os.mkdir(p) +10 | os.makedirs(p) + | ^^^^^^^^^^^ PTH103 +11 | os.rename(p) +12 | os.replace(p) + | + +full_name.py:11:1: PTH104 `os.rename()` should be replaced by `Path.rename()` + | + 9 | aaa = os.mkdir(p) +10 | os.makedirs(p) +11 | os.rename(p) + | ^^^^^^^^^ PTH104 +12 | os.replace(p) +13 | os.rmdir(p) + | + +full_name.py:12:1: PTH105 `os.replace()` should be replaced by `Path.replace()` + | +10 | os.makedirs(p) +11 | os.rename(p) +12 | os.replace(p) + | ^^^^^^^^^^ PTH105 +13 | os.rmdir(p) +14 | os.remove(p) + | + +full_name.py:13:1: PTH106 [*] `os.rmdir()` should be replaced by `Path.rmdir()` + | +11 | os.rename(p) +12 | os.replace(p) +13 | os.rmdir(p) + | ^^^^^^^^ PTH106 +14 | os.remove(p) +15 | os.unlink(p) + | + = help: Replace with `Path(...).rmdir()` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +10 11 | os.makedirs(p) +11 12 | os.rename(p) +12 13 | os.replace(p) +13 |-os.rmdir(p) + 14 |+pathlib.Path(p).rmdir() +14 15 | os.remove(p) +15 16 | os.unlink(p) +16 17 | os.getcwd(p) + +full_name.py:14:1: PTH107 [*] `os.remove()` should be replaced by `Path.unlink()` + | +12 | os.replace(p) +13 | os.rmdir(p) +14 | os.remove(p) + | ^^^^^^^^^ PTH107 +15 | os.unlink(p) +16 | os.getcwd(p) + | + = help: Replace with `Path(...).unlink()` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +11 12 | os.rename(p) +12 13 | os.replace(p) +13 14 | os.rmdir(p) +14 |-os.remove(p) + 15 |+pathlib.Path(p).unlink() +15 16 | os.unlink(p) +16 17 | os.getcwd(p) +17 18 | b = os.path.exists(p) + +full_name.py:15:1: PTH108 [*] `os.unlink()` should be replaced by `Path.unlink()` + | +13 | os.rmdir(p) +14 | os.remove(p) +15 | os.unlink(p) + | ^^^^^^^^^ PTH108 +16 | os.getcwd(p) +17 | b = os.path.exists(p) + | + = help: Replace with `Path(...).unlink()` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +12 13 | os.replace(p) +13 14 | os.rmdir(p) +14 15 | os.remove(p) +15 |-os.unlink(p) + 16 |+pathlib.Path(p).unlink() +16 17 | os.getcwd(p) +17 18 | b = os.path.exists(p) +18 19 | bb = os.path.expanduser(p) + +full_name.py:16:1: PTH109 `os.getcwd()` should be replaced by `Path.cwd()` + | +14 | os.remove(p) +15 | os.unlink(p) +16 | os.getcwd(p) + | ^^^^^^^^^ PTH109 +17 | b = os.path.exists(p) +18 | bb = os.path.expanduser(p) + | + +full_name.py:17:5: PTH110 [*] `os.path.exists()` should be replaced by `Path.exists()` + | +15 | os.unlink(p) +16 | os.getcwd(p) +17 | b = os.path.exists(p) + | ^^^^^^^^^^^^^^ PTH110 +18 | bb = os.path.expanduser(p) +19 | bbb = os.path.isdir(p) + | + = help: Replace with `Path(...).exists()` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +14 15 | os.remove(p) +15 16 | os.unlink(p) +16 17 | os.getcwd(p) +17 |-b = os.path.exists(p) + 18 |+b = pathlib.Path(p).exists() +18 19 | bb = os.path.expanduser(p) +19 20 | bbb = os.path.isdir(p) +20 21 | bbbb = os.path.isfile(p) + +full_name.py:18:6: PTH111 [*] `os.path.expanduser()` should be replaced by `Path.expanduser()` + | +16 | os.getcwd(p) +17 | b = os.path.exists(p) +18 | bb = os.path.expanduser(p) + | ^^^^^^^^^^^^^^^^^^ PTH111 +19 | bbb = os.path.isdir(p) +20 | bbbb = os.path.isfile(p) + | + = help: Replace with `Path(...).expanduser()` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +15 16 | os.unlink(p) +16 17 | os.getcwd(p) +17 18 | b = os.path.exists(p) +18 |-bb = os.path.expanduser(p) + 19 |+bb = pathlib.Path(p).expanduser() +19 20 | bbb = os.path.isdir(p) +20 21 | bbbb = os.path.isfile(p) +21 22 | bbbbb = os.path.islink(p) + +full_name.py:19:7: PTH112 [*] `os.path.isdir()` should be replaced by `Path.is_dir()` + | +17 | b = os.path.exists(p) +18 | bb = os.path.expanduser(p) +19 | bbb = os.path.isdir(p) + | ^^^^^^^^^^^^^ PTH112 +20 | bbbb = os.path.isfile(p) +21 | bbbbb = os.path.islink(p) + | + = help: Replace with `Path(...).is_dir()` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +16 17 | os.getcwd(p) +17 18 | b = os.path.exists(p) +18 19 | bb = os.path.expanduser(p) +19 |-bbb = os.path.isdir(p) + 20 |+bbb = pathlib.Path(p).is_dir() +20 21 | bbbb = os.path.isfile(p) +21 22 | bbbbb = os.path.islink(p) +22 23 | os.readlink(p) + +full_name.py:20:8: PTH113 [*] `os.path.isfile()` should be replaced by `Path.is_file()` + | +18 | bb = os.path.expanduser(p) +19 | bbb = os.path.isdir(p) +20 | bbbb = os.path.isfile(p) + | ^^^^^^^^^^^^^^ PTH113 +21 | bbbbb = os.path.islink(p) +22 | os.readlink(p) + | + = help: Replace with `Path(...).is_file()` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +17 18 | b = os.path.exists(p) +18 19 | bb = os.path.expanduser(p) +19 20 | bbb = os.path.isdir(p) +20 |-bbbb = os.path.isfile(p) + 21 |+bbbb = pathlib.Path(p).is_file() +21 22 | bbbbb = os.path.islink(p) +22 23 | os.readlink(p) +23 24 | os.stat(p) + +full_name.py:21:9: PTH114 [*] `os.path.islink()` should be replaced by `Path.is_symlink()` + | +19 | bbb = os.path.isdir(p) +20 | bbbb = os.path.isfile(p) +21 | bbbbb = os.path.islink(p) + | ^^^^^^^^^^^^^^ PTH114 +22 | os.readlink(p) +23 | os.stat(p) + | + = help: Replace with `Path(...).is_symlink()` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +18 19 | bb = os.path.expanduser(p) +19 20 | bbb = os.path.isdir(p) +20 21 | bbbb = os.path.isfile(p) +21 |-bbbbb = os.path.islink(p) + 22 |+bbbbb = pathlib.Path(p).is_symlink() +22 23 | os.readlink(p) +23 24 | os.stat(p) +24 25 | os.path.isabs(p) + +full_name.py:22:1: PTH115 [*] `os.readlink()` should be replaced by `Path.readlink()` + | +20 | bbbb = os.path.isfile(p) +21 | bbbbb = os.path.islink(p) +22 | os.readlink(p) + | ^^^^^^^^^^^ PTH115 +23 | os.stat(p) +24 | os.path.isabs(p) + | + = help: Replace with `Path(...).readlink()` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +19 20 | bbb = os.path.isdir(p) +20 21 | bbbb = os.path.isfile(p) +21 22 | bbbbb = os.path.islink(p) +22 |-os.readlink(p) + 23 |+pathlib.Path(p).readlink() +23 24 | os.stat(p) +24 25 | os.path.isabs(p) +25 26 | os.path.join(p, q) + +full_name.py:23:1: PTH116 `os.stat()` should be replaced by `Path.stat()`, `Path.owner()`, or `Path.group()` + | +21 | bbbbb = os.path.islink(p) +22 | os.readlink(p) +23 | os.stat(p) + | ^^^^^^^ PTH116 +24 | os.path.isabs(p) +25 | os.path.join(p, q) + | + +full_name.py:24:1: PTH117 [*] `os.path.isabs()` should be replaced by `Path.is_absolute()` + | +22 | os.readlink(p) +23 | os.stat(p) +24 | os.path.isabs(p) + | ^^^^^^^^^^^^^ PTH117 +25 | os.path.join(p, q) +26 | os.sep.join([p, q]) + | + = help: Replace with `Path(...).is_absolute()` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +21 22 | bbbbb = os.path.islink(p) +22 23 | os.readlink(p) +23 24 | os.stat(p) +24 |-os.path.isabs(p) + 25 |+pathlib.Path(p).is_absolute() +25 26 | os.path.join(p, q) +26 27 | os.sep.join([p, q]) +27 28 | os.sep.join((p, q)) + +full_name.py:25:1: PTH118 `os.path.join()` should be replaced by `Path` with `/` operator + | +23 | os.stat(p) +24 | os.path.isabs(p) +25 | os.path.join(p, q) + | ^^^^^^^^^^^^ PTH118 +26 | os.sep.join([p, q]) +27 | os.sep.join((p, q)) + | + +full_name.py:26:1: PTH118 `os.sep.join()` should be replaced by `Path` with `/` operator + | +24 | os.path.isabs(p) +25 | os.path.join(p, q) +26 | os.sep.join([p, q]) + | ^^^^^^^^^^^ PTH118 +27 | os.sep.join((p, q)) +28 | os.path.basename(p) + | + +full_name.py:27:1: PTH118 `os.sep.join()` should be replaced by `Path` with `/` operator + | +25 | os.path.join(p, q) +26 | os.sep.join([p, q]) +27 | os.sep.join((p, q)) + | ^^^^^^^^^^^ PTH118 +28 | os.path.basename(p) +29 | os.path.dirname(p) + | + +full_name.py:28:1: PTH119 [*] `os.path.basename()` should be replaced by `Path.name` + | +26 | os.sep.join([p, q]) +27 | os.sep.join((p, q)) +28 | os.path.basename(p) + | ^^^^^^^^^^^^^^^^ PTH119 +29 | os.path.dirname(p) +30 | os.path.samefile(p) + | + = help: Replace with `Path(...).name` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +25 26 | os.path.join(p, q) +26 27 | os.sep.join([p, q]) +27 28 | os.sep.join((p, q)) +28 |-os.path.basename(p) + 29 |+pathlib.Path(p).name +29 30 | os.path.dirname(p) +30 31 | os.path.samefile(p) +31 32 | os.path.splitext(p) + +full_name.py:29:1: PTH120 [*] `os.path.dirname()` should be replaced by `Path.parent` + | +27 | os.sep.join((p, q)) +28 | os.path.basename(p) +29 | os.path.dirname(p) + | ^^^^^^^^^^^^^^^ PTH120 +30 | os.path.samefile(p) +31 | os.path.splitext(p) + | + = help: Replace with `Path(...).parent` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +26 27 | os.sep.join([p, q]) +27 28 | os.sep.join((p, q)) +28 29 | os.path.basename(p) +29 |-os.path.dirname(p) + 30 |+pathlib.Path(p).parent +30 31 | os.path.samefile(p) +31 32 | os.path.splitext(p) +32 33 | with open(p) as fp: + +full_name.py:30:1: PTH121 `os.path.samefile()` should be replaced by `Path.samefile()` + | +28 | os.path.basename(p) +29 | os.path.dirname(p) +30 | os.path.samefile(p) + | ^^^^^^^^^^^^^^^^ PTH121 +31 | os.path.splitext(p) +32 | with open(p) as fp: + | + +full_name.py:31:1: PTH122 `os.path.splitext()` should be replaced by `Path.suffix`, `Path.stem`, and `Path.parent` + | +29 | os.path.dirname(p) +30 | os.path.samefile(p) +31 | os.path.splitext(p) + | ^^^^^^^^^^^^^^^^ PTH122 +32 | with open(p) as fp: +33 | fp.read() + | + +full_name.py:32:6: PTH123 `open()` should be replaced by `Path.open()` + | +30 | os.path.samefile(p) +31 | os.path.splitext(p) +32 | with open(p) as fp: + | ^^^^ PTH123 +33 | fp.read() +34 | open(p).close() + | + +full_name.py:34:1: PTH123 `open()` should be replaced by `Path.open()` + | +32 | with open(p) as fp: +33 | fp.read() +34 | open(p).close() + | ^^^^ PTH123 +35 | os.getcwdb(p) +36 | os.path.join(p, *q) + | + +full_name.py:35:1: PTH109 `os.getcwd()` should be replaced by `Path.cwd()` + | +33 | fp.read() +34 | open(p).close() +35 | os.getcwdb(p) + | ^^^^^^^^^^ PTH109 +36 | os.path.join(p, *q) +37 | os.sep.join(p, *q) + | + +full_name.py:36:1: PTH118 `os.path.join()` should be replaced by `Path.joinpath()` + | +34 | open(p).close() +35 | os.getcwdb(p) +36 | os.path.join(p, *q) + | ^^^^^^^^^^^^ PTH118 +37 | os.sep.join(p, *q) + | + +full_name.py:37:1: PTH118 `os.sep.join()` should be replaced by `Path.joinpath()` + | +35 | os.getcwdb(p) +36 | os.path.join(p, *q) +37 | os.sep.join(p, *q) + | ^^^^^^^^^^^ PTH118 +38 | +39 | # https://github.com/astral-sh/ruff/issues/7620 + | + +full_name.py:46:1: PTH123 `open()` should be replaced by `Path.open()` + | +44 | open(p, closefd=False) +45 | open(p, opener=opener) +46 | open(p, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None) + | ^^^^ PTH123 +47 | open(p, 'r', - 1, None, None, None, True, None) +48 | open(p, 'r', - 1, None, None, None, False, opener) + | + +full_name.py:47:1: PTH123 `open()` should be replaced by `Path.open()` + | +45 | open(p, opener=opener) +46 | open(p, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None) +47 | open(p, 'r', - 1, None, None, None, True, None) + | ^^^^ PTH123 +48 | open(p, 'r', - 1, None, None, None, False, opener) + | + +full_name.py:65:1: PTH123 `open()` should be replaced by `Path.open()` + | +63 | open(f()) +64 | +65 | open(b"foo") + | ^^^^ PTH123 +66 | byte_str = b"bar" +67 | open(byte_str) + | + +full_name.py:67:1: PTH123 `open()` should be replaced by `Path.open()` + | +65 | open(b"foo") +66 | byte_str = b"bar" +67 | open(byte_str) + | ^^^^ PTH123 +68 | +69 | def bytes_str_func() -> bytes: + | + +full_name.py:71:1: PTH123 `open()` should be replaced by `Path.open()` + | +69 | def bytes_str_func() -> bytes: +70 | return b"foo" +71 | open(bytes_str_func()) + | ^^^^ PTH123 +72 | +73 | # https://github.com/astral-sh/ruff/issues/17693 + | diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__preview_import_as.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__preview_import_as.py.snap new file mode 100644 index 0000000000000..bca1ad2b3201d --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__preview_import_as.py.snap @@ -0,0 +1,478 @@ +--- +source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs +--- +import_as.py:7:5: PTH100 [*] `os.path.abspath()` should be replaced by `Path.resolve()` + | +5 | q = "bar" +6 | +7 | a = foo_p.abspath(p) + | ^^^^^^^^^^^^^ PTH100 +8 | aa = foo.chmod(p) +9 | aaa = foo.mkdir(p) + | + = help: Replace with `Path(...).resolve()` + +ℹ Safe fix +1 1 | import os as foo +2 2 | import os.path as foo_p + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +6 7 | +7 |-a = foo_p.abspath(p) + 8 |+a = pathlib.Path(p).resolve() +8 9 | aa = foo.chmod(p) +9 10 | aaa = foo.mkdir(p) +10 11 | foo.makedirs(p) + +import_as.py:8:6: PTH101 `os.chmod()` should be replaced by `Path.chmod()` + | + 7 | a = foo_p.abspath(p) + 8 | aa = foo.chmod(p) + | ^^^^^^^^^ PTH101 + 9 | aaa = foo.mkdir(p) +10 | foo.makedirs(p) + | + +import_as.py:9:7: PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` + | + 7 | a = foo_p.abspath(p) + 8 | aa = foo.chmod(p) + 9 | aaa = foo.mkdir(p) + | ^^^^^^^^^ PTH102 +10 | foo.makedirs(p) +11 | foo.rename(p) + | + +import_as.py:10:1: PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` + | + 8 | aa = foo.chmod(p) + 9 | aaa = foo.mkdir(p) +10 | foo.makedirs(p) + | ^^^^^^^^^^^^ PTH103 +11 | foo.rename(p) +12 | foo.replace(p) + | + +import_as.py:11:1: PTH104 `os.rename()` should be replaced by `Path.rename()` + | + 9 | aaa = foo.mkdir(p) +10 | foo.makedirs(p) +11 | foo.rename(p) + | ^^^^^^^^^^ PTH104 +12 | foo.replace(p) +13 | foo.rmdir(p) + | + +import_as.py:12:1: PTH105 `os.replace()` should be replaced by `Path.replace()` + | +10 | foo.makedirs(p) +11 | foo.rename(p) +12 | foo.replace(p) + | ^^^^^^^^^^^ PTH105 +13 | foo.rmdir(p) +14 | foo.remove(p) + | + +import_as.py:13:1: PTH106 [*] `os.rmdir()` should be replaced by `Path.rmdir()` + | +11 | foo.rename(p) +12 | foo.replace(p) +13 | foo.rmdir(p) + | ^^^^^^^^^ PTH106 +14 | foo.remove(p) +15 | foo.unlink(p) + | + = help: Replace with `Path(...).rmdir()` + +ℹ Safe fix +1 1 | import os as foo +2 2 | import os.path as foo_p + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +10 11 | foo.makedirs(p) +11 12 | foo.rename(p) +12 13 | foo.replace(p) +13 |-foo.rmdir(p) + 14 |+pathlib.Path(p).rmdir() +14 15 | foo.remove(p) +15 16 | foo.unlink(p) +16 17 | foo.getcwd(p) + +import_as.py:14:1: PTH107 [*] `os.remove()` should be replaced by `Path.unlink()` + | +12 | foo.replace(p) +13 | foo.rmdir(p) +14 | foo.remove(p) + | ^^^^^^^^^^ PTH107 +15 | foo.unlink(p) +16 | foo.getcwd(p) + | + = help: Replace with `Path(...).unlink()` + +ℹ Safe fix +1 1 | import os as foo +2 2 | import os.path as foo_p + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +11 12 | foo.rename(p) +12 13 | foo.replace(p) +13 14 | foo.rmdir(p) +14 |-foo.remove(p) + 15 |+pathlib.Path(p).unlink() +15 16 | foo.unlink(p) +16 17 | foo.getcwd(p) +17 18 | b = foo_p.exists(p) + +import_as.py:15:1: PTH108 [*] `os.unlink()` should be replaced by `Path.unlink()` + | +13 | foo.rmdir(p) +14 | foo.remove(p) +15 | foo.unlink(p) + | ^^^^^^^^^^ PTH108 +16 | foo.getcwd(p) +17 | b = foo_p.exists(p) + | + = help: Replace with `Path(...).unlink()` + +ℹ Safe fix +1 1 | import os as foo +2 2 | import os.path as foo_p + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +12 13 | foo.replace(p) +13 14 | foo.rmdir(p) +14 15 | foo.remove(p) +15 |-foo.unlink(p) + 16 |+pathlib.Path(p).unlink() +16 17 | foo.getcwd(p) +17 18 | b = foo_p.exists(p) +18 19 | bb = foo_p.expanduser(p) + +import_as.py:16:1: PTH109 `os.getcwd()` should be replaced by `Path.cwd()` + | +14 | foo.remove(p) +15 | foo.unlink(p) +16 | foo.getcwd(p) + | ^^^^^^^^^^ PTH109 +17 | b = foo_p.exists(p) +18 | bb = foo_p.expanduser(p) + | + +import_as.py:17:5: PTH110 [*] `os.path.exists()` should be replaced by `Path.exists()` + | +15 | foo.unlink(p) +16 | foo.getcwd(p) +17 | b = foo_p.exists(p) + | ^^^^^^^^^^^^ PTH110 +18 | bb = foo_p.expanduser(p) +19 | bbb = foo_p.isdir(p) + | + = help: Replace with `Path(...).exists()` + +ℹ Safe fix +1 1 | import os as foo +2 2 | import os.path as foo_p + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +14 15 | foo.remove(p) +15 16 | foo.unlink(p) +16 17 | foo.getcwd(p) +17 |-b = foo_p.exists(p) + 18 |+b = pathlib.Path(p).exists() +18 19 | bb = foo_p.expanduser(p) +19 20 | bbb = foo_p.isdir(p) +20 21 | bbbb = foo_p.isfile(p) + +import_as.py:18:6: PTH111 [*] `os.path.expanduser()` should be replaced by `Path.expanduser()` + | +16 | foo.getcwd(p) +17 | b = foo_p.exists(p) +18 | bb = foo_p.expanduser(p) + | ^^^^^^^^^^^^^^^^ PTH111 +19 | bbb = foo_p.isdir(p) +20 | bbbb = foo_p.isfile(p) + | + = help: Replace with `Path(...).expanduser()` + +ℹ Safe fix +1 1 | import os as foo +2 2 | import os.path as foo_p + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +15 16 | foo.unlink(p) +16 17 | foo.getcwd(p) +17 18 | b = foo_p.exists(p) +18 |-bb = foo_p.expanduser(p) + 19 |+bb = pathlib.Path(p).expanduser() +19 20 | bbb = foo_p.isdir(p) +20 21 | bbbb = foo_p.isfile(p) +21 22 | bbbbb = foo_p.islink(p) + +import_as.py:19:7: PTH112 [*] `os.path.isdir()` should be replaced by `Path.is_dir()` + | +17 | b = foo_p.exists(p) +18 | bb = foo_p.expanduser(p) +19 | bbb = foo_p.isdir(p) + | ^^^^^^^^^^^ PTH112 +20 | bbbb = foo_p.isfile(p) +21 | bbbbb = foo_p.islink(p) + | + = help: Replace with `Path(...).is_dir()` + +ℹ Safe fix +1 1 | import os as foo +2 2 | import os.path as foo_p + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +16 17 | foo.getcwd(p) +17 18 | b = foo_p.exists(p) +18 19 | bb = foo_p.expanduser(p) +19 |-bbb = foo_p.isdir(p) + 20 |+bbb = pathlib.Path(p).is_dir() +20 21 | bbbb = foo_p.isfile(p) +21 22 | bbbbb = foo_p.islink(p) +22 23 | foo.readlink(p) + +import_as.py:20:8: PTH113 [*] `os.path.isfile()` should be replaced by `Path.is_file()` + | +18 | bb = foo_p.expanduser(p) +19 | bbb = foo_p.isdir(p) +20 | bbbb = foo_p.isfile(p) + | ^^^^^^^^^^^^ PTH113 +21 | bbbbb = foo_p.islink(p) +22 | foo.readlink(p) + | + = help: Replace with `Path(...).is_file()` + +ℹ Safe fix +1 1 | import os as foo +2 2 | import os.path as foo_p + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +17 18 | b = foo_p.exists(p) +18 19 | bb = foo_p.expanduser(p) +19 20 | bbb = foo_p.isdir(p) +20 |-bbbb = foo_p.isfile(p) + 21 |+bbbb = pathlib.Path(p).is_file() +21 22 | bbbbb = foo_p.islink(p) +22 23 | foo.readlink(p) +23 24 | foo.stat(p) + +import_as.py:21:9: PTH114 [*] `os.path.islink()` should be replaced by `Path.is_symlink()` + | +19 | bbb = foo_p.isdir(p) +20 | bbbb = foo_p.isfile(p) +21 | bbbbb = foo_p.islink(p) + | ^^^^^^^^^^^^ PTH114 +22 | foo.readlink(p) +23 | foo.stat(p) + | + = help: Replace with `Path(...).is_symlink()` + +ℹ Safe fix +1 1 | import os as foo +2 2 | import os.path as foo_p + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +18 19 | bb = foo_p.expanduser(p) +19 20 | bbb = foo_p.isdir(p) +20 21 | bbbb = foo_p.isfile(p) +21 |-bbbbb = foo_p.islink(p) + 22 |+bbbbb = pathlib.Path(p).is_symlink() +22 23 | foo.readlink(p) +23 24 | foo.stat(p) +24 25 | foo_p.isabs(p) + +import_as.py:22:1: PTH115 [*] `os.readlink()` should be replaced by `Path.readlink()` + | +20 | bbbb = foo_p.isfile(p) +21 | bbbbb = foo_p.islink(p) +22 | foo.readlink(p) + | ^^^^^^^^^^^^ PTH115 +23 | foo.stat(p) +24 | foo_p.isabs(p) + | + = help: Replace with `Path(...).readlink()` + +ℹ Safe fix +1 1 | import os as foo +2 2 | import os.path as foo_p + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +19 20 | bbb = foo_p.isdir(p) +20 21 | bbbb = foo_p.isfile(p) +21 22 | bbbbb = foo_p.islink(p) +22 |-foo.readlink(p) + 23 |+pathlib.Path(p).readlink() +23 24 | foo.stat(p) +24 25 | foo_p.isabs(p) +25 26 | foo_p.join(p, q) + +import_as.py:23:1: PTH116 `os.stat()` should be replaced by `Path.stat()`, `Path.owner()`, or `Path.group()` + | +21 | bbbbb = foo_p.islink(p) +22 | foo.readlink(p) +23 | foo.stat(p) + | ^^^^^^^^ PTH116 +24 | foo_p.isabs(p) +25 | foo_p.join(p, q) + | + +import_as.py:24:1: PTH117 [*] `os.path.isabs()` should be replaced by `Path.is_absolute()` + | +22 | foo.readlink(p) +23 | foo.stat(p) +24 | foo_p.isabs(p) + | ^^^^^^^^^^^ PTH117 +25 | foo_p.join(p, q) +26 | foo.sep.join([p, q]) + | + = help: Replace with `Path(...).is_absolute()` + +ℹ Safe fix +1 1 | import os as foo +2 2 | import os.path as foo_p + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +21 22 | bbbbb = foo_p.islink(p) +22 23 | foo.readlink(p) +23 24 | foo.stat(p) +24 |-foo_p.isabs(p) + 25 |+pathlib.Path(p).is_absolute() +25 26 | foo_p.join(p, q) +26 27 | foo.sep.join([p, q]) +27 28 | foo.sep.join((p, q)) + +import_as.py:25:1: PTH118 `os.path.join()` should be replaced by `Path` with `/` operator + | +23 | foo.stat(p) +24 | foo_p.isabs(p) +25 | foo_p.join(p, q) + | ^^^^^^^^^^ PTH118 +26 | foo.sep.join([p, q]) +27 | foo.sep.join((p, q)) + | + +import_as.py:26:1: PTH118 `os.sep.join()` should be replaced by `Path` with `/` operator + | +24 | foo_p.isabs(p) +25 | foo_p.join(p, q) +26 | foo.sep.join([p, q]) + | ^^^^^^^^^^^^ PTH118 +27 | foo.sep.join((p, q)) +28 | foo_p.basename(p) + | + +import_as.py:27:1: PTH118 `os.sep.join()` should be replaced by `Path` with `/` operator + | +25 | foo_p.join(p, q) +26 | foo.sep.join([p, q]) +27 | foo.sep.join((p, q)) + | ^^^^^^^^^^^^ PTH118 +28 | foo_p.basename(p) +29 | foo_p.dirname(p) + | + +import_as.py:28:1: PTH119 [*] `os.path.basename()` should be replaced by `Path.name` + | +26 | foo.sep.join([p, q]) +27 | foo.sep.join((p, q)) +28 | foo_p.basename(p) + | ^^^^^^^^^^^^^^ PTH119 +29 | foo_p.dirname(p) +30 | foo_p.samefile(p) + | + = help: Replace with `Path(...).name` + +ℹ Safe fix +1 1 | import os as foo +2 2 | import os.path as foo_p + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +25 26 | foo_p.join(p, q) +26 27 | foo.sep.join([p, q]) +27 28 | foo.sep.join((p, q)) +28 |-foo_p.basename(p) + 29 |+pathlib.Path(p).name +29 30 | foo_p.dirname(p) +30 31 | foo_p.samefile(p) +31 32 | foo_p.splitext(p) + +import_as.py:29:1: PTH120 [*] `os.path.dirname()` should be replaced by `Path.parent` + | +27 | foo.sep.join((p, q)) +28 | foo_p.basename(p) +29 | foo_p.dirname(p) + | ^^^^^^^^^^^^^ PTH120 +30 | foo_p.samefile(p) +31 | foo_p.splitext(p) + | + = help: Replace with `Path(...).parent` + +ℹ Safe fix +1 1 | import os as foo +2 2 | import os.path as foo_p + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +26 27 | foo.sep.join([p, q]) +27 28 | foo.sep.join((p, q)) +28 29 | foo_p.basename(p) +29 |-foo_p.dirname(p) + 30 |+pathlib.Path(p).parent +30 31 | foo_p.samefile(p) +31 32 | foo_p.splitext(p) + +import_as.py:30:1: PTH121 `os.path.samefile()` should be replaced by `Path.samefile()` + | +28 | foo_p.basename(p) +29 | foo_p.dirname(p) +30 | foo_p.samefile(p) + | ^^^^^^^^^^^^^^ PTH121 +31 | foo_p.splitext(p) + | + +import_as.py:31:1: PTH122 `os.path.splitext()` should be replaced by `Path.suffix`, `Path.stem`, and `Path.parent` + | +29 | foo_p.dirname(p) +30 | foo_p.samefile(p) +31 | foo_p.splitext(p) + | ^^^^^^^^^^^^^^ PTH122 + | diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__preview_import_from.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__preview_import_from.py.snap new file mode 100644 index 0000000000000..c56f5fc7d9063 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__preview_import_from.py.snap @@ -0,0 +1,521 @@ +--- +source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs +--- +import_from.py:9:5: PTH100 [*] `os.path.abspath()` should be replaced by `Path.resolve()` + | + 7 | q = "bar" + 8 | + 9 | a = abspath(p) + | ^^^^^^^ PTH100 +10 | aa = chmod(p) +11 | aaa = mkdir(p) + | + = help: Replace with `Path(...).resolve()` + +ℹ Safe fix +2 2 | from os import remove, unlink, getcwd, readlink, stat +3 3 | from os.path import abspath, exists, expanduser, isdir, isfile, islink +4 4 | from os.path import isabs, join, basename, dirname, samefile, splitext + 5 |+import pathlib +5 6 | +6 7 | p = "/foo" +7 8 | q = "bar" +8 9 | +9 |-a = abspath(p) + 10 |+a = pathlib.Path(p).resolve() +10 11 | aa = chmod(p) +11 12 | aaa = mkdir(p) +12 13 | makedirs(p) + +import_from.py:10:6: PTH101 `os.chmod()` should be replaced by `Path.chmod()` + | + 9 | a = abspath(p) +10 | aa = chmod(p) + | ^^^^^ PTH101 +11 | aaa = mkdir(p) +12 | makedirs(p) + | + +import_from.py:11:7: PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` + | + 9 | a = abspath(p) +10 | aa = chmod(p) +11 | aaa = mkdir(p) + | ^^^^^ PTH102 +12 | makedirs(p) +13 | rename(p) + | + +import_from.py:12:1: PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` + | +10 | aa = chmod(p) +11 | aaa = mkdir(p) +12 | makedirs(p) + | ^^^^^^^^ PTH103 +13 | rename(p) +14 | replace(p) + | + +import_from.py:13:1: PTH104 `os.rename()` should be replaced by `Path.rename()` + | +11 | aaa = mkdir(p) +12 | makedirs(p) +13 | rename(p) + | ^^^^^^ PTH104 +14 | replace(p) +15 | rmdir(p) + | + +import_from.py:14:1: PTH105 `os.replace()` should be replaced by `Path.replace()` + | +12 | makedirs(p) +13 | rename(p) +14 | replace(p) + | ^^^^^^^ PTH105 +15 | rmdir(p) +16 | remove(p) + | + +import_from.py:15:1: PTH106 [*] `os.rmdir()` should be replaced by `Path.rmdir()` + | +13 | rename(p) +14 | replace(p) +15 | rmdir(p) + | ^^^^^ PTH106 +16 | remove(p) +17 | unlink(p) + | + = help: Replace with `Path(...).rmdir()` + +ℹ Safe fix +2 2 | from os import remove, unlink, getcwd, readlink, stat +3 3 | from os.path import abspath, exists, expanduser, isdir, isfile, islink +4 4 | from os.path import isabs, join, basename, dirname, samefile, splitext + 5 |+import pathlib +5 6 | +6 7 | p = "/foo" +7 8 | q = "bar" +-------------------------------------------------------------------------------- +12 13 | makedirs(p) +13 14 | rename(p) +14 15 | replace(p) +15 |-rmdir(p) + 16 |+pathlib.Path(p).rmdir() +16 17 | remove(p) +17 18 | unlink(p) +18 19 | getcwd(p) + +import_from.py:16:1: PTH107 [*] `os.remove()` should be replaced by `Path.unlink()` + | +14 | replace(p) +15 | rmdir(p) +16 | remove(p) + | ^^^^^^ PTH107 +17 | unlink(p) +18 | getcwd(p) + | + = help: Replace with `Path(...).unlink()` + +ℹ Safe fix +2 2 | from os import remove, unlink, getcwd, readlink, stat +3 3 | from os.path import abspath, exists, expanduser, isdir, isfile, islink +4 4 | from os.path import isabs, join, basename, dirname, samefile, splitext + 5 |+import pathlib +5 6 | +6 7 | p = "/foo" +7 8 | q = "bar" +-------------------------------------------------------------------------------- +13 14 | rename(p) +14 15 | replace(p) +15 16 | rmdir(p) +16 |-remove(p) + 17 |+pathlib.Path(p).unlink() +17 18 | unlink(p) +18 19 | getcwd(p) +19 20 | b = exists(p) + +import_from.py:17:1: PTH108 [*] `os.unlink()` should be replaced by `Path.unlink()` + | +15 | rmdir(p) +16 | remove(p) +17 | unlink(p) + | ^^^^^^ PTH108 +18 | getcwd(p) +19 | b = exists(p) + | + = help: Replace with `Path(...).unlink()` + +ℹ Safe fix +2 2 | from os import remove, unlink, getcwd, readlink, stat +3 3 | from os.path import abspath, exists, expanduser, isdir, isfile, islink +4 4 | from os.path import isabs, join, basename, dirname, samefile, splitext + 5 |+import pathlib +5 6 | +6 7 | p = "/foo" +7 8 | q = "bar" +-------------------------------------------------------------------------------- +14 15 | replace(p) +15 16 | rmdir(p) +16 17 | remove(p) +17 |-unlink(p) + 18 |+pathlib.Path(p).unlink() +18 19 | getcwd(p) +19 20 | b = exists(p) +20 21 | bb = expanduser(p) + +import_from.py:18:1: PTH109 `os.getcwd()` should be replaced by `Path.cwd()` + | +16 | remove(p) +17 | unlink(p) +18 | getcwd(p) + | ^^^^^^ PTH109 +19 | b = exists(p) +20 | bb = expanduser(p) + | + +import_from.py:19:5: PTH110 [*] `os.path.exists()` should be replaced by `Path.exists()` + | +17 | unlink(p) +18 | getcwd(p) +19 | b = exists(p) + | ^^^^^^ PTH110 +20 | bb = expanduser(p) +21 | bbb = isdir(p) + | + = help: Replace with `Path(...).exists()` + +ℹ Safe fix +2 2 | from os import remove, unlink, getcwd, readlink, stat +3 3 | from os.path import abspath, exists, expanduser, isdir, isfile, islink +4 4 | from os.path import isabs, join, basename, dirname, samefile, splitext + 5 |+import pathlib +5 6 | +6 7 | p = "/foo" +7 8 | q = "bar" +-------------------------------------------------------------------------------- +16 17 | remove(p) +17 18 | unlink(p) +18 19 | getcwd(p) +19 |-b = exists(p) + 20 |+b = pathlib.Path(p).exists() +20 21 | bb = expanduser(p) +21 22 | bbb = isdir(p) +22 23 | bbbb = isfile(p) + +import_from.py:20:6: PTH111 [*] `os.path.expanduser()` should be replaced by `Path.expanduser()` + | +18 | getcwd(p) +19 | b = exists(p) +20 | bb = expanduser(p) + | ^^^^^^^^^^ PTH111 +21 | bbb = isdir(p) +22 | bbbb = isfile(p) + | + = help: Replace with `Path(...).expanduser()` + +ℹ Safe fix +2 2 | from os import remove, unlink, getcwd, readlink, stat +3 3 | from os.path import abspath, exists, expanduser, isdir, isfile, islink +4 4 | from os.path import isabs, join, basename, dirname, samefile, splitext + 5 |+import pathlib +5 6 | +6 7 | p = "/foo" +7 8 | q = "bar" +-------------------------------------------------------------------------------- +17 18 | unlink(p) +18 19 | getcwd(p) +19 20 | b = exists(p) +20 |-bb = expanduser(p) + 21 |+bb = pathlib.Path(p).expanduser() +21 22 | bbb = isdir(p) +22 23 | bbbb = isfile(p) +23 24 | bbbbb = islink(p) + +import_from.py:21:7: PTH112 [*] `os.path.isdir()` should be replaced by `Path.is_dir()` + | +19 | b = exists(p) +20 | bb = expanduser(p) +21 | bbb = isdir(p) + | ^^^^^ PTH112 +22 | bbbb = isfile(p) +23 | bbbbb = islink(p) + | + = help: Replace with `Path(...).is_dir()` + +ℹ Safe fix +2 2 | from os import remove, unlink, getcwd, readlink, stat +3 3 | from os.path import abspath, exists, expanduser, isdir, isfile, islink +4 4 | from os.path import isabs, join, basename, dirname, samefile, splitext + 5 |+import pathlib +5 6 | +6 7 | p = "/foo" +7 8 | q = "bar" +-------------------------------------------------------------------------------- +18 19 | getcwd(p) +19 20 | b = exists(p) +20 21 | bb = expanduser(p) +21 |-bbb = isdir(p) + 22 |+bbb = pathlib.Path(p).is_dir() +22 23 | bbbb = isfile(p) +23 24 | bbbbb = islink(p) +24 25 | readlink(p) + +import_from.py:22:8: PTH113 [*] `os.path.isfile()` should be replaced by `Path.is_file()` + | +20 | bb = expanduser(p) +21 | bbb = isdir(p) +22 | bbbb = isfile(p) + | ^^^^^^ PTH113 +23 | bbbbb = islink(p) +24 | readlink(p) + | + = help: Replace with `Path(...).is_file()` + +ℹ Safe fix +2 2 | from os import remove, unlink, getcwd, readlink, stat +3 3 | from os.path import abspath, exists, expanduser, isdir, isfile, islink +4 4 | from os.path import isabs, join, basename, dirname, samefile, splitext + 5 |+import pathlib +5 6 | +6 7 | p = "/foo" +7 8 | q = "bar" +-------------------------------------------------------------------------------- +19 20 | b = exists(p) +20 21 | bb = expanduser(p) +21 22 | bbb = isdir(p) +22 |-bbbb = isfile(p) + 23 |+bbbb = pathlib.Path(p).is_file() +23 24 | bbbbb = islink(p) +24 25 | readlink(p) +25 26 | stat(p) + +import_from.py:23:9: PTH114 [*] `os.path.islink()` should be replaced by `Path.is_symlink()` + | +21 | bbb = isdir(p) +22 | bbbb = isfile(p) +23 | bbbbb = islink(p) + | ^^^^^^ PTH114 +24 | readlink(p) +25 | stat(p) + | + = help: Replace with `Path(...).is_symlink()` + +ℹ Safe fix +2 2 | from os import remove, unlink, getcwd, readlink, stat +3 3 | from os.path import abspath, exists, expanduser, isdir, isfile, islink +4 4 | from os.path import isabs, join, basename, dirname, samefile, splitext + 5 |+import pathlib +5 6 | +6 7 | p = "/foo" +7 8 | q = "bar" +-------------------------------------------------------------------------------- +20 21 | bb = expanduser(p) +21 22 | bbb = isdir(p) +22 23 | bbbb = isfile(p) +23 |-bbbbb = islink(p) + 24 |+bbbbb = pathlib.Path(p).is_symlink() +24 25 | readlink(p) +25 26 | stat(p) +26 27 | isabs(p) + +import_from.py:24:1: PTH115 [*] `os.readlink()` should be replaced by `Path.readlink()` + | +22 | bbbb = isfile(p) +23 | bbbbb = islink(p) +24 | readlink(p) + | ^^^^^^^^ PTH115 +25 | stat(p) +26 | isabs(p) + | + = help: Replace with `Path(...).readlink()` + +ℹ Safe fix +2 2 | from os import remove, unlink, getcwd, readlink, stat +3 3 | from os.path import abspath, exists, expanduser, isdir, isfile, islink +4 4 | from os.path import isabs, join, basename, dirname, samefile, splitext + 5 |+import pathlib +5 6 | +6 7 | p = "/foo" +7 8 | q = "bar" +-------------------------------------------------------------------------------- +21 22 | bbb = isdir(p) +22 23 | bbbb = isfile(p) +23 24 | bbbbb = islink(p) +24 |-readlink(p) + 25 |+pathlib.Path(p).readlink() +25 26 | stat(p) +26 27 | isabs(p) +27 28 | join(p, q) + +import_from.py:25:1: PTH116 `os.stat()` should be replaced by `Path.stat()`, `Path.owner()`, or `Path.group()` + | +23 | bbbbb = islink(p) +24 | readlink(p) +25 | stat(p) + | ^^^^ PTH116 +26 | isabs(p) +27 | join(p, q) + | + +import_from.py:26:1: PTH117 [*] `os.path.isabs()` should be replaced by `Path.is_absolute()` + | +24 | readlink(p) +25 | stat(p) +26 | isabs(p) + | ^^^^^ PTH117 +27 | join(p, q) +28 | sep.join((p, q)) + | + = help: Replace with `Path(...).is_absolute()` + +ℹ Safe fix +2 2 | from os import remove, unlink, getcwd, readlink, stat +3 3 | from os.path import abspath, exists, expanduser, isdir, isfile, islink +4 4 | from os.path import isabs, join, basename, dirname, samefile, splitext + 5 |+import pathlib +5 6 | +6 7 | p = "/foo" +7 8 | q = "bar" +-------------------------------------------------------------------------------- +23 24 | bbbbb = islink(p) +24 25 | readlink(p) +25 26 | stat(p) +26 |-isabs(p) + 27 |+pathlib.Path(p).is_absolute() +27 28 | join(p, q) +28 29 | sep.join((p, q)) +29 30 | sep.join([p, q]) + +import_from.py:27:1: PTH118 `os.path.join()` should be replaced by `Path` with `/` operator + | +25 | stat(p) +26 | isabs(p) +27 | join(p, q) + | ^^^^ PTH118 +28 | sep.join((p, q)) +29 | sep.join([p, q]) + | + +import_from.py:28:1: PTH118 `os.sep.join()` should be replaced by `Path` with `/` operator + | +26 | isabs(p) +27 | join(p, q) +28 | sep.join((p, q)) + | ^^^^^^^^ PTH118 +29 | sep.join([p, q]) +30 | basename(p) + | + +import_from.py:29:1: PTH118 `os.sep.join()` should be replaced by `Path` with `/` operator + | +27 | join(p, q) +28 | sep.join((p, q)) +29 | sep.join([p, q]) + | ^^^^^^^^ PTH118 +30 | basename(p) +31 | dirname(p) + | + +import_from.py:30:1: PTH119 [*] `os.path.basename()` should be replaced by `Path.name` + | +28 | sep.join((p, q)) +29 | sep.join([p, q]) +30 | basename(p) + | ^^^^^^^^ PTH119 +31 | dirname(p) +32 | samefile(p) + | + = help: Replace with `Path(...).name` + +ℹ Safe fix +2 2 | from os import remove, unlink, getcwd, readlink, stat +3 3 | from os.path import abspath, exists, expanduser, isdir, isfile, islink +4 4 | from os.path import isabs, join, basename, dirname, samefile, splitext + 5 |+import pathlib +5 6 | +6 7 | p = "/foo" +7 8 | q = "bar" +-------------------------------------------------------------------------------- +27 28 | join(p, q) +28 29 | sep.join((p, q)) +29 30 | sep.join([p, q]) +30 |-basename(p) + 31 |+pathlib.Path(p).name +31 32 | dirname(p) +32 33 | samefile(p) +33 34 | splitext(p) + +import_from.py:31:1: PTH120 [*] `os.path.dirname()` should be replaced by `Path.parent` + | +29 | sep.join([p, q]) +30 | basename(p) +31 | dirname(p) + | ^^^^^^^ PTH120 +32 | samefile(p) +33 | splitext(p) + | + = help: Replace with `Path(...).parent` + +ℹ Safe fix +2 2 | from os import remove, unlink, getcwd, readlink, stat +3 3 | from os.path import abspath, exists, expanduser, isdir, isfile, islink +4 4 | from os.path import isabs, join, basename, dirname, samefile, splitext + 5 |+import pathlib +5 6 | +6 7 | p = "/foo" +7 8 | q = "bar" +-------------------------------------------------------------------------------- +28 29 | sep.join((p, q)) +29 30 | sep.join([p, q]) +30 31 | basename(p) +31 |-dirname(p) + 32 |+pathlib.Path(p).parent +32 33 | samefile(p) +33 34 | splitext(p) +34 35 | with open(p) as fp: + +import_from.py:32:1: PTH121 `os.path.samefile()` should be replaced by `Path.samefile()` + | +30 | basename(p) +31 | dirname(p) +32 | samefile(p) + | ^^^^^^^^ PTH121 +33 | splitext(p) +34 | with open(p) as fp: + | + +import_from.py:33:1: PTH122 `os.path.splitext()` should be replaced by `Path.suffix`, `Path.stem`, and `Path.parent` + | +31 | dirname(p) +32 | samefile(p) +33 | splitext(p) + | ^^^^^^^^ PTH122 +34 | with open(p) as fp: +35 | fp.read() + | + +import_from.py:34:6: PTH123 `open()` should be replaced by `Path.open()` + | +32 | samefile(p) +33 | splitext(p) +34 | with open(p) as fp: + | ^^^^ PTH123 +35 | fp.read() +36 | open(p).close() + | + +import_from.py:36:1: PTH123 `open()` should be replaced by `Path.open()` + | +34 | with open(p) as fp: +35 | fp.read() +36 | open(p).close() + | ^^^^ PTH123 + | + +import_from.py:43:10: PTH123 `open()` should be replaced by `Path.open()` + | +41 | from builtins import open +42 | +43 | with open(p) as _: ... # Error + | ^^^^ PTH123 + | diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__preview_import_from_as.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__preview_import_from_as.py.snap new file mode 100644 index 0000000000000..2728b6caf4ae3 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__preview_import_from_as.py.snap @@ -0,0 +1,491 @@ +--- +source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs +--- +import_from_as.py:14:5: PTH100 [*] `os.path.abspath()` should be replaced by `Path.resolve()` + | +12 | q = "bar" +13 | +14 | a = xabspath(p) + | ^^^^^^^^ PTH100 +15 | aa = xchmod(p) +16 | aaa = xmkdir(p) + | + = help: Replace with `Path(...).resolve()` + +ℹ Safe fix +7 7 | from os.path import isfile as xisfile, islink as xislink, isabs as xisabs +8 8 | from os.path import join as xjoin, basename as xbasename, dirname as xdirname +9 9 | from os.path import samefile as xsamefile, splitext as xsplitext + 10 |+import pathlib +10 11 | +11 12 | p = "/foo" +12 13 | q = "bar" +13 14 | +14 |-a = xabspath(p) + 15 |+a = pathlib.Path(p).resolve() +15 16 | aa = xchmod(p) +16 17 | aaa = xmkdir(p) +17 18 | xmakedirs(p) + +import_from_as.py:15:6: PTH101 `os.chmod()` should be replaced by `Path.chmod()` + | +14 | a = xabspath(p) +15 | aa = xchmod(p) + | ^^^^^^ PTH101 +16 | aaa = xmkdir(p) +17 | xmakedirs(p) + | + +import_from_as.py:16:7: PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` + | +14 | a = xabspath(p) +15 | aa = xchmod(p) +16 | aaa = xmkdir(p) + | ^^^^^^ PTH102 +17 | xmakedirs(p) +18 | xrename(p) + | + +import_from_as.py:17:1: PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` + | +15 | aa = xchmod(p) +16 | aaa = xmkdir(p) +17 | xmakedirs(p) + | ^^^^^^^^^ PTH103 +18 | xrename(p) +19 | xreplace(p) + | + +import_from_as.py:18:1: PTH104 `os.rename()` should be replaced by `Path.rename()` + | +16 | aaa = xmkdir(p) +17 | xmakedirs(p) +18 | xrename(p) + | ^^^^^^^ PTH104 +19 | xreplace(p) +20 | xrmdir(p) + | + +import_from_as.py:19:1: PTH105 `os.replace()` should be replaced by `Path.replace()` + | +17 | xmakedirs(p) +18 | xrename(p) +19 | xreplace(p) + | ^^^^^^^^ PTH105 +20 | xrmdir(p) +21 | xremove(p) + | + +import_from_as.py:20:1: PTH106 [*] `os.rmdir()` should be replaced by `Path.rmdir()` + | +18 | xrename(p) +19 | xreplace(p) +20 | xrmdir(p) + | ^^^^^^ PTH106 +21 | xremove(p) +22 | xunlink(p) + | + = help: Replace with `Path(...).rmdir()` + +ℹ Safe fix +7 7 | from os.path import isfile as xisfile, islink as xislink, isabs as xisabs +8 8 | from os.path import join as xjoin, basename as xbasename, dirname as xdirname +9 9 | from os.path import samefile as xsamefile, splitext as xsplitext + 10 |+import pathlib +10 11 | +11 12 | p = "/foo" +12 13 | q = "bar" +-------------------------------------------------------------------------------- +17 18 | xmakedirs(p) +18 19 | xrename(p) +19 20 | xreplace(p) +20 |-xrmdir(p) + 21 |+pathlib.Path(p).rmdir() +21 22 | xremove(p) +22 23 | xunlink(p) +23 24 | xgetcwd(p) + +import_from_as.py:21:1: PTH107 [*] `os.remove()` should be replaced by `Path.unlink()` + | +19 | xreplace(p) +20 | xrmdir(p) +21 | xremove(p) + | ^^^^^^^ PTH107 +22 | xunlink(p) +23 | xgetcwd(p) + | + = help: Replace with `Path(...).unlink()` + +ℹ Safe fix +7 7 | from os.path import isfile as xisfile, islink as xislink, isabs as xisabs +8 8 | from os.path import join as xjoin, basename as xbasename, dirname as xdirname +9 9 | from os.path import samefile as xsamefile, splitext as xsplitext + 10 |+import pathlib +10 11 | +11 12 | p = "/foo" +12 13 | q = "bar" +-------------------------------------------------------------------------------- +18 19 | xrename(p) +19 20 | xreplace(p) +20 21 | xrmdir(p) +21 |-xremove(p) + 22 |+pathlib.Path(p).unlink() +22 23 | xunlink(p) +23 24 | xgetcwd(p) +24 25 | b = xexists(p) + +import_from_as.py:22:1: PTH108 [*] `os.unlink()` should be replaced by `Path.unlink()` + | +20 | xrmdir(p) +21 | xremove(p) +22 | xunlink(p) + | ^^^^^^^ PTH108 +23 | xgetcwd(p) +24 | b = xexists(p) + | + = help: Replace with `Path(...).unlink()` + +ℹ Safe fix +7 7 | from os.path import isfile as xisfile, islink as xislink, isabs as xisabs +8 8 | from os.path import join as xjoin, basename as xbasename, dirname as xdirname +9 9 | from os.path import samefile as xsamefile, splitext as xsplitext + 10 |+import pathlib +10 11 | +11 12 | p = "/foo" +12 13 | q = "bar" +-------------------------------------------------------------------------------- +19 20 | xreplace(p) +20 21 | xrmdir(p) +21 22 | xremove(p) +22 |-xunlink(p) + 23 |+pathlib.Path(p).unlink() +23 24 | xgetcwd(p) +24 25 | b = xexists(p) +25 26 | bb = xexpanduser(p) + +import_from_as.py:23:1: PTH109 `os.getcwd()` should be replaced by `Path.cwd()` + | +21 | xremove(p) +22 | xunlink(p) +23 | xgetcwd(p) + | ^^^^^^^ PTH109 +24 | b = xexists(p) +25 | bb = xexpanduser(p) + | + +import_from_as.py:24:5: PTH110 [*] `os.path.exists()` should be replaced by `Path.exists()` + | +22 | xunlink(p) +23 | xgetcwd(p) +24 | b = xexists(p) + | ^^^^^^^ PTH110 +25 | bb = xexpanduser(p) +26 | bbb = xisdir(p) + | + = help: Replace with `Path(...).exists()` + +ℹ Safe fix +7 7 | from os.path import isfile as xisfile, islink as xislink, isabs as xisabs +8 8 | from os.path import join as xjoin, basename as xbasename, dirname as xdirname +9 9 | from os.path import samefile as xsamefile, splitext as xsplitext + 10 |+import pathlib +10 11 | +11 12 | p = "/foo" +12 13 | q = "bar" +-------------------------------------------------------------------------------- +21 22 | xremove(p) +22 23 | xunlink(p) +23 24 | xgetcwd(p) +24 |-b = xexists(p) + 25 |+b = pathlib.Path(p).exists() +25 26 | bb = xexpanduser(p) +26 27 | bbb = xisdir(p) +27 28 | bbbb = xisfile(p) + +import_from_as.py:25:6: PTH111 [*] `os.path.expanduser()` should be replaced by `Path.expanduser()` + | +23 | xgetcwd(p) +24 | b = xexists(p) +25 | bb = xexpanduser(p) + | ^^^^^^^^^^^ PTH111 +26 | bbb = xisdir(p) +27 | bbbb = xisfile(p) + | + = help: Replace with `Path(...).expanduser()` + +ℹ Safe fix +7 7 | from os.path import isfile as xisfile, islink as xislink, isabs as xisabs +8 8 | from os.path import join as xjoin, basename as xbasename, dirname as xdirname +9 9 | from os.path import samefile as xsamefile, splitext as xsplitext + 10 |+import pathlib +10 11 | +11 12 | p = "/foo" +12 13 | q = "bar" +-------------------------------------------------------------------------------- +22 23 | xunlink(p) +23 24 | xgetcwd(p) +24 25 | b = xexists(p) +25 |-bb = xexpanduser(p) + 26 |+bb = pathlib.Path(p).expanduser() +26 27 | bbb = xisdir(p) +27 28 | bbbb = xisfile(p) +28 29 | bbbbb = xislink(p) + +import_from_as.py:26:7: PTH112 [*] `os.path.isdir()` should be replaced by `Path.is_dir()` + | +24 | b = xexists(p) +25 | bb = xexpanduser(p) +26 | bbb = xisdir(p) + | ^^^^^^ PTH112 +27 | bbbb = xisfile(p) +28 | bbbbb = xislink(p) + | + = help: Replace with `Path(...).is_dir()` + +ℹ Safe fix +7 7 | from os.path import isfile as xisfile, islink as xislink, isabs as xisabs +8 8 | from os.path import join as xjoin, basename as xbasename, dirname as xdirname +9 9 | from os.path import samefile as xsamefile, splitext as xsplitext + 10 |+import pathlib +10 11 | +11 12 | p = "/foo" +12 13 | q = "bar" +-------------------------------------------------------------------------------- +23 24 | xgetcwd(p) +24 25 | b = xexists(p) +25 26 | bb = xexpanduser(p) +26 |-bbb = xisdir(p) + 27 |+bbb = pathlib.Path(p).is_dir() +27 28 | bbbb = xisfile(p) +28 29 | bbbbb = xislink(p) +29 30 | xreadlink(p) + +import_from_as.py:27:8: PTH113 [*] `os.path.isfile()` should be replaced by `Path.is_file()` + | +25 | bb = xexpanduser(p) +26 | bbb = xisdir(p) +27 | bbbb = xisfile(p) + | ^^^^^^^ PTH113 +28 | bbbbb = xislink(p) +29 | xreadlink(p) + | + = help: Replace with `Path(...).is_file()` + +ℹ Safe fix +7 7 | from os.path import isfile as xisfile, islink as xislink, isabs as xisabs +8 8 | from os.path import join as xjoin, basename as xbasename, dirname as xdirname +9 9 | from os.path import samefile as xsamefile, splitext as xsplitext + 10 |+import pathlib +10 11 | +11 12 | p = "/foo" +12 13 | q = "bar" +-------------------------------------------------------------------------------- +24 25 | b = xexists(p) +25 26 | bb = xexpanduser(p) +26 27 | bbb = xisdir(p) +27 |-bbbb = xisfile(p) + 28 |+bbbb = pathlib.Path(p).is_file() +28 29 | bbbbb = xislink(p) +29 30 | xreadlink(p) +30 31 | xstat(p) + +import_from_as.py:28:9: PTH114 [*] `os.path.islink()` should be replaced by `Path.is_symlink()` + | +26 | bbb = xisdir(p) +27 | bbbb = xisfile(p) +28 | bbbbb = xislink(p) + | ^^^^^^^ PTH114 +29 | xreadlink(p) +30 | xstat(p) + | + = help: Replace with `Path(...).is_symlink()` + +ℹ Safe fix +7 7 | from os.path import isfile as xisfile, islink as xislink, isabs as xisabs +8 8 | from os.path import join as xjoin, basename as xbasename, dirname as xdirname +9 9 | from os.path import samefile as xsamefile, splitext as xsplitext + 10 |+import pathlib +10 11 | +11 12 | p = "/foo" +12 13 | q = "bar" +-------------------------------------------------------------------------------- +25 26 | bb = xexpanduser(p) +26 27 | bbb = xisdir(p) +27 28 | bbbb = xisfile(p) +28 |-bbbbb = xislink(p) + 29 |+bbbbb = pathlib.Path(p).is_symlink() +29 30 | xreadlink(p) +30 31 | xstat(p) +31 32 | xisabs(p) + +import_from_as.py:29:1: PTH115 [*] `os.readlink()` should be replaced by `Path.readlink()` + | +27 | bbbb = xisfile(p) +28 | bbbbb = xislink(p) +29 | xreadlink(p) + | ^^^^^^^^^ PTH115 +30 | xstat(p) +31 | xisabs(p) + | + = help: Replace with `Path(...).readlink()` + +ℹ Safe fix +7 7 | from os.path import isfile as xisfile, islink as xislink, isabs as xisabs +8 8 | from os.path import join as xjoin, basename as xbasename, dirname as xdirname +9 9 | from os.path import samefile as xsamefile, splitext as xsplitext + 10 |+import pathlib +10 11 | +11 12 | p = "/foo" +12 13 | q = "bar" +-------------------------------------------------------------------------------- +26 27 | bbb = xisdir(p) +27 28 | bbbb = xisfile(p) +28 29 | bbbbb = xislink(p) +29 |-xreadlink(p) + 30 |+pathlib.Path(p).readlink() +30 31 | xstat(p) +31 32 | xisabs(p) +32 33 | xjoin(p, q) + +import_from_as.py:30:1: PTH116 `os.stat()` should be replaced by `Path.stat()`, `Path.owner()`, or `Path.group()` + | +28 | bbbbb = xislink(p) +29 | xreadlink(p) +30 | xstat(p) + | ^^^^^ PTH116 +31 | xisabs(p) +32 | xjoin(p, q) + | + +import_from_as.py:31:1: PTH117 [*] `os.path.isabs()` should be replaced by `Path.is_absolute()` + | +29 | xreadlink(p) +30 | xstat(p) +31 | xisabs(p) + | ^^^^^^ PTH117 +32 | xjoin(p, q) +33 | s.join((p, q)) + | + = help: Replace with `Path(...).is_absolute()` + +ℹ Safe fix +7 7 | from os.path import isfile as xisfile, islink as xislink, isabs as xisabs +8 8 | from os.path import join as xjoin, basename as xbasename, dirname as xdirname +9 9 | from os.path import samefile as xsamefile, splitext as xsplitext + 10 |+import pathlib +10 11 | +11 12 | p = "/foo" +12 13 | q = "bar" +-------------------------------------------------------------------------------- +28 29 | bbbbb = xislink(p) +29 30 | xreadlink(p) +30 31 | xstat(p) +31 |-xisabs(p) + 32 |+pathlib.Path(p).is_absolute() +32 33 | xjoin(p, q) +33 34 | s.join((p, q)) +34 35 | s.join([p, q]) + +import_from_as.py:32:1: PTH118 `os.path.join()` should be replaced by `Path` with `/` operator + | +30 | xstat(p) +31 | xisabs(p) +32 | xjoin(p, q) + | ^^^^^ PTH118 +33 | s.join((p, q)) +34 | s.join([p, q]) + | + +import_from_as.py:33:1: PTH118 `os.sep.join()` should be replaced by `Path` with `/` operator + | +31 | xisabs(p) +32 | xjoin(p, q) +33 | s.join((p, q)) + | ^^^^^^ PTH118 +34 | s.join([p, q]) +35 | xbasename(p) + | + +import_from_as.py:34:1: PTH118 `os.sep.join()` should be replaced by `Path` with `/` operator + | +32 | xjoin(p, q) +33 | s.join((p, q)) +34 | s.join([p, q]) + | ^^^^^^ PTH118 +35 | xbasename(p) +36 | xdirname(p) + | + +import_from_as.py:35:1: PTH119 [*] `os.path.basename()` should be replaced by `Path.name` + | +33 | s.join((p, q)) +34 | s.join([p, q]) +35 | xbasename(p) + | ^^^^^^^^^ PTH119 +36 | xdirname(p) +37 | xsamefile(p) + | + = help: Replace with `Path(...).name` + +ℹ Safe fix +7 7 | from os.path import isfile as xisfile, islink as xislink, isabs as xisabs +8 8 | from os.path import join as xjoin, basename as xbasename, dirname as xdirname +9 9 | from os.path import samefile as xsamefile, splitext as xsplitext + 10 |+import pathlib +10 11 | +11 12 | p = "/foo" +12 13 | q = "bar" +-------------------------------------------------------------------------------- +32 33 | xjoin(p, q) +33 34 | s.join((p, q)) +34 35 | s.join([p, q]) +35 |-xbasename(p) + 36 |+pathlib.Path(p).name +36 37 | xdirname(p) +37 38 | xsamefile(p) +38 39 | xsplitext(p) + +import_from_as.py:36:1: PTH120 [*] `os.path.dirname()` should be replaced by `Path.parent` + | +34 | s.join([p, q]) +35 | xbasename(p) +36 | xdirname(p) + | ^^^^^^^^ PTH120 +37 | xsamefile(p) +38 | xsplitext(p) + | + = help: Replace with `Path(...).parent` + +ℹ Safe fix +7 7 | from os.path import isfile as xisfile, islink as xislink, isabs as xisabs +8 8 | from os.path import join as xjoin, basename as xbasename, dirname as xdirname +9 9 | from os.path import samefile as xsamefile, splitext as xsplitext + 10 |+import pathlib +10 11 | +11 12 | p = "/foo" +12 13 | q = "bar" +-------------------------------------------------------------------------------- +33 34 | s.join((p, q)) +34 35 | s.join([p, q]) +35 36 | xbasename(p) +36 |-xdirname(p) + 37 |+pathlib.Path(p).parent +37 38 | xsamefile(p) +38 39 | xsplitext(p) + +import_from_as.py:37:1: PTH121 `os.path.samefile()` should be replaced by `Path.samefile()` + | +35 | xbasename(p) +36 | xdirname(p) +37 | xsamefile(p) + | ^^^^^^^^^ PTH121 +38 | xsplitext(p) + | + +import_from_as.py:38:1: PTH122 `os.path.splitext()` should be replaced by `Path.suffix`, `Path.stem`, and `Path.parent` + | +36 | xdirname(p) +37 | xsamefile(p) +38 | xsplitext(p) + | ^^^^^^^^^ PTH122 + | diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/violations.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/violations.rs index b64ffe50cfd95..2a36ae6f552b9 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/violations.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/violations.rs @@ -2,51 +2,6 @@ use ruff_macros::{ViolationMetadata, derive_message_formats}; use crate::Violation; -/// ## What it does -/// Checks for uses of `os.path.abspath`. -/// -/// ## Why is this bad? -/// `pathlib` offers a high-level API for path manipulation, as compared to -/// the lower-level API offered by `os.path`. When possible, using `Path` object -/// methods such as `Path.resolve()` can improve readability over the `os.path` -/// module's counterparts (e.g., `os.path.abspath()`). -/// -/// ## Examples -/// ```python -/// import os -/// -/// file_path = os.path.abspath("../path/to/file") -/// ``` -/// -/// Use instead: -/// ```python -/// from pathlib import Path -/// -/// file_path = Path("../path/to/file").resolve() -/// ``` -/// -/// ## Known issues -/// While using `pathlib` can improve the readability and type safety of your code, -/// it can be less performant than the lower-level alternatives that work directly with strings, -/// especially on older versions of Python. -/// -/// ## References -/// - [Python documentation: `Path.resolve`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.resolve) -/// - [Python documentation: `os.path.abspath`](https://docs.python.org/3/library/os.path.html#os.path.abspath) -/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) -/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) -/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) -/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) -#[derive(ViolationMetadata)] -pub(crate) struct OsPathAbspath; - -impl Violation for OsPathAbspath { - #[derive_message_formats] - fn message(&self) -> String { - "`os.path.abspath()` should be replaced by `Path.resolve()`".to_string() - } -} - /// ## What it does /// Checks for uses of `os.chmod`. /// @@ -275,141 +230,6 @@ impl Violation for OsReplace { } } -/// ## What it does -/// Checks for uses of `os.rmdir`. -/// -/// ## Why is this bad? -/// `pathlib` offers a high-level API for path manipulation, as compared to -/// the lower-level API offered by `os`. When possible, using `Path` object -/// methods such as `Path.rmdir()` can improve readability over the `os` -/// module's counterparts (e.g., `os.rmdir()`). -/// -/// ## Examples -/// ```python -/// import os -/// -/// os.rmdir("folder/") -/// ``` -/// -/// Use instead: -/// ```python -/// from pathlib import Path -/// -/// Path("folder/").rmdir() -/// ``` -/// -/// ## Known issues -/// While using `pathlib` can improve the readability and type safety of your code, -/// it can be less performant than the lower-level alternatives that work directly with strings, -/// especially on older versions of Python. -/// -/// ## References -/// - [Python documentation: `Path.rmdir`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.rmdir) -/// - [Python documentation: `os.rmdir`](https://docs.python.org/3/library/os.html#os.rmdir) -/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) -/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) -/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) -/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) -#[derive(ViolationMetadata)] -pub(crate) struct OsRmdir; - -impl Violation for OsRmdir { - #[derive_message_formats] - fn message(&self) -> String { - "`os.rmdir()` should be replaced by `Path.rmdir()`".to_string() - } -} - -/// ## What it does -/// Checks for uses of `os.remove`. -/// -/// ## Why is this bad? -/// `pathlib` offers a high-level API for path manipulation, as compared to -/// the lower-level API offered by `os`. When possible, using `Path` object -/// methods such as `Path.unlink()` can improve readability over the `os` -/// module's counterparts (e.g., `os.remove()`). -/// -/// ## Examples -/// ```python -/// import os -/// -/// os.remove("file.py") -/// ``` -/// -/// Use instead: -/// ```python -/// from pathlib import Path -/// -/// Path("file.py").unlink() -/// ``` -/// -/// ## Known issues -/// While using `pathlib` can improve the readability and type safety of your code, -/// it can be less performant than the lower-level alternatives that work directly with strings, -/// especially on older versions of Python. -/// -/// ## References -/// - [Python documentation: `Path.unlink`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.unlink) -/// - [Python documentation: `os.remove`](https://docs.python.org/3/library/os.html#os.remove) -/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) -/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) -/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) -/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) -#[derive(ViolationMetadata)] -pub(crate) struct OsRemove; - -impl Violation for OsRemove { - #[derive_message_formats] - fn message(&self) -> String { - "`os.remove()` should be replaced by `Path.unlink()`".to_string() - } -} - -/// ## What it does -/// Checks for uses of `os.unlink`. -/// -/// ## Why is this bad? -/// `pathlib` offers a high-level API for path manipulation, as compared to -/// the lower-level API offered by `os`. When possible, using `Path` object -/// methods such as `Path.unlink()` can improve readability over the `os` -/// module's counterparts (e.g., `os.unlink()`). -/// -/// ## Examples -/// ```python -/// import os -/// -/// os.unlink("file.py") -/// ``` -/// -/// Use instead: -/// ```python -/// from pathlib import Path -/// -/// Path("file.py").unlink() -/// ``` -/// -/// ## Known issues -/// While using `pathlib` can improve the readability and type safety of your code, -/// it can be less performant than the lower-level alternatives that work directly with strings, -/// especially on older versions of Python. -/// -/// ## References -/// - [Python documentation: `Path.unlink`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.unlink) -/// - [Python documentation: `os.unlink`](https://docs.python.org/3/library/os.html#os.unlink) -/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) -/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) -/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) -/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) -#[derive(ViolationMetadata)] -pub(crate) struct OsUnlink; - -impl Violation for OsUnlink { - #[derive_message_formats] - fn message(&self) -> String { - "`os.unlink()` should be replaced by `Path.unlink()`".to_string() - } -} - /// ## What it does /// Checks for uses of `os.getcwd` and `os.getcwdb`. /// @@ -456,276 +276,6 @@ impl Violation for OsGetcwd { } } -/// ## What it does -/// Checks for uses of `os.path.exists`. -/// -/// ## Why is this bad? -/// `pathlib` offers a high-level API for path manipulation, as compared to -/// the lower-level API offered by `os.path`. When possible, using `Path` object -/// methods such as `Path.exists()` can improve readability over the `os.path` -/// module's counterparts (e.g., `os.path.exists()`). -/// -/// ## Examples -/// ```python -/// import os -/// -/// os.path.exists("file.py") -/// ``` -/// -/// Use instead: -/// ```python -/// from pathlib import Path -/// -/// Path("file.py").exists() -/// ``` -/// -/// ## Known issues -/// While using `pathlib` can improve the readability and type safety of your code, -/// it can be less performant than the lower-level alternatives that work directly with strings, -/// especially on older versions of Python. -/// -/// ## References -/// - [Python documentation: `Path.exists`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.exists) -/// - [Python documentation: `os.path.exists`](https://docs.python.org/3/library/os.path.html#os.path.exists) -/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) -/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) -/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) -/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) -#[derive(ViolationMetadata)] -pub(crate) struct OsPathExists; - -impl Violation for OsPathExists { - #[derive_message_formats] - fn message(&self) -> String { - "`os.path.exists()` should be replaced by `Path.exists()`".to_string() - } -} - -/// ## What it does -/// Checks for uses of `os.path.expanduser`. -/// -/// ## Why is this bad? -/// `pathlib` offers a high-level API for path manipulation, as compared to -/// the lower-level API offered by `os.path`. When possible, using `Path` object -/// methods such as `Path.expanduser()` can improve readability over the `os.path` -/// module's counterparts (e.g., as `os.path.expanduser()`). -/// -/// ## Examples -/// ```python -/// import os -/// -/// os.path.expanduser("~/films/Monty Python") -/// ``` -/// -/// Use instead: -/// ```python -/// from pathlib import Path -/// -/// Path("~/films/Monty Python").expanduser() -/// ``` -/// -/// ## Known issues -/// While using `pathlib` can improve the readability and type safety of your code, -/// it can be less performant than the lower-level alternatives that work directly with strings, -/// especially on older versions of Python. -/// -/// ## References -/// - [Python documentation: `Path.expanduser`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.expanduser) -/// - [Python documentation: `os.path.expanduser`](https://docs.python.org/3/library/os.path.html#os.path.expanduser) -/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) -/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) -/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) -/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) -#[derive(ViolationMetadata)] -pub(crate) struct OsPathExpanduser; - -impl Violation for OsPathExpanduser { - #[derive_message_formats] - fn message(&self) -> String { - "`os.path.expanduser()` should be replaced by `Path.expanduser()`".to_string() - } -} - -/// ## What it does -/// Checks for uses of `os.path.isdir`. -/// -/// ## Why is this bad? -/// `pathlib` offers a high-level API for path manipulation, as compared to -/// the lower-level API offered by `os.path`. When possible, using `Path` object -/// methods such as `Path.is_dir()` can improve readability over the `os.path` -/// module's counterparts (e.g., `os.path.isdir()`). -/// -/// ## Examples -/// ```python -/// import os -/// -/// os.path.isdir("docs") -/// ``` -/// -/// Use instead: -/// ```python -/// from pathlib import Path -/// -/// Path("docs").is_dir() -/// ``` -/// -/// ## Known issues -/// While using `pathlib` can improve the readability and type safety of your code, -/// it can be less performant than the lower-level alternatives that work directly with strings, -/// especially on older versions of Python. -/// -/// ## References -/// - [Python documentation: `Path.is_dir`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.is_dir) -/// - [Python documentation: `os.path.isdir`](https://docs.python.org/3/library/os.path.html#os.path.isdir) -/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) -/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) -/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) -/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) -#[derive(ViolationMetadata)] -pub(crate) struct OsPathIsdir; - -impl Violation for OsPathIsdir { - #[derive_message_formats] - fn message(&self) -> String { - "`os.path.isdir()` should be replaced by `Path.is_dir()`".to_string() - } -} - -/// ## What it does -/// Checks for uses of `os.path.isfile`. -/// -/// ## Why is this bad? -/// `pathlib` offers a high-level API for path manipulation, as compared to -/// the lower-level API offered by `os.path`. When possible, using `Path` object -/// methods such as `Path.is_file()` can improve readability over the `os.path` -/// module's counterparts (e.g., `os.path.isfile()`). -/// -/// ## Examples -/// ```python -/// import os -/// -/// os.path.isfile("docs") -/// ``` -/// -/// Use instead: -/// ```python -/// from pathlib import Path -/// -/// Path("docs").is_file() -/// ``` -/// -/// ## Known issues -/// While using `pathlib` can improve the readability and type safety of your code, -/// it can be less performant than the lower-level alternatives that work directly with strings, -/// especially on older versions of Python. -/// -/// ## References -/// - [Python documentation: `Path.is_file`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.is_file) -/// - [Python documentation: `os.path.isfile`](https://docs.python.org/3/library/os.path.html#os.path.isfile) -/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) -/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) -/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) -/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) -#[derive(ViolationMetadata)] -pub(crate) struct OsPathIsfile; - -impl Violation for OsPathIsfile { - #[derive_message_formats] - fn message(&self) -> String { - "`os.path.isfile()` should be replaced by `Path.is_file()`".to_string() - } -} - -/// ## What it does -/// Checks for uses of `os.path.islink`. -/// -/// ## Why is this bad? -/// `pathlib` offers a high-level API for path manipulation, as compared to -/// the lower-level API offered by `os.path`. When possible, using `Path` object -/// methods such as `Path.is_symlink()` can improve readability over the `os.path` -/// module's counterparts (e.g., `os.path.islink()`). -/// -/// ## Examples -/// ```python -/// import os -/// -/// os.path.islink("docs") -/// ``` -/// -/// Use instead: -/// ```python -/// from pathlib import Path -/// -/// Path("docs").is_symlink() -/// ``` -/// -/// ## Known issues -/// While using `pathlib` can improve the readability and type safety of your code, -/// it can be less performant than the lower-level alternatives that work directly with strings, -/// especially on older versions of Python. -/// -/// ## References -/// - [Python documentation: `Path.is_symlink`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.is_symlink) -/// - [Python documentation: `os.path.islink`](https://docs.python.org/3/library/os.path.html#os.path.islink) -/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) -/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) -/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) -/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) -#[derive(ViolationMetadata)] -pub(crate) struct OsPathIslink; - -impl Violation for OsPathIslink { - #[derive_message_formats] - fn message(&self) -> String { - "`os.path.islink()` should be replaced by `Path.is_symlink()`".to_string() - } -} - -/// ## What it does -/// Checks for uses of `os.readlink`. -/// -/// ## Why is this bad? -/// `pathlib` offers a high-level API for path manipulation, as compared to -/// the lower-level API offered by `os`. When possible, using `Path` object -/// methods such as `Path.readlink()` can improve readability over the `os` -/// module's counterparts (e.g., `os.readlink()`). -/// -/// ## Examples -/// ```python -/// import os -/// -/// os.readlink(file_name) -/// ``` -/// -/// Use instead: -/// ```python -/// from pathlib import Path -/// -/// Path(file_name).readlink() -/// ``` -/// -/// ## Known issues -/// While using `pathlib` can improve the readability and type safety of your code, -/// it can be less performant than the lower-level alternatives that work directly with strings, -/// especially on older versions of Python. -/// -/// ## References -/// - [Python documentation: `Path.readlink`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.readline) -/// - [Python documentation: `os.readlink`](https://docs.python.org/3/library/os.html#os.readlink) -/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) -/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) -/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) -/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) -#[derive(ViolationMetadata)] -pub(crate) struct OsReadlink; - -impl Violation for OsReadlink { - #[derive_message_formats] - fn message(&self) -> String { - "`os.readlink()` should be replaced by `Path.readlink()`".to_string() - } -} - /// ## What it does /// Checks for uses of `os.stat`. /// @@ -781,53 +331,6 @@ impl Violation for OsStat { } } -/// ## What it does -/// Checks for uses of `os.path.isabs`. -/// -/// ## Why is this bad? -/// `pathlib` offers a high-level API for path manipulation, as compared to -/// the lower-level API offered by `os.path`. When possible, using `Path` object -/// methods such as `Path.is_absolute()` can improve readability over the `os.path` -/// module's counterparts (e.g., as `os.path.isabs()`). -/// -/// ## Examples -/// ```python -/// import os -/// -/// if os.path.isabs(file_name): -/// print("Absolute path!") -/// ``` -/// -/// Use instead: -/// ```python -/// from pathlib import Path -/// -/// if Path(file_name).is_absolute(): -/// print("Absolute path!") -/// ``` -/// -/// ## Known issues -/// While using `pathlib` can improve the readability and type safety of your code, -/// it can be less performant than the lower-level alternatives that work directly with strings, -/// especially on older versions of Python. -/// -/// ## References -/// - [Python documentation: `PurePath.is_absolute`](https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.is_absolute) -/// - [Python documentation: `os.path.isabs`](https://docs.python.org/3/library/os.path.html#os.path.isabs) -/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) -/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) -/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) -/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) -#[derive(ViolationMetadata)] -pub(crate) struct OsPathIsabs; - -impl Violation for OsPathIsabs { - #[derive_message_formats] - fn message(&self) -> String { - "`os.path.isabs()` should be replaced by `Path.is_absolute()`".to_string() - } -} - /// ## What it does /// Checks for uses of `os.path.join`. /// @@ -890,96 +393,6 @@ pub(crate) enum Joiner { Joinpath, } -/// ## What it does -/// Checks for uses of `os.path.basename`. -/// -/// ## Why is this bad? -/// `pathlib` offers a high-level API for path manipulation, as compared to -/// the lower-level API offered by `os.path`. When possible, using `Path` object -/// methods such as `Path.name` can improve readability over the `os.path` -/// module's counterparts (e.g., `os.path.basename()`). -/// -/// ## Examples -/// ```python -/// import os -/// -/// os.path.basename(__file__) -/// ``` -/// -/// Use instead: -/// ```python -/// from pathlib import Path -/// -/// Path(__file__).name -/// ``` -/// -/// ## Known issues -/// While using `pathlib` can improve the readability and type safety of your code, -/// it can be less performant than the lower-level alternatives that work directly with strings, -/// especially on older versions of Python. -/// -/// ## References -/// - [Python documentation: `PurePath.name`](https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.name) -/// - [Python documentation: `os.path.basename`](https://docs.python.org/3/library/os.path.html#os.path.basename) -/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) -/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) -/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) -/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) -#[derive(ViolationMetadata)] -pub(crate) struct OsPathBasename; - -impl Violation for OsPathBasename { - #[derive_message_formats] - fn message(&self) -> String { - "`os.path.basename()` should be replaced by `Path.name`".to_string() - } -} - -/// ## What it does -/// Checks for uses of `os.path.dirname`. -/// -/// ## Why is this bad? -/// `pathlib` offers a high-level API for path manipulation, as compared to -/// the lower-level API offered by `os.path`. When possible, using `Path` object -/// methods such as `Path.parent` can improve readability over the `os.path` -/// module's counterparts (e.g., `os.path.dirname()`). -/// -/// ## Examples -/// ```python -/// import os -/// -/// os.path.dirname(__file__) -/// ``` -/// -/// Use instead: -/// ```python -/// from pathlib import Path -/// -/// Path(__file__).parent -/// ``` -/// -/// ## Known issues -/// While using `pathlib` can improve the readability and type safety of your code, -/// it can be less performant than the lower-level alternatives that work directly with strings, -/// especially on older versions of Python. -/// -/// ## References -/// - [Python documentation: `PurePath.parent`](https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.parent) -/// - [Python documentation: `os.path.dirname`](https://docs.python.org/3/library/os.path.html#os.path.dirname) -/// - [PEP 428 – The pathlib module – object-oriented filesystem paths](https://peps.python.org/pep-0428/) -/// - [Correspondence between `os` and `pathlib`](https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module) -/// - [Why you should be using pathlib](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/) -/// - [No really, pathlib is great](https://treyhunner.com/2019/01/no-really-pathlib-is-great/) -#[derive(ViolationMetadata)] -pub(crate) struct OsPathDirname; - -impl Violation for OsPathDirname { - #[derive_message_formats] - fn message(&self) -> String { - "`os.path.dirname()` should be replaced by `Path.parent`".to_string() - } -} - /// ## What it does /// Checks for uses of `os.path.samefile`. ///