From 5ccd8655cd5b6a706cb261fbbc42cbf31b1b3d86 Mon Sep 17 00:00:00 2001 From: andrecs <12188364+andrecsilva@users.noreply.github.com> Date: Fri, 19 Jan 2024 10:13:53 -0300 Subject: [PATCH 1/4] Implementation, docs and tests for remove-assertion-in-pytest-raises --- .../test_remove_assertion_in_pytest_raises.py | 40 +++++++ src/codemodder/scripts/generate_docs.py | 4 + src/core_codemods/__init__.py | 2 + ...ython_remove-assertion-in-pytest-raises.md | 15 +++ .../remove_assertion_in_pytest_raises.py | 110 ++++++++++++++++++ .../test_remove_assertion_in_pytest_raises.py | 105 +++++++++++++++++ .../remove_assertion_in_pytest_raises.py | 7 ++ 7 files changed, 283 insertions(+) create mode 100644 integration_tests/test_remove_assertion_in_pytest_raises.py create mode 100644 src/core_codemods/docs/pixee_python_remove-assertion-in-pytest-raises.md create mode 100644 src/core_codemods/remove_assertion_in_pytest_raises.py create mode 100644 tests/codemods/test_remove_assertion_in_pytest_raises.py create mode 100644 tests/samples/remove_assertion_in_pytest_raises.py diff --git a/integration_tests/test_remove_assertion_in_pytest_raises.py b/integration_tests/test_remove_assertion_in_pytest_raises.py new file mode 100644 index 00000000..7cc3f482 --- /dev/null +++ b/integration_tests/test_remove_assertion_in_pytest_raises.py @@ -0,0 +1,40 @@ +from core_codemods.remove_assertion_in_pytest_raises import ( + RemoveAssertionInPytestRaises, + RemoveAssertionInPytestRaisesTransformer, +) +from integration_tests.base_test import ( + BaseIntegrationTest, + original_and_expected_from_code_path, +) + + +class TestRemoveAssertionInPytestRaises(BaseIntegrationTest): + codemod = RemoveAssertionInPytestRaises + code_path = "tests/samples/remove_assertion_in_pytest_raises.py" + original_code, expected_new_code = original_and_expected_from_code_path( + code_path, + [ + (5, """ assert 1\n"""), + (6, """ assert 2\n"""), + ], + ) + + # fmt: off + expected_diff =( + """--- \n""" + """+++ \n""" + """@@ -3,5 +3,5 @@\n""" + """ def test_foo():\n""" + """ with pytest.raises(ZeroDivisionError):\n""" + """ error = 1/0\n""" + """- assert 1\n""" + """- assert 2\n""" + """+ assert 1\n""" + """+ assert 2\n""" + ) + # fmt: on + + expected_line_change = "7" + change_description = RemoveAssertionInPytestRaisesTransformer.change_description + num_changed_files = 1 + num_changes = 4 diff --git a/src/codemodder/scripts/generate_docs.py b/src/codemodder/scripts/generate_docs.py index e7aedd91..91e19ffb 100644 --- a/src/codemodder/scripts/generate_docs.py +++ b/src/codemodder/scripts/generate_docs.py @@ -214,6 +214,10 @@ class DocMetadata: importance="Low", guidance_explained="Values compared to empty sequences should be verified in case they are falsy values that are not a sequence.", ), + "remove-assertion-in-pytest-raises": DocMetadata( + importance="Low", + guidance_explained="We believe this change is safe and will not cause any issues.", + ), } diff --git a/src/core_codemods/__init__.py b/src/core_codemods/__init__.py index 8be5f2d2..8842bbd2 100644 --- a/src/core_codemods/__init__.py +++ b/src/core_codemods/__init__.py @@ -47,6 +47,7 @@ from .flask_enable_csrf_protection import FlaskEnableCSRFProtection from .replace_flask_send_file import ReplaceFlaskSendFile from .fix_empty_sequence_comparison import FixEmptySequenceComparison +from .remove_assertion_in_pytest_raises import RemoveAssertionInPytestRaises registry = CodemodCollection( origin="pixee", @@ -100,5 +101,6 @@ FlaskEnableCSRFProtection, ReplaceFlaskSendFile, FixEmptySequenceComparison, + RemoveAssertionInPytestRaises, ], ) diff --git a/src/core_codemods/docs/pixee_python_remove-assertion-in-pytest-raises.md b/src/core_codemods/docs/pixee_python_remove-assertion-in-pytest-raises.md new file mode 100644 index 00000000..b8616179 --- /dev/null +++ b/src/core_codemods/docs/pixee_python_remove-assertion-in-pytest-raises.md @@ -0,0 +1,15 @@ +The context manager object `pytest.raises()` will assert if the code contained within its scope will raise an exception of type ``. The documentation points that the exception must be raised in the last line of its scope and any line afterwards won't be executed. +Including asserts at the end of the scope is a common error. This codemod addresses that by moving them out of the scope. +Our changes look something like this: + +```diff +import pytest + +def test_foo(): + with pytest.raises(ZeroDivisionError): + error = 1/0 +- assert 1 +- assert 2 ++ assert 1 ++ assert 2 +``` diff --git a/src/core_codemods/remove_assertion_in_pytest_raises.py b/src/core_codemods/remove_assertion_in_pytest_raises.py new file mode 100644 index 00000000..f1b804ff --- /dev/null +++ b/src/core_codemods/remove_assertion_in_pytest_raises.py @@ -0,0 +1,110 @@ +from typing import Union +import libcst as cst +from codemodder.codemods.base_codemod import Metadata, Reference, ReviewGuidance +from codemodder.codemods.libcst_transformer import ( + LibcstResultTransformer, + LibcstTransformerPipeline, +) +from codemodder.codemods.utils_mixin import NameResolutionMixin +from core_codemods.api.core_codemod import CoreCodemod + + +class RemoveAssertionInPytestRaisesTransformer( + LibcstResultTransformer, NameResolutionMixin +): + change_description = "Moved assertion out of with statement body" + + def leave_With( + self, original_node: cst.With, updated_node: cst.With + ) -> Union[ + cst.BaseStatement, cst.FlattenSentinel[cst.BaseStatement], cst.RemovalSentinel + ]: + if not self.filter_by_path_includes_or_excludes(original_node): + return updated_node + + # Are all items pytest.raises? + for item in original_node.items: + match item: + case cst.WithItem(item=cst.Call() as call): + maybe_call_base_name = self.find_base_name(call) + if ( + not maybe_call_base_name + or maybe_call_base_name != "pytest.raises" + ): + return updated_node + + case _: + return updated_node + + assert_stmts: list[cst.SimpleStatementLine] = [] + assert_position = len(original_node.body.body) + new_statement_before_asserts = None + match original_node.body: + case cst.SimpleStatementSuite(): + for stmt in reversed(original_node.body.body): + match stmt: + case cst.Assert(): + assert_position = assert_position - 1 + assert_stmts.append( + cst.SimpleStatementLine( + body=[ + stmt.with_changes( + semicolon=cst.MaybeSentinel.DEFAULT + ) + ] + ) + ) + self.report_change(stmt) + case _: + break + new_statement_before_asserts = original_node.body.body[ + assert_position - 1 + ].with_changes(semicolon=cst.MaybeSentinel.DEFAULT) + + case cst.IndentedBlock(): + for simple_stmt in reversed(original_node.body.body): + match simple_stmt: + case cst.SimpleStatementLine(body=[*head, cst.Assert() as ast]): + assert_position = assert_position - 1 + self.report_change(ast) + if head: + # TODO foo(); assert 1; assert 2 + pass + else: + assert_stmts.append(simple_stmt) + case _: + break + new_statement_before_asserts = original_node.body.body[ + assert_position - 1 + ] + + if assert_stmts: + assert_stmts.reverse() + new_with = updated_node.with_changes( + body=updated_node.body.with_changes( + body=[ + *updated_node.body.body[: assert_position - 1], + new_statement_before_asserts, + ] + ) + ) + return cst.FlattenSentinel([new_with, *assert_stmts]) + + return updated_node + + +RemoveAssertionInPytestRaises = CoreCodemod( + metadata=Metadata( + name="remove-assertion-in-pytest-raises", + summary="Moves assertions out of with statement body", + review_guidance=ReviewGuidance.MERGE_WITHOUT_REVIEW, + references=[ + Reference( + url="https://docs.pytest.org/en/7.4.x/reference/reference.html#pytest-raises", + description="", + ), + ], + ), + transformer=LibcstTransformerPipeline(RemoveAssertionInPytestRaisesTransformer), + detector=None, +) diff --git a/tests/codemods/test_remove_assertion_in_pytest_raises.py b/tests/codemods/test_remove_assertion_in_pytest_raises.py new file mode 100644 index 00000000..3d095b0a --- /dev/null +++ b/tests/codemods/test_remove_assertion_in_pytest_raises.py @@ -0,0 +1,105 @@ +from core_codemods.remove_assertion_in_pytest_raises import ( + RemoveAssertionInPytestRaises, +) +from tests.codemods.base_codemod_test import BaseCodemodTest + + +class TestRemoveAssertionInPytestRaises(BaseCodemodTest): + codemod = RemoveAssertionInPytestRaises + + def test_name(self): + assert self.codemod.name == "remove-assertion-in-pytest-raises" + + def test_simple(self, tmpdir): + input_code = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError): + 1/0 + assert True + """ + expected = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError): + 1/0 + assert True + """ + self.run_and_assert(tmpdir, input_code, expected) + + def test_multiple_raises(self, tmpdir): + input_code = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError), pytest.raises(IndexError): + 1/0 + [1,2][3] + assert True + """ + expected = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError), pytest.raises(IndexError): + 1/0 + [1,2][3] + assert True + """ + self.run_and_assert(tmpdir, input_code, expected) + + def test_multiple_asserts(self, tmpdir): + input_code = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError): + 1/0 + assert 1 + assert 2 + """ + expected = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError): + 1/0 + assert 1 + assert 2 + """ + self.run_and_assert(tmpdir, input_code, expected, num_changes=2) + + def test_simple_suite(self, tmpdir): + input_code = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError): 1/0; assert True + """ + expected = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError): 1/0 + assert True + """ + self.run_and_assert(tmpdir, input_code, expected) + + def test_multiple_suite(self, tmpdir): + input_code = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError): 1/0; assert True; assert False; + """ + expected = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError): 1/0 + assert True + assert False + """ + self.run_and_assert(tmpdir, input_code, expected, num_changes=2) + + def test_with_item_not_raises(self, tmpdir): + input_code = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError), open('') as file: + 1/0 + assert True + """ + self.run_and_assert(tmpdir, input_code, input_code) diff --git a/tests/samples/remove_assertion_in_pytest_raises.py b/tests/samples/remove_assertion_in_pytest_raises.py new file mode 100644 index 00000000..449408c0 --- /dev/null +++ b/tests/samples/remove_assertion_in_pytest_raises.py @@ -0,0 +1,7 @@ +import pytest + +def test_foo(): + with pytest.raises(ZeroDivisionError): + error = 1/0 + assert 1 + assert 2 From c2798566380b8853144b1627758d2300a78b81fe Mon Sep 17 00:00:00 2001 From: andrecs <12188364+andrecsilva@users.noreply.github.com> Date: Fri, 19 Jan 2024 10:01:03 -0300 Subject: [PATCH 2/4] Added support for suite statement in indented block --- .../test_remove_assertion_in_pytest_raises.py | 2 +- .../remove_assertion_in_pytest_raises.py | 159 ++++++++++++------ .../test_remove_assertion_in_pytest_raises.py | 52 ++++++ 3 files changed, 156 insertions(+), 57 deletions(-) diff --git a/integration_tests/test_remove_assertion_in_pytest_raises.py b/integration_tests/test_remove_assertion_in_pytest_raises.py index 7cc3f482..5125c893 100644 --- a/integration_tests/test_remove_assertion_in_pytest_raises.py +++ b/integration_tests/test_remove_assertion_in_pytest_raises.py @@ -37,4 +37,4 @@ class TestRemoveAssertionInPytestRaises(BaseIntegrationTest): expected_line_change = "7" change_description = RemoveAssertionInPytestRaisesTransformer.change_description num_changed_files = 1 - num_changes = 4 + num_changes = 2 diff --git a/src/core_codemods/remove_assertion_in_pytest_raises.py b/src/core_codemods/remove_assertion_in_pytest_raises.py index f1b804ff..2a9c7ffa 100644 --- a/src/core_codemods/remove_assertion_in_pytest_raises.py +++ b/src/core_codemods/remove_assertion_in_pytest_raises.py @@ -1,4 +1,4 @@ -from typing import Union +from typing import Sequence, Union import libcst as cst from codemodder.codemods.base_codemod import Metadata, Reference, ReviewGuidance from codemodder.codemods.libcst_transformer import ( @@ -14,16 +14,8 @@ class RemoveAssertionInPytestRaisesTransformer( ): change_description = "Moved assertion out of with statement body" - def leave_With( - self, original_node: cst.With, updated_node: cst.With - ) -> Union[ - cst.BaseStatement, cst.FlattenSentinel[cst.BaseStatement], cst.RemovalSentinel - ]: - if not self.filter_by_path_includes_or_excludes(original_node): - return updated_node - - # Are all items pytest.raises? - for item in original_node.items: + def _all_pytest_raises(self, node: cst.With): + for item in node.items: match item: case cst.WithItem(item=cst.Call() as call): maybe_call_base_name = self.find_base_name(call) @@ -31,63 +23,118 @@ def leave_With( not maybe_call_base_name or maybe_call_base_name != "pytest.raises" ): - return updated_node + return False case _: - return updated_node + return False + return True + + def _build_simple_statement_line(self, node: cst.BaseSmallStatement): + return cst.SimpleStatementLine( + body=[node.with_changes(semicolon=cst.MaybeSentinel.DEFAULT)] + ) + + def _remove_last_asserts_from_suite(self, node: Sequence[cst.BaseSmallStatement]): + assert_position = len(node) + assert_stmts = [] + new_statement_before_asserts = None + for stmt in reversed(node): + match stmt: + case cst.Assert(): + assert_position = assert_position - 1 + assert_stmts.append(self._build_simple_statement_line(stmt)) + self.report_change(stmt) + case _: + break + if assert_position > 0: + new_statement_before_asserts = node[assert_position - 1].with_changes( + semicolon=cst.MaybeSentinel.DEFAULT + ) + return assert_stmts, assert_position, new_statement_before_asserts + + def _remove_last_asserts_from_IndentedBlock(self, node: cst.IndentedBlock): + assert_position = len(node.body) + assert_stmts = [] + new_statement_before_asserts = None + for simple_stmt in reversed(node.body): + match simple_stmt: + case cst.SimpleStatementLine(body=[*head, cst.Assert()] as body): + assert_position = assert_position - 1 + if head: + sstmts, s_pos, new_stmt = self._remove_last_asserts_from_suite( + body + ) + assert_stmts.extend(sstmts) + if new_stmt: + new_statement_before_asserts = new_stmt + new_statement_before_asserts = simple_stmt.with_changes( + body=[ + *body[: s_pos - 1], + body[s_pos - 1].with_changes( + semicolon=cst.MaybeSentinel.DEFAULT + ), + ] + ) + break + else: + assert_stmts.append(simple_stmt) + self.report_change(simple_stmt) + if new_statement_before_asserts: + break + case _: + if assert_position > 0: + new_statement_before_asserts = node.body[assert_position - 1] + break + assert_stmts.reverse() + return assert_stmts, assert_position, new_statement_before_asserts + + def leave_With( + self, original_node: cst.With, updated_node: cst.With + ) -> Union[ + cst.BaseStatement, cst.FlattenSentinel[cst.BaseStatement], cst.RemovalSentinel + ]: + if not self.filter_by_path_includes_or_excludes(original_node): + return updated_node + + # Are all items pytest.raises? + if not self._all_pytest_raises(original_node): + return updated_node assert_stmts: list[cst.SimpleStatementLine] = [] assert_position = len(original_node.body.body) new_statement_before_asserts = None match original_node.body: case cst.SimpleStatementSuite(): - for stmt in reversed(original_node.body.body): - match stmt: - case cst.Assert(): - assert_position = assert_position - 1 - assert_stmts.append( - cst.SimpleStatementLine( - body=[ - stmt.with_changes( - semicolon=cst.MaybeSentinel.DEFAULT - ) - ] - ) - ) - self.report_change(stmt) - case _: - break - new_statement_before_asserts = original_node.body.body[ - assert_position - 1 - ].with_changes(semicolon=cst.MaybeSentinel.DEFAULT) - + ( + assert_stmts, + assert_position, + new_statement_before_asserts, + ) = self._remove_last_asserts_from_suite(original_node.body.body) + assert_stmts.reverse() case cst.IndentedBlock(): - for simple_stmt in reversed(original_node.body.body): - match simple_stmt: - case cst.SimpleStatementLine(body=[*head, cst.Assert() as ast]): - assert_position = assert_position - 1 - self.report_change(ast) - if head: - # TODO foo(); assert 1; assert 2 - pass - else: - assert_stmts.append(simple_stmt) - case _: - break - new_statement_before_asserts = original_node.body.body[ - assert_position - 1 - ] + ( + assert_stmts, + assert_position, + new_statement_before_asserts, + ) = self._remove_last_asserts_from_IndentedBlock(original_node.body) if assert_stmts: - assert_stmts.reverse() - new_with = updated_node.with_changes( - body=updated_node.body.with_changes( - body=[ - *updated_node.body.body[: assert_position - 1], - new_statement_before_asserts, - ] + # this means all the statements are asserts + if new_statement_before_asserts: + new_with = updated_node.with_changes( + body=updated_node.body.with_changes( + body=[ + *updated_node.body.body[: assert_position - 1], + new_statement_before_asserts, + ] + ) + ) + else: + new_with = updated_node.with_changes( + body=updated_node.body.with_changes( + body=[cst.SimpleStatementLine(body=[cst.Pass()])] + ) ) - ) return cst.FlattenSentinel([new_with, *assert_stmts]) return updated_node diff --git a/tests/codemods/test_remove_assertion_in_pytest_raises.py b/tests/codemods/test_remove_assertion_in_pytest_raises.py index 3d095b0a..7f18332c 100644 --- a/tests/codemods/test_remove_assertion_in_pytest_raises.py +++ b/tests/codemods/test_remove_assertion_in_pytest_raises.py @@ -27,6 +27,23 @@ def foo(): """ self.run_and_assert(tmpdir, input_code, expected) + def test_all_asserts(self, tmpdir): + # this is more of an edge case + input_code = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError): + assert True + """ + expected = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError): + pass + assert True + """ + self.run_and_assert(tmpdir, input_code, expected) + def test_multiple_raises(self, tmpdir): input_code = """\ import pytest @@ -65,6 +82,41 @@ def foo(): """ self.run_and_assert(tmpdir, input_code, expected, num_changes=2) + def test_multiple_asserts_mixed_early(self, tmpdir): + input_code = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError): + 1/0; assert 1; assert 2 + """ + expected = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError): + 1/0 + assert 1 + assert 2 + """ + self.run_and_assert(tmpdir, input_code, expected, num_changes=2) + + def test_multiple_asserts_mixed(self, tmpdir): + input_code = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError): + 1/0 + assert 1; assert 2 + """ + expected = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError): + 1/0 + assert 1 + assert 2 + """ + self.run_and_assert(tmpdir, input_code, expected, num_changes=2) + def test_simple_suite(self, tmpdir): input_code = """\ import pytest From e513ed5e4bb87184bdb3a3205f07bfe79428f878 Mon Sep 17 00:00:00 2001 From: andrecs <12188364+andrecsilva@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:21:26 -0300 Subject: [PATCH 3/4] Changed reported node and more tests --- .../test_remove_assertion_in_pytest_raises.py | 4 +- .../remove_assertion_in_pytest_raises.py | 9 +-- .../test_remove_assertion_in_pytest_raises.py | 68 +++++++++++++++++-- 3 files changed, 71 insertions(+), 10 deletions(-) diff --git a/integration_tests/test_remove_assertion_in_pytest_raises.py b/integration_tests/test_remove_assertion_in_pytest_raises.py index 5125c893..9e2e9975 100644 --- a/integration_tests/test_remove_assertion_in_pytest_raises.py +++ b/integration_tests/test_remove_assertion_in_pytest_raises.py @@ -34,7 +34,7 @@ class TestRemoveAssertionInPytestRaises(BaseIntegrationTest): ) # fmt: on - expected_line_change = "7" + expected_line_change = "4" change_description = RemoveAssertionInPytestRaisesTransformer.change_description num_changed_files = 1 - num_changes = 2 + num_changes = 1 diff --git a/src/core_codemods/remove_assertion_in_pytest_raises.py b/src/core_codemods/remove_assertion_in_pytest_raises.py index 2a9c7ffa..1ae84442 100644 --- a/src/core_codemods/remove_assertion_in_pytest_raises.py +++ b/src/core_codemods/remove_assertion_in_pytest_raises.py @@ -43,7 +43,6 @@ def _remove_last_asserts_from_suite(self, node: Sequence[cst.BaseSmallStatement] case cst.Assert(): assert_position = assert_position - 1 assert_stmts.append(self._build_simple_statement_line(stmt)) - self.report_change(stmt) case _: break if assert_position > 0: @@ -78,7 +77,6 @@ def _remove_last_asserts_from_IndentedBlock(self, node: cst.IndentedBlock): break else: assert_stmts.append(simple_stmt) - self.report_change(simple_stmt) if new_statement_before_asserts: break case _: @@ -93,7 +91,9 @@ def leave_With( ) -> Union[ cst.BaseStatement, cst.FlattenSentinel[cst.BaseStatement], cst.RemovalSentinel ]: - if not self.filter_by_path_includes_or_excludes(original_node): + if not self.filter_by_path_includes_or_excludes( + self.node_position(original_node) + ): return updated_node # Are all items pytest.raises? @@ -135,6 +135,7 @@ def leave_With( body=[cst.SimpleStatementLine(body=[cst.Pass()])] ) ) + self.report_change(original_node) return cst.FlattenSentinel([new_with, *assert_stmts]) return updated_node @@ -143,7 +144,7 @@ def leave_With( RemoveAssertionInPytestRaises = CoreCodemod( metadata=Metadata( name="remove-assertion-in-pytest-raises", - summary="Moves assertions out of with statement body", + summary="Moves assertions out of `pytest.raises` scope", review_guidance=ReviewGuidance.MERGE_WITHOUT_REVIEW, references=[ Reference( diff --git a/tests/codemods/test_remove_assertion_in_pytest_raises.py b/tests/codemods/test_remove_assertion_in_pytest_raises.py index 7f18332c..6ce94889 100644 --- a/tests/codemods/test_remove_assertion_in_pytest_raises.py +++ b/tests/codemods/test_remove_assertion_in_pytest_raises.py @@ -27,6 +27,40 @@ def foo(): """ self.run_and_assert(tmpdir, input_code, expected) + def test_simple_alias(self, tmpdir): + input_code = """\ + from pytest import raises as rise + def foo(): + with rise(ZeroDivisionError): + 1/0 + assert True + """ + expected = """\ + from pytest import raises as rise + def foo(): + with rise(ZeroDivisionError): + 1/0 + assert True + """ + self.run_and_assert(tmpdir, input_code, expected) + + def test_simple_from_import(self, tmpdir): + input_code = """\ + from pytest import raises + def foo(): + with raises(ZeroDivisionError): + 1/0 + assert True + """ + expected = """\ + from pytest import raises + def foo(): + with raises(ZeroDivisionError): + 1/0 + assert True + """ + self.run_and_assert(tmpdir, input_code, expected) + def test_all_asserts(self, tmpdir): # this is more of an edge case input_code = """\ @@ -80,7 +114,7 @@ def foo(): assert 1 assert 2 """ - self.run_and_assert(tmpdir, input_code, expected, num_changes=2) + self.run_and_assert(tmpdir, input_code, expected) def test_multiple_asserts_mixed_early(self, tmpdir): input_code = """\ @@ -97,7 +131,7 @@ def foo(): assert 1 assert 2 """ - self.run_and_assert(tmpdir, input_code, expected, num_changes=2) + self.run_and_assert(tmpdir, input_code, expected) def test_multiple_asserts_mixed(self, tmpdir): input_code = """\ @@ -115,7 +149,7 @@ def foo(): assert 1 assert 2 """ - self.run_and_assert(tmpdir, input_code, expected, num_changes=2) + self.run_and_assert(tmpdir, input_code, expected) def test_simple_suite(self, tmpdir): input_code = """\ @@ -144,7 +178,7 @@ def foo(): assert True assert False """ - self.run_and_assert(tmpdir, input_code, expected, num_changes=2) + self.run_and_assert(tmpdir, input_code, expected) def test_with_item_not_raises(self, tmpdir): input_code = """\ @@ -155,3 +189,29 @@ def foo(): assert True """ self.run_and_assert(tmpdir, input_code, input_code) + + def test_no_assertion_at_end(self, tmpdir): + input_code = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError), open('') as file: + assert True + 1/0 + """ + self.run_and_assert(tmpdir, input_code, input_code) + + def test_exclude_line(self, tmpdir): + input_code = expected = """\ + import pytest + def foo(): + with pytest.raises(ZeroDivisionError), open('') as file: + 1/0 + assert True + """ + lines_to_exclude = [2] + self.run_and_assert( + tmpdir, + input_code, + expected, + lines_to_exclude=lines_to_exclude, + ) From 2299d70766adb6b949d4045b8ab73d2d77bf8baf Mon Sep 17 00:00:00 2001 From: andrecs <12188364+andrecsilva@users.noreply.github.com> Date: Mon, 22 Jan 2024 08:16:26 -0300 Subject: [PATCH 4/4] Removed includes/excludes check and test --- .../remove_assertion_in_pytest_raises.py | 6 ++---- .../test_remove_assertion_in_pytest_raises.py | 16 ---------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/core_codemods/remove_assertion_in_pytest_raises.py b/src/core_codemods/remove_assertion_in_pytest_raises.py index 1ae84442..a0098b5f 100644 --- a/src/core_codemods/remove_assertion_in_pytest_raises.py +++ b/src/core_codemods/remove_assertion_in_pytest_raises.py @@ -91,10 +91,8 @@ def leave_With( ) -> Union[ cst.BaseStatement, cst.FlattenSentinel[cst.BaseStatement], cst.RemovalSentinel ]: - if not self.filter_by_path_includes_or_excludes( - self.node_position(original_node) - ): - return updated_node + # TODO: add filter by include or exclude that works for nodes + # that that have different start/end numbers. # Are all items pytest.raises? if not self._all_pytest_raises(original_node): diff --git a/tests/codemods/test_remove_assertion_in_pytest_raises.py b/tests/codemods/test_remove_assertion_in_pytest_raises.py index 6ce94889..a60b9abc 100644 --- a/tests/codemods/test_remove_assertion_in_pytest_raises.py +++ b/tests/codemods/test_remove_assertion_in_pytest_raises.py @@ -199,19 +199,3 @@ def foo(): 1/0 """ self.run_and_assert(tmpdir, input_code, input_code) - - def test_exclude_line(self, tmpdir): - input_code = expected = """\ - import pytest - def foo(): - with pytest.raises(ZeroDivisionError), open('') as file: - 1/0 - assert True - """ - lines_to_exclude = [2] - self.run_and_assert( - tmpdir, - input_code, - expected, - lines_to_exclude=lines_to_exclude, - )