-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* ♻️ refactor: use new structure * ♻ refactor: use pep group * ✅ test: add more pep695 case * 🔧 config: use pytest-cov * 🐛 fix: shield space in libcst * ✨ feat: support pep604 * 🐛 fix: fix deps in workflow * 🐛 fix: use m.matches instead of cst.ensure_type
- Loading branch information
Showing
15 changed files
with
319 additions
and
82 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
from .__version__ import __version__ | ||
from .utils import apply_transformer, transfer_code, transfer_file |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
from .transform_match import TransformMatchCommand | ||
from .transform_type_parameters import TransformTypeParametersCommand | ||
from .pep622 import TransformMatchCommand | ||
from .pep695 import TransformTypeParametersCommand |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .union_types import TransformUnionTypesCommand |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
from __future__ import annotations | ||
|
||
import libcst as cst | ||
from libcst import matchers as m | ||
from libcst.codemod import CodemodContext, VisitorBasedCodemodCommand | ||
from libcst.codemod.visitors import AddImportsVisitor | ||
from libcst.metadata import ScopeProvider | ||
|
||
|
||
class TransformUnionTypesCommand(VisitorBasedCodemodCommand): | ||
METADATA_DEPENDENCIES = (ScopeProvider,) | ||
|
||
def __init__(self, context: CodemodContext) -> None: | ||
super().__init__(context) | ||
|
||
def transform_union(self, op: cst.BinaryOperation) -> cst.Subscript | None: | ||
if not isinstance(op.operator, cst.BitOr): | ||
return None | ||
if isinstance((left := op.left), cst.BinaryOperation): | ||
left = self.transform_union(left) or left | ||
if isinstance((right := op.right), cst.BinaryOperation): | ||
right = self.transform_union(right) or right | ||
slices = [ | ||
cst.SubscriptElement( | ||
slice=cst.Index(value=left), | ||
), | ||
cst.SubscriptElement( | ||
slice=cst.Index(value=right), | ||
), | ||
] | ||
return cst.Subscript( | ||
value=cst.Name( | ||
value="Union", | ||
lpar=[], | ||
rpar=[], | ||
), | ||
slice=slices, | ||
) | ||
|
||
def leave_Call(self, original_node: cst.Call, updated_node: cst.Call): | ||
if not m.matches(original_node.func, m.Name("isinstance") | m.Name("issubclass")): | ||
return updated_node | ||
args = original_node.args | ||
|
||
if ( | ||
isinstance(cls_info := args[1].value, cst.BinaryOperation) | ||
and (cls_info := self.transform_union(cls_info)) is not None | ||
): | ||
return updated_node.with_changes( | ||
args=[ | ||
args[0], | ||
cst.Arg(cls_info), | ||
] | ||
) | ||
|
||
return updated_node | ||
|
||
def leave_Annotation(self, original_node: cst.Annotation, updated_node: cst.Annotation): | ||
if ( | ||
isinstance((op := original_node.annotation), cst.BinaryOperation) | ||
and (new_annotation := self.transform_union(op)) is not None | ||
): | ||
AddImportsVisitor.add_needed_import(self.context, "typing", "Union") | ||
return updated_node.with_changes(annotation=new_annotation) | ||
return updated_node |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .match import TransformMatchCommand |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .type_parameters import TransformTypeParametersCommand |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
from __future__ import annotations | ||
|
||
import libcst as cst | ||
import pytest | ||
from libcst.codemod import CodemodContext | ||
|
||
from pyfuture.codemod import TransformTypeParametersCommand | ||
|
||
|
||
def print_string(s: str): | ||
cc = s.split("\n") | ||
for c in cc[:-1]: | ||
print(f"\"{c.replace("\"", "\\\"")}\\n\"") | ||
print(f"\"{cc[-1].replace("\"", "\\\"")}\"") | ||
|
||
|
||
@pytest.mark.parametrize( | ||
("src", "expected"), | ||
( | ||
pytest.param( | ||
"def test(x: int) -> int:\n" " return x", | ||
"def test(x: int) -> int:\n" " return x", | ||
id="no generic function", | ||
), | ||
pytest.param( | ||
"def test[T: int](x: T) -> T:\n" " return x", | ||
"from typing import TypeVar\n" | ||
"\n" | ||
"def __wrapper_func_test():\n" | ||
' __test_T = TypeVar("__test_T", bound = int)\n' | ||
" def test(x: __test_T) -> __test_T:\n" | ||
" return x\n" | ||
" return test\n" | ||
"test = __wrapper_func_test()", | ||
id="single bound function", | ||
), | ||
pytest.param( | ||
"def test[T: int | str](x: T) -> T:\n" " return x", | ||
"from typing import TypeVar\n\n" | ||
"def __wrapper_func_test():\n" | ||
' __test_T = TypeVar("__test_T", bound = int | str)\n' | ||
" def test(x: __test_T) -> __test_T:\n" | ||
" return x\n" | ||
" return test\n" | ||
"test = __wrapper_func_test()", | ||
id="union bonud function", | ||
), | ||
), | ||
) | ||
def test_type_parameters(src: str, expected: str): | ||
module = cst.parse_module(src) | ||
new_module = TransformTypeParametersCommand(CodemodContext()).transform_module(module) | ||
print_string(new_module.code) | ||
assert new_module.code == expected |