From 87d0aa55614130adf6791d88e0dd0cb20282e54b Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sun, 5 Feb 2023 17:53:58 -0500 Subject: [PATCH] Move `python` into its own `ruff_python` crate (#2593) --- CONTRIBUTING.md | 1 + Cargo.lock | 10 +++ crates/ruff/Cargo.toml | 1 + crates/ruff/src/ast/mod.rs | 1 + crates/ruff/src/ast/typing.rs | 74 +++++++++++++++++++ crates/ruff/src/checkers/ast.rs | 21 ++++-- crates/ruff/src/lib.rs | 1 - .../rules/getattr_with_constant.rs | 4 +- .../rules/raise_without_from_inside_except.rs | 2 +- .../rules/setattr_with_constant.rs | 4 +- .../ruff/src/rules/flake8_builtins/rules.rs | 2 +- crates/ruff/src/rules/flake8_pie/rules.rs | 2 +- .../flake8_simplify/rules/yoda_conditions.rs | 2 +- crates/ruff/src/rules/isort/categorize.rs | 8 +- crates/ruff/src/rules/isort/sorting.rs | 2 +- crates/ruff/src/rules/pep8_naming/helpers.rs | 2 +- crates/ruff/src/rules/pep8_naming/rules.rs | 2 +- crates/ruff/src/rules/pyflakes/fixes.rs | 2 +- .../ruff/src/rules/pyflakes/rules/imports.rs | 2 +- ...convert_named_tuple_functional_to_class.rs | 4 +- .../convert_typed_dict_functional_to_class.rs | 4 +- .../rules/printf_string_formatting.rs | 4 +- crates/ruff/src/settings/types.rs | 3 +- crates/ruff_python/Cargo.toml | 11 +++ .../python => ruff_python/src}/builtins.rs | 0 .../src/python => ruff_python/src}/future.rs | 0 .../python => ruff_python/src}/identifiers.rs | 0 .../src/python => ruff_python/src}/keyword.rs | 0 .../python/mod.rs => ruff_python/src/lib.rs} | 0 .../src/python => ruff_python/src}/string.rs | 2 +- .../src/python => ruff_python/src}/sys.rs | 14 ++-- .../src/python => ruff_python/src}/typing.rs | 68 +---------------- scripts/generate_known_standard_library.py | 24 +++--- 33 files changed, 158 insertions(+), 119 deletions(-) create mode 100644 crates/ruff/src/ast/typing.rs create mode 100644 crates/ruff_python/Cargo.toml rename crates/{ruff/src/python => ruff_python/src}/builtins.rs (100%) rename crates/{ruff/src/python => ruff_python/src}/future.rs (100%) rename crates/{ruff/src/python => ruff_python/src}/identifiers.rs (100%) rename crates/{ruff/src/python => ruff_python/src}/keyword.rs (100%) rename crates/{ruff/src/python/mod.rs => ruff_python/src/lib.rs} (100%) rename crates/{ruff/src/python => ruff_python/src}/string.rs (96%) rename crates/{ruff/src/python => ruff_python/src}/sys.rs (99%) rename crates/{ruff/src/python => ruff_python/src}/typing.rs (74%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6911620ce1..d2d89f6d80 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -84,6 +84,7 @@ At time of writing, the repository includes the following crates: - `crates/ruff_cli`: binary crate containing Ruff's command-line interface. - `crates/ruff_dev`: binary crate containing utilities used in the development of Ruff itself (e.g., `cargo dev generate-all`). - `crates/ruff_macros`: library crate containing macros used by Ruff. +- `crates/ruff_python`: library crate implementing Python-specific functionality (e.g., lists of standard library modules by versionb). - `crates/flake8_to_ruff`: binary crate for generating Ruff configuration from Flake8 configuration. ### Example: Adding a new lint rule diff --git a/Cargo.lock b/Cargo.lock index c5e3b784ba..28c3e42a6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1929,6 +1929,7 @@ dependencies = [ "path-absolutize", "regex", "ruff_macros", + "ruff_python", "rustc-hash", "rustpython-ast", "rustpython-common", @@ -2018,6 +2019,15 @@ dependencies = [ "textwrap", ] +[[package]] +name = "ruff_python" +version = "0.0.241" +dependencies = [ + "once_cell", + "regex", + "rustc-hash", +] + [[package]] name = "rust-stemmers" version = "1.2.0" diff --git a/crates/ruff/Cargo.toml b/crates/ruff/Cargo.toml index c7f06ddd5b..00457588bc 100644 --- a/crates/ruff/Cargo.toml +++ b/crates/ruff/Cargo.toml @@ -40,6 +40,7 @@ once_cell = { version = "1.16.0" } path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] } regex = { version = "1.6.0" } ruff_macros = { version = "0.0.241", path = "../ruff_macros" } +ruff_python = { version = "0.0.241", path = "../ruff_python" } rustc-hash = { version = "1.1.0" } rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "adc23253e4b58980b407ba2760dbe61681d752fc" } rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "adc23253e4b58980b407ba2760dbe61681d752fc" } diff --git a/crates/ruff/src/ast/mod.rs b/crates/ruff/src/ast/mod.rs index bad57983e2..a62dbbdfb9 100644 --- a/crates/ruff/src/ast/mod.rs +++ b/crates/ruff/src/ast/mod.rs @@ -7,5 +7,6 @@ pub mod helpers; pub mod operations; pub mod relocate; pub mod types; +pub mod typing; pub mod visitor; pub mod whitespace; diff --git a/crates/ruff/src/ast/typing.rs b/crates/ruff/src/ast/typing.rs new file mode 100644 index 0000000000..fced01dcbb --- /dev/null +++ b/crates/ruff/src/ast/typing.rs @@ -0,0 +1,74 @@ +use rustpython_ast::{Expr, ExprKind}; + +use ruff_python::typing::{PEP_585_BUILTINS_ELIGIBLE, PEP_593_SUBSCRIPTS, SUBSCRIPTS}; + +use crate::ast::types::CallPath; + +pub enum Callable { + ForwardRef, + Cast, + NewType, + TypeVar, + NamedTuple, + TypedDict, + MypyExtension, +} + +pub enum SubscriptKind { + AnnotatedSubscript, + PEP593AnnotatedSubscript, +} + +pub fn match_annotated_subscript<'a, F>( + expr: &'a Expr, + resolve_call_path: F, + typing_modules: impl Iterator, +) -> Option +where + F: FnOnce(&'a Expr) -> Option>, +{ + if !matches!( + expr.node, + ExprKind::Name { .. } | ExprKind::Attribute { .. } + ) { + return None; + } + + resolve_call_path(expr).and_then(|call_path| { + if SUBSCRIPTS.contains(&call_path.as_slice()) { + return Some(SubscriptKind::AnnotatedSubscript); + } + if PEP_593_SUBSCRIPTS.contains(&call_path.as_slice()) { + return Some(SubscriptKind::PEP593AnnotatedSubscript); + } + + for module in typing_modules { + let module_call_path = module.split('.').collect::>(); + if call_path.starts_with(&module_call_path) { + for subscript in SUBSCRIPTS.iter() { + if call_path.last() == subscript.last() { + return Some(SubscriptKind::AnnotatedSubscript); + } + } + for subscript in PEP_593_SUBSCRIPTS.iter() { + if call_path.last() == subscript.last() { + return Some(SubscriptKind::PEP593AnnotatedSubscript); + } + } + } + } + + None + }) +} + +/// Returns `true` if `Expr` represents a reference to a typing object with a +/// PEP 585 built-in. +pub fn is_pep585_builtin<'a, F>(expr: &'a Expr, resolve_call_path: F) -> bool +where + F: FnOnce(&'a Expr) -> Option>, +{ + resolve_call_path(expr).map_or(false, |call_path| { + PEP_585_BUILTINS_ELIGIBLE.contains(&call_path.as_slice()) + }) +} diff --git a/crates/ruff/src/checkers/ast.rs b/crates/ruff/src/checkers/ast.rs index 314535395a..beee03bcbc 100644 --- a/crates/ruff/src/checkers/ast.rs +++ b/crates/ruff/src/checkers/ast.rs @@ -16,6 +16,9 @@ use rustpython_parser::ast::{ use rustpython_parser::parser; use smallvec::smallvec; +use ruff_python::builtins::{BUILTINS, MAGIC_GLOBALS}; +use ruff_python::typing::TYPING_EXTENSIONS; + use crate::ast::helpers::{binding_range, collect_call_path, extract_handler_names}; use crate::ast::operations::{extract_all_names, AllNamesFlags}; use crate::ast::relocate::relocate_expr; @@ -23,13 +26,11 @@ use crate::ast::types::{ Binding, BindingKind, CallPath, ClassDef, ExecutionContext, FunctionDef, Lambda, Node, Range, RefEquality, Scope, ScopeKind, }; +use crate::ast::typing::{match_annotated_subscript, Callable, SubscriptKind}; use crate::ast::visitor::{walk_excepthandler, Visitor}; -use crate::ast::{branch_detection, cast, helpers, operations, visitor}; +use crate::ast::{branch_detection, cast, helpers, operations, typing, visitor}; use crate::docstrings::definition::{Definition, DefinitionKind, Docstring, Documentable}; use crate::noqa::Directive; -use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS}; -use crate::python::typing; -use crate::python::typing::{Callable, SubscriptKind}; use crate::registry::{Diagnostic, Rule}; use crate::rules::{ flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except, flake8_boolean_trap, @@ -194,7 +195,7 @@ impl<'a> Checker<'a> { return true; } - if typing::TYPING_EXTENSIONS.contains(target) { + if TYPING_EXTENSIONS.contains(target) { if call_path.as_slice() == ["typing_extensions", target] { return true; } @@ -2086,7 +2087,7 @@ where || (self.settings.target_version >= PythonVersion::Py37 && self.annotations_future_enabled && self.in_annotation)) - && typing::is_pep585_builtin(self, expr) + && typing::is_pep585_builtin(expr, |expr| self.resolve_call_path(expr)) { pyupgrade::rules::use_pep585_annotation(self, expr); } @@ -2131,7 +2132,7 @@ where || (self.settings.target_version >= PythonVersion::Py37 && self.annotations_future_enabled && self.in_annotation)) - && typing::is_pep585_builtin(self, expr) + && typing::is_pep585_builtin(expr, |expr| self.resolve_call_path(expr)) { pyupgrade::rules::use_pep585_annotation(self, expr); } @@ -3399,7 +3400,11 @@ where self.in_subscript = true; visitor::walk_expr(self, expr); } else { - match typing::match_annotated_subscript(self, value) { + match match_annotated_subscript( + value, + |expr| self.resolve_call_path(expr), + self.settings.typing_modules.iter().map(String::as_str), + ) { Some(subscript) => { match subscript { // Ex) Optional[int] diff --git a/crates/ruff/src/lib.rs b/crates/ruff/src/lib.rs index d80f2c3470..75c30a207d 100644 --- a/crates/ruff/src/lib.rs +++ b/crates/ruff/src/lib.rs @@ -22,7 +22,6 @@ pub mod linter; pub mod logging; pub mod message; mod noqa; -mod python; pub mod registry; pub mod resolver; mod rule_redirects; diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/getattr_with_constant.rs b/crates/ruff/src/rules/flake8_bugbear/rules/getattr_with_constant.rs index 70d945809c..f6df05e628 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/getattr_with_constant.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/getattr_with_constant.rs @@ -3,11 +3,11 @@ use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::define_violation; use crate::fix::Fix; -use crate::python::identifiers::{is_identifier, is_mangled_private}; -use crate::python::keyword::KWLIST; use crate::registry::Diagnostic; use crate::violation::AlwaysAutofixableViolation; use ruff_macros::derive_message_formats; +use ruff_python::identifiers::{is_identifier, is_mangled_private}; +use ruff_python::keyword::KWLIST; use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location}; define_violation!( diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs b/crates/ruff/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs index e015f86ecf..fd78ddbf84 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/raise_without_from_inside_except.rs @@ -2,10 +2,10 @@ use crate::ast::types::Range; use crate::ast::visitor::Visitor; use crate::checkers::ast::Checker; use crate::define_violation; -use crate::python::string::is_lower; use crate::registry::Diagnostic; use crate::violation::Violation; use ruff_macros::derive_message_formats; +use ruff_python::string::is_lower; use rustpython_ast::{ExprKind, Stmt, StmtKind}; define_violation!( diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/setattr_with_constant.rs b/crates/ruff/src/rules/flake8_bugbear/rules/setattr_with_constant.rs index 8be13cfbe6..d6df9e2aa2 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/setattr_with_constant.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/setattr_with_constant.rs @@ -3,12 +3,12 @@ use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::define_violation; use crate::fix::Fix; -use crate::python::identifiers::{is_identifier, is_mangled_private}; -use crate::python::keyword::KWLIST; use crate::registry::Diagnostic; use crate::source_code::Stylist; use crate::violation::AlwaysAutofixableViolation; use ruff_macros::derive_message_formats; +use ruff_python::identifiers::{is_identifier, is_mangled_private}; +use ruff_python::keyword::KWLIST; use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location, Stmt, StmtKind}; define_violation!( diff --git a/crates/ruff/src/rules/flake8_builtins/rules.rs b/crates/ruff/src/rules/flake8_builtins/rules.rs index 5f799dde69..ec30a7c17e 100644 --- a/crates/ruff/src/rules/flake8_builtins/rules.rs +++ b/crates/ruff/src/rules/flake8_builtins/rules.rs @@ -1,10 +1,10 @@ use super::types::ShadowingType; use crate::ast::types::Range; use crate::define_violation; -use crate::python::builtins::BUILTINS; use crate::registry::{Diagnostic, DiagnosticKind}; use crate::violation::Violation; use ruff_macros::derive_message_formats; +use ruff_python::builtins::BUILTINS; use rustpython_ast::Located; define_violation!( diff --git a/crates/ruff/src/rules/flake8_pie/rules.rs b/crates/ruff/src/rules/flake8_pie/rules.rs index fba2317279..1c11ab4cbd 100644 --- a/crates/ruff/src/rules/flake8_pie/rules.rs +++ b/crates/ruff/src/rules/flake8_pie/rules.rs @@ -11,9 +11,9 @@ use crate::checkers::ast::Checker; use crate::define_violation; use crate::fix::Fix; use crate::message::Location; -use crate::python::identifiers::is_identifier; use crate::registry::Diagnostic; use crate::violation::{AlwaysAutofixableViolation, Violation}; +use ruff_python::identifiers::is_identifier; define_violation!( pub struct NoUnnecessaryPass; diff --git a/crates/ruff/src/rules/flake8_simplify/rules/yoda_conditions.rs b/crates/ruff/src/rules/flake8_simplify/rules/yoda_conditions.rs index f44a763542..3370665c86 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/yoda_conditions.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/yoda_conditions.rs @@ -2,11 +2,11 @@ use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::cst::matchers::{match_comparison, match_expression}; use crate::fix::Fix; -use crate::python::string::{self}; use crate::registry::Diagnostic; use crate::source_code::{Locator, Stylist}; use crate::violation::{Availability, Violation}; use crate::{define_violation, AutofixKind}; +use ruff_python::string::{self}; use anyhow::Result; use libcst_native::{Codegen, CodegenState, CompOp}; diff --git a/crates/ruff/src/rules/isort/categorize.rs b/crates/ruff/src/rules/isort/categorize.rs index e88c45493a..3360dbe037 100644 --- a/crates/ruff/src/rules/isort/categorize.rs +++ b/crates/ruff/src/rules/isort/categorize.rs @@ -6,10 +6,12 @@ use log::debug; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use super::types::{ImportBlock, Importable}; -use crate::python::sys::KNOWN_STANDARD_LIBRARY; +use ruff_python::sys::KNOWN_STANDARD_LIBRARY; + use crate::settings::types::PythonVersion; +use super::types::{ImportBlock, Importable}; + #[derive( Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Serialize, Deserialize, JsonSchema, Hash, )] @@ -58,7 +60,7 @@ pub fn categorize( } else if module_base == "__future__" { (ImportType::Future, Reason::Future) } else if KNOWN_STANDARD_LIBRARY - .get(&target_version) + .get(&target_version.as_tuple()) .unwrap() .contains(module_base) { diff --git a/crates/ruff/src/rules/isort/sorting.rs b/crates/ruff/src/rules/isort/sorting.rs index 1646366a50..1ef123d955 100644 --- a/crates/ruff/src/rules/isort/sorting.rs +++ b/crates/ruff/src/rules/isort/sorting.rs @@ -5,7 +5,7 @@ use std::collections::BTreeSet; use super::settings::RelativeImportsOrder; use super::types::EitherImport::{Import, ImportFrom}; use super::types::{AliasData, EitherImport, ImportFromData}; -use crate::python::string; +use ruff_python::string; #[derive(PartialOrd, Ord, PartialEq, Eq)] pub enum Prefix { diff --git a/crates/ruff/src/rules/pep8_naming/helpers.rs b/crates/ruff/src/rules/pep8_naming/helpers.rs index 6abd5e1e80..9def0d0575 100644 --- a/crates/ruff/src/rules/pep8_naming/helpers.rs +++ b/crates/ruff/src/rules/pep8_naming/helpers.rs @@ -2,7 +2,7 @@ use itertools::Itertools; use rustpython_ast::{Stmt, StmtKind}; use crate::checkers::ast::Checker; -use crate::python::string::{is_lower, is_upper}; +use ruff_python::string::{is_lower, is_upper}; pub fn is_camelcase(name: &str) -> bool { !is_lower(name) && !is_upper(name) && !name.contains('_') diff --git a/crates/ruff/src/rules/pep8_naming/rules.rs b/crates/ruff/src/rules/pep8_naming/rules.rs index 3c35e0d409..da6f5a720f 100644 --- a/crates/ruff/src/rules/pep8_naming/rules.rs +++ b/crates/ruff/src/rules/pep8_naming/rules.rs @@ -6,11 +6,11 @@ use crate::ast::helpers::identifier_range; use crate::ast::types::{Range, Scope, ScopeKind}; use crate::checkers::ast::Checker; use crate::define_violation; -use crate::python::string::{self}; use crate::registry::Diagnostic; use crate::source_code::Locator; use crate::violation::Violation; use ruff_macros::derive_message_formats; +use ruff_python::string::{self}; define_violation!( pub struct InvalidClassName { diff --git a/crates/ruff/src/rules/pyflakes/fixes.rs b/crates/ruff/src/rules/pyflakes/fixes.rs index 105abd2b71..9547858918 100644 --- a/crates/ruff/src/rules/pyflakes/fixes.rs +++ b/crates/ruff/src/rules/pyflakes/fixes.rs @@ -7,8 +7,8 @@ use rustpython_parser::lexer::Tok; use crate::ast::types::Range; use crate::cst::matchers::{match_expr, match_module}; use crate::fix::Fix; -use crate::python::string::strip_quotes_and_prefixes; use crate::source_code::{Locator, Stylist}; +use ruff_python::string::strip_quotes_and_prefixes; /// Generate a [`Fix`] to remove unused keys from format dict. pub fn remove_unused_format_arguments_from_dict( diff --git a/crates/ruff/src/rules/pyflakes/rules/imports.rs b/crates/ruff/src/rules/pyflakes/rules/imports.rs index 0f969cd53e..ab86e550c2 100644 --- a/crates/ruff/src/rules/pyflakes/rules/imports.rs +++ b/crates/ruff/src/rules/pyflakes/rules/imports.rs @@ -1,11 +1,11 @@ use crate::ast::types::Range; use crate::checkers::ast::Checker; -use crate::python::future::ALL_FEATURE_NAMES; use crate::registry::Diagnostic; use crate::violation::{Availability, Violation}; use crate::{define_violation, AutofixKind}; use itertools::Itertools; use ruff_macros::derive_message_formats; +use ruff_python::future::ALL_FEATURE_NAMES; use rustpython_ast::Alias; define_violation!( diff --git a/crates/ruff/src/rules/pyupgrade/rules/convert_named_tuple_functional_to_class.rs b/crates/ruff/src/rules/pyupgrade/rules/convert_named_tuple_functional_to_class.rs index c976568867..973d61def3 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/convert_named_tuple_functional_to_class.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/convert_named_tuple_functional_to_class.rs @@ -9,10 +9,10 @@ use crate::ast::helpers::{create_expr, create_stmt, unparse_stmt}; use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; -use crate::python::identifiers::is_identifier; -use crate::python::keyword::KWLIST; use crate::registry::Diagnostic; use crate::source_code::Stylist; +use ruff_python::identifiers::is_identifier; +use ruff_python::keyword::KWLIST; define_violation!( pub struct ConvertNamedTupleFunctionalToClass { diff --git a/crates/ruff/src/rules/pyupgrade/rules/convert_typed_dict_functional_to_class.rs b/crates/ruff/src/rules/pyupgrade/rules/convert_typed_dict_functional_to_class.rs index 2a21760ee8..6d81bd0bb1 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/convert_typed_dict_functional_to_class.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/convert_typed_dict_functional_to_class.rs @@ -9,10 +9,10 @@ use crate::ast::helpers::{create_expr, create_stmt, unparse_stmt}; use crate::ast::types::Range; use crate::checkers::ast::Checker; use crate::fix::Fix; -use crate::python::identifiers::is_identifier; -use crate::python::keyword::KWLIST; use crate::registry::Diagnostic; use crate::source_code::Stylist; +use ruff_python::identifiers::is_identifier; +use ruff_python::keyword::KWLIST; define_violation!( pub struct ConvertTypedDictFunctionalToClass { diff --git a/crates/ruff/src/rules/pyupgrade/rules/printf_string_formatting.rs b/crates/ruff/src/rules/pyupgrade/rules/printf_string_formatting.rs index f8350fa4d9..6156a98a7f 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/printf_string_formatting.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/printf_string_formatting.rs @@ -16,11 +16,11 @@ use crate::ast::types::Range; use crate::ast::whitespace::indentation; use crate::checkers::ast::Checker; use crate::fix::Fix; -use crate::python::identifiers::is_identifier; -use crate::python::keyword::KWLIST; use crate::registry::Diagnostic; use crate::rules::pydocstyle::helpers::{leading_quote, trailing_quote}; use crate::rules::pyupgrade::helpers::curly_escape; +use ruff_python::identifiers::is_identifier; +use ruff_python::keyword::KWLIST; define_violation!( pub struct PrintfStringFormatting; diff --git a/crates/ruff/src/settings/types.rs b/crates/ruff/src/settings/types.rs index 59755e12f8..d718456d68 100644 --- a/crates/ruff/src/settings/types.rs +++ b/crates/ruff/src/settings/types.rs @@ -10,11 +10,12 @@ use rustc_hash::FxHashSet; use schemars::JsonSchema; use serde::{de, Deserialize, Deserializer, Serialize}; -use super::hashable::HashableHashSet; use crate::registry::Rule; use crate::rule_selector::RuleSelector; use crate::{fs, warn_user_once}; +use super::hashable::HashableHashSet; + #[derive( Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize, Hash, JsonSchema, )] diff --git a/crates/ruff_python/Cargo.toml b/crates/ruff_python/Cargo.toml new file mode 100644 index 0000000000..6b8f002418 --- /dev/null +++ b/crates/ruff_python/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "ruff_python" +version = "0.0.241" +edition = "2021" + +[lib] + +[dependencies] +once_cell = { version = "1.16.0" } +regex = { version = "1.6.0" } +rustc-hash = { version = "1.1.0" } diff --git a/crates/ruff/src/python/builtins.rs b/crates/ruff_python/src/builtins.rs similarity index 100% rename from crates/ruff/src/python/builtins.rs rename to crates/ruff_python/src/builtins.rs diff --git a/crates/ruff/src/python/future.rs b/crates/ruff_python/src/future.rs similarity index 100% rename from crates/ruff/src/python/future.rs rename to crates/ruff_python/src/future.rs diff --git a/crates/ruff/src/python/identifiers.rs b/crates/ruff_python/src/identifiers.rs similarity index 100% rename from crates/ruff/src/python/identifiers.rs rename to crates/ruff_python/src/identifiers.rs diff --git a/crates/ruff/src/python/keyword.rs b/crates/ruff_python/src/keyword.rs similarity index 100% rename from crates/ruff/src/python/keyword.rs rename to crates/ruff_python/src/keyword.rs diff --git a/crates/ruff/src/python/mod.rs b/crates/ruff_python/src/lib.rs similarity index 100% rename from crates/ruff/src/python/mod.rs rename to crates/ruff_python/src/lib.rs diff --git a/crates/ruff/src/python/string.rs b/crates/ruff_python/src/string.rs similarity index 96% rename from crates/ruff/src/python/string.rs rename to crates/ruff_python/src/string.rs index 180433e31d..16499f2182 100644 --- a/crates/ruff/src/python/string.rs +++ b/crates/ruff_python/src/string.rs @@ -43,7 +43,7 @@ pub fn strip_quotes_and_prefixes(s: &str) -> &str { #[cfg(test)] mod tests { - use crate::python::string::{is_lower, is_upper, strip_quotes_and_prefixes}; + use crate::string::{is_lower, is_upper, strip_quotes_and_prefixes}; #[test] fn test_is_lower() { diff --git a/crates/ruff/src/python/sys.rs b/crates/ruff_python/src/sys.rs similarity index 99% rename from crates/ruff/src/python/sys.rs rename to crates/ruff_python/src/sys.rs index 2f4d3b7b7f..791b7db2d9 100644 --- a/crates/ruff/src/python/sys.rs +++ b/crates/ruff_python/src/sys.rs @@ -2,14 +2,12 @@ use once_cell::sync::Lazy; use rustc_hash::{FxHashMap, FxHashSet}; -use crate::settings::types::PythonVersion; - // See: https://pycqa.github.io/isort/docs/configuration/options.html#known-standard-library -pub static KNOWN_STANDARD_LIBRARY: Lazy>> = +pub static KNOWN_STANDARD_LIBRARY: Lazy>> = Lazy::new(|| { FxHashMap::from_iter([ ( - PythonVersion::Py37, + (3, 7), FxHashSet::from_iter([ "_ast", "_dummy_thread", @@ -230,7 +228,7 @@ pub static KNOWN_STANDARD_LIBRARY: Lazy> = Lazy::new(|| { @@ -62,7 +59,7 @@ pub static TYPING_EXTENSIONS: Lazy> = Lazy::new(|| { }); // See: https://docs.python.org/3/library/typing.html -const SUBSCRIPTS: &[&[&str]] = &[ +pub const SUBSCRIPTS: &[&[&str]] = &[ // builtins &["", "dict"], &["", "frozenset"], @@ -183,56 +180,15 @@ const SUBSCRIPTS: &[&[&str]] = &[ ]; // See: https://docs.python.org/3/library/typing.html -const PEP_593_SUBSCRIPTS: &[&[&str]] = &[ +pub const PEP_593_SUBSCRIPTS: &[&[&str]] = &[ // `typing` &["typing", "Annotated"], // `typing_extensions` &["typing_extensions", "Annotated"], ]; -pub enum SubscriptKind { - AnnotatedSubscript, - PEP593AnnotatedSubscript, -} - -pub fn match_annotated_subscript(checker: &Checker, expr: &Expr) -> Option { - if !matches!( - expr.node, - ExprKind::Name { .. } | ExprKind::Attribute { .. } - ) { - return None; - } - - checker.resolve_call_path(expr).and_then(|call_path| { - if SUBSCRIPTS.contains(&call_path.as_slice()) { - return Some(SubscriptKind::AnnotatedSubscript); - } - if PEP_593_SUBSCRIPTS.contains(&call_path.as_slice()) { - return Some(SubscriptKind::PEP593AnnotatedSubscript); - } - - for module in &checker.settings.typing_modules { - let module_call_path = module.split('.').collect::>(); - if call_path.starts_with(&module_call_path) { - for subscript in SUBSCRIPTS.iter() { - if call_path.last() == subscript.last() { - return Some(SubscriptKind::AnnotatedSubscript); - } - } - for subscript in PEP_593_SUBSCRIPTS.iter() { - if call_path.last() == subscript.last() { - return Some(SubscriptKind::PEP593AnnotatedSubscript); - } - } - } - } - - None - }) -} - // See: https://peps.python.org/pep-0585/ -const PEP_585_BUILTINS_ELIGIBLE: &[&[&str]] = &[ +pub const PEP_585_BUILTINS_ELIGIBLE: &[&[&str]] = &[ &["typing", "Dict"], &["typing", "FrozenSet"], &["typing", "List"], @@ -241,21 +197,3 @@ const PEP_585_BUILTINS_ELIGIBLE: &[&[&str]] = &[ &["typing", "Type"], &["typing_extensions", "Type"], ]; - -/// Returns `true` if `Expr` represents a reference to a typing object with a -/// PEP 585 built-in. -pub fn is_pep585_builtin(checker: &Checker, expr: &Expr) -> bool { - checker.resolve_call_path(expr).map_or(false, |call_path| { - PEP_585_BUILTINS_ELIGIBLE.contains(&call_path.as_slice()) - }) -} - -pub enum Callable { - ForwardRef, - Cast, - NewType, - TypeVar, - NamedTuple, - TypedDict, - MypyExtension, -} diff --git a/scripts/generate_known_standard_library.py b/scripts/generate_known_standard_library.py index ce9437eb0d..7133c71a84 100644 --- a/scripts/generate_known_standard_library.py +++ b/scripts/generate_known_standard_library.py @@ -8,13 +8,13 @@ from pathlib import Path from sphinx.ext.intersphinx import fetch_inventory URL = "https://docs.python.org/{}/objects.inv" -PATH = Path("src") / "python" / "sys.rs" -VERSIONS = [ - ("3", "7"), - ("3", "8"), - ("3", "9"), - ("3", "10"), - ("3", "11"), +PATH = Path("crates") / "ruff_python" / "src" / "sys.rs" +VERSIONS: list[tuple[int, int]] = [ + (3, 7), + (3, 8), + (3, 9), + (3, 10), + (3, 11), ] @@ -36,16 +36,14 @@ with PATH.open("w") as f: use once_cell::sync::Lazy; use rustc_hash::{FxHashMap, FxHashSet}; -use crate::settings::types::PythonVersion; - // See: https://pycqa.github.io/isort/docs/configuration/options.html#known-standard-library -pub static KNOWN_STANDARD_LIBRARY: Lazy>> = +pub static KNOWN_STANDARD_LIBRARY: Lazy>> = Lazy::new(|| { FxHashMap::from_iter([ """, ) - for version_info in VERSIONS: - version = ".".join(version_info) + for (major, minor) in VERSIONS: + version = f"{major}.{minor}" url = URL.format(version) invdata = fetch_inventory(FakeApp(), "", url) @@ -67,7 +65,7 @@ pub static KNOWN_STANDARD_LIBRARY: Lazy