This commit is contained in:
chiri 2025-12-16 20:09:18 +03:00
parent 385dd2770b
commit 93312009de
5 changed files with 247 additions and 5 deletions

View File

@ -136,4 +136,38 @@ os.chmod("pth1_file", 0o700, None, True, 1, *[1], **{"x": 1}, foo=1)
os.rename("pth1_file", "pth1_file1", None, None, 1, *[1], **{"x": 1}, foo=1)
os.replace("pth1_file1", "pth1_file", None, None, 1, *[1], **{"x": 1}, foo=1)
os.path.samefile("pth1_file", "pth1_link", 1, *[1], **{"x": 1}, foo=1)
os.path.samefile("pth1_file", "pth1_link", 1, *[1], **{"x": 1}, foo=1)
# See: https://github.com/astral-sh/ruff/issues/21794
import sys
if os.rename("pth1.py", "pth1.py.bak"):
print("rename: truthy")
else:
print("rename: falsey")
if os.replace("pth1.py.bak", "pth1.py"):
print("replace: truthy")
else:
print("replace: falsey")
try:
for _ in os.getcwd():
print("getcwd: iterable")
break
except TypeError as e:
print("getcwd: not iterable")
try:
for _ in os.getcwdb():
print("getcwdb: iterable")
break
except TypeError as e:
print("getcwdb: not iterable")
try:
for _ in os.readlink(sys.executable):
print("readlink: iterable")
break
except TypeError as e:
print("readlink: not iterable")

View File

