diff --git a/README.md b/README.md index e65529b44c9cc..5eaaf116a4066 100644 --- a/README.md +++ b/README.md @@ -1290,7 +1290,7 @@ For more, see [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/) | PTH106 | pathlib-rmdir | `os.rmdir` should be replaced by `.rmdir()` | | | PTH107 | pathlib-remove | `os.remove` should be replaced by `.unlink()` | | | PTH108 | pathlib-unlink | `os.unlink` should be replaced by `.unlink()` | | -| PTH109 | pathlib-getcwd | `os.getcwd` should be replaced by `Path.cwd()` | | +| PTH109 | [pathlib-getcwd](https://github.com/charliermarsh/ruff/blob/main/docs/rules/pathlib-getcwd.md) | `os.getcwd` should be replaced by `Path.cwd()` | 🛠 | | PTH110 | pathlib-exists | `os.path.exists` should be replaced by `.exists()` | | | PTH111 | pathlib-expanduser | `os.path.expanduser` should be replaced by `.expanduser()` | | | PTH112 | pathlib-is-dir | `os.path.isdir` should be replaced by `.is_dir()` | | @@ -1299,13 +1299,18 @@ For more, see [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/) | PTH115 | pathlib-readlink | `os.readlink` should be replaced by `.readlink()` | | | PTH116 | pathlib-stat | `os.stat` should be replaced by `.stat()` or `.owner()` or `.group()` | | | PTH117 | pathlib-is-abs | `os.path.isabs` should be replaced by `.is_absolute()` | | -| PTH118 | pathlib-join | `os.path.join` should be replaced by foo_path / "bar" | | +| PTH118 | pathlib-join | `os.path.join` should be replaced by `foo_path / "bar"` | | | PTH119 | pathlib-basename | `os.path.basename` should be replaced by `.name` | | | PTH120 | pathlib-dirname | `os.path.dirname` should be replaced by `.parent` | | | PTH121 | pathlib-samefile | `os.path.samefile` should be replaced by `.samefile()` | | | PTH122 | pathlib-splitext | `os.path.splitext` should be replaced by `.suffix` | | | PTH123 | pathlib-open | `open("foo")` should be replaced by `Path("foo").open()` | | | PTH124 | pathlib-py-path | `py.path` is in maintenance mode, use `pathlib` instead | | +| PTH200 | [path-constructor-current-directory](https://github.com/charliermarsh/ruff/blob/main/docs/rules/path-constructor-current-directory.md) | Do not pass the current directory explicitly to `Path` | 🛠 | +| PTH201 | pathlib-getsize | `os.path.getsize` should be replaced by `stat().st_size` | | +| PTH202 | pathlib-getatime | `os.path.getatime` should be replaced by `stat().st_atime` | | +| PTH203 | pathlib-getmtime | `os.path.getmtime` should be replaced by `stat().st_mtime` | | +| PTH204 | pathlib-getctime | `os.path.getctime` should be replaced by `stat().st_ctime` | | ### eradicate (ERA) diff --git a/crates/ruff/resources/test/fixtures/flake8_use_pathlib/full_name.py b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/full_name.py index 12371193cb412..65064c8d57fc4 100644 --- a/crates/ruff/resources/test/fixtures/flake8_use_pathlib/full_name.py +++ b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/full_name.py @@ -1,5 +1,6 @@ import os import os.path +import pathlib p = "/foo" diff --git a/crates/ruff/resources/test/fixtures/flake8_use_pathlib/guarded.py b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/guarded.py new file mode 100644 index 0000000000000..4bad4242666f8 --- /dev/null +++ b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/guarded.py @@ -0,0 +1,34 @@ +# ensure that no fixes are applied when`pathlib` is not imported +# (needed until this item is resolved: https://github.com/charliermarsh/ruff/issues/835) +import os +import os.path + +p = "/foo" + +a = os.path.abspath(p) +aa = os.chmod(p) +aaa = os.mkdir(p) +os.makedirs(p) +os.rename(p) +os.replace(p) +os.rmdir(p) +os.remove(p) +os.unlink(p) +os.getcwd(p) +b = os.path.exists(p) +bb = os.path.expanduser(p) +bbb = os.path.isdir(p) +bbbb = os.path.isfile(p) +bbbbb = os.path.islink(p) +os.readlink(p) +os.stat(p) +os.path.isabs(p) +os.path.join(p) +os.path.basename(p) +os.path.dirname(p) +os.path.samefile(p) +os.path.splitext(p) +with open(p) as fp: + fp.read() +open(p).close() +os.getcwdb(p) diff --git a/crates/ruff/resources/test/fixtures/flake8_use_pathlib/import_as.py b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/import_as.py index 446412f18de67..5c04829ba59b3 100644 --- a/crates/ruff/resources/test/fixtures/flake8_use_pathlib/import_as.py +++ b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/import_as.py @@ -1,5 +1,6 @@ import os as foo import os.path as foo_p +import pathlib as pth p = "/foo" diff --git a/crates/ruff/resources/test/fixtures/flake8_use_pathlib/import_from.py b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/import_from.py index 9e4dc71a2d647..59c90ebb5413b 100644 --- a/crates/ruff/resources/test/fixtures/flake8_use_pathlib/import_from.py +++ b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/import_from.py @@ -2,6 +2,7 @@ from os import remove, unlink, getcwd, readlink, stat from os.path import abspath, exists, expanduser, isdir, isfile, islink from os.path import isabs, join, basename, dirname, samefile, splitext +from pathlib import Path p = "/foo" diff --git a/crates/ruff/resources/test/fixtures/flake8_use_pathlib/import_from_as.py b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/import_from_as.py index 2beff6d7e7bb5..43617aae1329e 100644 --- a/crates/ruff/resources/test/fixtures/flake8_use_pathlib/import_from_as.py +++ b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/import_from_as.py @@ -7,6 +7,7 @@ from os.path import isfile as xisfile, islink as xislink, isabs as xisabs from os.path import join as xjoin, basename as xbasename, dirname as xdirname from os.path import samefile as xsamefile, splitext as xsplitext +from pathlib import Path as pth p = "/foo" diff --git a/crates/ruff/resources/test/fixtures/flake8_use_pathlib/simplify_pathlib_constructor.py b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/simplify_pathlib_constructor.py new file mode 100644 index 0000000000000..27c0cdd48c3ca --- /dev/null +++ b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/simplify_pathlib_constructor.py @@ -0,0 +1,12 @@ +from pathlib import Path +from pathlib import Path as pth + +# match +_ = Path(".") +_ = pth(".") + +# no match +_ = Path() +print(".") +Path("file.txt") +Path(".", "folder") diff --git a/crates/ruff/resources/test/fixtures/flake8_use_pathlib/stat.py b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/stat.py new file mode 100644 index 0000000000000..8a69bf87064b3 --- /dev/null +++ b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/stat.py @@ -0,0 +1,19 @@ +import os +from pathlib import Path + +os.path.getatime("filename") +os.path.getatime(b"filename") +os.path.getatime(Path("filename")) + +os.path.getmtime("filename") +os.path.getmtime(b"filename") +os.path.getmtime(Path("filename")) + +os.path.getctime("filename") +os.path.getctime(b"filename") +os.path.getctime(Path("filename")) + +os.path.getsize("filename") +os.path.getsize(b"filename") +os.path.getsize(Path("filename")) +os.path.getsize(__file__) diff --git a/crates/ruff/resources/test/fixtures/flake8_use_pathlib/use_pathlib.py b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/use_pathlib.py index 748442f50a806..8298db4b3aaaf 100644 --- a/crates/ruff/resources/test/fixtures/flake8_use_pathlib/use_pathlib.py +++ b/crates/ruff/resources/test/fixtures/flake8_use_pathlib/use_pathlib.py @@ -1,3 +1,18 @@ +import os from pathlib import Path (Path("") / "").open() + +_ = Path(os.getcwd()) + +_ = Path( + os.\ + getcwd() +) + +_ = Path( + os.getcwdb(), +) + +# should not be unwrapped +_ = Path(os.getcwd(), hello='world') diff --git a/crates/ruff/src/ast/helpers.rs b/crates/ruff/src/ast/helpers.rs index 015327e1f987a..89db23aa9b377 100644 --- a/crates/ruff/src/ast/helpers.rs +++ b/crates/ruff/src/ast/helpers.rs @@ -1146,6 +1146,66 @@ pub fn is_logger_candidate(func: &Expr) -> bool { false } +/// Return the appropriate `sys.exit` reference based on the current set of +/// imports, or `None` is `sys.exit` hasn't been imported. +pub fn get_member_import_name_alias( + checker: &Checker, + module: &str, + member: &str, +) -> Option { + checker.current_scopes().find_map(|scope| { + scope + .bindings + .values() + .find_map(|index| match &checker.bindings[*index].kind { + // e.g. module=sys object=exit + // `import sys` -> `sys.exit` + // `import sys as sys2` -> `sys2.exit` + BindingKind::Importation(name, full_name) => { + if full_name == &module { + Some(format!("{name}.{member}")) + } else { + None + } + } + // e.g. module=os.path object=join + // `from os.path import join` -> `join` + // `from os.path import join as join2` -> `join2` + BindingKind::FromImportation(name, full_name) => { + let mut parts = full_name.split('.'); + if parts.next() == Some(module) + && parts.next() == Some(member) + && parts.next().is_none() + { + Some((*name).to_string()) + } else { + None + } + } + // e.g. module=os.path object=join + // `from os.path import *` -> `join` + BindingKind::StarImportation(_, name) => { + if name.as_ref().map(|name| name == module).unwrap_or_default() { + Some(member.to_string()) + } else { + None + } + } + // e.g. module=os.path object=join + // `import os.path ` -> `os.path.join` + BindingKind::SubmoduleImportation(_, full_name) => { + if full_name == &module { + Some(format!("{full_name}.{member}")) + } else { + None + } + } + // Non-imports. + _ => None, + }) + }) +} + #[cfg(test)] mod tests { use anyhow::Result; diff --git a/crates/ruff/src/checkers/ast.rs b/crates/ruff/src/checkers/ast.rs index 82f398e492067..fb87b28669015 100644 --- a/crates/ruff/src/checkers/ast.rs +++ b/crates/ruff/src/checkers/ast.rs @@ -2756,7 +2756,19 @@ where || self.settings.rules.enabled(&Rule::PathlibOpen) || self.settings.rules.enabled(&Rule::PathlibPyPath) { - flake8_use_pathlib::helpers::replaceable_by_pathlib(self, func); + flake8_use_pathlib::helpers::replaceable_by_pathlib( + self, + func, + self.current_expr_parent().map(Into::into), + ); + } + + if self + .settings + .rules + .enabled(&Rule::PathConstructorCurrentDirectory) + { + flake8_use_pathlib::rules::simplify_path_constructor(self, expr, func); } // flake8-logging-format diff --git a/crates/ruff/src/registry.rs b/crates/ruff/src/registry.rs index 0dc874533a92c..781d51ef9888b 100644 --- a/crates/ruff/src/registry.rs +++ b/crates/ruff/src/registry.rs @@ -534,6 +534,11 @@ ruff_macros::define_rule_mapping!( PTH122 => rules::flake8_use_pathlib::violations::PathlibSplitext, PTH123 => rules::flake8_use_pathlib::violations::PathlibOpen, PTH124 => rules::flake8_use_pathlib::violations::PathlibPyPath, + PTH200 => rules::flake8_use_pathlib::rules::PathConstructorCurrentDirectory, + PTH201 => rules::flake8_use_pathlib::violations::PathlibGetsize, + PTH202 => rules::flake8_use_pathlib::violations::PathlibGetatime, + PTH203 => rules::flake8_use_pathlib::violations::PathlibGetmtime, + PTH204 => rules::flake8_use_pathlib::violations::PathlibGetctime, // flake8-logging-format G001 => rules::flake8_logging_format::violations::LoggingStringFormat, G002 => rules::flake8_logging_format::violations::LoggingPercentFormat, diff --git a/crates/ruff/src/rules/flake8_use_pathlib/fixes.rs b/crates/ruff/src/rules/flake8_use_pathlib/fixes.rs new file mode 100644 index 0000000000000..74e67a003715c --- /dev/null +++ b/crates/ruff/src/rules/flake8_use_pathlib/fixes.rs @@ -0,0 +1,67 @@ +use rustpython_parser::ast::{Expr, ExprKind}; + +use crate::ast::helpers; +use crate::ast::types::Range; +use crate::autofix::apply_fix; +use crate::checkers::ast::Checker; +use crate::fix::Fix; +use crate::registry::DiagnosticKind; +use crate::source_code::Locator; + +pub fn pathlib_fix( + checker: &mut Checker, + diagnostic: &DiagnosticKind, + func: &Expr, + parent: Option<&Expr>, +) -> Option { + // Guard that Path is imported, `content` contains the name or aliaas + if let Some(content) = helpers::get_member_import_name_alias(checker, "pathlib", "Path") { + let mut fix = match diagnostic { + DiagnosticKind::PathlibGetcwd(_) => Some(Fix::replacement( + format!("{content}.cwd"), + func.location, + func.end_location.unwrap(), + )), + _ => None, + }; + + // Wrapped in a `Path()` call + if let Some(fixme) = fix.clone() { + if let Some(parent) = parent { + if checker + .resolve_call_path(parent) + .map_or(false, |call_path| { + call_path.as_slice() == ["pathlib", "Path"] + }) + { + if let ExprKind::Call { args, keywords, .. } = &parent.node { + if args.len() == 1 && keywords.is_empty() { + // Reset the line index + let fixme = Fix::replacement( + fixme.content.to_string(), + helpers::to_relative(fixme.location, func.location), + helpers::to_relative(fixme.end_location, func.location), + ); + + // Apply the fix + let arg = args.first().unwrap(); + let contents = checker.locator.slice_source_code_range(&Range::new( + arg.location, + arg.end_location.unwrap(), + )); + + fix = Some(Fix::replacement( + apply_fix(&fixme, &Locator::new(contents)), + parent.location, + parent.end_location.unwrap(), + )); + } + } + } + } + } + fix + } else { + None + } +} diff --git a/crates/ruff/src/rules/flake8_use_pathlib/helpers.rs b/crates/ruff/src/rules/flake8_use_pathlib/helpers.rs index 68277e13c7923..eb6e9c22c5da3 100644 --- a/crates/ruff/src/rules/flake8_use_pathlib/helpers.rs +++ b/crates/ruff/src/rules/flake8_use_pathlib/helpers.rs @@ -1,18 +1,20 @@ use rustpython_parser::ast::Expr; +use super::fixes::pathlib_fix; use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::registry::{Diagnostic, DiagnosticKind}; use crate::rules::flake8_use_pathlib::violations::{ PathlibAbspath, PathlibBasename, PathlibChmod, PathlibDirname, PathlibExists, - PathlibExpanduser, PathlibGetcwd, PathlibIsAbs, PathlibIsDir, PathlibIsFile, PathlibIsLink, - PathlibJoin, PathlibMakedirs, PathlibMkdir, PathlibOpen, PathlibPyPath, PathlibReadlink, - PathlibRemove, PathlibRename, PathlibReplace, PathlibRmdir, PathlibSamefile, PathlibSplitext, - PathlibStat, PathlibUnlink, + PathlibExpanduser, PathlibGetatime, PathlibGetctime, PathlibGetcwd, PathlibGetmtime, + PathlibGetsize, PathlibIsAbs, PathlibIsDir, PathlibIsFile, PathlibIsLink, PathlibJoin, + PathlibMakedirs, PathlibMkdir, PathlibOpen, PathlibPyPath, PathlibReadlink, PathlibRemove, + PathlibRename, PathlibReplace, PathlibRmdir, PathlibSamefile, PathlibSplitext, PathlibStat, + PathlibUnlink, }; use crate::settings::types::PythonVersion; -pub fn replaceable_by_pathlib(checker: &mut Checker, expr: &Expr) { +pub fn replaceable_by_pathlib(checker: &mut Checker, expr: &Expr, parent: Option<&Expr>) { if let Some(diagnostic_kind) = checker .resolve_call_path(expr) @@ -46,12 +48,20 @@ pub fn replaceable_by_pathlib(checker: &mut Checker, expr: &Expr) { ["os", "readlink"] if checker.settings.target_version >= PythonVersion::Py39 => { Some(PathlibReadlink.into()) } + ["os", "path", "getsize"] => Some(PathlibGetsize.into()), + ["os", "path", "getatime"] => Some(PathlibGetatime.into()), + ["os", "path", "getmtime"] => Some(PathlibGetmtime.into()), + ["os", "path", "getctime"] => Some(PathlibGetctime.into()), _ => None, }) { - let diagnostic = + let mut diagnostic = Diagnostic::new::(diagnostic_kind, Range::from_located(expr)); - + if checker.patch(diagnostic.kind.rule()) { + if let Some(fix) = pathlib_fix(checker, &diagnostic.kind, expr, parent) { + diagnostic.amend(fix); + } + } if checker.settings.rules.enabled(diagnostic.kind.rule()) { checker.diagnostics.push(diagnostic); } diff --git a/crates/ruff/src/rules/flake8_use_pathlib/mod.rs b/crates/ruff/src/rules/flake8_use_pathlib/mod.rs index 90f7bc395afc7..91bf482c4c071 100644 --- a/crates/ruff/src/rules/flake8_use_pathlib/mod.rs +++ b/crates/ruff/src/rules/flake8_use_pathlib/mod.rs @@ -1,5 +1,7 @@ //! Rules from [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/). +pub(crate) mod fixes; pub(crate) mod helpers; +pub(crate) mod rules; pub(crate) mod violations; #[cfg(test)] @@ -18,6 +20,9 @@ mod tests { #[test_case(Path::new("import_from_as.py"); "PTH1_3")] #[test_case(Path::new("import_from.py"); "PTH1_4")] #[test_case(Path::new("use_pathlib.py"); "PTH1_5")] + #[test_case(Path::new("stat.py"); "PTH1_6")] + #[test_case(Path::new("simplify_pathlib_constructor.py"); "PTH1_7")] + #[test_case(Path::new("guarded.py"); "PTH1_8")] fn rules(path: &Path) -> Result<()> { let snapshot = format!("{}", path.to_string_lossy()); let diagnostics = test_path( @@ -47,6 +52,11 @@ mod tests { Rule::PathlibSamefile, Rule::PathlibSplitext, Rule::PathlibOpen, + Rule::PathlibGetsize, + Rule::PathlibGetatime, + Rule::PathlibGetmtime, + Rule::PathlibGetctime, + Rule::PathConstructorCurrentDirectory, ]), )?; insta::assert_yaml_snapshot!(snapshot, diagnostics); diff --git a/crates/ruff/src/rules/flake8_use_pathlib/rules/mod.rs b/crates/ruff/src/rules/flake8_use_pathlib/rules/mod.rs new file mode 100644 index 0000000000000..3c49b0c8aa22a --- /dev/null +++ b/crates/ruff/src/rules/flake8_use_pathlib/rules/mod.rs @@ -0,0 +1,3 @@ +pub use simplify_path_constructor::{simplify_path_constructor, PathConstructorCurrentDirectory}; + +mod simplify_path_constructor; diff --git a/crates/ruff/src/rules/flake8_use_pathlib/rules/simplify_path_constructor.rs b/crates/ruff/src/rules/flake8_use_pathlib/rules/simplify_path_constructor.rs new file mode 100644 index 0000000000000..a32cbed4ba41d --- /dev/null +++ b/crates/ruff/src/rules/flake8_use_pathlib/rules/simplify_path_constructor.rs @@ -0,0 +1,74 @@ +use ruff_macros::{define_violation, derive_message_formats}; +use rustpython_parser::ast::{Constant, Expr, ExprKind}; + +use crate::ast::types::Range; +use crate::checkers::ast::Checker; +use crate::fix::Fix; +use crate::registry::Diagnostic; +use crate::violation::{AutofixKind, Availability, Violation}; + +define_violation!( + /// ## What it does + /// This rule detects pathlib's `Path` initializations with the default current directory argument. + /// + /// ## Why is this bad? + /// The `Path()` constructor defaults to the current directory, so don't pass the + /// current directory (`"."`) explicitly. + /// + /// ## Example + /// ```python + /// from pathlib import Path + /// + /// _ = Path(".") + /// ``` + /// + /// Use instead: + /// ```python + /// from pathlib import Path + /// + /// _ = Path() + /// ``` + pub struct PathConstructorCurrentDirectory; +); +impl Violation for PathConstructorCurrentDirectory { + const AUTOFIX: Option = Some(AutofixKind::new(Availability::Sometimes)); + + #[derive_message_formats] + fn message(&self) -> String { + format!("Do not pass the current directory explicitly to `Path`") + } + + fn autofix_title_formatter(&self) -> Option String> { + Some(|PathConstructorCurrentDirectory| format!("Replace `Path(\".\")` with `Path()`")) + } +} + +/// PTH200 +pub fn simplify_path_constructor(checker: &mut Checker, expr: &Expr, func: &Expr) { + if checker.resolve_call_path(func).map_or(false, |call_path| { + call_path.as_slice() == ["pathlib", "Path"] + }) { + if let ExprKind::Call { args, keywords, .. } = &expr.node { + if keywords.is_empty() && args.len() == 1 { + let arg = &args.first().unwrap(); + if let ExprKind::Constant { + value: Constant::Str(value), + .. + } = &arg.node + { + if value == "." { + let mut diagnostic = Diagnostic::new( + PathConstructorCurrentDirectory, + Range::from_located(expr), + ); + if checker.patch(diagnostic.kind.rule()) { + diagnostic + .amend(Fix::deletion(arg.location, arg.end_location.unwrap())); + } + checker.diagnostics.push(diagnostic); + }; + }; + } + } + } +} diff --git a/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__full_name.py.snap b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__full_name.py.snap index 4ab33e250a039..2da1d596753c3 100644 --- a/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__full_name.py.snap +++ b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__full_name.py.snap @@ -1,265 +1,279 @@ --- -source: src/rules/flake8_use_pathlib/mod.rs +source: crates/ruff/src/rules/flake8_use_pathlib/mod.rs expression: diagnostics --- - kind: PathlibAbspath: ~ location: - row: 6 + row: 7 column: 4 end_location: - row: 6 + row: 7 column: 19 fix: ~ parent: ~ - kind: PathlibChmod: ~ location: - row: 7 + row: 8 column: 5 end_location: - row: 7 + row: 8 column: 13 fix: ~ parent: ~ - kind: PathlibMkdir: ~ location: - row: 8 + row: 9 column: 6 end_location: - row: 8 + row: 9 column: 14 fix: ~ parent: ~ - kind: PathlibMakedirs: ~ location: - row: 9 + row: 10 column: 0 end_location: - row: 9 + row: 10 column: 11 fix: ~ parent: ~ - kind: PathlibRename: ~ location: - row: 10 + row: 11 column: 0 end_location: - row: 10 + row: 11 column: 9 fix: ~ parent: ~ - kind: PathlibReplace: ~ location: - row: 11 + row: 12 column: 0 end_location: - row: 11 + row: 12 column: 10 fix: ~ parent: ~ - kind: PathlibRmdir: ~ location: - row: 12 + row: 13 column: 0 end_location: - row: 12 + row: 13 column: 8 fix: ~ parent: ~ - kind: PathlibRemove: ~ location: - row: 13 + row: 14 column: 0 end_location: - row: 13 + row: 14 column: 9 fix: ~ parent: ~ - kind: PathlibUnlink: ~ location: - row: 14 + row: 15 column: 0 end_location: - row: 14 + row: 15 column: 9 fix: ~ parent: ~ - kind: PathlibGetcwd: ~ location: - row: 15 + row: 16 column: 0 end_location: - row: 15 + row: 16 column: 9 - fix: ~ + fix: + content: pathlib.Path.cwd + location: + row: 16 + column: 0 + end_location: + row: 16 + column: 9 parent: ~ - kind: PathlibExists: ~ location: - row: 16 + row: 17 column: 4 end_location: - row: 16 + row: 17 column: 18 fix: ~ parent: ~ - kind: PathlibExpanduser: ~ location: - row: 17 + row: 18 column: 5 end_location: - row: 17 + row: 18 column: 23 fix: ~ parent: ~ - kind: PathlibIsDir: ~ location: - row: 18 + row: 19 column: 6 end_location: - row: 18 + row: 19 column: 19 fix: ~ parent: ~ - kind: PathlibIsFile: ~ location: - row: 19 + row: 20 column: 7 end_location: - row: 19 + row: 20 column: 21 fix: ~ parent: ~ - kind: PathlibIsLink: ~ location: - row: 20 + row: 21 column: 8 end_location: - row: 20 + row: 21 column: 22 fix: ~ parent: ~ - kind: PathlibReadlink: ~ location: - row: 21 + row: 22 column: 0 end_location: - row: 21 + row: 22 column: 11 fix: ~ parent: ~ - kind: PathlibStat: ~ location: - row: 22 + row: 23 column: 0 end_location: - row: 22 + row: 23 column: 7 fix: ~ parent: ~ - kind: PathlibIsAbs: ~ location: - row: 23 + row: 24 column: 0 end_location: - row: 23 + row: 24 column: 13 fix: ~ parent: ~ - kind: PathlibJoin: ~ location: - row: 24 + row: 25 column: 0 end_location: - row: 24 + row: 25 column: 12 fix: ~ parent: ~ - kind: PathlibBasename: ~ location: - row: 25 + row: 26 column: 0 end_location: - row: 25 + row: 26 column: 16 fix: ~ parent: ~ - kind: PathlibDirname: ~ location: - row: 26 + row: 27 column: 0 end_location: - row: 26 + row: 27 column: 15 fix: ~ parent: ~ - kind: PathlibSamefile: ~ location: - row: 27 + row: 28 column: 0 end_location: - row: 27 + row: 28 column: 16 fix: ~ parent: ~ - kind: PathlibSplitext: ~ location: - row: 28 + row: 29 column: 0 end_location: - row: 28 + row: 29 column: 16 fix: ~ parent: ~ - kind: PathlibOpen: ~ location: - row: 29 + row: 30 column: 5 end_location: - row: 29 + row: 30 column: 9 fix: ~ parent: ~ - kind: PathlibOpen: ~ location: - row: 31 + row: 32 column: 0 end_location: - row: 31 + row: 32 column: 4 fix: ~ parent: ~ - kind: PathlibGetcwd: ~ location: - row: 32 + row: 33 column: 0 end_location: - row: 32 + row: 33 column: 10 - fix: ~ + fix: + content: pathlib.Path.cwd + location: + row: 33 + column: 0 + end_location: + row: 33 + column: 10 parent: ~ diff --git a/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__guarded.py.snap b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__guarded.py.snap new file mode 100644 index 0000000000000..9de06dabe88d0 --- /dev/null +++ b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__guarded.py.snap @@ -0,0 +1,265 @@ +--- +source: crates/ruff/src/rules/flake8_use_pathlib/mod.rs +expression: diagnostics +--- +- kind: + PathlibAbspath: ~ + location: + row: 8 + column: 4 + end_location: + row: 8 + column: 19 + fix: ~ + parent: ~ +- kind: + PathlibChmod: ~ + location: + row: 9 + column: 5 + end_location: + row: 9 + column: 13 + fix: ~ + parent: ~ +- kind: + PathlibMkdir: ~ + location: + row: 10 + column: 6 + end_location: + row: 10 + column: 14 + fix: ~ + parent: ~ +- kind: + PathlibMakedirs: ~ + location: + row: 11 + column: 0 + end_location: + row: 11 + column: 11 + fix: ~ + parent: ~ +- kind: + PathlibRename: ~ + location: + row: 12 + column: 0 + end_location: + row: 12 + column: 9 + fix: ~ + parent: ~ +- kind: + PathlibReplace: ~ + location: + row: 13 + column: 0 + end_location: + row: 13 + column: 10 + fix: ~ + parent: ~ +- kind: + PathlibRmdir: ~ + location: + row: 14 + column: 0 + end_location: + row: 14 + column: 8 + fix: ~ + parent: ~ +- kind: + PathlibRemove: ~ + location: + row: 15 + column: 0 + end_location: + row: 15 + column: 9 + fix: ~ + parent: ~ +- kind: + PathlibUnlink: ~ + location: + row: 16 + column: 0 + end_location: + row: 16 + column: 9 + fix: ~ + parent: ~ +- kind: + PathlibGetcwd: ~ + location: + row: 17 + column: 0 + end_location: + row: 17 + column: 9 + fix: ~ + parent: ~ +- kind: + PathlibExists: ~ + location: + row: 18 + column: 4 + end_location: + row: 18 + column: 18 + fix: ~ + parent: ~ +- kind: + PathlibExpanduser: ~ + location: + row: 19 + column: 5 + end_location: + row: 19 + column: 23 + fix: ~ + parent: ~ +- kind: + PathlibIsDir: ~ + location: + row: 20 + column: 6 + end_location: + row: 20 + column: 19 + fix: ~ + parent: ~ +- kind: + PathlibIsFile: ~ + location: + row: 21 + column: 7 + end_location: + row: 21 + column: 21 + fix: ~ + parent: ~ +- kind: + PathlibIsLink: ~ + location: + row: 22 + column: 8 + end_location: + row: 22 + column: 22 + fix: ~ + parent: ~ +- kind: + PathlibReadlink: ~ + location: + row: 23 + column: 0 + end_location: + row: 23 + column: 11 + fix: ~ + parent: ~ +- kind: + PathlibStat: ~ + location: + row: 24 + column: 0 + end_location: + row: 24 + column: 7 + fix: ~ + parent: ~ +- kind: + PathlibIsAbs: ~ + location: + row: 25 + column: 0 + end_location: + row: 25 + column: 13 + fix: ~ + parent: ~ +- kind: + PathlibJoin: ~ + location: + row: 26 + column: 0 + end_location: + row: 26 + column: 12 + fix: ~ + parent: ~ +- kind: + PathlibBasename: ~ + location: + row: 27 + column: 0 + end_location: + row: 27 + column: 16 + fix: ~ + parent: ~ +- kind: + PathlibDirname: ~ + location: + row: 28 + column: 0 + end_location: + row: 28 + column: 15 + fix: ~ + parent: ~ +- kind: + PathlibSamefile: ~ + location: + row: 29 + column: 0 + end_location: + row: 29 + column: 16 + fix: ~ + parent: ~ +- kind: + PathlibSplitext: ~ + location: + row: 30 + column: 0 + end_location: + row: 30 + column: 16 + fix: ~ + parent: ~ +- kind: + PathlibOpen: ~ + location: + row: 31 + column: 5 + end_location: + row: 31 + column: 9 + fix: ~ + parent: ~ +- kind: + PathlibOpen: ~ + location: + row: 33 + column: 0 + end_location: + row: 33 + column: 4 + fix: ~ + parent: ~ +- kind: + PathlibGetcwd: ~ + location: + row: 34 + column: 0 + end_location: + row: 34 + column: 10 + fix: ~ + parent: ~ + diff --git a/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__import_as.py.snap b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__import_as.py.snap index 956e8e62b6b9f..fc9ab5ab10abc 100644 --- a/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__import_as.py.snap +++ b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__import_as.py.snap @@ -1,234 +1,241 @@ --- -source: src/rules/flake8_use_pathlib/mod.rs +source: crates/ruff/src/rules/flake8_use_pathlib/mod.rs expression: diagnostics --- - kind: PathlibAbspath: ~ location: - row: 6 + row: 7 column: 4 end_location: - row: 6 + row: 7 column: 17 fix: ~ parent: ~ - kind: PathlibChmod: ~ location: - row: 7 + row: 8 column: 5 end_location: - row: 7 + row: 8 column: 14 fix: ~ parent: ~ - kind: PathlibMkdir: ~ location: - row: 8 + row: 9 column: 6 end_location: - row: 8 + row: 9 column: 15 fix: ~ parent: ~ - kind: PathlibMakedirs: ~ location: - row: 9 + row: 10 column: 0 end_location: - row: 9 + row: 10 column: 12 fix: ~ parent: ~ - kind: PathlibRename: ~ location: - row: 10 + row: 11 column: 0 end_location: - row: 10 + row: 11 column: 10 fix: ~ parent: ~ - kind: PathlibReplace: ~ location: - row: 11 + row: 12 column: 0 end_location: - row: 11 + row: 12 column: 11 fix: ~ parent: ~ - kind: PathlibRmdir: ~ location: - row: 12 + row: 13 column: 0 end_location: - row: 12 + row: 13 column: 9 fix: ~ parent: ~ - kind: PathlibRemove: ~ location: - row: 13 + row: 14 column: 0 end_location: - row: 13 + row: 14 column: 10 fix: ~ parent: ~ - kind: PathlibUnlink: ~ location: - row: 14 + row: 15 column: 0 end_location: - row: 14 + row: 15 column: 10 fix: ~ parent: ~ - kind: PathlibGetcwd: ~ location: - row: 15 + row: 16 column: 0 end_location: - row: 15 + row: 16 column: 10 - fix: ~ + fix: + content: pth.Path.cwd + location: + row: 16 + column: 0 + end_location: + row: 16 + column: 10 parent: ~ - kind: PathlibExists: ~ location: - row: 16 + row: 17 column: 4 end_location: - row: 16 + row: 17 column: 16 fix: ~ parent: ~ - kind: PathlibExpanduser: ~ location: - row: 17 + row: 18 column: 5 end_location: - row: 17 + row: 18 column: 21 fix: ~ parent: ~ - kind: PathlibIsDir: ~ location: - row: 18 + row: 19 column: 6 end_location: - row: 18 + row: 19 column: 17 fix: ~ parent: ~ - kind: PathlibIsFile: ~ location: - row: 19 + row: 20 column: 7 end_location: - row: 19 + row: 20 column: 19 fix: ~ parent: ~ - kind: PathlibIsLink: ~ location: - row: 20 + row: 21 column: 8 end_location: - row: 20 + row: 21 column: 20 fix: ~ parent: ~ - kind: PathlibReadlink: ~ location: - row: 21 + row: 22 column: 0 end_location: - row: 21 + row: 22 column: 12 fix: ~ parent: ~ - kind: PathlibStat: ~ location: - row: 22 + row: 23 column: 0 end_location: - row: 22 + row: 23 column: 8 fix: ~ parent: ~ - kind: PathlibIsAbs: ~ location: - row: 23 + row: 24 column: 0 end_location: - row: 23 + row: 24 column: 11 fix: ~ parent: ~ - kind: PathlibJoin: ~ location: - row: 24 + row: 25 column: 0 end_location: - row: 24 + row: 25 column: 10 fix: ~ parent: ~ - kind: PathlibBasename: ~ location: - row: 25 + row: 26 column: 0 end_location: - row: 25 + row: 26 column: 14 fix: ~ parent: ~ - kind: PathlibDirname: ~ location: - row: 26 + row: 27 column: 0 end_location: - row: 26 + row: 27 column: 13 fix: ~ parent: ~ - kind: PathlibSamefile: ~ location: - row: 27 + row: 28 column: 0 end_location: - row: 27 + row: 28 column: 14 fix: ~ parent: ~ - kind: PathlibSplitext: ~ location: - row: 28 + row: 29 column: 0 end_location: - row: 28 + row: 29 column: 14 fix: ~ parent: ~ diff --git a/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__import_from.py.snap b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__import_from.py.snap index a3f6ff105b440..16d86722760af 100644 --- a/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__import_from.py.snap +++ b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__import_from.py.snap @@ -1,254 +1,261 @@ --- -source: src/rules/flake8_use_pathlib/mod.rs +source: crates/ruff/src/rules/flake8_use_pathlib/mod.rs expression: diagnostics --- - kind: PathlibAbspath: ~ location: - row: 8 + row: 9 column: 4 end_location: - row: 8 + row: 9 column: 11 fix: ~ parent: ~ - kind: PathlibChmod: ~ location: - row: 9 + row: 10 column: 5 end_location: - row: 9 + row: 10 column: 10 fix: ~ parent: ~ - kind: PathlibMkdir: ~ location: - row: 10 + row: 11 column: 6 end_location: - row: 10 + row: 11 column: 11 fix: ~ parent: ~ - kind: PathlibMakedirs: ~ location: - row: 11 + row: 12 column: 0 end_location: - row: 11 + row: 12 column: 8 fix: ~ parent: ~ - kind: PathlibRename: ~ location: - row: 12 + row: 13 column: 0 end_location: - row: 12 + row: 13 column: 6 fix: ~ parent: ~ - kind: PathlibReplace: ~ location: - row: 13 + row: 14 column: 0 end_location: - row: 13 + row: 14 column: 7 fix: ~ parent: ~ - kind: PathlibRmdir: ~ location: - row: 14 + row: 15 column: 0 end_location: - row: 14 + row: 15 column: 5 fix: ~ parent: ~ - kind: PathlibRemove: ~ location: - row: 15 + row: 16 column: 0 end_location: - row: 15 + row: 16 column: 6 fix: ~ parent: ~ - kind: PathlibUnlink: ~ location: - row: 16 + row: 17 column: 0 end_location: - row: 16 + row: 17 column: 6 fix: ~ parent: ~ - kind: PathlibGetcwd: ~ location: - row: 17 + row: 18 column: 0 end_location: - row: 17 + row: 18 column: 6 - fix: ~ + fix: + content: Path.cwd + location: + row: 18 + column: 0 + end_location: + row: 18 + column: 6 parent: ~ - kind: PathlibExists: ~ location: - row: 18 + row: 19 column: 4 end_location: - row: 18 + row: 19 column: 10 fix: ~ parent: ~ - kind: PathlibExpanduser: ~ location: - row: 19 + row: 20 column: 5 end_location: - row: 19 + row: 20 column: 15 fix: ~ parent: ~ - kind: PathlibIsDir: ~ location: - row: 20 + row: 21 column: 6 end_location: - row: 20 + row: 21 column: 11 fix: ~ parent: ~ - kind: PathlibIsFile: ~ location: - row: 21 + row: 22 column: 7 end_location: - row: 21 + row: 22 column: 13 fix: ~ parent: ~ - kind: PathlibIsLink: ~ location: - row: 22 + row: 23 column: 8 end_location: - row: 22 + row: 23 column: 14 fix: ~ parent: ~ - kind: PathlibReadlink: ~ location: - row: 23 + row: 24 column: 0 end_location: - row: 23 + row: 24 column: 8 fix: ~ parent: ~ - kind: PathlibStat: ~ location: - row: 24 + row: 25 column: 0 end_location: - row: 24 + row: 25 column: 4 fix: ~ parent: ~ - kind: PathlibIsAbs: ~ location: - row: 25 + row: 26 column: 0 end_location: - row: 25 + row: 26 column: 5 fix: ~ parent: ~ - kind: PathlibJoin: ~ location: - row: 26 + row: 27 column: 0 end_location: - row: 26 + row: 27 column: 4 fix: ~ parent: ~ - kind: PathlibBasename: ~ location: - row: 27 + row: 28 column: 0 end_location: - row: 27 + row: 28 column: 8 fix: ~ parent: ~ - kind: PathlibDirname: ~ location: - row: 28 + row: 29 column: 0 end_location: - row: 28 + row: 29 column: 7 fix: ~ parent: ~ - kind: PathlibSamefile: ~ location: - row: 29 + row: 30 column: 0 end_location: - row: 29 + row: 30 column: 8 fix: ~ parent: ~ - kind: PathlibSplitext: ~ location: - row: 30 + row: 31 column: 0 end_location: - row: 30 + row: 31 column: 8 fix: ~ parent: ~ - kind: PathlibOpen: ~ location: - row: 31 + row: 32 column: 5 end_location: - row: 31 + row: 32 column: 9 fix: ~ parent: ~ - kind: PathlibOpen: ~ location: - row: 33 + row: 34 column: 0 end_location: - row: 33 + row: 34 column: 4 fix: ~ parent: ~ diff --git a/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__import_from_as.py.snap b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__import_from_as.py.snap index f0ae20f26d990..0bdd522f74615 100644 --- a/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__import_from_as.py.snap +++ b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__import_from_as.py.snap @@ -1,234 +1,241 @@ --- -source: src/rules/flake8_use_pathlib/mod.rs +source: crates/ruff/src/rules/flake8_use_pathlib/mod.rs expression: diagnostics --- - kind: PathlibAbspath: ~ location: - row: 13 + row: 14 column: 4 end_location: - row: 13 + row: 14 column: 12 fix: ~ parent: ~ - kind: PathlibChmod: ~ location: - row: 14 + row: 15 column: 5 end_location: - row: 14 + row: 15 column: 11 fix: ~ parent: ~ - kind: PathlibMkdir: ~ location: - row: 15 + row: 16 column: 6 end_location: - row: 15 + row: 16 column: 12 fix: ~ parent: ~ - kind: PathlibMakedirs: ~ location: - row: 16 + row: 17 column: 0 end_location: - row: 16 + row: 17 column: 9 fix: ~ parent: ~ - kind: PathlibRename: ~ location: - row: 17 + row: 18 column: 0 end_location: - row: 17 + row: 18 column: 7 fix: ~ parent: ~ - kind: PathlibReplace: ~ location: - row: 18 + row: 19 column: 0 end_location: - row: 18 + row: 19 column: 8 fix: ~ parent: ~ - kind: PathlibRmdir: ~ location: - row: 19 + row: 20 column: 0 end_location: - row: 19 + row: 20 column: 6 fix: ~ parent: ~ - kind: PathlibRemove: ~ location: - row: 20 + row: 21 column: 0 end_location: - row: 20 + row: 21 column: 7 fix: ~ parent: ~ - kind: PathlibUnlink: ~ location: - row: 21 + row: 22 column: 0 end_location: - row: 21 + row: 22 column: 7 fix: ~ parent: ~ - kind: PathlibGetcwd: ~ location: - row: 22 + row: 23 column: 0 end_location: - row: 22 + row: 23 column: 7 - fix: ~ + fix: + content: pth.cwd + location: + row: 23 + column: 0 + end_location: + row: 23 + column: 7 parent: ~ - kind: PathlibExists: ~ location: - row: 23 + row: 24 column: 4 end_location: - row: 23 + row: 24 column: 11 fix: ~ parent: ~ - kind: PathlibExpanduser: ~ location: - row: 24 + row: 25 column: 5 end_location: - row: 24 + row: 25 column: 16 fix: ~ parent: ~ - kind: PathlibIsDir: ~ location: - row: 25 + row: 26 column: 6 end_location: - row: 25 + row: 26 column: 12 fix: ~ parent: ~ - kind: PathlibIsFile: ~ location: - row: 26 + row: 27 column: 7 end_location: - row: 26 + row: 27 column: 14 fix: ~ parent: ~ - kind: PathlibIsLink: ~ location: - row: 27 + row: 28 column: 8 end_location: - row: 27 + row: 28 column: 15 fix: ~ parent: ~ - kind: PathlibReadlink: ~ location: - row: 28 + row: 29 column: 0 end_location: - row: 28 + row: 29 column: 9 fix: ~ parent: ~ - kind: PathlibStat: ~ location: - row: 29 + row: 30 column: 0 end_location: - row: 29 + row: 30 column: 5 fix: ~ parent: ~ - kind: PathlibIsAbs: ~ location: - row: 30 + row: 31 column: 0 end_location: - row: 30 + row: 31 column: 6 fix: ~ parent: ~ - kind: PathlibJoin: ~ location: - row: 31 + row: 32 column: 0 end_location: - row: 31 + row: 32 column: 5 fix: ~ parent: ~ - kind: PathlibBasename: ~ location: - row: 32 + row: 33 column: 0 end_location: - row: 32 + row: 33 column: 9 fix: ~ parent: ~ - kind: PathlibDirname: ~ location: - row: 33 + row: 34 column: 0 end_location: - row: 33 + row: 34 column: 8 fix: ~ parent: ~ - kind: PathlibSamefile: ~ location: - row: 34 + row: 35 column: 0 end_location: - row: 34 + row: 35 column: 9 fix: ~ parent: ~ - kind: PathlibSplitext: ~ location: - row: 35 + row: 36 column: 0 end_location: - row: 35 + row: 36 column: 9 fix: ~ parent: ~ diff --git a/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__simplify_pathlib_constructor.py.snap b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__simplify_pathlib_constructor.py.snap new file mode 100644 index 0000000000000..6a0e43e180a8d --- /dev/null +++ b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__simplify_pathlib_constructor.py.snap @@ -0,0 +1,39 @@ +--- +source: crates/ruff/src/rules/flake8_use_pathlib/mod.rs +expression: diagnostics +--- +- kind: + PathConstructorCurrentDirectory: ~ + location: + row: 5 + column: 4 + end_location: + row: 5 + column: 13 + fix: + content: "" + location: + row: 5 + column: 9 + end_location: + row: 5 + column: 12 + parent: ~ +- kind: + PathConstructorCurrentDirectory: ~ + location: + row: 6 + column: 4 + end_location: + row: 6 + column: 12 + fix: + content: "" + location: + row: 6 + column: 8 + end_location: + row: 6 + column: 11 + parent: ~ + diff --git a/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__stat.py.snap b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__stat.py.snap new file mode 100644 index 0000000000000..908476ff1bc51 --- /dev/null +++ b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__stat.py.snap @@ -0,0 +1,135 @@ +--- +source: crates/ruff/src/rules/flake8_use_pathlib/mod.rs +expression: diagnostics +--- +- kind: + PathlibGetatime: ~ + location: + row: 4 + column: 0 + end_location: + row: 4 + column: 16 + fix: ~ + parent: ~ +- kind: + PathlibGetatime: ~ + location: + row: 5 + column: 0 + end_location: + row: 5 + column: 16 + fix: ~ + parent: ~ +- kind: + PathlibGetatime: ~ + location: + row: 6 + column: 0 + end_location: + row: 6 + column: 16 + fix: ~ + parent: ~ +- kind: + PathlibGetmtime: ~ + location: + row: 8 + column: 0 + end_location: + row: 8 + column: 16 + fix: ~ + parent: ~ +- kind: + PathlibGetmtime: ~ + location: + row: 9 + column: 0 + end_location: + row: 9 + column: 16 + fix: ~ + parent: ~ +- kind: + PathlibGetmtime: ~ + location: + row: 10 + column: 0 + end_location: + row: 10 + column: 16 + fix: ~ + parent: ~ +- kind: + PathlibGetctime: ~ + location: + row: 12 + column: 0 + end_location: + row: 12 + column: 16 + fix: ~ + parent: ~ +- kind: + PathlibGetctime: ~ + location: + row: 13 + column: 0 + end_location: + row: 13 + column: 16 + fix: ~ + parent: ~ +- kind: + PathlibGetctime: ~ + location: + row: 14 + column: 0 + end_location: + row: 14 + column: 16 + fix: ~ + parent: ~ +- kind: + PathlibGetsize: ~ + location: + row: 16 + column: 0 + end_location: + row: 16 + column: 15 + fix: ~ + parent: ~ +- kind: + PathlibGetsize: ~ + location: + row: 17 + column: 0 + end_location: + row: 17 + column: 15 + fix: ~ + parent: ~ +- kind: + PathlibGetsize: ~ + location: + row: 18 + column: 0 + end_location: + row: 18 + column: 15 + fix: ~ + parent: ~ +- kind: + PathlibGetsize: ~ + location: + row: 19 + column: 0 + end_location: + row: 19 + column: 15 + fix: ~ + parent: ~ + diff --git a/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__use_pathlib.py.snap b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__use_pathlib.py.snap index 359d76f726403..5c13987c28ea2 100644 --- a/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__use_pathlib.py.snap +++ b/crates/ruff/src/rules/flake8_use_pathlib/snapshots/ruff__rules__flake8_use_pathlib__tests__use_pathlib.py.snap @@ -1,6 +1,73 @@ --- -source: src/rules/flake8_use_pathlib/mod.rs +source: crates/ruff/src/rules/flake8_use_pathlib/mod.rs expression: diagnostics --- -[] +- kind: + PathlibGetcwd: ~ + location: + row: 6 + column: 9 + end_location: + row: 6 + column: 18 + fix: + content: Path.cwd + location: + row: 6 + column: 9 + end_location: + row: 6 + column: 18 + parent: ~ +- kind: + PathlibGetcwd: ~ + location: + row: 9 + column: 4 + end_location: + row: 10 + column: 14 + fix: + content: Path.cwd + location: + row: 9 + column: 4 + end_location: + row: 10 + column: 14 + parent: ~ +- kind: + PathlibGetcwd: ~ + location: + row: 14 + column: 4 + end_location: + row: 14 + column: 14 + fix: + content: Path.cwd + location: + row: 14 + column: 4 + end_location: + row: 14 + column: 14 + parent: ~ +- kind: + PathlibGetcwd: ~ + location: + row: 18 + column: 9 + end_location: + row: 18 + column: 18 + fix: + content: Path.cwd + location: + row: 18 + column: 9 + end_location: + row: 18 + column: 18 + parent: ~ diff --git a/crates/ruff/src/rules/flake8_use_pathlib/violations.rs b/crates/ruff/src/rules/flake8_use_pathlib/violations.rs index 084887c2dbb68..42a8174a50a58 100644 --- a/crates/ruff/src/rules/flake8_use_pathlib/violations.rs +++ b/crates/ruff/src/rules/flake8_use_pathlib/violations.rs @@ -1,6 +1,6 @@ use ruff_macros::{define_violation, derive_message_formats}; -use crate::violation::Violation; +use crate::violation::{AutofixKind, Availability, Violation}; // PTH100 define_violation!( @@ -103,13 +103,42 @@ impl Violation for PathlibUnlink { // PTH109 define_violation!( + /// ## What is does + /// Detects the use of `os.getcwd` and `os.getcwdb`. + /// Autofix is available when the `pathlib` module is imported. + /// + /// ## Why is this bad? + /// A modern alternative to `os.getcwd()` is the `Path.cwd()` function + /// + /// ## Examples + /// ```python + /// cwd = os.getcwd() + /// ``` + /// + /// Use instead: + /// ```python + /// cwd = Path.cwd() + /// ``` + /// + /// ## References + /// * [PEP 428](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/) + pub struct PathlibGetcwd; ); impl Violation for PathlibGetcwd { + const AUTOFIX: Option = Some(AutofixKind::new(Availability::Sometimes)); + #[derive_message_formats] fn message(&self) -> String { format!("`os.getcwd` should be replaced by `Path.cwd()`") } + + fn autofix_title_formatter(&self) -> Option String> { + Some(|PathlibGetcwd| format!("Replace `os.getcwd` with `Path.cwd()`")) + } } // PTH110 @@ -207,7 +236,7 @@ define_violation!( impl Violation for PathlibJoin { #[derive_message_formats] fn message(&self) -> String { - format!("`os.path.join` should be replaced by foo_path / \"bar\"") + format!("`os.path.join` should be replaced by `foo_path / \"bar\"`") } } @@ -276,3 +305,48 @@ impl Violation for PathlibPyPath { format!("`py.path` is in maintenance mode, use `pathlib` instead") } } + +// TODO: add documentation +// PTH201 +define_violation!( + pub struct PathlibGetsize; +); +impl Violation for PathlibGetsize { + #[derive_message_formats] + fn message(&self) -> String { + format!("`os.path.getsize` should be replaced by `stat().st_size`") + } +} + +// PTH202 +define_violation!( + pub struct PathlibGetatime; +); +impl Violation for PathlibGetatime { + #[derive_message_formats] + fn message(&self) -> String { + format!("`os.path.getatime` should be replaced by `stat().st_atime`") + } +} + +// PTH203 +define_violation!( + pub struct PathlibGetmtime; +); +impl Violation for PathlibGetmtime { + #[derive_message_formats] + fn message(&self) -> String { + format!("`os.path.getmtime` should be replaced by `stat().st_mtime`") + } +} + +// PTH204 +define_violation!( + pub struct PathlibGetctime; +); +impl Violation for PathlibGetctime { + #[derive_message_formats] + fn message(&self) -> String { + format!("`os.path.getctime` should be replaced by `stat().st_ctime`") + } +} diff --git a/crates/ruff/src/rules/pylint/rules/consider_using_sys_exit.rs b/crates/ruff/src/rules/pylint/rules/consider_using_sys_exit.rs index 6275ce8d17489..06969de468a0e 100644 --- a/crates/ruff/src/rules/pylint/rules/consider_using_sys_exit.rs +++ b/crates/ruff/src/rules/pylint/rules/consider_using_sys_exit.rs @@ -1,6 +1,7 @@ use ruff_macros::{define_violation, derive_message_formats}; use rustpython_parser::ast::{Expr, ExprKind}; +use crate::ast::helpers; use crate::ast::types::{BindingKind, Range}; use crate::checkers::ast::Checker; use crate::fix::Fix; @@ -39,62 +40,6 @@ fn is_module_star_imported(checker: &Checker, module: &str) -> bool { }) } -/// Return the appropriate `sys.exit` reference based on the current set of -/// imports, or `None` is `sys.exit` hasn't been imported. -fn get_member_import_name_alias(checker: &Checker, module: &str, member: &str) -> Option { - checker.current_scopes().find_map(|scope| { - scope - .bindings - .values() - .find_map(|index| match &checker.bindings[*index].kind { - // e.g. module=sys object=exit - // `import sys` -> `sys.exit` - // `import sys as sys2` -> `sys2.exit` - BindingKind::Importation(name, full_name) => { - if full_name == &module { - Some(format!("{name}.{member}")) - } else { - None - } - } - // e.g. module=os.path object=join - // `from os.path import join` -> `join` - // `from os.path import join as join2` -> `join2` - BindingKind::FromImportation(name, full_name) => { - let mut parts = full_name.split('.'); - if parts.next() == Some(module) - && parts.next() == Some(member) - && parts.next().is_none() - { - Some((*name).to_string()) - } else { - None - } - } - // e.g. module=os.path object=join - // `from os.path import *` -> `join` - BindingKind::StarImportation(_, name) => { - if name.as_ref().map(|name| name == module).unwrap_or_default() { - Some(member.to_string()) - } else { - None - } - } - // e.g. module=os.path object=join - // `import os.path ` -> `os.path.join` - BindingKind::SubmoduleImportation(_, full_name) => { - if full_name == &module { - Some(format!("{full_name}.{member}")) - } else { - None - } - } - // Non-imports. - _ => None, - }) - }) -} - /// RUF004 pub fn consider_using_sys_exit(checker: &mut Checker, func: &Expr) { let ExprKind::Name { id, .. } = &func.node else { @@ -117,7 +62,7 @@ pub fn consider_using_sys_exit(checker: &mut Checker, func: &Expr) { Range::from_located(func), ); if checker.patch(diagnostic.kind.rule()) { - if let Some(content) = get_member_import_name_alias(checker, "sys", "exit") { + if let Some(content) = helpers::get_member_import_name_alias(checker, "sys", "exit") { diagnostic.amend(Fix::replacement( content, func.location, diff --git a/docs/rules/path-constructor-current-directory.md b/docs/rules/path-constructor-current-directory.md new file mode 100644 index 0000000000000..8a756dcab7dab --- /dev/null +++ b/docs/rules/path-constructor-current-directory.md @@ -0,0 +1,26 @@ +# path-constructor-current-directory (PTH200) + +Derived from the **flake8-use-pathlib** linter. + +Autofix is sometimes available. + +## What it does +This rule detects pathlib's `Path` initializations with the default current directory argument. + +## Why is this bad? +The `Path()` constructor defaults to the current directory, so don't pass the +current directory (`"."`) explicitly. + +## Example +```python +from pathlib import Path + +_ = Path(".") +``` + +Use instead: +```python +from pathlib import Path + +_ = Path() +``` \ No newline at end of file diff --git a/docs/rules/pathlib-getcwd.md b/docs/rules/pathlib-getcwd.md new file mode 100644 index 0000000000000..37be84dd926b2 --- /dev/null +++ b/docs/rules/pathlib-getcwd.md @@ -0,0 +1,28 @@ +# pathlib-getcwd (PTH109) + +Derived from the **flake8-use-pathlib** linter. + +Autofix is sometimes available. + +## What is does +Detects the use of `os.getcwd` and `os.getcwdb`. +Autofix is available when the `pathlib` module is imported. + +## Why is this bad? +A modern alternative to `os.getcwd()` is the `Path.cwd()` function + +## Examples +```python +cwd = os.getcwd() +``` + +Use instead: +```python +cwd = Path.cwd() +``` + +## References +* [PEP 428](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/) \ No newline at end of file diff --git a/ruff.schema.json b/ruff.schema.json index 53afb04fb57a2..b464b55b47cfa 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -1804,6 +1804,13 @@ "PTH122", "PTH123", "PTH124", + "PTH2", + "PTH20", + "PTH200", + "PTH201", + "PTH202", + "PTH203", + "PTH204", "PYI", "PYI0", "PYI00",