diff --git a/crates/ruff_linter/src/preview.rs b/crates/ruff_linter/src/preview.rs index 836ba4feea..342f35ae93 100644 --- a/crates/ruff_linter/src/preview.rs +++ b/crates/ruff_linter/src/preview.rs @@ -254,6 +254,11 @@ pub(crate) const fn is_b006_check_guaranteed_mutable_expr_enabled( settings.preview.is_enabled() } +// https://github.com/astral-sh/ruff/pull/21373 +pub(crate) const fn is_import_conventions_preview_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} + // github.com/astral-sh/ruff/issues/20004 pub(crate) const fn is_b006_unsafe_fix_preserve_assignment_expr_enabled( settings: &LinterSettings, diff --git a/crates/ruff_linter/src/rules/flake8_import_conventions/mod.rs b/crates/ruff_linter/src/rules/flake8_import_conventions/mod.rs index 96c6b00f65..aaeae94ed4 100644 --- a/crates/ruff_linter/src/rules/flake8_import_conventions/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_import_conventions/mod.rs @@ -12,20 +12,20 @@ mod tests { use crate::assert_diagnostics; use crate::registry::Rule; use crate::rules::flake8_import_conventions::settings::{BannedAliases, default_aliases}; - use crate::settings::LinterSettings; + use crate::settings::{LinterSettings, types::PreviewMode}; use crate::test::test_path; #[test] fn defaults() -> Result<()> { + let mut settings = LinterSettings { + flake8_import_conventions: super::settings::Settings::default(), + ..LinterSettings::for_rules([Rule::UnconventionalImportAlias, Rule::BannedImportAlias]) + }; + // Enable preview mode to test preview-only conventions + settings.preview = PreviewMode::Enabled; let diagnostics = test_path( Path::new("flake8_import_conventions/defaults.py"), - &LinterSettings { - flake8_import_conventions: super::settings::Settings::default(), - ..LinterSettings::for_rules([ - Rule::UnconventionalImportAlias, - Rule::BannedImportAlias, - ]) - }, + &settings, )?; assert_diagnostics!(diagnostics); Ok(()) diff --git a/crates/ruff_linter/src/rules/flake8_import_conventions/rules/banned_import_alias.rs b/crates/ruff_linter/src/rules/flake8_import_conventions/rules/banned_import_alias.rs index 080e5f3977..b64261437a 100644 --- a/crates/ruff_linter/src/rules/flake8_import_conventions/rules/banned_import_alias.rs +++ b/crates/ruff_linter/src/rules/flake8_import_conventions/rules/banned_import_alias.rs @@ -6,7 +6,8 @@ use ruff_text_size::Ranged; use crate::Violation; use crate::checkers::ast::Checker; -use crate::rules::flake8_import_conventions::settings::BannedAliases; +use crate::preview::is_import_conventions_preview_enabled; +use crate::rules::flake8_import_conventions::settings::{BannedAliases, preview_banned_aliases}; /// ## What it does /// Checks for imports that use non-standard naming conventions, like @@ -56,7 +57,17 @@ pub(crate) fn banned_import_alias( asname: &str, banned_conventions: &FxHashMap, ) { - if let Some(banned_aliases) = banned_conventions.get(name) { + // Merge preview banned aliases if preview mode is enabled + let banned_aliases = if is_import_conventions_preview_enabled(checker.settings()) { + banned_conventions.get(name).cloned().or_else(|| { + let preview_banned = preview_banned_aliases(); + preview_banned.get(name).cloned() + }) + } else { + banned_conventions.get(name).cloned() + }; + + if let Some(banned_aliases) = banned_aliases.as_ref() { if banned_aliases .iter() .any(|banned_alias| banned_alias == asname) diff --git a/crates/ruff_linter/src/rules/flake8_import_conventions/rules/unconventional_import_alias.rs b/crates/ruff_linter/src/rules/flake8_import_conventions/rules/unconventional_import_alias.rs index 6827e99b93..0d2f1b094d 100644 --- a/crates/ruff_linter/src/rules/flake8_import_conventions/rules/unconventional_import_alias.rs +++ b/crates/ruff_linter/src/rules/flake8_import_conventions/rules/unconventional_import_alias.rs @@ -5,6 +5,8 @@ use ruff_python_semantic::{Binding, Imported}; use ruff_text_size::Ranged; use crate::checkers::ast::Checker; +use crate::preview::is_import_conventions_preview_enabled; +use crate::rules::flake8_import_conventions::settings::preview_aliases; use crate::{Fix, FixAvailability, Violation}; use crate::renamer::Renamer; @@ -66,12 +68,26 @@ pub(crate) fn unconventional_import_alias( return; }; let qualified_name = import.qualified_name().to_string(); - let Some(expected_alias) = conventions.get(qualified_name.as_str()) else { + + // Merge preview conventions if preview mode is enabled + let expected_alias = if is_import_conventions_preview_enabled(checker.settings()) { + conventions + .get(qualified_name.as_str()) + .cloned() + .or_else(|| { + let preview_aliases_map = preview_aliases(); + preview_aliases_map.get(qualified_name.as_str()).cloned() + }) + } else { + conventions.get(qualified_name.as_str()).cloned() + }; + + let Some(expected_alias) = expected_alias else { return; }; let name = binding.name(checker.source()); - if name == expected_alias { + if name == expected_alias.as_str() { return; } @@ -83,12 +99,12 @@ pub(crate) fn unconventional_import_alias( binding.range(), ); if !import.is_submodule_import() { - if checker.semantic().is_available(expected_alias) { + if checker.semantic().is_available(&expected_alias) { diagnostic.try_set_fix(|| { let scope = &checker.semantic().scopes[binding.scope]; let (edit, rest) = Renamer::rename( name, - expected_alias, + &expected_alias, scope, checker.semantic(), checker.stylist(), diff --git a/crates/ruff_linter/src/rules/flake8_import_conventions/settings.rs b/crates/ruff_linter/src/rules/flake8_import_conventions/settings.rs index c8e4f947d0..53023c5e55 100644 --- a/crates/ruff_linter/src/rules/flake8_import_conventions/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_import_conventions/settings.rs @@ -18,9 +18,7 @@ const CONVENTIONAL_ALIASES: &[(&str, &str)] = &[ ("numpy.typing", "npt"), ("pandas", "pd"), ("plotly.express", "px"), - ("plotly.graph_objects", "go"), ("seaborn", "sns"), - ("statsmodels.api", "sm"), ("tensorflow", "tf"), ("tkinter", "tk"), ("holoviews", "hv"), @@ -30,6 +28,9 @@ const CONVENTIONAL_ALIASES: &[(&str, &str)] = &[ ("xml.etree.ElementTree", "ET"), ]; +const PREVIEW_ALIASES: &[(&str, &str)] = + &[("plotly.graph_objects", "go"), ("statsmodels.api", "sm")]; + #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, CacheKey)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] @@ -75,7 +76,14 @@ pub fn default_aliases() -> FxHashMap { .collect::>() } -pub fn default_banned_aliases() -> FxHashMap { +pub fn preview_aliases() -> FxHashMap { + PREVIEW_ALIASES + .iter() + .map(|(k, v)| ((*k).to_string(), (*v).to_string())) + .collect::>() +} + +pub fn preview_banned_aliases() -> FxHashMap { FxHashMap::from_iter([( "geopandas".to_string(), BannedAliases::from_iter(["gpd".to_string()]), @@ -86,7 +94,7 @@ impl Default for Settings { fn default() -> Self { Self { aliases: default_aliases(), - banned_aliases: default_banned_aliases(), + banned_aliases: FxHashMap::default(), banned_from: FxHashSet::default(), } } diff --git a/crates/ruff_workspace/src/options.rs b/crates/ruff_workspace/src/options.rs index c2e8f53a98..d90641cd30 100644 --- a/crates/ruff_workspace/src/options.rs +++ b/crates/ruff_workspace/src/options.rs @@ -1534,7 +1534,7 @@ pub struct Flake8ImportConventionsOptions { /// The conventional aliases for imports. These aliases can be extended by /// the [`extend-aliases`](#lint_flake8-import-conventions_extend-aliases) option. #[option( - default = r#"{"altair": "alt", "matplotlib": "mpl", "matplotlib.pyplot": "plt", "numpy": "np", "numpy.typing": "npt", "pandas": "pd", "plotly.express": "px", "plotly.graph_objects": "go", "seaborn": "sns", "statsmodels.api": "sm", "tensorflow": "tf", "tkinter": "tk", "holoviews": "hv", "panel": "pn", "polars": "pl", "pyarrow": "pa", "xml.etree.ElementTree": "ET"}"#, + default = r#"{"altair": "alt", "matplotlib": "mpl", "matplotlib.pyplot": "plt", "numpy": "np", "numpy.typing": "npt", "pandas": "pd", "plotly.express": "px", "seaborn": "sns", "tensorflow": "tf", "tkinter": "tk", "holoviews": "hv", "panel": "pn", "polars": "pl", "pyarrow": "pa", "xml.etree.ElementTree": "ET"}"#, value_type = "dict[str, str]", scope = "aliases", example = r#" @@ -1564,7 +1564,7 @@ pub struct Flake8ImportConventionsOptions { /// A mapping from module to its banned import aliases. #[option( - default = r#"{"geopandas": ["gpd"]}"#, + default = r#"{}"#, value_type = "dict[str, list[str]]", scope = "banned-aliases", example = r#" @@ -1687,7 +1687,7 @@ impl Flake8ImportConventionsOptions { aliases: normalized_aliases, banned_aliases: self .banned_aliases - .unwrap_or_else(flake8_import_conventions::settings::default_banned_aliases), + .unwrap_or_else(FxHashMap::default), banned_from: self.banned_from.unwrap_or_default(), }) }