From da620c714c3fad17125fd8ac03e594959c8dd106 Mon Sep 17 00:00:00 2001 From: Daniel D'Avella Date: Mon, 12 Feb 2024 16:18:39 -0500 Subject: [PATCH 1/6] Add unit test --- tests/codemods/test_subprocess_shell_false.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/codemods/test_subprocess_shell_false.py b/tests/codemods/test_subprocess_shell_false.py index 46e5efce..8ff4bb2d 100644 --- a/tests/codemods/test_subprocess_shell_false.py +++ b/tests/codemods/test_subprocess_shell_false.py @@ -67,3 +67,13 @@ def test_exclude_line(self, tmpdir): expected, lines_to_exclude=lines_to_exclude, ) + + @each_func + def test_has_noqa(self, tmpdir, func): + input_code = ( + expected + ) = f""" + import subprocess + subprocess.{func}(args, shell=True) # noqa: S603 + """ + self.run_and_assert(tmpdir, input_code, expected) From 4588e26578788d15e813e2eec196b8f9afae2fa8 Mon Sep 17 00:00:00 2001 From: Daniel D'Avella Date: Mon, 12 Feb 2024 17:04:29 -0500 Subject: [PATCH 2/6] Move annotation check to separate module --- src/codemodder/codemods/check_annotations.py | 105 +++++++++++++++++++ src/core_codemods/remove_unused_imports.py | 90 +--------------- 2 files changed, 107 insertions(+), 88 deletions(-) create mode 100644 src/codemodder/codemods/check_annotations.py diff --git a/src/codemodder/codemods/check_annotations.py b/src/codemodder/codemods/check_annotations.py new file mode 100644 index 00000000..cf5ada64 --- /dev/null +++ b/src/codemodder/codemods/check_annotations.py @@ -0,0 +1,105 @@ +import re + +import libcst as cst +from libcst import CSTVisitor, ensure_type, matchers +from libcst.metadata import ParentNodeProvider + +from pylint.utils.pragma_parser import parse_pragma + +NOQA_PATTERN = re.compile(r"^#\s*noqa", re.IGNORECASE) + + +__all__ = ["is_disabled_by_annotations"] + + +class _GatherCommentNodes(CSTVisitor): + METADATA_DEPENDENCIES = (ParentNodeProvider,) + + def __init__(self, metadata) -> None: + self.comments: list[cst.Comment] = [] + super().__init__() + self.metadata = metadata + + def leave_Comment(self, original_node: cst.Comment) -> None: + self.comments.append(original_node) + + def _is_disabled_by_linter(self, node: cst.CSTNode) -> bool: + """ + Check if the import has a #noqa or # pylint: disable(-next)=unused_imports comment attached to it. + """ + match self.get_metadata(ParentNodeProvider, node): + case cst.SimpleStatementLine() as parent: + stmt = ensure_type(parent, cst.SimpleStatementLine) + + # has a trailing comment string anywhere in the node + stmt.body[0].visit(self) + # has a trailing comment string anywhere in the node + if stmt.trailing_whitespace.comment: + self.comments.append(stmt.trailing_whitespace.comment) + + for comment in self.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( + stmt, + matchers.SimpleStatementLine( + leading_lines=[ + matchers.ZeroOrMore(), + matchers.EmptyLine(comment=matchers.Comment()), + ] + ), + ): + comment_string = stmt.leading_lines[-1].comment.value + if NOQA_PATTERN.match(comment_string): + return True + if comment_string and _is_pylint_disable_next_unused_imports( + comment_string + ): + return True + return False + + +def _is_pylint_disable_unused_imports(comment: str) -> bool: + # If pragma parse fails, ignore + 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: + # If pragma parse fails, ignore + 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 + + +def is_disabled_by_annotations(node: cst.CSTNode, metadata) -> bool: + """ + Check if the import has a #noqa or # pylint: disable(-next)=unused_imports comment attached to it. + """ + visitor = _GatherCommentNodes(metadata) + node.visit(visitor) + return visitor._is_disabled_by_linter(node) diff --git a/src/core_codemods/remove_unused_imports.py b/src/core_codemods/remove_unused_imports.py index f83b2842..a779677a 100644 --- a/src/core_codemods/remove_unused_imports.py +++ b/src/core_codemods/remove_unused_imports.py @@ -1,7 +1,6 @@ import re import libcst as cst -from libcst import CSTVisitor, ensure_type, matchers from libcst.codemod.visitors import GatherUnusedImportsVisitor from libcst.metadata import ( PositionProvider, @@ -10,16 +9,13 @@ ParentNodeProvider, ) -from pylint.utils.pragma_parser import parse_pragma - from core_codemods.api import SimpleCodemod, Metadata, ReviewGuidance from codemodder.change import Change +from codemodder.codemods.check_annotations import is_disabled_by_annotations from codemodder.codemods.transformations.remove_unused_imports import ( RemoveUnusedImportsTransformer, ) -NOQA_PATTERN = re.compile(r"^#\s*noqa", re.IGNORECASE) - class RemoveUnusedImports(SimpleCodemod): metadata = Metadata( @@ -47,7 +43,7 @@ def transform_module_impl(self, tree: cst.Module) -> cst.Module: for import_alias, importt in gather_unused_visitor.unused_imports: pos = self.get_metadata(PositionProvider, import_alias) if self.filter_by_path_includes_or_excludes(pos): - if not self._is_disabled_by_linter(importt): + if not is_disabled_by_annotations(importt, self.metadata): self.file_context.codemod_changes.append( Change(pos.start.line, self.change_description) ) @@ -65,88 +61,6 @@ def filter_by_path_includes_or_excludes(self, pos_to_match) -> bool: return any(match_line(pos_to_match, line) for line in self.line_include) return True - def _is_disabled_by_linter(self, node: cst.CSTNode) -> bool: - """ - Check if the import has a #noqa or # pylint: disable(-next)=unused_imports comment attached to it. - """ - parent = self.get_metadata(ParentNodeProvider, node) - if parent and matchers.matches(parent, matchers.SimpleStatementLine()): - stmt = ensure_type(parent, cst.SimpleStatementLine) - - # 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( - stmt, - matchers.SimpleStatementLine( - leading_lines=[ - matchers.ZeroOrMore(), - matchers.EmptyLine(comment=matchers.Comment()), - ] - ), - ): - comment_string = stmt.leading_lines[-1].comment.value - if NOQA_PATTERN.match(comment_string): - return True - if comment_string and _is_pylint_disable_next_unused_imports( - comment_string - ): - return True - 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: - # If pragma parse fails, ignore - 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: - # If pragma parse fails, ignore - 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 From 63a58c3b958f2006e7aa3350bb9d7ddd862b5c20 Mon Sep 17 00:00:00 2001 From: Daniel D'Avella Date: Tue, 13 Feb 2024 10:30:48 -0500 Subject: [PATCH 3/6] Generalize the rule names that can be used --- src/codemodder/codemods/check_annotations.py | 81 ++++++++++++-------- src/core_codemods/remove_unused_imports.py | 4 +- 2 files changed, 51 insertions(+), 34 deletions(-) diff --git a/src/codemodder/codemods/check_annotations.py b/src/codemodder/codemods/check_annotations.py index cf5ada64..d4ead8ed 100644 --- a/src/codemodder/codemods/check_annotations.py +++ b/src/codemodder/codemods/check_annotations.py @@ -1,8 +1,11 @@ import re +from typing import Mapping import libcst as cst from libcst import CSTVisitor, ensure_type, matchers from libcst.metadata import ParentNodeProvider +from libcst.metadata.base_provider import ProviderT # noqa: F401 +from libcst._nodes.base import CSTNode # noqa: F401 from pylint.utils.pragma_parser import parse_pragma @@ -15,10 +18,17 @@ class _GatherCommentNodes(CSTVisitor): METADATA_DEPENDENCIES = (ParentNodeProvider,) - def __init__(self, metadata) -> None: + messages: list[str] + + def __init__( + self, + metadata: Mapping[ProviderT, Mapping[CSTNode, object]], + messages: list[str], + ) -> None: self.comments: list[cst.Comment] = [] super().__init__() self.metadata = metadata + self.messages = messages def leave_Comment(self, original_node: cst.Comment) -> None: self.comments.append(original_node) @@ -43,8 +53,11 @@ def _is_disabled_by_linter(self, node: cst.CSTNode) -> bool: trailing_comment_string ): return True - if trailing_comment_string and _is_pylint_disable_unused_imports( + if ( trailing_comment_string + and self._is_pylint_disable_unused_imports( + trailing_comment_string + ) ): return True @@ -61,45 +74,47 @@ def _is_disabled_by_linter(self, node: cst.CSTNode) -> bool: comment_string = stmt.leading_lines[-1].comment.value if NOQA_PATTERN.match(comment_string): return True - if comment_string and _is_pylint_disable_next_unused_imports( + if comment_string and self._is_pylint_disable_next_unused_imports( comment_string ): return True return False + def _is_pylint_disable_unused_imports(self, comment: str) -> bool: + # If pragma parse fails, ignore + try: + parsed = parse_pragma(comment) + for p in parsed: + if p.action == "disable" and any( + message in p.messages for message in self.messages + ): + return True + except Exception: + pass + return False + + def _is_pylint_disable_next_unused_imports(self, comment: str) -> bool: + # If pragma parse fails, ignore + try: + parsed = parse_pragma(comment) + for p in parsed: + if p.action == "disable-next" and any( + message in p.messages for message in self.messages + ): + return True + except Exception: + pass + return False + -def _is_pylint_disable_unused_imports(comment: str) -> bool: - # If pragma parse fails, ignore - 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: - # If pragma parse fails, ignore - 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 - - -def is_disabled_by_annotations(node: cst.CSTNode, metadata) -> bool: +def is_disabled_by_annotations( + node: cst.CSTNode, + metadata: Mapping[ProviderT, Mapping[CSTNode, object]], + messages: list[str], +) -> bool: """ Check if the import has a #noqa or # pylint: disable(-next)=unused_imports comment attached to it. """ - visitor = _GatherCommentNodes(metadata) + visitor = _GatherCommentNodes(metadata, messages) node.visit(visitor) return visitor._is_disabled_by_linter(node) diff --git a/src/core_codemods/remove_unused_imports.py b/src/core_codemods/remove_unused_imports.py index a779677a..b63cf6ee 100644 --- a/src/core_codemods/remove_unused_imports.py +++ b/src/core_codemods/remove_unused_imports.py @@ -43,7 +43,9 @@ def transform_module_impl(self, tree: cst.Module) -> cst.Module: for import_alias, importt in gather_unused_visitor.unused_imports: pos = self.get_metadata(PositionProvider, import_alias) if self.filter_by_path_includes_or_excludes(pos): - if not is_disabled_by_annotations(importt, self.metadata): + if not is_disabled_by_annotations( + importt, self.metadata, messages=["unused-import", "W0611"] # type: ignore + ): self.file_context.codemod_changes.append( Change(pos.start.line, self.change_description) ) From d510ee8c0e626c178fa65e8fea50352d32bba5db Mon Sep 17 00:00:00 2001 From: Daniel D'Avella Date: Tue, 13 Feb 2024 11:24:10 -0500 Subject: [PATCH 4/6] Generalize and apply to subprocess-shell-false --- src/codemodder/codemods/check_annotations.py | 101 ++++++++++-------- src/core_codemods/remove_unused_imports.py | 4 +- src/core_codemods/subprocess_shell_false.py | 9 ++ tests/codemods/test_subprocess_shell_false.py | 11 ++ 4 files changed, 78 insertions(+), 47 deletions(-) diff --git a/src/codemodder/codemods/check_annotations.py b/src/codemodder/codemods/check_annotations.py index d4ead8ed..3cfed23d 100644 --- a/src/codemodder/codemods/check_annotations.py +++ b/src/codemodder/codemods/check_annotations.py @@ -2,14 +2,14 @@ from typing import Mapping import libcst as cst -from libcst import CSTVisitor, ensure_type, matchers +from libcst import CSTVisitor, matchers from libcst.metadata import ParentNodeProvider from libcst.metadata.base_provider import ProviderT # noqa: F401 from libcst._nodes.base import CSTNode # noqa: F401 from pylint.utils.pragma_parser import parse_pragma -NOQA_PATTERN = re.compile(r"^#\s*noqa", re.IGNORECASE) +NOQA_PATTERN = re.compile(r"^#\s*noqa(:\s+[A-Z]+[A-Z0-9]+)?", re.IGNORECASE) __all__ = ["is_disabled_by_annotations"] @@ -33,53 +33,66 @@ def __init__( def leave_Comment(self, original_node: cst.Comment) -> None: self.comments.append(original_node) - def _is_disabled_by_linter(self, node: cst.CSTNode) -> bool: + def _process_simple_statement_line(self, stmt: cst.SimpleStatementLine) -> bool: + # has a trailing comment string anywhere in the node + stmt.body[0].visit(self) + # has a trailing comment string anywhere in the node + if stmt.trailing_whitespace.comment: + self.comments.append(stmt.trailing_whitespace.comment) + + for comment in self.comments: + trailing_comment_string = comment.value + if trailing_comment_string and self._noqa_message_match( + trailing_comment_string + ): + return True + if trailing_comment_string and self._is_pylint_disable_unused_imports( + trailing_comment_string + ): + return True + + # has a comment right above it + if matchers.matches( + stmt, + matchers.SimpleStatementLine( + leading_lines=[ + matchers.ZeroOrMore(), + matchers.EmptyLine(comment=matchers.Comment()), + ] + ), + ): + comment_string = stmt.leading_lines[-1].comment.value + if self._noqa_message_match(comment_string): + return True + if comment_string and self._is_pylint_disable_next_unused_imports( + comment_string + ): + return True + + return False + + def is_disabled_by_linter(self, node: cst.CSTNode) -> bool: """ Check if the import has a #noqa or # pylint: disable(-next)=unused_imports comment attached to it. """ match self.get_metadata(ParentNodeProvider, node): - case cst.SimpleStatementLine() as parent: - stmt = ensure_type(parent, cst.SimpleStatementLine) - - # has a trailing comment string anywhere in the node - stmt.body[0].visit(self) - # has a trailing comment string anywhere in the node - if stmt.trailing_whitespace.comment: - self.comments.append(stmt.trailing_whitespace.comment) - - for comment in self.comments: - trailing_comment_string = comment.value - if trailing_comment_string and NOQA_PATTERN.match( - trailing_comment_string - ): - return True - if ( - trailing_comment_string - and self._is_pylint_disable_unused_imports( - trailing_comment_string - ) - ): - return True - - # has a comment right above it - if matchers.matches( - stmt, - matchers.SimpleStatementLine( - leading_lines=[ - matchers.ZeroOrMore(), - matchers.EmptyLine(comment=matchers.Comment()), - ] - ), - ): - comment_string = stmt.leading_lines[-1].comment.value - if NOQA_PATTERN.match(comment_string): - return True - if comment_string and self._is_pylint_disable_next_unused_imports( - comment_string - ): - return True + case cst.SimpleStatementLine() as stmt: + return self._process_simple_statement_line(stmt) + case cst.Expr() as expr: + match self.get_metadata(ParentNodeProvider, expr): + case cst.SimpleStatementLine() as stmt: + return self._process_simple_statement_line(stmt) return False + def _noqa_message_match(self, comment: str) -> bool: + if not (match := NOQA_PATTERN.match(comment)): + return False + + if match.group(1): + return match.group(1).strip(":").strip() in self.messages + + return True + def _is_pylint_disable_unused_imports(self, comment: str) -> bool: # If pragma parse fails, ignore try: @@ -117,4 +130,4 @@ def is_disabled_by_annotations( """ visitor = _GatherCommentNodes(metadata, messages) node.visit(visitor) - return visitor._is_disabled_by_linter(node) + return visitor.is_disabled_by_linter(node) diff --git a/src/core_codemods/remove_unused_imports.py b/src/core_codemods/remove_unused_imports.py index b63cf6ee..8881f790 100644 --- a/src/core_codemods/remove_unused_imports.py +++ b/src/core_codemods/remove_unused_imports.py @@ -1,5 +1,3 @@ -import re - import libcst as cst from libcst.codemod.visitors import GatherUnusedImportsVisitor from libcst.metadata import ( @@ -44,7 +42,7 @@ def transform_module_impl(self, tree: cst.Module) -> cst.Module: pos = self.get_metadata(PositionProvider, import_alias) if self.filter_by_path_includes_or_excludes(pos): if not is_disabled_by_annotations( - importt, self.metadata, messages=["unused-import", "W0611"] # type: ignore + importt, self.metadata, messages=["unused-import", "W0611", "F401"] # type: ignore ): self.file_context.codemod_changes.append( Change(pos.start.line, self.change_description) diff --git a/src/core_codemods/subprocess_shell_false.py b/src/core_codemods/subprocess_shell_false.py index b9d40c05..73ead45d 100644 --- a/src/core_codemods/subprocess_shell_false.py +++ b/src/core_codemods/subprocess_shell_false.py @@ -1,5 +1,8 @@ import libcst as cst from libcst import matchers +from libcst.metadata import ParentNodeProvider + +from codemodder.codemods.check_annotations import is_disabled_by_annotations from codemodder.codemods.utils_mixin import NameResolutionMixin from codemodder.codemods.libcst_transformer import NewArg from core_codemods.api import ( @@ -31,6 +34,8 @@ class SubprocessShellFalse(SimpleCodemod, NameResolutionMixin): for func in {"run", "call", "check_output", "check_call", "Popen"} ] + METADATA_DEPENDENCIES = SimpleCodemod.METADATA_DEPENDENCIES + (ParentNodeProvider,) + def leave_Call(self, original_node: cst.Call, updated_node: cst.Call): if not self.filter_by_path_includes_or_excludes( self.node_position(original_node) @@ -44,6 +49,10 @@ def leave_Call(self, original_node: cst.Call, updated_node: cst.Call): matchers.Arg( keyword=matchers.Name("shell"), value=matchers.Name("True") ), + ) and not is_disabled_by_annotations( + original_node, + self.metadata, # type: ignore + messages=["S603"], ): self.report_change(original_node) new_args = self.replace_args( diff --git a/tests/codemods/test_subprocess_shell_false.py b/tests/codemods/test_subprocess_shell_false.py index 8ff4bb2d..2bc2794a 100644 --- a/tests/codemods/test_subprocess_shell_false.py +++ b/tests/codemods/test_subprocess_shell_false.py @@ -77,3 +77,14 @@ def test_has_noqa(self, tmpdir, func): subprocess.{func}(args, shell=True) # noqa: S603 """ self.run_and_assert(tmpdir, input_code, expected) + + def test_different_noqa_message(self, tmpdir): + input_code = """ + import subprocess + subprocess.run(args, shell=True) # noqa: S604 + """ + expected = """ + import subprocess + subprocess.run(args, shell=False) # noqa: S604 + """ + self.run_and_assert(tmpdir, input_code, expected) From 453aa9c8f8844a01c4631192f3267df7fb3ca30d Mon Sep 17 00:00:00 2001 From: Daniel D'Avella Date: Tue, 13 Feb 2024 11:33:36 -0500 Subject: [PATCH 5/6] Refactor to avoid matchers --- src/codemodder/codemods/check_annotations.py | 22 +++++++------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/codemodder/codemods/check_annotations.py b/src/codemodder/codemods/check_annotations.py index 3cfed23d..4104b232 100644 --- a/src/codemodder/codemods/check_annotations.py +++ b/src/codemodder/codemods/check_annotations.py @@ -2,7 +2,7 @@ from typing import Mapping import libcst as cst -from libcst import CSTVisitor, matchers +from libcst import CSTVisitor from libcst.metadata import ParentNodeProvider from libcst.metadata.base_provider import ProviderT # noqa: F401 from libcst._nodes.base import CSTNode # noqa: F401 @@ -52,22 +52,16 @@ def _process_simple_statement_line(self, stmt: cst.SimpleStatementLine) -> bool: return True # has a comment right above it - if matchers.matches( - stmt, - matchers.SimpleStatementLine( + match stmt: + case cst.SimpleStatementLine( leading_lines=[ - matchers.ZeroOrMore(), - matchers.EmptyLine(comment=matchers.Comment()), + *_, + cst.EmptyLine(comment=cst.Comment(value=comment_string)), ] - ), - ): - comment_string = stmt.leading_lines[-1].comment.value - if self._noqa_message_match(comment_string): - return True - if comment_string and self._is_pylint_disable_next_unused_imports( - comment_string ): - return True + return self._noqa_message_match(comment_string) or ( + self._is_pylint_disable_next_unused_imports(comment_string) + ) return False From b3cf5cb1f8e8d59e2a6e259208912e106ae461df Mon Sep 17 00:00:00 2001 From: Daniel D'Avella Date: Tue, 13 Feb 2024 11:44:27 -0500 Subject: [PATCH 6/6] A bit of cleanup --- src/codemodder/codemods/check_annotations.py | 4 ++-- src/core_codemods/remove_unused_imports.py | 6 +++++- src/core_codemods/subprocess_shell_false.py | 3 ++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/codemodder/codemods/check_annotations.py b/src/codemodder/codemods/check_annotations.py index 4104b232..353374fb 100644 --- a/src/codemodder/codemods/check_annotations.py +++ b/src/codemodder/codemods/check_annotations.py @@ -67,7 +67,7 @@ def _process_simple_statement_line(self, stmt: cst.SimpleStatementLine) -> bool: def is_disabled_by_linter(self, node: cst.CSTNode) -> bool: """ - Check if the import has a #noqa or # pylint: disable(-next)=unused_imports comment attached to it. + Check if the import has a #noqa or # pylint: disable(-next) comment attached to it. """ match self.get_metadata(ParentNodeProvider, node): case cst.SimpleStatementLine() as stmt: @@ -120,7 +120,7 @@ def is_disabled_by_annotations( messages: list[str], ) -> bool: """ - Check if the import has a #noqa or # pylint: disable(-next)=unused_imports comment attached to it. + Check if the import has a #noqa or # pylint: disable(-next) comment attached to it. """ visitor = _GatherCommentNodes(metadata, messages) node.visit(visitor) diff --git a/src/core_codemods/remove_unused_imports.py b/src/core_codemods/remove_unused_imports.py index 8881f790..a8fa5dae 100644 --- a/src/core_codemods/remove_unused_imports.py +++ b/src/core_codemods/remove_unused_imports.py @@ -30,6 +30,8 @@ class RemoveUnusedImports(SimpleCodemod): ParentNodeProvider, ) + IGNORE_ANNOTATIONS = ["unused-import", "F401", "W0611"] + 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": @@ -42,7 +44,9 @@ def transform_module_impl(self, tree: cst.Module) -> cst.Module: pos = self.get_metadata(PositionProvider, import_alias) if self.filter_by_path_includes_or_excludes(pos): if not is_disabled_by_annotations( - importt, self.metadata, messages=["unused-import", "W0611", "F401"] # type: ignore + importt, + self.metadata, # type: ignore + messages=self.IGNORE_ANNOTATIONS, ): self.file_context.codemod_changes.append( Change(pos.start.line, self.change_description) diff --git a/src/core_codemods/subprocess_shell_false.py b/src/core_codemods/subprocess_shell_false.py index 73ead45d..2cf7b408 100644 --- a/src/core_codemods/subprocess_shell_false.py +++ b/src/core_codemods/subprocess_shell_false.py @@ -35,6 +35,7 @@ class SubprocessShellFalse(SimpleCodemod, NameResolutionMixin): ] METADATA_DEPENDENCIES = SimpleCodemod.METADATA_DEPENDENCIES + (ParentNodeProvider,) + IGNORE_ANNOTATIONS = ["S603"] def leave_Call(self, original_node: cst.Call, updated_node: cst.Call): if not self.filter_by_path_includes_or_excludes( @@ -52,7 +53,7 @@ def leave_Call(self, original_node: cst.Call, updated_node: cst.Call): ) and not is_disabled_by_annotations( original_node, self.metadata, # type: ignore - messages=["S603"], + messages=self.IGNORE_ANNOTATIONS, ): self.report_change(original_node) new_args = self.replace_args(