diff --git a/libcst/codemod/commands/rename.py b/libcst/codemod/commands/rename.py index 8b6302c9..9ad4334d 100644 --- a/libcst/codemod/commands/rename.py +++ b/libcst/codemod/commands/rename.py @@ -214,7 +214,7 @@ def leave_ImportFrom( return updated_node else: - new_names = [] + new_names: list[cst.ImportAlias] = [] for import_alias in names: alias_name = get_full_name_for_node(import_alias.name) if alias_name is not None: @@ -252,6 +252,10 @@ def leave_ImportFrom( # This import might be in use elsewhere in the code, so schedule a potential removal. self.scheduled_removals.add(original_node) new_names.append(import_alias) + if isinstance(new_names[-1].comma, cst.Comma): + new_names[-1] = new_names[-1].with_changes( + comma=cst.MaybeSentinel.DEFAULT + ) return updated_node.with_changes(names=new_names) return updated_node diff --git a/libcst/codemod/commands/tests/test_rename.py b/libcst/codemod/commands/tests/test_rename.py index 20e1c7d4..8245a34c 100644 --- a/libcst/codemod/commands/tests/test_rename.py +++ b/libcst/codemod/commands/tests/test_rename.py @@ -382,6 +382,28 @@ class Foo(d.z): new_name="d.z", ) + def test_comma_import(self) -> None: + before = """ + import a, b, c + + class Foo(a.z): + bar: b.bar + baz: c.baz + """ + after = """ + import a, b, d + + class Foo(a.z): + bar: b.bar + baz: d.baz + """ + self.assertCodemod( + before, + after, + old_name="c.baz", + new_name="d.baz", + ) + def test_other_import_froms_untouched(self) -> None: before = """ from a import b, c, d @@ -405,6 +427,29 @@ class Foo(b): new_name="f.b", ) + def test_comma_import_from(self) -> None: + before = """ + from a import b, c, d + + class Foo(b): + bar: c.bar + baz: d.baz + """ + after = """ + from a import b, c + from f import d + + class Foo(b): + bar: c.bar + baz: d.baz + """ + self.assertCodemod( + before, + after, + old_name="a.d", + new_name="f.d", + ) + def test_no_removal_of_import_in_use(self) -> None: before = """ import a