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

Add rule for import shadowing #111

Merged
merged 2 commits into from
Nov 1, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,21 @@ Requirements
- Python 3.8, 3.9, 3.10, 3.11, and pypy3
- flake8

Rules
-----

A001:
A variable is shadowing a Python builtin.

A002:
An argument is shadowing a Python builtin.

A003:
A class attribute is shadowing a Python builtin.

A004:
An import statement is shadowing a Python builtin.

License
-------
GPL 2.0
20 changes: 15 additions & 5 deletions flake8_builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
class BuiltinsChecker:
name = 'flake8_builtins'
version = '1.5.2'
assign_msg = 'A001 variable "{0}" is shadowing a python builtin'
argument_msg = 'A002 argument "{0}" is shadowing a python builtin'
class_attribute_msg = 'A003 class attribute "{0}" is shadowing a python builtin'
assign_msg = 'A001 variable "{0}" is shadowing a Python builtin'
argument_msg = 'A002 argument "{0}" is shadowing a Python builtin'
class_attribute_msg = 'A003 class attribute "{0}" is shadowing a Python builtin'
import_msg = 'A004 import statement "{0}" is shadowing a Python builtin'

names = []
ignore_list = {
Expand Down Expand Up @@ -223,8 +224,17 @@ def check_comprehension(self, statement):

def check_import(self, statement):
for name in statement.names:
if name.asname in self.names:
yield self.error(statement, variable=name.asname)
collision = None
if name.name in self.names and name.asname is None:
collision = name.name
elif name.asname in self.names:
collision = name.asname
if collision:
yield self.error(
statement,
message=self.import_msg,
variable=collision,
)

def check_class(self, statement):
if statement.name in self.names:
Expand Down
15 changes: 12 additions & 3 deletions run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ def bla(dict=3):
b = 4"""
check_code(source, 'A002')


def test_kwonly_argument_message():
source = """
def bla(*, list):
Expand Down Expand Up @@ -357,21 +356,31 @@ def test_list_comprehension_multiple_as_list():
check_code(source, 'A001')


def test_import():
source = """from numpy import max"""
check_code(source, 'A004')


def test_import_as():
source = 'import zope.component.getSite as int'
check_code(source, 'A001')
check_code(source, 'A004')


def test_import_from_as():
source = 'from zope.component import getSite as int'
check_code(source, 'A001')
check_code(source, 'A004')


def test_import_as_nothing():
source = 'import zope.component.getSite as something_else'
check_code(source)


def test_import_collision_as_nothing():
source = """from numpy import max as non_shadowing_max"""
check_code(source)


def test_import_from_as_nothing():
source = 'from zope.component import getSite as something_else'
check_code(source)
Expand Down