Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from pathlib import (
Path,
PosixPath,
PurePath,
PurePosixPath,
PureWindowsPath,
WindowsPath,
)
import pathlib


path = Path()
posix_path: pathlib.PosixPath = PosixPath()
pure_path: PurePath = PurePath()
pure_posix_path = pathlib.PurePosixPath()
pure_windows_path: PureWindowsPath = pathlib.PureWindowsPath()
windows_path: pathlib.WindowsPath = pathlib.WindowsPath()


### No Errors
path.with_suffix(".")
posix_path.with_suffix(".")
pure_path.with_suffix(".")
pure_posix_path.with_suffix(".")
pure_windows_path.with_suffix(".")
windows_path.with_suffix(".")
19 changes: 19 additions & 0 deletions crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod tests {
use std::path::Path;

use anyhow::Result;
use ruff_python_ast::PythonVersion;
use test_case::test_case;

use crate::assert_diagnostics;
Expand Down Expand Up @@ -143,4 +144,22 @@ mod tests {
assert_diagnostics!(snapshot, diagnostics);
Ok(())
}

#[test_case(Rule::InvalidPathlibWithSuffix, Path::new("PTH210_2.py"))]
fn pathlib_with_suffix_py314(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!(
"py314__{}_{}",
rule_code.noqa_code(),
path.to_string_lossy()
);
let diagnostics = test_path(
Path::new("flake8_use_pathlib").join(path).as_path(),
&settings::LinterSettings {
unresolved_target_version: PythonVersion::PY314.into(),
..settings::LinterSettings::for_rule(rule_code)
},
)?;
assert_diagnostics!(snapshot, diagnostics);
Ok(())
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
use crate::checkers::ast::Checker;
use crate::{Edit, Fix, FixAvailability, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, StringFlags};
use ruff_python_ast::{self as ast, PythonVersion, StringFlags};
use ruff_python_semantic::SemanticModel;
use ruff_python_semantic::analyze::typing;
use ruff_text_size::Ranged;

/// ## What it does
/// Checks for `pathlib.Path.with_suffix()` calls where
/// the given suffix does not have a leading dot
/// or the given suffix is a single dot `"."`.
/// or the given suffix is a single dot `"."` and the
/// Python version is less than 3.14.
///
/// ## Why is this bad?
/// `Path.with_suffix()` will raise an error at runtime
/// if the given suffix is not prefixed with a dot
/// or it is a single dot `"."`.
/// or, in versions prior to Python 3.14, if it is a single dot `"."`.
///
/// ## Example
///
Expand Down Expand Up @@ -57,9 +58,6 @@ use ruff_text_size::Ranged;
/// No fix is offered if the suffix `"."` is given, since the intent is unclear.
#[derive(ViolationMetadata)]
pub(crate) struct InvalidPathlibWithSuffix {
// TODO: Since "." is a correct suffix in Python 3.14,
// we will need to update this rule and documentation
// once Ruff supports Python 3.14.
single_dot: bool,
}

Expand Down Expand Up @@ -116,6 +114,13 @@ pub(crate) fn invalid_pathlib_with_suffix(checker: &Checker, call: &ast::ExprCal
};

let single_dot = string_value == ".";

// As of Python 3.14, a single dot is considered a valid suffix.
// https://docs.python.org/3.14/library/pathlib.html#pathlib.PurePath.with_suffix
if single_dot && checker.target_version() >= PythonVersion::PY314 {
return;
}

let mut diagnostic =
checker.report_diagnostic(InvalidPathlibWithSuffix { single_dot }, call.range);
if !single_dot {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs
---

Loading