Skip to content

Commit 6b483c6

Browse files
authored
Add codemod to rename typing aliases of builtins (#1267)
* add codemod to rename typing aliases of builtins * format
1 parent 403782d commit 6b483c6

4 files changed

Lines changed: 84 additions & 1 deletion

File tree

libcst/codemod/commands/rename.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ def leave_Module(
328328
# If bypass_import is False, we know that no import statements were directly renamed, and the fact
329329
# that we have any `self.scheduled_removals` tells us we encountered a matching `old_name` in the code.
330330
if not self.bypass_import and self.scheduled_removals:
331-
if self.new_module:
331+
if self.new_module and self.new_module != "builtins":
332332
new_obj: Optional[str] = (
333333
self.new_mod_or_obj.split(".")[0] if self.new_mod_or_obj else None
334334
)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
#
6+
# pyre-strict
7+
from functools import partial
8+
from typing import cast, Generator
9+
10+
from libcst.codemod import Codemod, MagicArgsCodemodCommand
11+
from libcst.codemod.commands.rename import RenameCommand
12+
13+
14+
class RenameTypingGenericAliases(MagicArgsCodemodCommand):
15+
DESCRIPTION: str = (
16+
"Rename typing module aliases of builtin generics in Python 3.9+, for example: `typing.List` -> `list`"
17+
)
18+
19+
MAPPING: dict[str, str] = {
20+
"typing.List": "builtins.list",
21+
"typing.Tuple": "builtins.tuple",
22+
"typing.Dict": "builtins.dict",
23+
"typing.FrozenSet": "builtins.frozenset",
24+
"typing.Set": "builtins.set",
25+
"typing.Type": "builtins.type",
26+
}
27+
28+
def get_transforms(self) -> Generator[type[Codemod], None, None]:
29+
for from_type, to_type in self.MAPPING.items():
30+
yield cast(
31+
type[Codemod],
32+
partial(
33+
RenameCommand,
34+
old_name=from_type,
35+
new_name=to_type,
36+
),
37+
)

libcst/codemod/commands/tests/test_rename.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,19 @@ def test() -> None:
2828

2929
self.assertCodemod(before, after, old_name="foo.bar", new_name="baz.qux")
3030

31+
def test_rename_to_builtin(self) -> None:
32+
before = """
33+
from typing import List
34+
x: List[int] = []
35+
"""
36+
after = """
37+
x: list[int] = []
38+
"""
39+
40+
self.assertCodemod(
41+
before, after, old_name="typing.List", new_name="builtins.list"
42+
)
43+
3144
def test_rename_name_asname(self) -> None:
3245
before = """
3346
from foo import bar as bla
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
#
6+
# pyre-strict
7+
8+
from libcst.codemod import CodemodTest
9+
from libcst.codemod.commands.rename_typing_generic_aliases import (
10+
RenameTypingGenericAliases,
11+
)
12+
13+
14+
class TestRenameCommand(CodemodTest):
15+
TRANSFORM = RenameTypingGenericAliases
16+
17+
def test_rename_typing_generic_alias(self) -> None:
18+
before = """
19+
from typing import List, Set, Dict, FrozenSet, Tuple
20+
x: List[int] = []
21+
y: Set[int] = set()
22+
z: Dict[str, int] = {}
23+
a: FrozenSet[str] = frozenset()
24+
b: Tuple[int, str] = (1, "hello")
25+
"""
26+
after = """
27+
x: list[int] = []
28+
y: set[int] = set()
29+
z: dict[str, int] = {}
30+
a: frozenset[str] = frozenset()
31+
b: tuple[int, str] = (1, "hello")
32+
"""
33+
self.assertCodemod(before, after)

0 commit comments

Comments
 (0)