Skip to content

Commit 2efa3fb

Browse files
dylwil3MichaReiserAlexWaygood
authored
[flake8-import-conventions] Syntax check aliases supplied in configuration for unconventional-import-alias (ICN001) (#14477)
Co-authored-by: Micha Reiser <[email protected]> Co-authored-by: Alex Waygood <[email protected]>
1 parent b9da430 commit 2efa3fb

File tree

5 files changed

+141
-5
lines changed

5 files changed

+141
-5
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ruff/tests/lint.rs

+72
Original file line numberDiff line numberDiff line change
@@ -1966,3 +1966,75 @@ fn nested_implicit_namespace_package() -> Result<()> {
19661966

19671967
Ok(())
19681968
}
1969+
1970+
#[test]
1971+
fn flake8_import_convention_invalid_aliases_config_alias_name() -> Result<()> {
1972+
let tempdir = TempDir::new()?;
1973+
let ruff_toml = tempdir.path().join("ruff.toml");
1974+
fs::write(
1975+
&ruff_toml,
1976+
r#"
1977+
[lint.flake8-import-conventions.aliases]
1978+
"module.name" = "invalid.alias"
1979+
"#,
1980+
)?;
1981+
1982+
insta::with_settings!({
1983+
filters => vec![(tempdir_filter(&tempdir).as_str(), "[TMP]/")]
1984+
}, {
1985+
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
1986+
.args(STDIN_BASE_OPTIONS)
1987+
.arg("--config")
1988+
.arg(&ruff_toml)
1989+
, @r###"
1990+
success: false
1991+
exit_code: 2
1992+
----- stdout -----
1993+
1994+
----- stderr -----
1995+
ruff failed
1996+
Cause: Failed to parse [TMP]/ruff.toml
1997+
Cause: TOML parse error at line 2, column 2
1998+
|
1999+
2 | [lint.flake8-import-conventions.aliases]
2000+
| ^^^^
2001+
invalid value: string "invalid.alias", expected a Python identifier
2002+
"###);});
2003+
Ok(())
2004+
}
2005+
2006+
#[test]
2007+
fn flake8_import_convention_invalid_aliases_config_module_name() -> Result<()> {
2008+
let tempdir = TempDir::new()?;
2009+
let ruff_toml = tempdir.path().join("ruff.toml");
2010+
fs::write(
2011+
&ruff_toml,
2012+
r#"
2013+
[lint.flake8-import-conventions.aliases]
2014+
"module..invalid" = "alias"
2015+
"#,
2016+
)?;
2017+
2018+
insta::with_settings!({
2019+
filters => vec![(tempdir_filter(&tempdir).as_str(), "[TMP]/")]
2020+
}, {
2021+
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
2022+
.args(STDIN_BASE_OPTIONS)
2023+
.arg("--config")
2024+
.arg(&ruff_toml)
2025+
, @r###"
2026+
success: false
2027+
exit_code: 2
2028+
----- stdout -----
2029+
2030+
----- stderr -----
2031+
ruff failed
2032+
Cause: Failed to parse [TMP]/ruff.toml
2033+
Cause: TOML parse error at line 2, column 2
2034+
|
2035+
2 | [lint.flake8-import-conventions.aliases]
2036+
| ^^^^
2037+
invalid value: string "module..invalid", expected a sequence of Python identifiers delimited by periods
2038+
"###);});
2039+
Ok(())
2040+
}

crates/ruff_workspace/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ ruff_macros = { workspace = true }
2121
ruff_python_ast = { workspace = true }
2222
ruff_python_formatter = { workspace = true, features = ["serde"] }
2323
ruff_python_semantic = { workspace = true, features = ["serde"] }
24+
ruff_python_stdlib = {workspace = true}
2425
ruff_source_file = { workspace = true }
2526

2627
anyhow = { workspace = true }

crates/ruff_workspace/src/options.rs

+63-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use regex::Regex;
22
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
3-
use serde::{Deserialize, Serialize};
3+
use serde::de::{self};
4+
use serde::{Deserialize, Deserializer, Serialize};
45
use std::collections::BTreeMap;
56
use std::path::PathBuf;
67
use strum::IntoEnumIterator;
@@ -34,6 +35,7 @@ use ruff_macros::{CombineOptions, OptionsMetadata};
3435
use ruff_python_ast::name::Name;
3536
use ruff_python_formatter::{DocstringCodeLineWidth, QuoteStyle};
3637
use ruff_python_semantic::NameImports;
38+
use ruff_python_stdlib::identifiers::is_identifier;
3739

3840
#[derive(Clone, Debug, PartialEq, Eq, Default, OptionsMetadata, Serialize, Deserialize)]
3941
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
@@ -1327,7 +1329,7 @@ pub struct Flake8ImportConventionsOptions {
13271329
scipy = "sp"
13281330
"#
13291331
)]
1330-
pub aliases: Option<FxHashMap<String, String>>,
1332+
pub aliases: Option<FxHashMap<ModuleName, Alias>>,
13311333

13321334
/// A mapping from module to conventional import alias. These aliases will
13331335
/// be added to the [`aliases`](#lint_flake8-import-conventions_aliases) mapping.
@@ -1370,10 +1372,67 @@ pub struct Flake8ImportConventionsOptions {
13701372
pub banned_from: Option<FxHashSet<String>>,
13711373
}
13721374

1375+
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, Serialize)]
1376+
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1377+
pub struct ModuleName(String);
1378+
1379+
impl ModuleName {
1380+
pub fn into_string(self) -> String {
1381+
self.0
1382+
}
1383+
}
1384+
1385+
impl<'de> Deserialize<'de> for ModuleName {
1386+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1387+
where
1388+
D: Deserializer<'de>,
1389+
{
1390+
let name = String::deserialize(deserializer)?;
1391+
if name.is_empty() || name.split('.').any(|part| !is_identifier(part)) {
1392+
Err(de::Error::invalid_value(
1393+
de::Unexpected::Str(&name),
1394+
&"a sequence of Python identifiers delimited by periods",
1395+
))
1396+
} else {
1397+
Ok(Self(name))
1398+
}
1399+
}
1400+
}
1401+
1402+
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, Serialize)]
1403+
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1404+
pub struct Alias(String);
1405+
1406+
impl Alias {
1407+
pub fn into_string(self) -> String {
1408+
self.0
1409+
}
1410+
}
1411+
1412+
impl<'de> Deserialize<'de> for Alias {
1413+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1414+
where
1415+
D: Deserializer<'de>,
1416+
{
1417+
let name = String::deserialize(deserializer)?;
1418+
if is_identifier(&name) {
1419+
Ok(Self(name))
1420+
} else {
1421+
Err(de::Error::invalid_value(
1422+
de::Unexpected::Str(&name),
1423+
&"a Python identifier",
1424+
))
1425+
}
1426+
}
1427+
}
1428+
13731429
impl Flake8ImportConventionsOptions {
13741430
pub fn into_settings(self) -> flake8_import_conventions::settings::Settings {
1375-
let mut aliases = match self.aliases {
1376-
Some(options_aliases) => options_aliases,
1431+
let mut aliases: FxHashMap<String, String> = match self.aliases {
1432+
Some(options_aliases) => options_aliases
1433+
.into_iter()
1434+
.map(|(module, alias)| (module.into_string(), alias.into_string()))
1435+
.collect(),
13771436
None => flake8_import_conventions::settings::default_aliases(),
13781437
};
13791438
if let Some(extend_aliases) = self.extend_aliases {

ruff.schema.json

+4-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)