-
Notifications
You must be signed in to change notification settings - Fork 10
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
Fix pragma error in RemoveUnusedImports #102
Changes from all commits
aa6d0f5
ace5ea6
422a9d0
196b357
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
from libcst import ensure_type, matchers | ||
from libcst import CSTVisitor, ensure_type, matchers | ||
from libcst.codemod.visitors import GatherUnusedImportsVisitor | ||
from libcst.metadata import ( | ||
PositionProvider, | ||
|
@@ -20,7 +20,7 @@ | |
import re | ||
from pylint.utils.pragma_parser import parse_pragma | ||
|
||
NOQA_PATTERN = re.compile(r"^#\s*noqa") | ||
NOQA_PATTERN = re.compile(r"^#\s*noqa", re.IGNORECASE) | ||
|
||
|
||
class RemoveUnusedImports(BaseCodemod, Codemod): | ||
|
@@ -45,6 +45,9 @@ def __init__(self, codemod_context: CodemodContext, *codemod_args): | |
BaseCodemod.__init__(self, *codemod_args) | ||
|
||
def transform_module_impl(self, tree: cst.Module) -> cst.Module: | ||
# Do nothing in __init__.py files | ||
if self.file_context.file_path.name == "__init__.py": | ||
return tree | ||
gather_unused_visitor = GatherUnusedImportsVisitor(self.context) | ||
tree.visit(gather_unused_visitor) | ||
# filter the gathered imports by line excludes/includes | ||
|
@@ -78,18 +81,23 @@ def _is_disabled_by_linter(self, node: cst.CSTNode) -> bool: | |
if parent and matchers.matches(parent, matchers.SimpleStatementLine()): | ||
stmt = ensure_type(parent, cst.SimpleStatementLine) | ||
|
||
# has a trailing comment string | ||
trailing_comment_string = ( | ||
stmt.trailing_whitespace.comment.value | ||
if stmt.trailing_whitespace.comment | ||
else None | ||
) | ||
if trailing_comment_string and NOQA_PATTERN.match(trailing_comment_string): | ||
return True | ||
if trailing_comment_string and _is_pylint_disable_unused_imports( | ||
trailing_comment_string | ||
): | ||
return True | ||
# has a trailing comment string anywhere in the node | ||
comments_visitor = GatherCommentNodes() | ||
stmt.body[0].visit(comments_visitor) | ||
# has a trailing comment string anywhere in the node | ||
if stmt.trailing_whitespace.comment: | ||
comments_visitor.comments.append(stmt.trailing_whitespace.comment) | ||
|
||
for comment in comments_visitor.comments: | ||
trailing_comment_string = comment.value | ||
if trailing_comment_string and NOQA_PATTERN.match( | ||
trailing_comment_string | ||
): | ||
return True | ||
if trailing_comment_string and _is_pylint_disable_unused_imports( | ||
trailing_comment_string | ||
): | ||
return True | ||
|
||
# has a comment right above it | ||
if matchers.matches( | ||
|
@@ -111,25 +119,42 @@ def _is_disabled_by_linter(self, node: cst.CSTNode) -> bool: | |
return False | ||
|
||
|
||
class GatherCommentNodes(CSTVisitor): | ||
def __init__(self) -> None: | ||
self.comments: list[cst.Comment] = [] | ||
super().__init__() | ||
|
||
def leave_Comment(self, original_node: cst.Comment) -> None: | ||
self.comments.append(original_node) | ||
|
||
|
||
def match_line(pos, line): | ||
return pos.start.line == line and pos.end.line == line | ||
|
||
|
||
def _is_pylint_disable_unused_imports(comment: str) -> bool: | ||
parsed = parse_pragma(comment) | ||
for p in parsed: | ||
if p.action == "disable" and ( | ||
"unused-import" in p.messages or "W0611" in p.messages | ||
): | ||
return True | ||
# If pragma parse fails, ignore | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have a test for this case? |
||
try: | ||
parsed = parse_pragma(comment) | ||
for p in parsed: | ||
if p.action == "disable" and ( | ||
"unused-import" in p.messages or "W0611" in p.messages | ||
): | ||
return True | ||
except Exception: | ||
pass | ||
return False | ||
|
||
|
||
def _is_pylint_disable_next_unused_imports(comment: str) -> bool: | ||
parsed = parse_pragma(comment) | ||
for p in parsed: | ||
if p.action == "disable-next" and ( | ||
"unused-import" in p.messages or "W0611" in p.messages | ||
): | ||
return True | ||
# If pragma parse fails, ignore | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have a test for this case? It would be useful to add the case that caused the error in the first place. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was failing because of a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably honor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do (and have tests for it). I've also fixed an issue in this PR where the regular expression that detects the |
||
try: | ||
parsed = parse_pragma(comment) | ||
for p in parsed: | ||
if p.action == "disable-next" and ( | ||
"unused-import" in p.messages or "W0611" in p.messages | ||
): | ||
return True | ||
except Exception: | ||
pass | ||
return False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this getting tested by a unit test already?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, I'll add one.