mirror of https://github.com/astral-sh/ruff
Use dynamic builtins list based on Python version (#13172)
## Summary Closes https://github.com/astral-sh/ruff/issues/13037.
This commit is contained in:
parent
3abd5c08a5
commit
c4aad4b161
|
|
@ -3,3 +3,9 @@ import float
|
|||
from some import other as int
|
||||
from some import input, exec
|
||||
from directory import new as dir
|
||||
|
||||
# See: https://github.com/astral-sh/ruff/issues/13037
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3, 11):
|
||||
from exceptiongroup import BaseExceptionGroup, ExceptionGroup
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ use ruff_python_semantic::{
|
|||
ModuleKind, ModuleSource, NodeId, ScopeId, ScopeKind, SemanticModel, SemanticModelFlags,
|
||||
StarImport, SubmoduleImport,
|
||||
};
|
||||
use ruff_python_stdlib::builtins::{IPYTHON_BUILTINS, MAGIC_GLOBALS, PYTHON_BUILTINS};
|
||||
use ruff_python_stdlib::builtins::{python_builtins, IPYTHON_BUILTINS, MAGIC_GLOBALS};
|
||||
use ruff_python_trivia::CommentRanges;
|
||||
use ruff_source_file::{Locator, OneIndexed, SourceRow};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
|
@ -1912,7 +1912,7 @@ impl<'a> Checker<'a> {
|
|||
}
|
||||
|
||||
fn bind_builtins(&mut self) {
|
||||
for builtin in PYTHON_BUILTINS
|
||||
for builtin in python_builtins(self.settings.target_version.minor())
|
||||
.iter()
|
||||
.chain(MAGIC_GLOBALS.iter())
|
||||
.chain(
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
use crate::settings::types::PythonVersion;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_stdlib::builtins::{is_ipython_builtin, is_python_builtin};
|
||||
|
||||
pub(super) fn shadows_builtin(
|
||||
name: &str,
|
||||
ignorelist: &[String],
|
||||
source_type: PySourceType,
|
||||
ignorelist: &[String],
|
||||
python_version: PythonVersion,
|
||||
) -> bool {
|
||||
if is_python_builtin(name) || source_type.is_ipynb() && is_ipython_builtin(name) {
|
||||
if is_python_builtin(name, python_version.minor())
|
||||
|| source_type.is_ipynb() && is_ipython_builtin(name)
|
||||
{
|
||||
ignorelist.iter().all(|ignore| ignore != name)
|
||||
} else {
|
||||
false
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ mod tests {
|
|||
|
||||
use crate::assert_messages;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::types::PythonVersion;
|
||||
use crate::settings::LinterSettings;
|
||||
use crate::test::test_path;
|
||||
|
||||
|
|
@ -112,4 +113,18 @@ mod tests {
|
|||
assert_messages!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::BuiltinImportShadowing, Path::new("A004.py"))]
|
||||
fn rules_py312(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}_py38", rule_code.noqa_code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("flake8_builtins").join(path).as_path(),
|
||||
&LinterSettings {
|
||||
target_version: PythonVersion::Py38,
|
||||
..LinterSettings::for_rule(rule_code)
|
||||
},
|
||||
)?;
|
||||
assert_messages!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,8 +66,9 @@ impl Violation for BuiltinArgumentShadowing {
|
|||
pub(crate) fn builtin_argument_shadowing(checker: &mut Checker, parameter: &Parameter) {
|
||||
if shadows_builtin(
|
||||
parameter.name.as_str(),
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.source_type,
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.settings.target_version,
|
||||
) {
|
||||
// Ignore `@override` and `@overload` decorated functions.
|
||||
if checker
|
||||
|
|
|
|||
|
|
@ -98,8 +98,9 @@ pub(crate) fn builtin_attribute_shadowing(
|
|||
|
||||
if shadows_builtin(
|
||||
name,
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.source_type,
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.settings.target_version,
|
||||
) {
|
||||
// Ignore explicit overrides.
|
||||
if class_def.decorator_list.iter().any(|decorator| {
|
||||
|
|
|
|||
|
|
@ -36,8 +36,9 @@ pub(crate) fn builtin_import_shadowing(checker: &mut Checker, alias: &Alias) {
|
|||
let name = alias.asname.as_ref().unwrap_or(&alias.name);
|
||||
if shadows_builtin(
|
||||
name.as_str(),
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.source_type,
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.settings.target_version,
|
||||
) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
BuiltinImportShadowing {
|
||||
|
|
|
|||
|
|
@ -42,8 +42,9 @@ pub(crate) fn builtin_lambda_argument_shadowing(checker: &mut Checker, lambda: &
|
|||
let name = ¶m.parameter.name;
|
||||
if shadows_builtin(
|
||||
name.as_ref(),
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.source_type,
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.settings.target_version,
|
||||
) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
BuiltinLambdaArgumentShadowing {
|
||||
|
|
|
|||
|
|
@ -61,8 +61,9 @@ impl Violation for BuiltinVariableShadowing {
|
|||
pub(crate) fn builtin_variable_shadowing(checker: &mut Checker, name: &str, range: TextRange) {
|
||||
if shadows_builtin(
|
||||
name,
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.source_type,
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.settings.target_version,
|
||||
) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
BuiltinVariableShadowing {
|
||||
|
|
|
|||
|
|
@ -52,4 +52,20 @@ A004.py:5:30: A004 Import `dir` is shadowing a Python builtin
|
|||
4 | from some import input, exec
|
||||
5 | from directory import new as dir
|
||||
| ^^^ A004
|
||||
6 |
|
||||
7 | # See: https://github.com/astral-sh/ruff/issues/13037
|
||||
|
|
||||
|
||||
A004.py:11:32: A004 Import `BaseExceptionGroup` is shadowing a Python builtin
|
||||
|
|
||||
10 | if sys.version_info < (3, 11):
|
||||
11 | from exceptiongroup import BaseExceptionGroup, ExceptionGroup
|
||||
| ^^^^^^^^^^^^^^^^^^ A004
|
||||
|
|
||||
|
||||
A004.py:11:52: A004 Import `ExceptionGroup` is shadowing a Python builtin
|
||||
|
|
||||
10 | if sys.version_info < (3, 11):
|
||||
11 | from exceptiongroup import BaseExceptionGroup, ExceptionGroup
|
||||
| ^^^^^^^^^^^^^^ A004
|
||||
|
|
||||
|
|
|
|||
|
|
@ -45,3 +45,17 @@ A004.py:4:25: A004 Import `exec` is shadowing a Python builtin
|
|||
| ^^^^ A004
|
||||
5 | from directory import new as dir
|
||||
|
|
||||
|
||||
A004.py:11:32: A004 Import `BaseExceptionGroup` is shadowing a Python builtin
|
||||
|
|
||||
10 | if sys.version_info < (3, 11):
|
||||
11 | from exceptiongroup import BaseExceptionGroup, ExceptionGroup
|
||||
| ^^^^^^^^^^^^^^^^^^ A004
|
||||
|
|
||||
|
||||
A004.py:11:52: A004 Import `ExceptionGroup` is shadowing a Python builtin
|
||||
|
|
||||
10 | if sys.version_info < (3, 11):
|
||||
11 | from exceptiongroup import BaseExceptionGroup, ExceptionGroup
|
||||
| ^^^^^^^^^^^^^^ A004
|
||||
|
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs
|
||||
---
|
||||
A004.py:1:16: A004 Import `sum` is shadowing a Python builtin
|
||||
|
|
||||
1 | import some as sum
|
||||
| ^^^ A004
|
||||
2 | import float
|
||||
3 | from some import other as int
|
||||
|
|
||||
|
||||
A004.py:2:8: A004 Import `float` is shadowing a Python builtin
|
||||
|
|
||||
1 | import some as sum
|
||||
2 | import float
|
||||
| ^^^^^ A004
|
||||
3 | from some import other as int
|
||||
4 | from some import input, exec
|
||||
|
|
||||
|
||||
A004.py:3:27: A004 Import `int` is shadowing a Python builtin
|
||||
|
|
||||
1 | import some as sum
|
||||
2 | import float
|
||||
3 | from some import other as int
|
||||
| ^^^ A004
|
||||
4 | from some import input, exec
|
||||
5 | from directory import new as dir
|
||||
|
|
||||
|
||||
A004.py:4:18: A004 Import `input` is shadowing a Python builtin
|
||||
|
|
||||
2 | import float
|
||||
3 | from some import other as int
|
||||
4 | from some import input, exec
|
||||
| ^^^^^ A004
|
||||
5 | from directory import new as dir
|
||||
|
|
||||
|
||||
A004.py:4:25: A004 Import `exec` is shadowing a Python builtin
|
||||
|
|
||||
2 | import float
|
||||
3 | from some import other as int
|
||||
4 | from some import input, exec
|
||||
| ^^^^ A004
|
||||
5 | from directory import new as dir
|
||||
|
|
||||
|
||||
A004.py:5:30: A004 Import `dir` is shadowing a Python builtin
|
||||
|
|
||||
3 | from some import other as int
|
||||
4 | from some import input, exec
|
||||
5 | from directory import new as dir
|
||||
| ^^^ A004
|
||||
6 |
|
||||
7 | # See: https://github.com/astral-sh/ruff/issues/13037
|
||||
|
|
||||
|
|
@ -1,12 +1,37 @@
|
|||
/// A list of all Python builtins.
|
||||
/// A list of all builtins that are available in IPython.
|
||||
///
|
||||
/// How to create this list:
|
||||
/// ```python
|
||||
/// import json
|
||||
/// from subprocess import check_output
|
||||
///
|
||||
/// builtins_python = json.loads(check_output(["python3", "-c" "import json; print(json.dumps(dir(__builtins__)))"]))
|
||||
/// builtins_ipython = json.loads(check_output(["ipython3", "-c" "import json; print(json.dumps(dir(__builtins__)))"]))
|
||||
/// print(sorted(set(builtins_ipython) - set(builtins_python)))
|
||||
/// ```
|
||||
///
|
||||
/// Intended to be kept in sync with [`is_ipython_builtin`].
|
||||
pub const IPYTHON_BUILTINS: &[&str] = &["__IPYTHON__", "display", "get_ipython"];
|
||||
|
||||
/// Globally defined names which are not attributes of the builtins module, or
|
||||
/// are only present on some platforms.
|
||||
pub const MAGIC_GLOBALS: &[&str] = &[
|
||||
"WindowsError",
|
||||
"__annotations__",
|
||||
"__builtins__",
|
||||
"__cached__",
|
||||
"__file__",
|
||||
];
|
||||
|
||||
/// Return the list of builtins for the given Python minor version.
|
||||
///
|
||||
/// Intended to be kept in sync with [`is_python_builtin`].
|
||||
pub const PYTHON_BUILTINS: &[&str] = &[
|
||||
pub fn python_builtins(minor: u8) -> Vec<&'static str> {
|
||||
let mut builtins = vec![
|
||||
"ArithmeticError",
|
||||
"AssertionError",
|
||||
"AttributeError",
|
||||
"BaseException",
|
||||
"BaseExceptionGroup",
|
||||
"BlockingIOError",
|
||||
"BrokenPipeError",
|
||||
"BufferError",
|
||||
|
|
@ -19,10 +44,8 @@ pub const PYTHON_BUILTINS: &[&str] = &[
|
|||
"DeprecationWarning",
|
||||
"EOFError",
|
||||
"Ellipsis",
|
||||
"EncodingWarning",
|
||||
"EnvironmentError",
|
||||
"Exception",
|
||||
"ExceptionGroup",
|
||||
"False",
|
||||
"FileExistsError",
|
||||
"FileNotFoundError",
|
||||
|
|
@ -85,9 +108,7 @@ pub const PYTHON_BUILTINS: &[&str] = &[
|
|||
"__package__",
|
||||
"__spec__",
|
||||
"abs",
|
||||
"aiter",
|
||||
"all",
|
||||
"anext",
|
||||
"any",
|
||||
"ascii",
|
||||
"bin",
|
||||
|
|
@ -161,43 +182,33 @@ pub const PYTHON_BUILTINS: &[&str] = &[
|
|||
"zip",
|
||||
];
|
||||
|
||||
/// A list of all builtins that are available in IPython.
|
||||
///
|
||||
/// How to create this list:
|
||||
/// ```python
|
||||
/// import json
|
||||
/// from subprocess import check_output
|
||||
///
|
||||
/// builtins_python = json.loads(check_output(["python3", "-c" "import json; print(json.dumps(dir(__builtins__)))"]))
|
||||
/// builtins_ipython = json.loads(check_output(["ipython3", "-c" "import json; print(json.dumps(dir(__builtins__)))"]))
|
||||
/// print(sorted(set(builtins_ipython) - set(builtins_python)))
|
||||
/// ```
|
||||
///
|
||||
/// Intended to be kept in sync with [`is_ipython_builtin`].
|
||||
pub const IPYTHON_BUILTINS: &[&str] = &["__IPYTHON__", "display", "get_ipython"];
|
||||
if minor >= 10 {
|
||||
builtins.extend(vec!["EncodingWarning", "aiter", "anext"]);
|
||||
}
|
||||
|
||||
/// Globally defined names which are not attributes of the builtins module, or
|
||||
/// are only present on some platforms.
|
||||
pub const MAGIC_GLOBALS: &[&str] = &[
|
||||
"WindowsError",
|
||||
"__annotations__",
|
||||
"__builtins__",
|
||||
"__cached__",
|
||||
"__file__",
|
||||
];
|
||||
if minor >= 11 {
|
||||
builtins.extend(vec!["BaseExceptionGroup", "ExceptionGroup"]);
|
||||
}
|
||||
|
||||
if minor >= 13 {
|
||||
builtins.extend(vec!["PythonFinalizationError"]);
|
||||
}
|
||||
|
||||
builtins
|
||||
}
|
||||
|
||||
/// Returns `true` if the given name is that of a Python builtin.
|
||||
///
|
||||
/// Intended to be kept in sync with [`PYTHON_BUILTINS`].
|
||||
pub fn is_python_builtin(name: &str) -> bool {
|
||||
// Constructed by converting the `PYTHON_BUILTINS` slice to a `match` expression.
|
||||
/// Intended to be kept in sync with [`python_builtins`].
|
||||
pub fn is_python_builtin(name: &str, minor_version: u8) -> bool {
|
||||
matches!(
|
||||
name,
|
||||
(minor_version, name),
|
||||
(
|
||||
_,
|
||||
"ArithmeticError"
|
||||
| "AssertionError"
|
||||
| "AttributeError"
|
||||
| "BaseException"
|
||||
| "BaseExceptionGroup"
|
||||
| "BlockingIOError"
|
||||
| "BrokenPipeError"
|
||||
| "BufferError"
|
||||
|
|
@ -210,10 +221,8 @@ pub fn is_python_builtin(name: &str) -> bool {
|
|||
| "DeprecationWarning"
|
||||
| "EOFError"
|
||||
| "Ellipsis"
|
||||
| "EncodingWarning"
|
||||
| "EnvironmentError"
|
||||
| "Exception"
|
||||
| "ExceptionGroup"
|
||||
| "False"
|
||||
| "FileExistsError"
|
||||
| "FileNotFoundError"
|
||||
|
|
@ -276,9 +285,7 @@ pub fn is_python_builtin(name: &str) -> bool {
|
|||
| "__package__"
|
||||
| "__spec__"
|
||||
| "abs"
|
||||
| "aiter"
|
||||
| "all"
|
||||
| "anext"
|
||||
| "any"
|
||||
| "ascii"
|
||||
| "bin"
|
||||
|
|
@ -350,6 +357,9 @@ pub fn is_python_builtin(name: &str) -> bool {
|
|||
| "type"
|
||||
| "vars"
|
||||
| "zip"
|
||||
) | (10..=13, "EncodingWarning" | "aiter" | "anext")
|
||||
| (11..=13, "BaseExceptionGroup" | "ExceptionGroup")
|
||||
| (13, "PythonFinalizationError")
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue