diff --git a/crates/ruff/src/ast/context.rs b/crates/ruff/src/ast/context.rs index 9c08a1b17a..108360b88b 100644 --- a/crates/ruff/src/ast/context.rs +++ b/crates/ruff/src/ast/context.rs @@ -5,12 +5,12 @@ use rustc_hash::FxHashMap; use rustpython_parser::ast::{Expr, Stmt}; use smallvec::smallvec; +use ruff_python_stdlib::path::is_python_stub_file; use ruff_python_stdlib::typing::TYPING_EXTENSIONS; use crate::ast::helpers::{collect_call_path, from_relative_import, Exceptions}; use crate::ast::types::{Binding, BindingKind, CallPath, ExecutionContext, RefEquality, Scope}; use crate::ast::visibility::{module_visibility, Modifier, VisibleScope}; -use crate::resolver::is_interface_definition_path; #[allow(clippy::struct_excessive_bools)] pub struct Context<'a> { @@ -82,7 +82,7 @@ impl<'a> Context<'a> { in_type_checking_block: false, seen_import_boundary: false, futures_allowed: true, - annotations_future_enabled: is_interface_definition_path(path), + annotations_future_enabled: is_python_stub_file(path), handled_exceptions: Vec::default(), } } diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index 7cc70af22f..bf8da6f22d 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -14,6 +14,7 @@ use rustpython_parser::ast::{ }; use ruff_python_stdlib::builtins::{BUILTINS, MAGIC_GLOBALS}; +use ruff_python_stdlib::path::is_python_stub_file; use crate::ast::context::Context; use crate::ast::helpers::{binding_range, extract_handled_exceptions, to_module_path, Exceptions}; @@ -31,7 +32,6 @@ use crate::docstrings::definition::{ transition_scope, Definition, DefinitionKind, Docstring, Documentable, }; use crate::registry::{Diagnostic, Rule}; -use crate::resolver::is_interface_definition_path; use crate::rules::{ flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except, flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_datetimez, flake8_debugger, @@ -58,7 +58,7 @@ pub struct Checker<'a> { pub path: &'a Path, module_path: Option>, package: Option<&'a Path>, - is_interface_definition: bool, + is_stub: bool, autofix: flags::Autofix, noqa: flags::Noqa, pub settings: &'a Settings, @@ -97,7 +97,7 @@ impl<'a> Checker<'a> { path, package, module_path: module_path.clone(), - is_interface_definition: is_interface_definition_path(path), + is_stub: is_python_stub_file(path), locator, stylist: style, indexer, @@ -378,7 +378,7 @@ where } } - if self.is_interface_definition { + if self.is_stub { if self.settings.rules.enabled(&Rule::PassStatementStubBody) { flake8_pyi::rules::pass_statement_stub_body(self, body); } @@ -756,7 +756,7 @@ where flake8_bugbear::rules::useless_expression(self, body); } - if !self.is_interface_definition { + if !self.is_stub { if self .settings .rules @@ -771,7 +771,7 @@ where ); } } - if self.is_interface_definition { + if self.is_stub { if self.settings.rules.enabled(&Rule::PassStatementStubBody) { flake8_pyi::rules::pass_statement_stub_body(self, body); } @@ -1805,7 +1805,7 @@ where } } - if self.is_interface_definition { + if self.is_stub { if self.settings.rules.enabled(&Rule::PrefixTypeParams) { flake8_pyi::rules::prefix_type_params(self, value, targets); } @@ -3301,7 +3301,7 @@ where flake8_simplify::rules::yoda_conditions(self, expr, left, ops, comparators); } - if self.is_interface_definition { + if self.is_stub { if self .settings .rules @@ -3886,7 +3886,7 @@ where flake8_bugbear::rules::function_call_argument_default(self, arguments); } - if self.is_interface_definition { + if self.is_stub { if self .settings .rules @@ -3895,7 +3895,7 @@ where flake8_pyi::rules::typed_argument_simple_defaults(self, arguments); } } - if self.is_interface_definition { + if self.is_stub { if self.settings.rules.enabled(&Rule::ArgumentSimpleDefaults) { flake8_pyi::rules::argument_simple_defaults(self, arguments); } @@ -4710,7 +4710,7 @@ impl<'a> Checker<'a> { } fn check_dead_scopes(&mut self) { - let enforce_typing_imports = !self.is_interface_definition + let enforce_typing_imports = !self.is_stub && (self .settings .rules @@ -5241,7 +5241,7 @@ impl<'a> Checker<'a> { } overloaded_name = flake8_annotations::helpers::overloaded_name(self, &definition); } - if self.is_interface_definition { + if self.is_stub { if self.settings.rules.enabled(&Rule::DocstringInStub) { flake8_pyi::rules::docstring_in_stubs(self, definition.docstring); } diff --git a/crates/ruff/src/checkers/tokens.rs b/crates/ruff/src/checkers/tokens.rs index a8f8e0c2d3..bc5d3c2576 100644 --- a/crates/ruff/src/checkers/tokens.rs +++ b/crates/ruff/src/checkers/tokens.rs @@ -18,7 +18,7 @@ pub fn check_tokens( tokens: &[LexResult], settings: &Settings, autofix: flags::Autofix, - is_interface_definition: bool, + is_stub: bool, ) -> Vec { let mut diagnostics: Vec = vec![]; @@ -164,7 +164,7 @@ pub fn check_tokens( } // PYI033 - if enforce_type_comment_in_stub && is_interface_definition { + if enforce_type_comment_in_stub && is_stub { diagnostics.extend(flake8_pyi::rules::type_comment_in_stub(tokens)); } diff --git a/crates/ruff/src/linter.rs b/crates/ruff/src/linter.rs index 94b62c61e8..3221c4f83f 100644 --- a/crates/ruff/src/linter.rs +++ b/crates/ruff/src/linter.rs @@ -4,6 +4,7 @@ use std::path::Path; use anyhow::{anyhow, Result}; use colored::Colorize; use log::error; +use ruff_python_stdlib::path::is_python_stub_file; use rustc_hash::FxHashMap; use rustpython_parser::lexer::LexResult; use rustpython_parser::ParseError; @@ -21,7 +22,6 @@ use crate::doc_lines::{doc_lines_from_ast, doc_lines_from_tokens}; use crate::message::{Message, Source}; use crate::noqa::{add_noqa, rule_is_ignored}; use crate::registry::{Diagnostic, Rule}; -use crate::resolver::is_interface_definition_path; use crate::rules::pycodestyle; use crate::settings::{flags, Settings}; use crate::source_code::{Indexer, Locator, Stylist}; @@ -84,14 +84,8 @@ pub fn check_path( .iter_enabled() .any(|rule_code| rule_code.lint_source().is_tokens()) { - let is_interface_definition = is_interface_definition_path(path); - diagnostics.extend(check_tokens( - locator, - &tokens, - settings, - autofix, - is_interface_definition, - )); + let is_stub = is_python_stub_file(path); + diagnostics.extend(check_tokens(locator, &tokens, settings, autofix, is_stub)); } // Run the filesystem-based rules. diff --git a/crates/ruff/src/resolver.rs b/crates/ruff/src/resolver.rs index 9206e36c51..421aa309c2 100644 --- a/crates/ruff/src/resolver.rs +++ b/crates/ruff/src/resolver.rs @@ -10,6 +10,7 @@ use ignore::{DirEntry, WalkBuilder, WalkState}; use itertools::Itertools; use log::debug; use path_absolutize::path_dedot; +use ruff_python_stdlib::path::is_python_file; use rustc_hash::FxHashSet; use crate::fs; @@ -191,20 +192,9 @@ fn match_exclusion(file_path: &str, file_basename: &str, exclusion: &globset::Gl exclusion.is_match(file_path) || exclusion.is_match(file_basename) } -/// Return `true` if the [`Path`] appears to be that of a Python file. -fn is_python_path(path: &Path) -> bool { - path.extension() - .map_or(false, |ext| ext == "py" || ext == "pyi") -} - -/// Return `true` if the [`Path`] appears to be that of a Python interface definition file (`.pyi`). -pub fn is_interface_definition_path(path: &Path) -> bool { - path.extension().map_or(false, |ext| ext == "pyi") -} - /// Return `true` if the [`DirEntry`] appears to be that of a Python file. pub fn is_python_entry(entry: &DirEntry) -> bool { - is_python_path(entry.path()) + is_python_file(entry.path()) && !entry .file_type() .map_or(false, |file_type| file_type.is_dir()) @@ -430,28 +420,13 @@ mod tests { use crate::fs; use crate::resolver::{ - is_file_excluded, is_python_path, match_exclusion, resolve_settings_with_processor, - NoOpProcessor, PyprojectDiscovery, Relativity, Resolver, + is_file_excluded, match_exclusion, resolve_settings_with_processor, NoOpProcessor, + PyprojectDiscovery, Relativity, Resolver, }; use crate::settings::pyproject::find_settings_toml; use crate::settings::types::FilePattern; use crate::test::test_resource_path; - #[test] - fn inclusions() { - let path = Path::new("foo/bar/baz.py").absolutize().unwrap(); - assert!(is_python_path(&path)); - - let path = Path::new("foo/bar/baz.pyi").absolutize().unwrap(); - assert!(is_python_path(&path)); - - let path = Path::new("foo/bar/baz.js").absolutize().unwrap(); - assert!(!is_python_path(&path)); - - let path = Path::new("foo/bar/baz").absolutize().unwrap(); - assert!(!is_python_path(&path)); - } - fn make_exclusion(file_pattern: FilePattern) -> GlobSet { let mut builder = globset::GlobSetBuilder::new(); file_pattern.add_to(&mut builder).unwrap(); diff --git a/crates/ruff/src/rules/isort/track.rs b/crates/ruff/src/rules/isort/track.rs index 25d3255b6a..143a75a7a2 100644 --- a/crates/ruff/src/rules/isort/track.rs +++ b/crates/ruff/src/rules/isort/track.rs @@ -1,5 +1,6 @@ use std::path::Path; +use ruff_python_stdlib::path::is_python_stub_file; use rustpython_parser::ast::{ Alias, Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, Keyword, MatchCase, Operator, Pattern, Stmt, StmtKind, @@ -9,7 +10,6 @@ use rustpython_parser::ast::{ use super::helpers; use crate::ast::visitor::Visitor; use crate::directives::IsortDirectives; -use crate::resolver::is_interface_definition_path; use crate::source_code::Locator; #[derive(Debug)] @@ -29,7 +29,7 @@ pub struct Block<'a> { pub struct ImportTracker<'a> { locator: &'a Locator<'a>, directives: &'a IsortDirectives, - pyi: bool, + is_stub: bool, blocks: Vec>, split_index: usize, nested: bool, @@ -40,7 +40,7 @@ impl<'a> ImportTracker<'a> { Self { locator, directives, - pyi: is_interface_definition_path(path), + is_stub: is_python_stub_file(path), blocks: vec![Block::default()], split_index: 0, nested: false, @@ -65,7 +65,7 @@ impl<'a> ImportTracker<'a> { return None; } - Some(if self.pyi { + Some(if self.is_stub { // Black treats interface files differently, limiting to one newline // (`Trailing::Sibling`). Trailer::Sibling diff --git a/crates/ruff_python_stdlib/src/lib.rs b/crates/ruff_python_stdlib/src/lib.rs index 26696bd2a2..daa9776582 100644 --- a/crates/ruff_python_stdlib/src/lib.rs +++ b/crates/ruff_python_stdlib/src/lib.rs @@ -3,6 +3,7 @@ pub mod bytes; pub mod future; pub mod identifiers; pub mod keyword; +pub mod path; pub mod str; pub mod sys; pub mod typing; diff --git a/crates/ruff_python_stdlib/src/path.rs b/crates/ruff_python_stdlib/src/path.rs new file mode 100644 index 0000000000..2c48f44c2d --- /dev/null +++ b/crates/ruff_python_stdlib/src/path.rs @@ -0,0 +1,34 @@ +use std::path::Path; + +/// Return `true` if the [`Path`] appears to be that of a Python file. +pub fn is_python_file(path: &Path) -> bool { + path.extension() + .map_or(false, |ext| ext == "py" || ext == "pyi") +} + +/// Return `true` if the [`Path`] appears to be that of a Python interface definition file (`.pyi`). +pub fn is_python_stub_file(path: &Path) -> bool { + path.extension().map_or(false, |ext| ext == "pyi") +} + +#[cfg(test)] +mod tests { + use std::path::Path; + + use crate::path::is_python_file; + + #[test] + fn inclusions() { + let path = Path::new("foo/bar/baz.py"); + assert!(is_python_file(path)); + + let path = Path::new("foo/bar/baz.pyi"); + assert!(is_python_file(path)); + + let path = Path::new("foo/bar/baz.js"); + assert!(!is_python_file(path)); + + let path = Path::new("foo/bar/baz"); + assert!(!is_python_file(path)); + } +}