-
-
Notifications
You must be signed in to change notification settings - Fork 32.1k
gh-129598: allow multi stmts for ast single with ';' #129620
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
992e404
gh-129598: allow multi stmts for ast single with ';'
tom-pytel fb200e4
📜🤖 Added by blurb_it.
blurb-it[bot] 766f2bc
allowsemi -> allow_semi and test
tom-pytel fe48e7e
requested changes
tom-pytel e8f2896
test cleanup
tom-pytel 9c525cd
add try-finally to top level ast.mod unparse
tom-pytel fc0d9ce
Merge branch 'main' into fix-issue-129598
tom-pytel 4bce2f6
Merge branch 'main' into fix-issue-129598
tom-pytel ba23ffe
Merge branch 'main' into fix-issue-129598
tom-pytel 0833ab2
Merge branch 'main' into fix-issue-129598
tom-pytel 70102d6
split up test
tom-pytel 56c87ac
Merge branch 'main' into fix-issue-129598
tom-pytel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 |
---|---|---|
|
@@ -674,6 +674,7 @@ def __init__(self): | |
self._type_ignores = {} | ||
self._indent = 0 | ||
self._in_try_star = False | ||
self._in_interactive = False | ||
|
||
def interleave(self, inter, f, seq): | ||
"""Call f on each item in seq, calling inter() in between.""" | ||
|
@@ -702,11 +703,20 @@ def maybe_newline(self): | |
if self._source: | ||
self.write("\n") | ||
|
||
def fill(self, text=""): | ||
def maybe_semicolon(self): | ||
"""Adds a "; " delimiter if it isn't the start of generated source""" | ||
if self._source: | ||
self.write("; ") | ||
|
||
def fill(self, text="", *, allow_semicolon=True): | ||
"""Indent a piece of text and append it, according to the current | ||
indentation level""" | ||
self.maybe_newline() | ||
self.write(" " * self._indent + text) | ||
indentation level, or only delineate with semicolon if applicable""" | ||
if self._in_interactive and not self._indent and allow_semicolon: | ||
self.maybe_semicolon() | ||
self.write(text) | ||
picnixz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
else: | ||
self.maybe_newline() | ||
self.write(" " * self._indent + text) | ||
|
||
def write(self, *text): | ||
"""Add new source parts""" | ||
|
@@ -812,8 +822,17 @@ def visit_Module(self, node): | |
ignore.lineno: f"ignore{ignore.tag}" | ||
for ignore in node.type_ignores | ||
} | ||
self._write_docstring_and_traverse_body(node) | ||
self._type_ignores.clear() | ||
try: | ||
self._write_docstring_and_traverse_body(node) | ||
finally: | ||
self._type_ignores.clear() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another question is whether the mapping precedences should be cleared in visit() as well. |
||
|
||
def visit_Interactive(self, node): | ||
self._in_interactive = True | ||
try: | ||
self._write_docstring_and_traverse_body(node) | ||
finally: | ||
self._in_interactive = False | ||
|
||
def visit_FunctionType(self, node): | ||
with self.delimit("(", ")"): | ||
|
@@ -945,17 +964,17 @@ def visit_Raise(self, node): | |
self.traverse(node.cause) | ||
|
||
def do_visit_try(self, node): | ||
self.fill("try") | ||
self.fill("try", allow_semicolon=False) | ||
with self.block(): | ||
self.traverse(node.body) | ||
for ex in node.handlers: | ||
self.traverse(ex) | ||
if node.orelse: | ||
self.fill("else") | ||
self.fill("else", allow_semicolon=False) | ||
with self.block(): | ||
self.traverse(node.orelse) | ||
if node.finalbody: | ||
self.fill("finally") | ||
self.fill("finally", allow_semicolon=False) | ||
with self.block(): | ||
self.traverse(node.finalbody) | ||
|
||
|
@@ -976,7 +995,7 @@ def visit_TryStar(self, node): | |
self._in_try_star = prev_in_try_star | ||
|
||
def visit_ExceptHandler(self, node): | ||
self.fill("except*" if self._in_try_star else "except") | ||
self.fill("except*" if self._in_try_star else "except", allow_semicolon=False) | ||
if node.type: | ||
self.write(" ") | ||
self.traverse(node.type) | ||
|
@@ -989,9 +1008,9 @@ def visit_ExceptHandler(self, node): | |
def visit_ClassDef(self, node): | ||
self.maybe_newline() | ||
for deco in node.decorator_list: | ||
self.fill("@") | ||
self.fill("@", allow_semicolon=False) | ||
self.traverse(deco) | ||
self.fill("class " + node.name) | ||
self.fill("class " + node.name, allow_semicolon=False) | ||
if hasattr(node, "type_params"): | ||
self._type_params_helper(node.type_params) | ||
with self.delimit_if("(", ")", condition = node.bases or node.keywords): | ||
|
@@ -1021,10 +1040,10 @@ def visit_AsyncFunctionDef(self, node): | |
def _function_helper(self, node, fill_suffix): | ||
self.maybe_newline() | ||
for deco in node.decorator_list: | ||
self.fill("@") | ||
self.fill("@", allow_semicolon=False) | ||
self.traverse(deco) | ||
def_str = fill_suffix + " " + node.name | ||
self.fill(def_str) | ||
self.fill(def_str, allow_semicolon=False) | ||
if hasattr(node, "type_params"): | ||
self._type_params_helper(node.type_params) | ||
with self.delimit("(", ")"): | ||
|
@@ -1075,54 +1094,54 @@ def visit_AsyncFor(self, node): | |
self._for_helper("async for ", node) | ||
|
||
def _for_helper(self, fill, node): | ||
self.fill(fill) | ||
self.fill(fill, allow_semicolon=False) | ||
self.set_precedence(_Precedence.TUPLE, node.target) | ||
self.traverse(node.target) | ||
self.write(" in ") | ||
self.traverse(node.iter) | ||
with self.block(extra=self.get_type_comment(node)): | ||
self.traverse(node.body) | ||
if node.orelse: | ||
self.fill("else") | ||
self.fill("else", allow_semicolon=False) | ||
with self.block(): | ||
self.traverse(node.orelse) | ||
|
||
def visit_If(self, node): | ||
self.fill("if ") | ||
self.fill("if ", allow_semicolon=False) | ||
self.traverse(node.test) | ||
with self.block(): | ||
self.traverse(node.body) | ||
# collapse nested ifs into equivalent elifs. | ||
while node.orelse and len(node.orelse) == 1 and isinstance(node.orelse[0], If): | ||
node = node.orelse[0] | ||
self.fill("elif ") | ||
self.fill("elif ", allow_semicolon=False) | ||
self.traverse(node.test) | ||
with self.block(): | ||
self.traverse(node.body) | ||
# final else | ||
if node.orelse: | ||
self.fill("else") | ||
self.fill("else", allow_semicolon=False) | ||
with self.block(): | ||
self.traverse(node.orelse) | ||
|
||
def visit_While(self, node): | ||
self.fill("while ") | ||
self.fill("while ", allow_semicolon=False) | ||
self.traverse(node.test) | ||
with self.block(): | ||
self.traverse(node.body) | ||
if node.orelse: | ||
self.fill("else") | ||
self.fill("else", allow_semicolon=False) | ||
with self.block(): | ||
self.traverse(node.orelse) | ||
|
||
def visit_With(self, node): | ||
self.fill("with ") | ||
self.fill("with ", allow_semicolon=False) | ||
self.interleave(lambda: self.write(", "), self.traverse, node.items) | ||
with self.block(extra=self.get_type_comment(node)): | ||
self.traverse(node.body) | ||
|
||
def visit_AsyncWith(self, node): | ||
self.fill("async with ") | ||
self.fill("async with ", allow_semicolon=False) | ||
self.interleave(lambda: self.write(", "), self.traverse, node.items) | ||
with self.block(extra=self.get_type_comment(node)): | ||
self.traverse(node.body) | ||
|
@@ -1264,7 +1283,7 @@ def visit_Name(self, node): | |
self.write(node.id) | ||
|
||
def _write_docstring(self, node): | ||
self.fill() | ||
self.fill(allow_semicolon=False) | ||
if node.kind == "u": | ||
self.write("u") | ||
self._write_str_avoiding_backslashes(node.value, quote_types=_MULTI_QUOTES) | ||
|
@@ -1558,7 +1577,7 @@ def visit_Slice(self, node): | |
self.traverse(node.step) | ||
|
||
def visit_Match(self, node): | ||
self.fill("match ") | ||
self.fill("match ", allow_semicolon=False) | ||
self.traverse(node.subject) | ||
with self.block(): | ||
for case in node.cases: | ||
|
@@ -1652,7 +1671,7 @@ def visit_withitem(self, node): | |
self.traverse(node.optional_vars) | ||
|
||
def visit_match_case(self, node): | ||
self.fill("case ") | ||
self.fill("case ", allow_semicolon=False) | ||
self.traverse(node.pattern) | ||
if node.guard: | ||
self.write(" if ") | ||
|
This file contains hidden or 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
1 change: 1 addition & 0 deletions
1
Misc/NEWS.d/next/Library/2025-02-03-16-27-14.gh-issue-129598.0js33I.rst
This file contains hidden or 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 @@ | ||
Fix :func:`ast.unparse` when :class:`ast.Interactive` contains multiple statements. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.