diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_use_pathlib/full_name.py b/crates/ruff_linter/resources/test/fixtures/flake8_use_pathlib/full_name.py index 9a63baf8c1..81dba0e270 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_use_pathlib/full_name.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_use_pathlib/full_name.py @@ -106,4 +106,22 @@ os.replace("src", "dst", src_dir_fd=1) os.replace("src", "dst", dst_dir_fd=2) os.getcwd() -os.getcwdb() \ No newline at end of file +os.getcwdb() + +os.mkdir(path="directory") + +os.mkdir( + # comment 1 + "directory", + mode=0o777 +) + +os.mkdir("directory", mode=0o777, dir_fd=1) + +os.makedirs("name", 0o777, exist_ok=False) + +os.makedirs("name", 0o777, False) + +os.makedirs(name="name", mode=0o777, exist_ok=False) + +os.makedirs("name", unknown_kwarg=True) diff --git a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs index b5270b4f97..51824c3a65 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs @@ -1039,8 +1039,6 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) { flake8_simplify::rules::zip_dict_keys_and_values(checker, call); } if checker.any_rule_enabled(&[ - Rule::OsMkdir, - Rule::OsMakedirs, Rule::OsStat, Rule::OsPathJoin, Rule::OsPathSplitext, @@ -1120,6 +1118,12 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) { if checker.is_rule_enabled(Rule::OsPathSamefile) { flake8_use_pathlib::rules::os_path_samefile(checker, call, segments); } + if checker.is_rule_enabled(Rule::OsMkdir) { + flake8_use_pathlib::rules::os_mkdir(checker, call, segments); + } + if checker.is_rule_enabled(Rule::OsMakedirs) { + flake8_use_pathlib::rules::os_makedirs(checker, call, segments); + } if checker.is_rule_enabled(Rule::PathConstructorCurrentDirectory) { flake8_use_pathlib::rules::path_constructor_current_directory( checker, call, segments, diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 5addc9592f..d395eed6f2 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -921,8 +921,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { // flake8-use-pathlib (Flake8UsePathlib, "100") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsPathAbspath), (Flake8UsePathlib, "101") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsChmod), - (Flake8UsePathlib, "102") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsMkdir), - (Flake8UsePathlib, "103") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsMakedirs), + (Flake8UsePathlib, "102") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsMkdir), + (Flake8UsePathlib, "103") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsMakedirs), (Flake8UsePathlib, "104") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsRename), (Flake8UsePathlib, "105") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsReplace), (Flake8UsePathlib, "106") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::OsRmdir), diff --git a/crates/ruff_linter/src/preview.rs b/crates/ruff_linter/src/preview.rs index 73edcbf71d..64c185989f 100644 --- a/crates/ruff_linter/src/preview.rs +++ b/crates/ruff_linter/src/preview.rs @@ -159,6 +159,16 @@ pub(crate) const fn is_fix_os_getcwd_enabled(settings: &LinterSettings) -> bool settings.preview.is_enabled() } +// https://github.com/astral-sh/ruff/pull/19514 +pub(crate) const fn is_fix_os_mkdir_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} + +// https://github.com/astral-sh/ruff/pull/19514 +pub(crate) const fn is_fix_os_makedirs_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 b71a635493..19a00b26b0 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,11 @@ -use crate::checkers::ast::Checker; -use crate::importer::ImportRequest; -use crate::{Applicability, Edit, Fix, Violation}; use ruff_python_ast::{self as ast, Expr, ExprCall}; use ruff_python_semantic::{SemanticModel, analyze::typing}; use ruff_text_size::Ranged; +use crate::checkers::ast::Checker; +use crate::importer::ImportRequest; +use crate::{Applicability, Edit, Fix, Violation}; + pub(crate) fn is_keyword_only_argument_non_default(arguments: &ast::Arguments, name: &str) -> bool { arguments .find_keyword(name) @@ -183,3 +184,17 @@ pub(crate) fn check_os_pathlib_two_arg_calls( }); } } + +pub(crate) fn has_unknown_keywords_or_starred_expr( + arguments: &ast::Arguments, + allowed: &[&str], +) -> bool { + if arguments.args.iter().any(Expr::is_starred_expr) { + return true; + } + + arguments.keywords.iter().any(|kw| match &kw.arg { + Some(arg) => !allowed.contains(&arg.as_str()), + None => true, + }) +} 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 b1412707e9..7c9c180fb6 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 @@ -2,6 +2,8 @@ pub(crate) use glob_rule::*; pub(crate) use invalid_pathlib_with_suffix::*; pub(crate) use os_chmod::*; pub(crate) use os_getcwd::*; +pub(crate) use os_makedirs::*; +pub(crate) use os_mkdir::*; pub(crate) use os_path_abspath::*; pub(crate) use os_path_basename::*; pub(crate) use os_path_dirname::*; @@ -30,6 +32,8 @@ mod glob_rule; mod invalid_pathlib_with_suffix; mod os_chmod; mod os_getcwd; +mod os_makedirs; +mod os_mkdir; mod os_path_abspath; mod os_path_basename; mod os_path_dirname; diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_makedirs.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_makedirs.rs new file mode 100644 index 0000000000..c280d3ef77 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_makedirs.rs @@ -0,0 +1,152 @@ +use ruff_diagnostics::{Applicability, Edit, Fix}; +use ruff_macros::{ViolationMetadata, derive_message_formats}; +use ruff_python_ast::{ArgOrKeyword, ExprCall}; +use ruff_text_size::Ranged; + +use crate::checkers::ast::Checker; +use crate::importer::ImportRequest; +use crate::preview::is_fix_os_makedirs_enabled; +use crate::rules::flake8_use_pathlib::helpers::{ + has_unknown_keywords_or_starred_expr, is_pathlib_path_call, +}; +use crate::{FixAvailability, Violation}; + +/// ## What it does +/// Checks for uses of `os.makedirs`. +/// +/// ## 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.mkdir(parents=True)` can improve readability over the +/// `os` module's counterparts (e.g., `os.makedirs()`. +/// +/// ## Examples +/// ```python +/// import os +/// +/// os.makedirs("./nested/directory/") +/// ``` +/// +/// Use instead: +/// ```python +/// from pathlib import Path +/// +/// Path("./nested/directory/").mkdir(parents=True) +/// ``` +/// +/// ## 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.mkdir`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.mkdir) +/// - [Python documentation: `os.makedirs`](https://docs.python.org/3/library/os.html#os.makedirs) +/// - [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 OsMakedirs; + +impl Violation for OsMakedirs { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + + #[derive_message_formats] + fn message(&self) -> String { + "`os.makedirs()` should be replaced by `Path.mkdir(parents=True)`".to_string() + } + + fn fix_title(&self) -> Option { + Some("Replace with `Path(...).mkdir(parents=True)`".to_string()) + } +} + +/// PTH103 +pub(crate) fn os_makedirs(checker: &Checker, call: &ExprCall, segments: &[&str]) { + if segments != ["os", "makedirs"] { + return; + } + + let range = call.range(); + let mut diagnostic = checker.report_diagnostic(OsMakedirs, call.func.range()); + + let Some(name) = call.arguments.find_argument_value("name", 0) else { + return; + }; + + if !is_fix_os_makedirs_enabled(checker.settings()) { + return; + } + + // Signature as of Python 3.13 (https://docs.python.org/3/library/os.html#os.makedirs) + // ```text + // 0 1 2 + // os.makedirs(name, mode=0o777, exist_ok=False) + // ``` + // We should not offer autofixes if there are more arguments + // than in the original signature + if call.arguments.len() > 3 { + return; + } + // We should not offer autofixes if there are keyword arguments + // that don't match the original function signature + if has_unknown_keywords_or_starred_expr(&call.arguments, &["name", "mode", "exist_ok"]) { + return; + } + + diagnostic.try_set_fix(|| { + let (import_edit, binding) = checker.importer().get_or_import_symbol( + &ImportRequest::import("pathlib", "Path"), + call.start(), + checker.semantic(), + )?; + + let applicability = if checker.comment_ranges().intersects(range) { + Applicability::Unsafe + } else { + Applicability::Safe + }; + + let locator = checker.locator(); + + let name_code = locator.slice(name.range()); + + let mode = call.arguments.find_argument("mode", 1); + let exist_ok = call.arguments.find_argument("exist_ok", 2); + + let mkdir_args = match (mode, exist_ok) { + // Default to a keyword argument when alone. + (None, None) => "parents=True".to_string(), + // If either argument is missing, it's safe to add `parents` at the end. + (None, Some(arg)) | (Some(arg), None) => { + format!("{}, parents=True", locator.slice(arg)) + } + // If they're all positional, `parents` has to be positional too. + (Some(ArgOrKeyword::Arg(mode)), Some(ArgOrKeyword::Arg(exist_ok))) => { + format!("{}, True, {}", locator.slice(mode), locator.slice(exist_ok)) + } + // If either argument is a keyword, we can put `parents` at the end again. + (Some(mode), Some(exist_ok)) => format!( + "{}, {}, parents=True", + locator.slice(mode), + locator.slice(exist_ok) + ), + }; + + let replacement = if is_pathlib_path_call(checker, name) { + format!("{name_code}.mkdir({mkdir_args})") + } else { + format!("{binding}({name_code}).mkdir({mkdir_args})") + }; + + Ok(Fix::applicable_edits( + Edit::range_replacement(replacement, range), + [import_edit], + applicability, + )) + }); +} diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_mkdir.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_mkdir.rs new file mode 100644 index 0000000000..754191910e --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/rules/os_mkdir.rs @@ -0,0 +1,136 @@ +use ruff_diagnostics::{Applicability, Edit, Fix}; +use ruff_macros::{ViolationMetadata, derive_message_formats}; +use ruff_python_ast::ExprCall; +use ruff_text_size::Ranged; + +use crate::checkers::ast::Checker; +use crate::importer::ImportRequest; +use crate::preview::is_fix_os_mkdir_enabled; +use crate::rules::flake8_use_pathlib::helpers::{ + has_unknown_keywords_or_starred_expr, is_keyword_only_argument_non_default, + is_pathlib_path_call, +}; +use crate::{FixAvailability, Violation}; + +/// ## What it does +/// Checks for uses of `os.mkdir`. +/// +/// ## 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.mkdir()` can improve readability over the `os` +/// module's counterparts (e.g., `os.mkdir()`). +/// +/// ## Examples +/// ```python +/// import os +/// +/// os.mkdir("./directory/") +/// ``` +/// +/// Use instead: +/// ```python +/// from pathlib import Path +/// +/// Path("./directory/").mkdir() +/// ``` +/// +/// ## 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.mkdir`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.mkdir) +/// - [Python documentation: `os.mkdir`](https://docs.python.org/3/library/os.html#os.mkdir) +/// - [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 OsMkdir; + +impl Violation for OsMkdir { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + + #[derive_message_formats] + fn message(&self) -> String { + "`os.mkdir()` should be replaced by `Path.mkdir()`".to_string() + } + + fn fix_title(&self) -> Option { + Some("Replace with `Path(...).mkdir()`".to_string()) + } +} + +/// PTH102 +pub(crate) fn os_mkdir(checker: &Checker, call: &ExprCall, segments: &[&str]) { + if segments != ["os", "mkdir"] { + 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.mkdir) + // ```text + // 0 1 2 + // os.mkdir(path, mode=0o777, *, dir_fd=None) + // ``` + if is_keyword_only_argument_non_default(&call.arguments, "dir_fd") { + return; + } + + let range = call.range(); + let mut diagnostic = checker.report_diagnostic(OsMkdir, call.func.range()); + + let Some(path) = call.arguments.find_argument_value("path", 0) else { + return; + }; + + if !is_fix_os_mkdir_enabled(checker.settings()) { + return; + } + + if call.arguments.len() > 2 { + return; + } + + if has_unknown_keywords_or_starred_expr(&call.arguments, &["path", "mode"]) { + return; + } + + diagnostic.try_set_fix(|| { + let (import_edit, binding) = checker.importer().get_or_import_symbol( + &ImportRequest::import("pathlib", "Path"), + call.start(), + checker.semantic(), + )?; + + let applicability = if checker.comment_ranges().intersects(range) { + Applicability::Unsafe + } else { + Applicability::Safe + }; + + let path_code = checker.locator().slice(path.range()); + + let mkdir_args = call + .arguments + .find_argument_value("mode", 1) + .map(|expr| format!("mode={}", checker.locator().slice(expr.range()))) + .unwrap_or_default(); + + let replacement = if is_pathlib_path_call(checker, path) { + format!("{path_code}.mkdir({mkdir_args})") + } else { + format!("{binding}({path_code}).mkdir({mkdir_args})") + }; + + Ok(Fix::applicable_edits( + Edit::range_replacement(replacement, range), + [import_edit], + applicability, + )) + }); +} 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 65026a0108..b35ecd77c1 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 @@ -8,8 +8,7 @@ use crate::rules::flake8_use_pathlib::helpers::{ use crate::rules::flake8_use_pathlib::{ rules::Glob, violations::{ - BuiltinOpen, Joiner, OsListdir, OsMakedirs, OsMkdir, OsPathJoin, OsPathSplitext, OsStat, - OsSymlink, PyPath, + BuiltinOpen, Joiner, OsListdir, OsPathJoin, OsPathSplitext, OsStat, OsSymlink, PyPath, }, }; @@ -20,21 +19,6 @@ pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) { let range = call.func.range(); match qualified_name.segments() { - // PTH102 - ["os", "makedirs"] => checker.report_diagnostic_if_enabled(OsMakedirs, range), - // PTH103 - ["os", "mkdir"] => { - // `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.mkdir) - // ```text - // 0 1 2 - // os.mkdir(path, mode=0o777, *, dir_fd=None) - // ``` - if is_keyword_only_argument_non_default(&call.arguments, "dir_fd") { - return; - } - checker.report_diagnostic_if_enabled(OsMkdir, range) - } // PTH116 ["os", "stat"] => { // `dir_fd` is not supported by pathlib, so check if it's set to non-default values. 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 7a22a5615d..84900549de 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 @@ -34,6 +34,7 @@ PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` 10 | os.makedirs(p) 11 | os.rename(p) | +help: Replace with `Path(...).mkdir()` PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` --> full_name.py:10:1 @@ -45,6 +46,7 @@ PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` 11 | os.rename(p) 12 | os.replace(p) | +help: Replace with `Path(...).mkdir(parents=True)` PTH104 `os.rename()` should be replaced by `Path.rename()` --> full_name.py:11:1 @@ -419,5 +421,77 @@ PTH109 `os.getcwd()` should be replaced by `Path.cwd()` 108 | os.getcwd() 109 | os.getcwdb() | ^^^^^^^^^^ +110 | +111 | os.mkdir(path="directory") | help: Replace with `Path.cwd()` + +PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` + --> full_name.py:111:1 + | +109 | os.getcwdb() +110 | +111 | os.mkdir(path="directory") + | ^^^^^^^^ +112 | +113 | os.mkdir( + | +help: Replace with `Path(...).mkdir()` + +PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` + --> full_name.py:113:1 + | +111 | os.mkdir(path="directory") +112 | +113 | os.mkdir( + | ^^^^^^^^ +114 | # comment 1 +115 | "directory", + | +help: Replace with `Path(...).mkdir()` + +PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` + --> full_name.py:121:1 + | +119 | os.mkdir("directory", mode=0o777, dir_fd=1) +120 | +121 | os.makedirs("name", 0o777, exist_ok=False) + | ^^^^^^^^^^^ +122 | +123 | os.makedirs("name", 0o777, False) + | +help: Replace with `Path(...).mkdir(parents=True)` + +PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` + --> full_name.py:123:1 + | +121 | os.makedirs("name", 0o777, exist_ok=False) +122 | +123 | os.makedirs("name", 0o777, False) + | ^^^^^^^^^^^ +124 | +125 | os.makedirs(name="name", mode=0o777, exist_ok=False) + | +help: Replace with `Path(...).mkdir(parents=True)` + +PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` + --> full_name.py:125:1 + | +123 | os.makedirs("name", 0o777, False) +124 | +125 | os.makedirs(name="name", mode=0o777, exist_ok=False) + | ^^^^^^^^^^^ +126 | +127 | os.makedirs("name", unknown_kwarg=True) + | +help: Replace with `Path(...).mkdir(parents=True)` + +PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` + --> full_name.py:127:1 + | +125 | os.makedirs(name="name", mode=0o777, exist_ok=False) +126 | +127 | os.makedirs("name", unknown_kwarg=True) + | ^^^^^^^^^^^ + | +help: Replace with `Path(...).mkdir(parents=True)` 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 3d94e4eb06..f2b4eeff44 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 @@ -34,6 +34,7 @@ PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` 10 | foo.makedirs(p) 11 | foo.rename(p) | +help: Replace with `Path(...).mkdir()` PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` --> import_as.py:10:1 @@ -45,6 +46,7 @@ PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` 11 | foo.rename(p) 12 | foo.replace(p) | +help: Replace with `Path(...).mkdir(parents=True)` PTH104 `os.rename()` should be replaced by `Path.rename()` --> import_as.py:11:1 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 5e8b12f577..010c779654 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 @@ -34,6 +34,7 @@ PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` 12 | makedirs(p) 13 | rename(p) | +help: Replace with `Path(...).mkdir()` PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` --> import_from.py:12:1 @@ -45,6 +46,7 @@ PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` 13 | rename(p) 14 | replace(p) | +help: Replace with `Path(...).mkdir(parents=True)` PTH104 `os.rename()` should be replaced by `Path.rename()` --> import_from.py:13:1 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 7a85401333..1499204295 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 @@ -34,6 +34,7 @@ PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` 17 | xmakedirs(p) 18 | xrename(p) | +help: Replace with `Path(...).mkdir()` PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` --> import_from_as.py:17:1 @@ -45,6 +46,7 @@ PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` 18 | xrename(p) 19 | xreplace(p) | +help: Replace with `Path(...).mkdir(parents=True)` PTH104 `os.rename()` should be replaced by `Path.rename()` --> import_from_as.py:18:1 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 index e3a23ed180..8ed5325600 100644 --- 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 @@ -38,7 +38,7 @@ PTH101 `os.chmod()` should be replaced by `Path.chmod()` | help: Replace with `Path(...).chmod(...)` -PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` +PTH102 [*] `os.mkdir()` should be replaced by `Path.mkdir()` --> full_name.py:9:7 | 7 | a = os.path.abspath(p) @@ -48,8 +48,25 @@ PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` 10 | os.makedirs(p) 11 | os.rename(p) | +help: Replace with `Path(...).mkdir()` -PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` +ℹ 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 8 | a = os.path.abspath(p) +8 9 | aa = os.chmod(p) +9 |-aaa = os.mkdir(p) + 10 |+aaa = pathlib.Path(p).mkdir() +10 11 | os.makedirs(p) +11 12 | os.rename(p) +12 13 | os.replace(p) + +PTH103 [*] `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` --> full_name.py:10:1 | 8 | aa = os.chmod(p) @@ -59,6 +76,24 @@ PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` 11 | os.rename(p) 12 | os.replace(p) | +help: Replace with `Path(...).mkdir(parents=True)` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +7 8 | a = os.path.abspath(p) +8 9 | aa = os.chmod(p) +9 10 | aaa = os.mkdir(p) +10 |-os.makedirs(p) + 11 |+pathlib.Path(p).mkdir(parents=True) +11 12 | os.rename(p) +12 13 | os.replace(p) +13 14 | os.rmdir(p) PTH104 `os.rename()` should be replaced by `Path.rename()` --> full_name.py:11:1 @@ -645,6 +680,8 @@ help: Replace with `Path.cwd()` 108 |-os.getcwd() 109 |+pathlib.Path.cwd() 109 110 | os.getcwdb() +110 111 | +111 112 | os.mkdir(path="directory") PTH109 [*] `os.getcwd()` should be replaced by `Path.cwd()` --> full_name.py:109:1 @@ -652,6 +689,8 @@ PTH109 [*] `os.getcwd()` should be replaced by `Path.cwd()` 108 | os.getcwd() 109 | os.getcwdb() | ^^^^^^^^^^ +110 | +111 | os.mkdir(path="directory") | help: Replace with `Path.cwd()` @@ -668,3 +707,164 @@ help: Replace with `Path.cwd()` 108 109 | os.getcwd() 109 |-os.getcwdb() 110 |+pathlib.Path.cwd() +110 111 | +111 112 | os.mkdir(path="directory") +112 113 | + +PTH102 [*] `os.mkdir()` should be replaced by `Path.mkdir()` + --> full_name.py:111:1 + | +109 | os.getcwdb() +110 | +111 | os.mkdir(path="directory") + | ^^^^^^^^ +112 | +113 | os.mkdir( + | +help: Replace with `Path(...).mkdir()` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +108 109 | os.getcwd() +109 110 | os.getcwdb() +110 111 | +111 |-os.mkdir(path="directory") + 112 |+pathlib.Path("directory").mkdir() +112 113 | +113 114 | os.mkdir( +114 115 | # comment 1 + +PTH102 [*] `os.mkdir()` should be replaced by `Path.mkdir()` + --> full_name.py:113:1 + | +111 | os.mkdir(path="directory") +112 | +113 | os.mkdir( + | ^^^^^^^^ +114 | # comment 1 +115 | "directory", + | +help: Replace with `Path(...).mkdir()` + +ℹ Unsafe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +110 111 | +111 112 | os.mkdir(path="directory") +112 113 | +113 |-os.mkdir( +114 |- # comment 1 +115 |- "directory", +116 |- mode=0o777 +117 |-) + 114 |+pathlib.Path("directory").mkdir(mode=0o777) +118 115 | +119 116 | os.mkdir("directory", mode=0o777, dir_fd=1) +120 117 | + +PTH103 [*] `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` + --> full_name.py:121:1 + | +119 | os.mkdir("directory", mode=0o777, dir_fd=1) +120 | +121 | os.makedirs("name", 0o777, exist_ok=False) + | ^^^^^^^^^^^ +122 | +123 | os.makedirs("name", 0o777, False) + | +help: Replace with `Path(...).mkdir(parents=True)` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +118 119 | +119 120 | os.mkdir("directory", mode=0o777, dir_fd=1) +120 121 | +121 |-os.makedirs("name", 0o777, exist_ok=False) + 122 |+pathlib.Path("name").mkdir(0o777, exist_ok=False, parents=True) +122 123 | +123 124 | os.makedirs("name", 0o777, False) +124 125 | + +PTH103 [*] `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` + --> full_name.py:123:1 + | +121 | os.makedirs("name", 0o777, exist_ok=False) +122 | +123 | os.makedirs("name", 0o777, False) + | ^^^^^^^^^^^ +124 | +125 | os.makedirs(name="name", mode=0o777, exist_ok=False) + | +help: Replace with `Path(...).mkdir(parents=True)` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +120 121 | +121 122 | os.makedirs("name", 0o777, exist_ok=False) +122 123 | +123 |-os.makedirs("name", 0o777, False) + 124 |+pathlib.Path("name").mkdir(0o777, True, False) +124 125 | +125 126 | os.makedirs(name="name", mode=0o777, exist_ok=False) +126 127 | + +PTH103 [*] `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` + --> full_name.py:125:1 + | +123 | os.makedirs("name", 0o777, False) +124 | +125 | os.makedirs(name="name", mode=0o777, exist_ok=False) + | ^^^^^^^^^^^ +126 | +127 | os.makedirs("name", unknown_kwarg=True) + | +help: Replace with `Path(...).mkdir(parents=True)` + +ℹ Safe fix +1 1 | import os +2 2 | import os.path + 3 |+import pathlib +3 4 | +4 5 | p = "/foo" +5 6 | q = "bar" +-------------------------------------------------------------------------------- +122 123 | +123 124 | os.makedirs("name", 0o777, False) +124 125 | +125 |-os.makedirs(name="name", mode=0o777, exist_ok=False) + 126 |+pathlib.Path("name").mkdir(mode=0o777, exist_ok=False, parents=True) +126 127 | +127 128 | os.makedirs("name", unknown_kwarg=True) + +PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` + --> full_name.py:127:1 + | +125 | os.makedirs(name="name", mode=0o777, exist_ok=False) +126 | +127 | os.makedirs("name", unknown_kwarg=True) + | ^^^^^^^^^^^ + | +help: Replace with `Path(...).mkdir(parents=True)` 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 index 5a32aa5f53..167f4ecd2c 100644 --- 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 @@ -38,7 +38,7 @@ PTH101 `os.chmod()` should be replaced by `Path.chmod()` | help: Replace with `Path(...).chmod(...)` -PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` +PTH102 [*] `os.mkdir()` should be replaced by `Path.mkdir()` --> import_as.py:9:7 | 7 | a = foo_p.abspath(p) @@ -48,8 +48,25 @@ PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` 10 | foo.makedirs(p) 11 | foo.rename(p) | +help: Replace with `Path(...).mkdir()` -PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` +ℹ 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 8 | a = foo_p.abspath(p) +8 9 | aa = foo.chmod(p) +9 |-aaa = foo.mkdir(p) + 10 |+aaa = pathlib.Path(p).mkdir() +10 11 | foo.makedirs(p) +11 12 | foo.rename(p) +12 13 | foo.replace(p) + +PTH103 [*] `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` --> import_as.py:10:1 | 8 | aa = foo.chmod(p) @@ -59,6 +76,24 @@ PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` 11 | foo.rename(p) 12 | foo.replace(p) | +help: Replace with `Path(...).mkdir(parents=True)` + +ℹ 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" +-------------------------------------------------------------------------------- +7 8 | a = foo_p.abspath(p) +8 9 | aa = foo.chmod(p) +9 10 | aaa = foo.mkdir(p) +10 |-foo.makedirs(p) + 11 |+pathlib.Path(p).mkdir(parents=True) +11 12 | foo.rename(p) +12 13 | foo.replace(p) +13 14 | foo.rmdir(p) PTH104 `os.rename()` should be replaced by `Path.rename()` --> import_as.py:11:1 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 index 1d295095d3..9b1262c76c 100644 --- 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 @@ -39,7 +39,7 @@ PTH101 `os.chmod()` should be replaced by `Path.chmod()` | help: Replace with `Path(...).chmod(...)` -PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` +PTH102 [*] `os.mkdir()` should be replaced by `Path.mkdir()` --> import_from.py:11:7 | 9 | a = abspath(p) @@ -49,8 +49,26 @@ PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` 12 | makedirs(p) 13 | rename(p) | +help: Replace with `Path(...).mkdir()` -PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` +ℹ 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 10 | a = abspath(p) +10 11 | aa = chmod(p) +11 |-aaa = mkdir(p) + 12 |+aaa = pathlib.Path(p).mkdir() +12 13 | makedirs(p) +13 14 | rename(p) +14 15 | replace(p) + +PTH103 [*] `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` --> import_from.py:12:1 | 10 | aa = chmod(p) @@ -60,6 +78,25 @@ PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` 13 | rename(p) 14 | replace(p) | +help: Replace with `Path(...).mkdir(parents=True)` + +ℹ 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" +-------------------------------------------------------------------------------- +9 10 | a = abspath(p) +10 11 | aa = chmod(p) +11 12 | aaa = mkdir(p) +12 |-makedirs(p) + 13 |+pathlib.Path(p).mkdir(parents=True) +13 14 | rename(p) +14 15 | replace(p) +15 16 | rmdir(p) PTH104 `os.rename()` should be replaced by `Path.rename()` --> import_from.py:13:1 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 index 1d59d9f304..c964f4b123 100644 --- 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 @@ -39,7 +39,7 @@ PTH101 `os.chmod()` should be replaced by `Path.chmod()` | help: Replace with `Path(...).chmod(...)` -PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` +PTH102 [*] `os.mkdir()` should be replaced by `Path.mkdir()` --> import_from_as.py:16:7 | 14 | a = xabspath(p) @@ -49,8 +49,26 @@ PTH102 `os.mkdir()` should be replaced by `Path.mkdir()` 17 | xmakedirs(p) 18 | xrename(p) | +help: Replace with `Path(...).mkdir()` -PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` +ℹ 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 15 | a = xabspath(p) +15 16 | aa = xchmod(p) +16 |-aaa = xmkdir(p) + 17 |+aaa = pathlib.Path(p).mkdir() +17 18 | xmakedirs(p) +18 19 | xrename(p) +19 20 | xreplace(p) + +PTH103 [*] `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` --> import_from_as.py:17:1 | 15 | aa = xchmod(p) @@ -60,6 +78,25 @@ PTH103 `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` 18 | xrename(p) 19 | xreplace(p) | +help: Replace with `Path(...).mkdir(parents=True)` + +ℹ 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" +-------------------------------------------------------------------------------- +14 15 | a = xabspath(p) +15 16 | aa = xchmod(p) +16 17 | aaa = xmkdir(p) +17 |-xmakedirs(p) + 18 |+pathlib.Path(p).mkdir(parents=True) +18 19 | xrename(p) +19 20 | xreplace(p) +20 21 | xrmdir(p) PTH104 `os.rename()` should be replaced by `Path.rename()` --> import_from_as.py:18:1 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 e9d7419737..7a5661aae2 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/violations.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/violations.rs @@ -2,96 +2,6 @@ use ruff_macros::{ViolationMetadata, derive_message_formats}; use crate::Violation; -/// ## What it does -/// Checks for uses of `os.makedirs`. -/// -/// ## 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.mkdir(parents=True)` can improve readability over the -/// `os` module's counterparts (e.g., `os.makedirs()`. -/// -/// ## Examples -/// ```python -/// import os -/// -/// os.makedirs("./nested/directory/") -/// ``` -/// -/// Use instead: -/// ```python -/// from pathlib import Path -/// -/// Path("./nested/directory/").mkdir(parents=True) -/// ``` -/// -/// ## 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.mkdir`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.mkdir) -/// - [Python documentation: `os.makedirs`](https://docs.python.org/3/library/os.html#os.makedirs) -/// - [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 OsMakedirs; - -impl Violation for OsMakedirs { - #[derive_message_formats] - fn message(&self) -> String { - "`os.makedirs()` should be replaced by `Path.mkdir(parents=True)`".to_string() - } -} - -/// ## What it does -/// Checks for uses of `os.mkdir`. -/// -/// ## 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.mkdir()` can improve readability over the `os` -/// module's counterparts (e.g., `os.mkdir()`). -/// -/// ## Examples -/// ```python -/// import os -/// -/// os.mkdir("./directory/") -/// ``` -/// -/// Use instead: -/// ```python -/// from pathlib import Path -/// -/// Path("./directory/").mkdir() -/// ``` -/// -/// ## 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.mkdir`](https://docs.python.org/3/library/pathlib.html#pathlib.Path.mkdir) -/// - [Python documentation: `os.mkdir`](https://docs.python.org/3/library/os.html#os.mkdir) -/// - [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 OsMkdir; - -impl Violation for OsMkdir { - #[derive_message_formats] - fn message(&self) -> String { - "`os.mkdir()` should be replaced by `Path.mkdir()`".to_string() - } -} - /// ## What it does /// Checks for uses of `os.stat`. ///