diff --git a/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_no_tool_preview_enabled.snap b/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_no_tool_preview_enabled.snap index 3590199c4ddc0c..885ea0a77fa493 100644 --- a/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_no_tool_preview_enabled.snap +++ b/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_no_tool_preview_enabled.snap @@ -146,14 +146,18 @@ linter.flake8_import_conventions.aliases = { pandas = pd, panel = pn, plotly.express = px, + plotly.graph_objects = go, polars = pl, pyarrow = pa, seaborn = sns, + statsmodels.api = sm, tensorflow = tf, tkinter = tk, xml.etree.ElementTree = ET, } -linter.flake8_import_conventions.banned_aliases = {} +linter.flake8_import_conventions.banned_aliases = { + geopandas = [gpd], +} linter.flake8_import_conventions.banned_from = [] linter.flake8_pytest_style.fixture_parentheses = false linter.flake8_pytest_style.parametrize_names_type = tuple diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_import_conventions/defaults.py b/crates/ruff_linter/resources/test/fixtures/flake8_import_conventions/defaults.py index 0342d4203495f8..52a1163e5f433a 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_import_conventions/defaults.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_import_conventions/defaults.py @@ -30,3 +30,17 @@ def conventional_aliases(): import seaborn as sns import tkinter as tk import networkx as nx + + +# ICN001: plotly.graph_objects should be imported as go +import plotly.graph_objects # should require alias +import plotly.graph_objects as go # ok + +# ICN001: statsmodels.api should be imported as sm +import statsmodels.api # should require alias +import statsmodels.api as sm # ok + +# ICN002: geopandas should not be imported as gpd +import geopandas as gpd # banned +import geopandas # ok +import geopandas as gdf # ok diff --git a/crates/ruff_linter/src/preview.rs b/crates/ruff_linter/src/preview.rs index 05d4159a3d90a8..3cdb0e7d0f49fc 100644 --- a/crates/ruff_linter/src/preview.rs +++ b/crates/ruff_linter/src/preview.rs @@ -5,7 +5,7 @@ //! which specific feature this preview check is for. Having named functions simplifies the promotion: //! Simply delete the function and let Rust tell you which checks you have to remove. -use crate::settings::LinterSettings; +use crate::settings::{LinterSettings, types::PreviewMode}; // Rule-specific behavior @@ -297,3 +297,8 @@ pub(crate) const fn is_resolve_string_annotation_pyi041_enabled(settings: &Linte pub(crate) const fn is_baseloader_safe_in_yaml_load_enabled(settings: &LinterSettings) -> bool { settings.preview.is_enabled() } + +// https://github.com/astral-sh/ruff/pull/21373 +pub(crate) const fn is_expanded_import_conventions_enabled(preview: PreviewMode) -> bool { + preview.is_enabled() +} 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 386e35035cc38b..f36daca226e0d2 100644 --- a/crates/ruff_linter/src/rules/flake8_import_conventions/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_import_conventions/mod.rs @@ -9,11 +9,11 @@ mod tests { use anyhow::Result; use rustc_hash::{FxHashMap, FxHashSet}; - 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; + use crate::{assert_diagnostics, assert_diagnostics_diff}; #[test] fn defaults() -> Result<()> { @@ -25,9 +25,31 @@ mod tests { Ok(()) } + #[test] + fn defaults_preview() -> Result<()> { + assert_diagnostics_diff!( + Path::new("flake8_import_conventions/defaults.py"), + &LinterSettings { + flake8_import_conventions: super::settings::Settings::new(PreviewMode::Disabled), + ..LinterSettings::for_rules([ + Rule::UnconventionalImportAlias, + Rule::BannedImportAlias + ]) + }, + &LinterSettings { + flake8_import_conventions: super::settings::Settings::new(PreviewMode::Enabled), + ..LinterSettings::for_rules([ + Rule::UnconventionalImportAlias, + Rule::BannedImportAlias + ]) + }, + ); + Ok(()) + } + #[test] fn custom() -> Result<()> { - let mut aliases = default_aliases(); + let mut aliases = default_aliases(PreviewMode::Disabled); aliases.extend(FxHashMap::from_iter([ ("dask.array".to_string(), "da".to_string()), ("dask.dataframe".to_string(), "dd".to_string()), @@ -53,7 +75,7 @@ mod tests { Path::new("flake8_import_conventions/custom_banned.py"), &LinterSettings { flake8_import_conventions: super::settings::Settings { - aliases: default_aliases(), + aliases: default_aliases(PreviewMode::Disabled), banned_aliases: FxHashMap::from_iter([ ( "typing".to_string(), @@ -87,7 +109,7 @@ mod tests { Path::new("flake8_import_conventions/custom_banned_from.py"), &LinterSettings { flake8_import_conventions: super::settings::Settings { - aliases: default_aliases(), + aliases: default_aliases(PreviewMode::Disabled), banned_aliases: FxHashMap::default(), banned_from: FxHashSet::from_iter([ "logging.config".to_string(), @@ -126,7 +148,7 @@ mod tests { #[test] fn override_defaults() -> Result<()> { - let mut aliases = default_aliases(); + let mut aliases = default_aliases(PreviewMode::Disabled); aliases.extend(FxHashMap::from_iter([( "numpy".to_string(), "nmp".to_string(), @@ -149,7 +171,7 @@ mod tests { #[test] fn from_imports() -> Result<()> { - let mut aliases = default_aliases(); + let mut aliases = default_aliases(PreviewMode::Disabled); aliases.extend(FxHashMap::from_iter([ ("xml.dom.minidom".to_string(), "md".to_string()), ( @@ -185,7 +207,7 @@ mod tests { #[test] fn same_name() -> Result<()> { - let mut aliases = default_aliases(); + let mut aliases = default_aliases(PreviewMode::Disabled); aliases.extend(FxHashMap::from_iter([( "django.conf.settings".to_string(), "settings".to_string(), 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 f00406de4a1816..7574d1f2d5c3fd 100644 --- a/crates/ruff_linter/src/rules/flake8_import_conventions/settings.rs +++ b/crates/ruff_linter/src/rules/flake8_import_conventions/settings.rs @@ -8,6 +8,8 @@ use serde::{Deserialize, Serialize}; use ruff_macros::CacheKey; use crate::display_settings; +use crate::preview::is_expanded_import_conventions_enabled; +use crate::settings::types::PreviewMode; const CONVENTIONAL_ALIASES: &[(&str, &str)] = &[ ("altair", "alt"), @@ -28,6 +30,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))] @@ -66,18 +71,49 @@ pub struct Settings { pub banned_from: FxHashSet, } -pub fn default_aliases() -> FxHashMap { - CONVENTIONAL_ALIASES +pub fn default_aliases(preview: PreviewMode) -> FxHashMap { + let mut aliases = CONVENTIONAL_ALIASES .iter() .map(|(k, v)| ((*k).to_string(), (*v).to_string())) - .collect::>() + .collect::>(); + + if is_expanded_import_conventions_enabled(preview) { + aliases.extend( + PREVIEW_ALIASES + .iter() + .map(|(k, v)| ((*k).to_string(), (*v).to_string())), + ); + } + + aliases +} + +pub fn default_banned_aliases(preview: PreviewMode) -> FxHashMap { + if is_expanded_import_conventions_enabled(preview) { + FxHashMap::from_iter([( + "geopandas".to_string(), + BannedAliases::from_iter(["gpd".to_string()]), + )]) + } else { + FxHashMap::default() + } +} + +impl Settings { + pub fn new(preview: PreviewMode) -> Self { + Self { + aliases: default_aliases(preview), + banned_aliases: default_banned_aliases(preview), + banned_from: FxHashSet::default(), + } + } } impl Default for Settings { fn default() -> Self { Self { - aliases: default_aliases(), - banned_aliases: FxHashMap::default(), + aliases: default_aliases(PreviewMode::Disabled), + banned_aliases: default_banned_aliases(PreviewMode::Disabled), banned_from: FxHashSet::default(), } } diff --git a/crates/ruff_linter/src/rules/flake8_import_conventions/snapshots/ruff_linter__rules__flake8_import_conventions__tests__defaults_preview.snap b/crates/ruff_linter/src/rules/flake8_import_conventions/snapshots/ruff_linter__rules__flake8_import_conventions__tests__defaults_preview.snap new file mode 100644 index 00000000000000..b56ab7fca7c0ff --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_import_conventions/snapshots/ruff_linter__rules__flake8_import_conventions__tests__defaults_preview.snap @@ -0,0 +1,47 @@ +--- +source: crates/ruff_linter/src/rules/flake8_import_conventions/mod.rs +--- +--- Linter settings --- ++ plotly.graph_objects = go, ++ statsmodels.api = sm, +-linter.flake8_import_conventions.banned_aliases = {} ++linter.flake8_import_conventions.banned_aliases = { ++ geopandas = [gpd], ++} + +--- Summary --- +Removed: 0 +Added: 3 + +--- Added --- +ICN001 `plotly.graph_objects` should be imported as `go` + --> defaults.py:36:8 + | +35 | # ICN001: plotly.graph_objects should be imported as go +36 | import plotly.graph_objects # should require alias + | ^^^^^^^^^^^^^^^^^^^^ +37 | import plotly.graph_objects as go # ok + | +help: Alias `plotly.graph_objects` to `go` + + +ICN001 `statsmodels.api` should be imported as `sm` + --> defaults.py:40:8 + | +39 | # ICN001: statsmodels.api should be imported as sm +40 | import statsmodels.api # should require alias + | ^^^^^^^^^^^^^^^ +41 | import statsmodels.api as sm # ok + | +help: Alias `statsmodels.api` to `sm` + + +ICN002 `geopandas` should not be imported as `gpd` + --> defaults.py:44:1 + | +43 | # ICN002: geopandas should not be imported as gpd +44 | import geopandas as gpd # banned + | ^^^^^^^^^^^^^^^^^^^^^^^ +45 | import geopandas # ok +46 | import geopandas as gdf # ok + | diff --git a/crates/ruff_workspace/src/configuration.rs b/crates/ruff_workspace/src/configuration.rs index c2c6857d70a30e..ea41ad752ff77d 100644 --- a/crates/ruff_workspace/src/configuration.rs +++ b/crates/ruff_workspace/src/configuration.rs @@ -249,9 +249,11 @@ impl Configuration { .unwrap_or_default(); let flake8_import_conventions = lint .flake8_import_conventions - .map(Flake8ImportConventionsOptions::try_into_settings) + .map(|options| options.try_into_settings(lint_preview)) .transpose()? - .unwrap_or_default(); + .unwrap_or_else(|| { + ruff_linter::rules::flake8_import_conventions::settings::Settings::new(lint_preview) + }); conflicting_import_settings(&isort, &flake8_import_conventions)?; diff --git a/crates/ruff_workspace/src/options.rs b/crates/ruff_workspace/src/options.rs index f7b77e286e861a..eb54e489b924e6 100644 --- a/crates/ruff_workspace/src/options.rs +++ b/crates/ruff_workspace/src/options.rs @@ -32,7 +32,7 @@ use ruff_linter::rules::{ pycodestyle, pydoclint, pydocstyle, pyflakes, pylint, pyupgrade, ruff, }; use ruff_linter::settings::types::{ - IdentifierPattern, Language, OutputFormat, PythonVersion, RequiredVersion, + IdentifierPattern, Language, OutputFormat, PreviewMode, PythonVersion, RequiredVersion, }; use ruff_linter::{RuleSelector, warn_user_once}; use ruff_macros::{CombineOptions, OptionsMetadata}; @@ -1689,13 +1689,14 @@ impl<'de> Deserialize<'de> for Alias { impl Flake8ImportConventionsOptions { pub fn try_into_settings( self, + preview: PreviewMode, ) -> anyhow::Result { let mut aliases: FxHashMap = match self.aliases { Some(options_aliases) => options_aliases .into_iter() .map(|(module, alias)| (module.into_string(), alias.into_string())) .collect(), - None => flake8_import_conventions::settings::default_aliases(), + None => flake8_import_conventions::settings::default_aliases(preview), }; if let Some(extend_aliases) = self.extend_aliases { aliases.extend( @@ -1716,9 +1717,13 @@ impl Flake8ImportConventionsOptions { normalized_aliases.insert(module, normalized_alias); } + let banned_aliases = self.banned_aliases.unwrap_or_else(|| { + flake8_import_conventions::settings::default_banned_aliases(preview) + }); + Ok(flake8_import_conventions::settings::Settings { aliases: normalized_aliases, - banned_aliases: self.banned_aliases.unwrap_or_default(), + banned_aliases, banned_from: self.banned_from.unwrap_or_default(), }) }