Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce A006 for lambda shadowing #123

Merged
merged 6 commits into from Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGES.rst
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions README.rst
Expand Up @@ -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
18 changes: 18 additions & 0 deletions flake8_builtins.py
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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:
Expand Down
14 changes: 14 additions & 0 deletions run_tests.py
Expand Up @@ -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):
Expand All @@ -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"""
Expand Down