Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- #623, #819, #863 Support MatchOr, MatchSequence, MatchStar (@jheld, @lieryan)
- #870 Add default implementation for is_dir() (@lieryan)
- #872 Fix unicode handling in patchedast (@lieryan)
- Support PEP 695 type parameters on class definitions and type aliases in patchedast
Comment thread
lieryan marked this conversation as resolved.
Outdated

# Release 1.14.0

Expand Down
15 changes: 11 additions & 4 deletions rope/refactor/patchedast.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ def _ClassDef(self, node):
for decorator in node.decorator_list:
children.extend(("@", decorator))
children.extend(["class", node.name])
self._add_type_params(node, children)
if node.bases:
children.append("(")
children.extend(self._child_nodes(node.bases, ","))
Expand Down Expand Up @@ -493,9 +494,7 @@ def _handle_function_def_node(self, node, is_async):
children.extend(("@", decorator))
children.extend(["async", "def"] if is_async else ["def"])
children.append(node.name)
type_params = getattr(node, "type_params", [])
if type_params:
children.extend(["[", *self._child_nodes(type_params, ","), "]"])
self._add_type_params(node, children)
children.extend(["(", node.args, ")"])
children.append(":")
children.extend(node.body)
Expand Down Expand Up @@ -872,9 +871,17 @@ def _MatchMapping(self, node):
self._handle(node, children)

def _TypeAlias(self, node):
children = ["type", node.name, node.value]
children = ["type", node.name]
self._add_type_params(node, children)
children.append(node.value)
self._handle(node, children)

def _add_type_params(self, node, children):
"""Append the PEP 695 ``[T, ...]`` clause of a def, class or type alias."""
type_params = getattr(node, "type_params", [])
if type_params:
children.extend(["[", *self._child_nodes(type_params, ","), "]"])

def _TypeVar(self, node):
children = [node.name]
if node.bound:
Expand Down
61 changes: 61 additions & 0 deletions ropetest/refactor/patchedasttest.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,67 @@ def test_handling_format_strings_basic(self):
checker.check_children("JoinedStr", ['f"', "abc", "FormattedValue", "", '"'])
checker.check_children("FormattedValue", ["{", "", "Name", "", "}"])

@testutils.only_for_versions_higher("3.12")
def test_handling_pep695_type_alias(self):
source = "type Alias[T] = list[T]\n"
Comment thread
lieryan marked this conversation as resolved.
Outdated
ast_frag = patchedast.get_patched_ast(source, True)
checker = _ResultChecker(self, ast_frag)
checker.check_children(
"TypeAlias",
["type", " ", "Name", "", "[", "", "TypeVar", "", "]", " = ",
"Subscript"],
)

@testutils.only_for_versions_higher("3.12")
def test_handling_pep695_generic_function(self):
# TP occurs only in the type parameter list, so its region can only
# come from the "[TP]" clause rendered between the name and "(".
source = "def f[TP](x):\n return x\n"
Comment thread
lieryan marked this conversation as resolved.
Outdated
ast_frag = patchedast.get_patched_ast(source, True)
checker = _ResultChecker(self, ast_frag)
checker.check_children(
"FunctionDef",
["def", " ", "f", "", "[", "", "TypeVar", "", "]", "", "(", "",
"arguments", "", ")", "", ":", "\n ", "Return"],
)
start = source.index("TP")
checker.check_region("TypeVar", start, start + len("TP"))

@testutils.only_for_versions_higher("3.12")
def test_handling_pep695_generic_class(self):
source = "class C[TP]:\n pass\n"
Comment thread
lieryan marked this conversation as resolved.
Outdated
ast_frag = patchedast.get_patched_ast(source, True)
checker = _ResultChecker(self, ast_frag)
checker.check_children(
"ClassDef",
["class", " ", "C", "", "[", "", "TypeVar", "", "]", "", ":",
"\n ", "Pass"],
)
start = source.index("TP")
checker.check_region("TypeVar", start, start + len("TP"))

@testutils.only_for_versions_higher("3.10")
def test_handling_match_sequence_and_star(self):
source = "match x:\n case [1, *rest]:\n pass\n"
Comment thread
lieryan marked this conversation as resolved.
Outdated
ast_frag = patchedast.get_patched_ast(source, True)
checker = _ResultChecker(self, ast_frag)
checker.check_children(
"MatchSequence",
["[", "", "MatchValue", "", ",", " ", "MatchStar", "", "]"],
)
start = source.index("[1, *rest]")
checker.check_region("MatchSequence", start, start + len("[1, *rest]"))
checker.check_children("MatchStar", ["*", "", "rest"])

@testutils.only_for_versions_higher("3.10")
def test_handling_match_or_and_singleton(self):
source = "match x:\n case 1 | None:\n pass\n"
Comment thread
lieryan marked this conversation as resolved.
Outdated
ast_frag = patchedast.get_patched_ast(source, True)
checker = _ResultChecker(self, ast_frag)
checker.check_children(
"MatchOr", ["MatchValue", " ", "|", " ", "MatchSingleton"]
)

@testutils.only_for_versions_higher("3.6")
def test_handling_format_strings_with_implicit_join(self):
source = '''"1" + rf'abc{a}' f"""xxx{b} """\n'''
Expand Down
Loading