diff --git a/CHANGES.rst b/CHANGES.rst index 00bff2b..31c0f43 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,10 +3,11 @@ Changelog ========= -2.3.1 (unreleased) +2.3.1 (2024-04-01) ------------------ -- Nothing changed yet. +- Add rule for lambda argument shadowing (`A006`). + [cielavenir] 2.3.0 (2024-03-29) diff --git a/README.rst b/README.rst index 8268d4f..c0e4f02 100644 --- a/README.rst +++ b/README.rst @@ -104,6 +104,9 @@ A004: A005: A module is shadowing a Python builtin module (e.g: `logging` or `socket`) +A006: + A lambda argument is shadowing a Python builtin. + License ------- GPL 2.0 diff --git a/flake8_builtins.py b/flake8_builtins.py index ed23b74..1b3ef4f 100644 --- a/flake8_builtins.py +++ b/flake8_builtins.py @@ -15,6 +15,7 @@ class BuiltinsChecker: class_attribute_msg = 'A003 class attribute "{0}" is shadowing a Python builtin' import_msg = 'A004 import statement "{0}" is shadowing a Python builtin' module_name_msg = 'A005 the module is shadowing a Python builtin module "{0}"' + lambda_argument_msg = 'A006 lambda argument "{0}" is shadowing a Python builtin' names = [] ignore_list = { @@ -113,6 +114,9 @@ def run(self): elif isinstance(statement, function_nodes): value = self.check_function_definition(statement) + elif isinstance(statement, ast.Lambda): + value = self.check_lambda_definition(statement) + elif isinstance(statement, for_nodes): value = self.check_for_loop(statement) @@ -181,6 +185,20 @@ def check_function_definition(self, statement): variable=arg.arg, ) + def check_lambda_definition(self, statement): + all_arguments = [] + all_arguments.extend(statement.args.args) + all_arguments.extend(getattr(statement.args, 'kwonlyargs', [])) + all_arguments.extend(getattr(statement.args, 'posonlyargs', [])) + + for arg in all_arguments: + if isinstance(arg, ast.arg) and arg.arg in self.names: + yield self.error( + arg, + message=self.lambda_argument_msg, + variable=arg.arg, + ) + def check_for_loop(self, statement): stack = [statement.target] while stack: diff --git a/run_tests.py b/run_tests.py index e46b57b..8e15e8b 100644 --- a/run_tests.py +++ b/run_tests.py @@ -156,6 +156,11 @@ def bla(list): check_code(source, 'A002') +def test_lambda_argument_message(): + source = 'takefirst = lambda list: list[0]' + check_code(source, 'A006') + + def test_keyword_argument_message(): source = """ def bla(dict=3): @@ -182,6 +187,15 @@ def bla(list, /): """ check_code(source, 'A002') +@pytest.mark.skipif( + sys.version_info < (3, 8), + reason='This syntax is only valid in Python 3.8+', +) +def test_lambda_posonly_argument_message(): + source = """ + takefirst = lambda list, /: list[0] + """ + check_code(source, 'A006') def test_no_error(): source = """def bla(first):\n b = 4"""