Use dynamic builtins list based on Python version (#13172)

## Summary

Closes https://github.com/astral-sh/ruff/issues/13037.
This commit is contained in:
Charlie Marsh 2024-09-01 13:03:44 -04:00 committed by GitHub
parent 3abd5c08a5
commit c4aad4b161
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 460 additions and 333 deletions

View file

@ -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

View file

@ -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(

View file

@ -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

View file

@ -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(())
}
}

View file

@ -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

View file

@ -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| {

View file

@ -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 {

View file

@ -42,8 +42,9 @@ pub(crate) fn builtin_lambda_argument_shadowing(checker: &mut Checker, lambda: &
let name = &param.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 {

View file

@ -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 {

View file

@ -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
|

View file

@ -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
|

View file

@ -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
|

View file

@ -1,166 +1,3 @@
/// A list of all Python builtins.
///
/// Intended to be kept in sync with [`is_python_builtin`].
pub const PYTHON_BUILTINS: &[&str] = &[
"ArithmeticError",
"AssertionError",
"AttributeError",
"BaseException",
"BaseExceptionGroup",
"BlockingIOError",
"BrokenPipeError",
"BufferError",
"BytesWarning",
"ChildProcessError",
"ConnectionAbortedError",
"ConnectionError",
"ConnectionRefusedError",
"ConnectionResetError",
"DeprecationWarning",
"EOFError",
"Ellipsis",
"EncodingWarning",
"EnvironmentError",
"Exception",
"ExceptionGroup",
"False",
"FileExistsError",
"FileNotFoundError",
"FloatingPointError",
"FutureWarning",
"GeneratorExit",
"IOError",
"ImportError",
"ImportWarning",
"IndentationError",
"IndexError",
"InterruptedError",
"IsADirectoryError",
"KeyError",
"KeyboardInterrupt",
"LookupError",
"MemoryError",
"ModuleNotFoundError",
"NameError",
"None",
"NotADirectoryError",
"NotImplemented",
"NotImplementedError",
"OSError",
"OverflowError",
"PendingDeprecationWarning",
"PermissionError",
"ProcessLookupError",
"RecursionError",
"ReferenceError",
"ResourceWarning",
"RuntimeError",
"RuntimeWarning",
"StopAsyncIteration",
"StopIteration",
"SyntaxError",
"SyntaxWarning",
"SystemError",
"SystemExit",
"TabError",
"TimeoutError",
"True",
"TypeError",
"UnboundLocalError",
"UnicodeDecodeError",
"UnicodeEncodeError",
"UnicodeError",
"UnicodeTranslateError",
"UnicodeWarning",
"UserWarning",
"ValueError",
"Warning",
"ZeroDivisionError",
"__build_class__",
"__debug__",
"__doc__",
"__import__",
"__loader__",
"__name__",
"__package__",
"__spec__",
"abs",
"aiter",
"all",
"anext",
"any",
"ascii",
"bin",
"bool",
"breakpoint",
"bytearray",
"bytes",
"callable",
"chr",
"classmethod",
"compile",
"complex",
"copyright",
"credits",
"delattr",
"dict",
"dir",
"divmod",
"enumerate",
"eval",
"exec",
"exit",
"filter",
"float",
"format",
"frozenset",
"getattr",
"globals",
"hasattr",
"hash",
"help",
"hex",
"id",
"input",
"int",
"isinstance",
"issubclass",
"iter",
"len",
"license",
"list",
"locals",
"map",
"max",
"memoryview",
"min",
"next",
"object",
"oct",
"open",
"ord",
"pow",
"print",
"property",
"quit",
"range",
"repr",
"reversed",
"round",
"set",
"setattr",
"slice",
"sorted",
"staticmethod",
"str",
"sum",
"super",
"tuple",
"type",
"vars",
"zip",
];
/// A list of all builtins that are available in IPython.
///
/// How to create this list:
@ -186,170 +23,343 @@ pub const MAGIC_GLOBALS: &[&str] = &[
"__file__",
];
/// Return the list of builtins for the given Python minor version.
///
/// Intended to be kept in sync with [`is_python_builtin`].
pub fn python_builtins(minor: u8) -> Vec<&'static str> {
let mut builtins = vec![
"ArithmeticError",
"AssertionError",
"AttributeError",
"BaseException",
"BlockingIOError",
"BrokenPipeError",
"BufferError",
"BytesWarning",
"ChildProcessError",
"ConnectionAbortedError",
"ConnectionError",
"ConnectionRefusedError",
"ConnectionResetError",
"DeprecationWarning",
"EOFError",
"Ellipsis",
"EnvironmentError",
"Exception",
"False",
"FileExistsError",
"FileNotFoundError",
"FloatingPointError",
"FutureWarning",
"GeneratorExit",
"IOError",
"ImportError",
"ImportWarning",
"IndentationError",
"IndexError",
"InterruptedError",
"IsADirectoryError",
"KeyError",
"KeyboardInterrupt",
"LookupError",
"MemoryError",
"ModuleNotFoundError",
"NameError",
"None",
"NotADirectoryError",
"NotImplemented",
"NotImplementedError",
"OSError",
"OverflowError",
"PendingDeprecationWarning",
"PermissionError",
"ProcessLookupError",
"RecursionError",
"ReferenceError",
"ResourceWarning",
"RuntimeError",
"RuntimeWarning",
"StopAsyncIteration",
"StopIteration",
"SyntaxError",
"SyntaxWarning",
"SystemError",
"SystemExit",
"TabError",
"TimeoutError",
"True",
"TypeError",
"UnboundLocalError",
"UnicodeDecodeError",
"UnicodeEncodeError",
"UnicodeError",
"UnicodeTranslateError",
"UnicodeWarning",
"UserWarning",
"ValueError",
"Warning",
"ZeroDivisionError",
"__build_class__",
"__debug__",
"__doc__",
"__import__",
"__loader__",
"__name__",
"__package__",
"__spec__",
"abs",
"all",
"any",
"ascii",
"bin",
"bool",
"breakpoint",
"bytearray",
"bytes",
"callable",
"chr",
"classmethod",
"compile",
"complex",
"copyright",
"credits",
"delattr",
"dict",
"dir",
"divmod",
"enumerate",
"eval",
"exec",
"exit",
"filter",
"float",
"format",
"frozenset",
"getattr",
"globals",
"hasattr",
"hash",
"help",
"hex",
"id",
"input",
"int",
"isinstance",
"issubclass",
"iter",
"len",
"license",
"list",
"locals",
"map",
"max",
"memoryview",
"min",
"next",
"object",
"oct",
"open",
"ord",
"pow",
"print",
"property",
"quit",
"range",
"repr",
"reversed",
"round",
"set",
"setattr",
"slice",
"sorted",
"staticmethod",
"str",
"sum",
"super",
"tuple",
"type",
"vars",
"zip",
];
if minor >= 10 {
builtins.extend(vec!["EncodingWarning", "aiter", "anext"]);
}
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,
"ArithmeticError"
| "AssertionError"
| "AttributeError"
| "BaseException"
| "BaseExceptionGroup"
| "BlockingIOError"
| "BrokenPipeError"
| "BufferError"
| "BytesWarning"
| "ChildProcessError"
| "ConnectionAbortedError"
| "ConnectionError"
| "ConnectionRefusedError"
| "ConnectionResetError"
| "DeprecationWarning"
| "EOFError"
| "Ellipsis"
| "EncodingWarning"
| "EnvironmentError"
| "Exception"
| "ExceptionGroup"
| "False"
| "FileExistsError"
| "FileNotFoundError"
| "FloatingPointError"
| "FutureWarning"
| "GeneratorExit"
| "IOError"
| "ImportError"
| "ImportWarning"
| "IndentationError"
| "IndexError"
| "InterruptedError"
| "IsADirectoryError"
| "KeyError"
| "KeyboardInterrupt"
| "LookupError"
| "MemoryError"
| "ModuleNotFoundError"
| "NameError"
| "None"
| "NotADirectoryError"
| "NotImplemented"
| "NotImplementedError"
| "OSError"
| "OverflowError"
| "PendingDeprecationWarning"
| "PermissionError"
| "ProcessLookupError"
| "RecursionError"
| "ReferenceError"
| "ResourceWarning"
| "RuntimeError"
| "RuntimeWarning"
| "StopAsyncIteration"
| "StopIteration"
| "SyntaxError"
| "SyntaxWarning"
| "SystemError"
| "SystemExit"
| "TabError"
| "TimeoutError"
| "True"
| "TypeError"
| "UnboundLocalError"
| "UnicodeDecodeError"
| "UnicodeEncodeError"
| "UnicodeError"
| "UnicodeTranslateError"
| "UnicodeWarning"
| "UserWarning"
| "ValueError"
| "Warning"
| "ZeroDivisionError"
| "__build_class__"
| "__debug__"
| "__doc__"
| "__import__"
| "__loader__"
| "__name__"
| "__package__"
| "__spec__"
| "abs"
| "aiter"
| "all"
| "anext"
| "any"
| "ascii"
| "bin"
| "bool"
| "breakpoint"
| "bytearray"
| "bytes"
| "callable"
| "chr"
| "classmethod"
| "compile"
| "complex"
| "copyright"
| "credits"
| "delattr"
| "dict"
| "dir"
| "divmod"
| "enumerate"
| "eval"
| "exec"
| "exit"
| "filter"
| "float"
| "format"
| "frozenset"
| "getattr"
| "globals"
| "hasattr"
| "hash"
| "help"
| "hex"
| "id"
| "input"
| "int"
| "isinstance"
| "issubclass"
| "iter"
| "len"
| "license"
| "list"
| "locals"
| "map"
| "max"
| "memoryview"
| "min"
| "next"
| "object"
| "oct"
| "open"
| "ord"
| "pow"
| "print"
| "property"
| "quit"
| "range"
| "repr"
| "reversed"
| "round"
| "set"
| "setattr"
| "slice"
| "sorted"
| "staticmethod"
| "str"
| "sum"
| "super"
| "tuple"
| "type"
| "vars"
| "zip"
(minor_version, name),
(
_,
"ArithmeticError"
| "AssertionError"
| "AttributeError"
| "BaseException"
| "BlockingIOError"
| "BrokenPipeError"
| "BufferError"
| "BytesWarning"
| "ChildProcessError"
| "ConnectionAbortedError"
| "ConnectionError"
| "ConnectionRefusedError"
| "ConnectionResetError"
| "DeprecationWarning"
| "EOFError"
| "Ellipsis"
| "EnvironmentError"
| "Exception"
| "False"
| "FileExistsError"
| "FileNotFoundError"
| "FloatingPointError"
| "FutureWarning"
| "GeneratorExit"
| "IOError"
| "ImportError"
| "ImportWarning"
| "IndentationError"
| "IndexError"
| "InterruptedError"
| "IsADirectoryError"
| "KeyError"
| "KeyboardInterrupt"
| "LookupError"
| "MemoryError"
| "ModuleNotFoundError"
| "NameError"
| "None"
| "NotADirectoryError"
| "NotImplemented"
| "NotImplementedError"
| "OSError"
| "OverflowError"
| "PendingDeprecationWarning"
| "PermissionError"
| "ProcessLookupError"
| "RecursionError"
| "ReferenceError"
| "ResourceWarning"
| "RuntimeError"
| "RuntimeWarning"
| "StopAsyncIteration"
| "StopIteration"
| "SyntaxError"
| "SyntaxWarning"
| "SystemError"
| "SystemExit"
| "TabError"
| "TimeoutError"
| "True"
| "TypeError"
| "UnboundLocalError"
| "UnicodeDecodeError"
| "UnicodeEncodeError"
| "UnicodeError"
| "UnicodeTranslateError"
| "UnicodeWarning"
| "UserWarning"
| "ValueError"
| "Warning"
| "ZeroDivisionError"
| "__build_class__"
| "__debug__"
| "__doc__"
| "__import__"
| "__loader__"
| "__name__"
| "__package__"
| "__spec__"
| "abs"
| "all"
| "any"
| "ascii"
| "bin"
| "bool"
| "breakpoint"
| "bytearray"
| "bytes"
| "callable"
| "chr"
| "classmethod"
| "compile"
| "complex"
| "copyright"
| "credits"
| "delattr"
| "dict"
| "dir"
| "divmod"
| "enumerate"
| "eval"
| "exec"
| "exit"
| "filter"
| "float"
| "format"
| "frozenset"
| "getattr"
| "globals"
| "hasattr"
| "hash"
| "help"
| "hex"
| "id"
| "input"
| "int"
| "isinstance"
| "issubclass"
| "iter"
| "len"
| "license"
| "list"
| "locals"
| "map"
| "max"
| "memoryview"
| "min"
| "next"
| "object"
| "oct"
| "open"
| "ord"
| "pow"
| "print"
| "property"
| "quit"
| "range"
| "repr"
| "reversed"
| "round"
| "set"
| "setattr"
| "slice"
| "sorted"
| "staticmethod"
| "str"
| "sum"
| "super"
| "tuple"
| "type"
| "vars"
| "zip"
) | (10..=13, "EncodingWarning" | "aiter" | "anext")
| (11..=13, "BaseExceptionGroup" | "ExceptionGroup")
| (13, "PythonFinalizationError")
)
}