@ -1,4 +1,4 @@
use ruff_python_ast::{self as ast, Arguments, Expr, ExprCall};
use ruff_python_ast::{self as ast, Arguments, Expr, ExprCall, Stmt};
use ruff_python_semantic::{SemanticModel, analyze::typing};
use ruff_text_size::Ranged;
@ -93,7 +93,9 @@ pub(crate) fn check_os_pathlib_single_arg_calls(
let applicability = match applicability {
Applicability::DisplayOnly => Applicability::DisplayOnly,
_ if checker.comment_ranges().intersects(range) => Applicability::Unsafe,
_ if checker.comment_ranges().intersects(range) || is_statement(checker) => {
Applicability::Unsafe
}
_ => applicability,
};
@ -174,7 +176,9 @@ pub(crate) fn check_os_pathlib_two_arg_calls(
let applicability = match applicability {
Applicability::DisplayOnly => Applicability::DisplayOnly,
_ if checker.comment_ranges().intersects(range) => Applicability::Unsafe,
_ if checker.comment_ranges().intersects(range) || is_statement(checker) => {
Applicability::Unsafe
}
_ => applicability,
};
@ -213,3 +217,10 @@ pub(crate) fn is_argument_non_default(arguments: &Arguments, name: &str, positio
pub(crate) fn is_top_level_expression_call(checker: &Checker) -> bool {
checker.semantic().current_expression_parent().is_none()
}
pub(crate) fn is_statement(checker: &Checker) -> bool {
matches!(
checker.semantic().current_statement(),
Stmt::If(_) | Stmt::For(_) | Stmt::While(_)
)
}

View File

@ -6,7 +6,7 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::importer::ImportRequest;
use crate::preview::is_fix_os_getcwd_enabled;
use crate::rules::flake8_use_pathlib::helpers::is_top_level_expression_call;
use crate::rules::flake8_use_pathlib::helpers::{is_statement, is_top_level_expression_call};
use crate::{FixAvailability, Violation};
/// ## What it does
@ -90,6 +90,7 @@ pub(crate) fn os_getcwd(checker: &Checker, call: &ExprCall, segments: &[&str]) {
// Unsafe when the fix would delete comments or change a used return value
let applicability = if checker.comment_ranges().intersects(range)
|| !is_top_level_expression_call(checker)
|| is_statement(checker)
{
Applicability::Unsafe
} else {

View File

@ -567,5 +567,64 @@ PTH121 `os.path.samefile()` should be replaced by `Path.samefile()`
138 |
139 | os.path.samefile("pth1_file", "pth1_link", 1, *[1], **{"x": 1}, foo=1)
| ^^^^^^^^^^^^^^^^
140 |
141 | import sys
|
help: Replace with `Path(...).samefile()`
PTH104 `os.rename()` should be replaced by `Path.rename()`
--> full_name.py:143:4
|
141 | import sys
142 |
143 | if os.rename("pth1.py", "pth1.py.bak"):
| ^^^^^^^^^
144 | print("rename: truthy")
145 | else:
|
help: Replace with `Path(...).rename(...)`
PTH105 `os.replace()` should be replaced by `Path.replace()`
--> full_name.py:148:4
|
146 | print("rename: falsey")
147 |
148 | if os.replace("pth1.py.bak", "pth1.py"):
| ^^^^^^^^^^
149 | print("replace: truthy")
150 | else:
|
help: Replace with `Path(...).replace(...)`
PTH109 `os.getcwd()` should be replaced by `Path.cwd()`
--> full_name.py:154:14
|
153 | try:
154 | for _ in os.getcwd():
| ^^^^^^^^^
155 | print("getcwd: iterable")
156 | break
|
help: Replace with `Path.cwd()`
PTH109 `os.getcwd()` should be replaced by `Path.cwd()`
--> full_name.py:161:14
|
160 | try:
161 | for _ in os.getcwdb():
| ^^^^^^^^^^
162 | print("getcwdb: iterable")
163 | break
|
help: Replace with `Path.cwd()`
PTH115 `os.readlink()` should be replaced by `Path.readlink()`
--> full_name.py:168:14
|
167 | try:
168 | for _ in os.readlink(sys.executable):
| ^^^^^^^^^^^
169 | print("readlink: iterable")
170 | break
|
help: Replace with `Path(...).readlink()`

View File

@ -1037,5 +1037,142 @@ PTH121 `os.path.samefile()` should be replaced by `Path.samefile()`
138 |
139 | os.path.samefile("pth1_file", "pth1_link", 1, *[1], **{"x": 1}, foo=1)
| ^^^^^^^^^^^^^^^^
140 |
141 | import sys
|
help: Replace with `Path(...).samefile()`
PTH104 [*] `os.rename()` should be replaced by `Path.rename()`
--> full_name.py:143:4
|
141 | import sys
142 |
143 | if os.rename("pth1.py", "pth1.py.bak"):
| ^^^^^^^^^
144 | print("rename: truthy")
145 | else:
|
help: Replace with `Path(...).rename(...)`
139 | os.path.samefile("pth1_file", "pth1_link", 1, *[1], **{"x": 1}, foo=1)
140 |
141 | import sys
142 + import pathlib
143 |
- if os.rename("pth1.py", "pth1.py.bak"):
144 + if pathlib.Path("pth1.py").rename("pth1.py.bak"):
145 | print("rename: truthy")
146 | else:
147 | print("rename: falsey")
note: This is an unsafe fix and may change runtime behavior
PTH105 [*] `os.replace()` should be replaced by `Path.replace()`
--> full_name.py:148:4
|
146 | print("rename: falsey")
147 |
148 | if os.replace("pth1.py.bak", "pth1.py"):
| ^^^^^^^^^^
149 | print("replace: truthy")
150 | else:
|
help: Replace with `Path(...).replace(...)`
139 | os.path.samefile("pth1_file", "pth1_link", 1, *[1], **{"x": 1}, foo=1)
140 |
141 | import sys
142 + import pathlib
143 |
144 | if os.rename("pth1.py", "pth1.py.bak"):
145 | print("rename: truthy")
146 | else:
147 | print("rename: falsey")
148 |
- if os.replace("pth1.py.bak", "pth1.py"):
149 + if pathlib.Path("pth1.py.bak").replace("pth1.py"):
150 | print("replace: truthy")
151 | else:
152 | print("replace: falsey")
note: This is an unsafe fix and may change runtime behavior
PTH109 [*] `os.getcwd()` should be replaced by `Path.cwd()`
--> full_name.py:154:14
|
153 | try:
154 | for _ in os.getcwd():
| ^^^^^^^^^
155 | print("getcwd: iterable")
156 | break
|
help: Replace with `Path.cwd()`
139 | os.path.samefile("pth1_file", "pth1_link", 1, *[1], **{"x": 1}, foo=1)
140 |
141 | import sys
142 + import pathlib
143 |
144 | if os.rename("pth1.py", "pth1.py.bak"):
145 | print("rename: truthy")
--------------------------------------------------------------------------------
152 | print("replace: falsey")
153 |
154 | try:
- for _ in os.getcwd():
155 + for _ in pathlib.Path.cwd():
156 | print("getcwd: iterable")
157 | break
158 | except TypeError as e:
note: This is an unsafe fix and may change runtime behavior
PTH109 [*] `os.getcwd()` should be replaced by `Path.cwd()`
--> full_name.py:161:14
|
160 | try:
161 | for _ in os.getcwdb():
| ^^^^^^^^^^
162 | print("getcwdb: iterable")
163 | break
|
help: Replace with `Path.cwd()`
139 | os.path.samefile("pth1_file", "pth1_link", 1, *[1], **{"x": 1}, foo=1)
140 |
141 | import sys
142 + import pathlib
143 |
144 | if os.rename("pth1.py", "pth1.py.bak"):
145 | print("rename: truthy")
--------------------------------------------------------------------------------
159 | print("getcwd: not iterable")
160 |
161 | try:
- for _ in os.getcwdb():
162 + for _ in pathlib.Path.cwd():
163 | print("getcwdb: iterable")
164 | break
165 | except TypeError as e:
note: This is an unsafe fix and may change runtime behavior
PTH115 [*] `os.readlink()` should be replaced by `Path.readlink()`
--> full_name.py:168:14
|
167 | try:
168 | for _ in os.readlink(sys.executable):
| ^^^^^^^^^^^
169 | print("readlink: iterable")
170 | break
|
help: Replace with `Path(...).readlink()`
139 | os.path.samefile("pth1_file", "pth1_link", 1, *[1], **{"x": 1}, foo=1)
140 |
141 | import sys
142 + import pathlib
143 |
144 | if os.rename("pth1.py", "pth1.py.bak"):
145 | print("rename: truthy")
--------------------------------------------------------------------------------
166 | print("getcwdb: not iterable")
167 |
168 | try:
- for _ in os.readlink(sys.executable):
169 + for _ in pathlib.Path(sys.executable).readlink():
170 | print("readlink: iterable")
171 | break
172 | except TypeError as e:
note: This is an unsafe fix and may change runtime behavior