Skip to content

Commit 5b4afd3

Browse files
authored
Harmonise methods for distinguishing different Python source types (#13682)
1 parent b9827a4 commit 5b4afd3

File tree

9 files changed

+40
-53
lines changed

9 files changed

+40
-53
lines changed

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ruff_dev/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ ruff_python_ast = { workspace = true }
2020
ruff_python_codegen = { workspace = true }
2121
ruff_python_formatter = { workspace = true }
2222
ruff_python_parser = { workspace = true }
23-
ruff_python_stdlib = { workspace = true }
2423
ruff_python_trivia = { workspace = true }
2524
ruff_workspace = { workspace = true, features = ["schemars"] }
2625

crates/ruff_dev/src/round_trip.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use std::path::PathBuf;
66

77
use anyhow::Result;
88

9+
use ruff_python_ast::PySourceType;
910
use ruff_python_codegen::round_trip;
10-
use ruff_python_stdlib::path::is_jupyter_notebook;
1111

1212
#[derive(clap::Args)]
1313
pub(crate) struct Args {
@@ -18,7 +18,7 @@ pub(crate) struct Args {
1818

1919
pub(crate) fn main(args: &Args) -> Result<()> {
2020
let path = args.file.as_path();
21-
if is_jupyter_notebook(path) {
21+
if PySourceType::from(path).is_ipynb() {
2222
println!("{}", ruff_notebook::round_trip(path)?);
2323
} else {
2424
let contents = fs::read_to_string(&args.file)?;

crates/ruff_linter/src/rules/flake8_builtins/rules/builtin_module_shadowing.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::path::Path;
22

33
use ruff_diagnostics::{Diagnostic, Violation};
44
use ruff_macros::{derive_message_formats, violation};
5+
use ruff_python_ast::PySourceType;
56
use ruff_python_stdlib::path::is_module_file;
67
use ruff_python_stdlib::sys::is_known_standard_library;
78
use ruff_text_size::TextRange;
@@ -42,10 +43,7 @@ pub(crate) fn builtin_module_shadowing(
4243
allowed_modules: &[String],
4344
target_version: PythonVersion,
4445
) -> Option<Diagnostic> {
45-
if !path
46-
.extension()
47-
.is_some_and(|ext| ext == "py" || ext == "pyi")
48-
{
46+
if !PySourceType::try_from_path(path).is_some_and(PySourceType::is_py_file_or_stub) {
4947
return None;
5048
}
5149

crates/ruff_linter/src/rules/flake8_no_pep420/rules/implicit_namespace_package.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::path::{Path, PathBuf};
22

33
use ruff_diagnostics::{Diagnostic, Violation};
44
use ruff_macros::{derive_message_formats, violation};
5+
use ruff_python_ast::PySourceType;
56
use ruff_python_trivia::CommentRanges;
67
use ruff_source_file::Locator;
78
use ruff_text_size::{TextRange, TextSize};
@@ -51,7 +52,7 @@ pub(crate) fn implicit_namespace_package(
5152
) -> Option<Diagnostic> {
5253
if package.is_none()
5354
// Ignore non-`.py` files, which don't require an `__init__.py`.
54-
&& path.extension().is_some_and( |ext| ext == "py")
55+
&& PySourceType::try_from_path(path).is_some_and(PySourceType::is_py_file)
5556
// Ignore any files that are direct children of the project root.
5657
&& !path
5758
.parent()

crates/ruff_linter/src/rules/pep8_naming/rules/invalid_module_name.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::path::Path;
33

44
use ruff_diagnostics::{Diagnostic, Violation};
55
use ruff_macros::{derive_message_formats, violation};
6+
use ruff_python_ast::PySourceType;
67
use ruff_python_stdlib::identifiers::{is_migration_name, is_module_name};
78
use ruff_python_stdlib::path::is_module_file;
89
use ruff_text_size::TextRange;
@@ -53,10 +54,7 @@ pub(crate) fn invalid_module_name(
5354
package: Option<&Path>,
5455
ignore_names: &IgnoreNames,
5556
) -> Option<Diagnostic> {
56-
if !path
57-
.extension()
58-
.is_some_and(|ext| ext == "py" || ext == "pyi")
59-
{
57+
if !PySourceType::try_from_path(path).is_some_and(PySourceType::is_py_file_or_stub) {
6058
return None;
6159
}
6260

crates/ruff_python_ast/src/lib.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ pub enum TomlSourceType {
6868
Unrecognized,
6969
}
7070

71-
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, is_macro::Is)]
71+
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
7272
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7373
pub enum PySourceType {
7474
/// The source is a Python file (`.py`).
@@ -99,13 +99,33 @@ impl PySourceType {
9999

100100
Some(ty)
101101
}
102-
}
103102

104-
impl<P: AsRef<Path>> From<P> for PySourceType {
105-
fn from(path: P) -> Self {
103+
pub fn try_from_path(path: impl AsRef<Path>) -> Option<Self> {
106104
path.as_ref()
107105
.extension()
108106
.and_then(OsStr::to_str)
109-
.map_or(Self::Python, Self::from_extension)
107+
.and_then(Self::try_from_extension)
108+
}
109+
110+
pub const fn is_py_file(self) -> bool {
111+
matches!(self, Self::Python)
112+
}
113+
114+
pub const fn is_stub(self) -> bool {
115+
matches!(self, Self::Stub)
116+
}
117+
118+
pub const fn is_py_file_or_stub(self) -> bool {
119+
matches!(self, Self::Python | Self::Stub)
120+
}
121+
122+
pub const fn is_ipynb(self) -> bool {
123+
matches!(self, Self::Ipynb)
124+
}
125+
}
126+
127+
impl<P: AsRef<Path>> From<P> for PySourceType {
128+
fn from(path: P) -> Self {
129+
Self::try_from_path(path).unwrap_or_default()
110130
}
111131
}

crates/ruff_python_semantic/src/model.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ use rustc_hash::FxHashMap;
55

66
use ruff_python_ast::helpers::from_relative_import;
77
use ruff_python_ast::name::{QualifiedName, UnqualifiedName};
8-
use ruff_python_ast::{self as ast, Expr, ExprContext, Operator, Stmt};
9-
use ruff_python_stdlib::path::is_python_stub_file;
8+
use ruff_python_ast::{self as ast, Expr, ExprContext, Operator, PySourceType, Stmt};
109
use ruff_text_size::{Ranged, TextRange, TextSize};
1110

1211
use crate::binding::{
@@ -2246,7 +2245,7 @@ bitflags! {
22462245

22472246
impl SemanticModelFlags {
22482247
pub fn new(path: &Path) -> Self {
2249-
if is_python_stub_file(path) {
2248+
if PySourceType::from(path).is_stub() {
22502249
Self::STUB_FILE
22512250
} else {
22522251
Self::default()
Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::ffi::OsStr;
12
use std::path::Path;
23

34
/// Return `true` if the [`Path`] is named `pyproject.toml`.
@@ -6,38 +7,10 @@ pub fn is_pyproject_toml(path: &Path) -> bool {
67
.is_some_and(|name| name == "pyproject.toml")
78
}
89

9-
/// Return `true` if the [`Path`] appears to be that of a Python interface definition file (`.pyi`).
10-
pub fn is_python_stub_file(path: &Path) -> bool {
11-
path.extension().is_some_and(|ext| ext == "pyi")
12-
}
13-
14-
/// Return `true` if the [`Path`] appears to be that of a Jupyter notebook (`.ipynb`).
15-
pub fn is_jupyter_notebook(path: &Path) -> bool {
16-
path.extension().is_some_and(|ext| ext == "ipynb")
17-
}
18-
1910
/// Return `true` if a [`Path`] should use the name of its parent directory as its module name.
2011
pub fn is_module_file(path: &Path) -> bool {
21-
path.file_name().is_some_and(|file_name| {
22-
file_name == "__init__.py"
23-
|| file_name == "__init__.pyi"
24-
|| file_name == "__main__.py"
25-
|| file_name == "__main__.pyi"
26-
})
27-
}
28-
29-
#[cfg(test)]
30-
mod tests {
31-
use std::path::Path;
32-
33-
use crate::path::is_jupyter_notebook;
34-
35-
#[test]
36-
fn test_is_jupyter_notebook() {
37-
let path = Path::new("foo/bar/baz.ipynb");
38-
assert!(is_jupyter_notebook(path));
39-
40-
let path = Path::new("foo/bar/baz.py");
41-
assert!(!is_jupyter_notebook(path));
42-
}
12+
matches!(
13+
path.file_name().and_then(OsStr::to_str),
14+
Some("__init__.py" | "__init__.pyi" | "__main__.py" | "__main__.pyi")
15+
)
4316
}

0 commit comments

Comments
 (0)