-
-
Notifications
You must be signed in to change notification settings - Fork 2
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
implement autofix for TRIO100 #149
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -100,20 +100,20 @@ def visit(self, node: ast.AST): | |
|
||
|
||
class Flake8TrioRunner_cst: | ||
def __init__(self, options: Namespace): | ||
def __init__(self, options: Namespace, module: Module): | ||
super().__init__() | ||
self.state = SharedState(options) | ||
self.options = options | ||
self.visitors: tuple[Flake8TrioVisitor_cst, ...] = tuple( | ||
v(self.state) for v in ERROR_CLASSES_CST if self.selected(v.error_codes) | ||
) | ||
self.module = module | ||
|
||
def run(self, module: Module) -> Iterable[Error]: | ||
def run(self) -> Iterable[Error]: | ||
if not self.visitors: | ||
return | ||
wrapper = cst.MetadataWrapper(module) | ||
for v in self.visitors: | ||
_ = wrapper.visit(v) | ||
self.module = cst.MetadataWrapper(self.module).visit(v) | ||
yield from self.state.problems | ||
Comment on lines
-111
to
117
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. now saves and updates self.module after visiting. |
||
|
||
def selected(self, error_codes: dict[str, str]) -> bool: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,12 @@ | |
import libcst.matchers as m | ||
|
||
from .flake8triovisitor import Flake8TrioVisitor_cst | ||
from .helpers import AttributeCall, error_class_cst, with_has_call | ||
from .helpers import ( | ||
AttributeCall, | ||
error_class_cst, | ||
flatten_preserving_comments, | ||
with_has_call, | ||
) | ||
|
||
|
||
@error_class_cst | ||
|
@@ -46,12 +51,16 @@ def visit_With(self, node: cst.With) -> None: | |
else: | ||
self.has_checkpoint_stack.append(True) | ||
|
||
def leave_With(self, original_node: cst.With, updated_node: cst.With) -> cst.With: | ||
def leave_With( | ||
self, original_node: cst.With, updated_node: cst.With | ||
) -> cst.BaseStatement | cst.FlattenSentinel[cst.BaseStatement]: | ||
if not self.has_checkpoint_stack.pop(): | ||
for res in self.node_dict[original_node]: | ||
self.error(res.node, res.base, res.function) | ||
# if: autofixing is enabled for this code | ||
# then: remove the with and pop out it's body | ||
|
||
if self.options.autofix and len(updated_node.items) == 1: | ||
return flatten_preserving_comments(updated_node) | ||
Comment on lines
58
to
+62
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. I assume that we don't actually emit the error for autofixed code? 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. This currently always does, I'll introduce ways of configuring that soon :) |
||
|
||
return updated_node | ||
|
||
def visit_For(self, node: cst.For): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# type: ignore | ||
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. there's not a ton to see here ("trio100_simple_autofix.py" is easier to read), but if you want to review it you should view this and |
||
|
||
import trio | ||
|
||
# error: 5, "trio", "move_on_after" | ||
... | ||
|
||
|
||
async def function_name(): | ||
# fmt: off | ||
...; ...; ... | ||
# fmt: on | ||
# error: 15, "trio", "fail_after" | ||
... | ||
# error: 15, "trio", "fail_at" | ||
... | ||
# error: 15, "trio", "move_on_after" | ||
... | ||
# error: 15, "trio", "move_on_at" | ||
... | ||
# error: 15, "trio", "CancelScope" | ||
... | ||
|
||
with trio.move_on_after(10): | ||
await trio.sleep(1) | ||
|
||
with trio.move_on_after(10): | ||
await trio.sleep(1) | ||
print("hello") | ||
|
||
with trio.move_on_after(10): | ||
while True: | ||
await trio.sleep(1) | ||
print("hello") | ||
|
||
with open("filename") as _: | ||
... | ||
|
||
# error: 9, "trio", "fail_after" | ||
... | ||
|
||
send_channel, receive_channel = trio.open_memory_channel(0) | ||
async with trio.fail_after(10): | ||
async with send_channel: | ||
... | ||
|
||
async with trio.fail_after(10): | ||
async for _ in receive_channel: | ||
... | ||
|
||
# error: 15, "trio", "fail_after" | ||
for _ in receive_channel: | ||
... | ||
|
||
# fix missed alarm when function is defined inside the with scope | ||
# error: 9, "trio", "move_on_after" | ||
|
||
async def foo(): | ||
await trio.sleep(1) | ||
|
||
# error: 9, "trio", "move_on_after" | ||
if ...: | ||
|
||
async def foo(): | ||
if ...: | ||
await trio.sleep(1) | ||
|
||
async with random_ignored_library.fail_after(10): | ||
... | ||
|
||
|
||
async def function_name2(): | ||
with ( | ||
open("") as _, | ||
trio.fail_after(10), # error: 8, "trio", "fail_after" | ||
): | ||
... | ||
|
||
with ( | ||
trio.fail_after(5), # error: 8, "trio", "fail_after" | ||
open("") as _, | ||
trio.move_on_after(5), # error: 8, "trio", "move_on_after" | ||
): | ||
... |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import trio | ||
|
||
# a | ||
# b | ||
# error: 5, "trio", "move_on_after" | ||
# c | ||
# d | ||
print(1) # e | ||
# f | ||
# g | ||
print(2) # h | ||
# i | ||
# j | ||
print(3) # k | ||
# l | ||
# m | ||
pass | ||
# n | ||
|
||
# error: 5, "trio", "move_on_after" | ||
... | ||
|
||
|
||
# a | ||
# b | ||
# fmt: off | ||
...;...;... | ||
# fmt: on | ||
# c | ||
# d | ||
|
||
# Doesn't autofix With's with multiple withitems | ||
with ( | ||
trio.move_on_after(10), # error: 4, "trio", "move_on_after" | ||
open("") as f, | ||
): | ||
... | ||
|
||
|
||
# multiline with, despite only being one statement | ||
# a | ||
# b | ||
# c | ||
# error: 4, "trio", "move_on_after" | ||
# d | ||
# e | ||
# f | ||
# g | ||
# h | ||
# this comment is kept | ||
... | ||
|
||
# fmt: off | ||
# a | ||
# b | ||
# error: 4, "trio", "move_on_after" | ||
# c | ||
...; ...; ... | ||
# fmt: on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the runner is kind of .. not necessary and clunky, esp as currently
__init__
does a bunch of logic itself. Might warrant some restructuring.