Skip to content

Commit aaae7db

Browse files
committed
Add 'unasync: remove' feature
1 parent 1b6fd0c commit aaae7db

File tree

3 files changed

+90
-10
lines changed

3 files changed

+90
-10
lines changed

src/unasync/__init__.py

+31-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Top-level package for unasync."""
22

3+
import ast
34
import collections
45
import errno
56
import os
@@ -68,17 +69,37 @@ def _unasync_file(self, filepath):
6869
encoding, _ = std_tokenize.detect_encoding(f.readline)
6970

7071
with open(filepath, "rt", encoding=encoding) as f:
71-
tokens = tokenize_rt.src_to_tokens(f.read())
72-
tokens = self._unasync_tokens(tokens)
73-
result = tokenize_rt.tokens_to_src(tokens)
74-
outfilepath = filepath.replace(self.fromdir, self.todir)
75-
os.makedirs(os.path.dirname(outfilepath), exist_ok=True)
76-
with open(outfilepath, "wb") as f:
77-
f.write(result.encode(encoding))
78-
79-
def _unasync_tokens(self, tokens):
72+
contents = f.read()
73+
tokens = self._unasync_tokenize(contents=contents, filename=filepath)
74+
result = tokenize_rt.tokens_to_src(tokens)
75+
outfilepath = filepath.replace(self.fromdir, self.todir)
76+
os.makedirs(os.path.dirname(outfilepath), exist_ok=True)
77+
with open(outfilepath, "wb") as f:
78+
f.write(result.encode(encoding))
79+
80+
def _unasync_tokenize(self, contents, filename):
81+
tokens = tokenize_rt.src_to_tokens(contents)
82+
83+
comment_lines_locations = []
84+
for token in tokens:
85+
# find line numbers where "unasync: remove" comments are found
86+
if token.name == 'COMMENT' and 'unasync: remove' in token.src: # XXX: maybe make this a little more strict
87+
comment_lines_locations.append(token.line)
88+
89+
lines_to_remove = set()
90+
if comment_lines_locations: # only parse ast if we actually have "unasync: remove" comments
91+
tree = ast.parse(contents, filename=filename)
92+
for node in ast.walk(tree):
93+
# find nodes whose line number (start line) intersect with unasync: remove comments
94+
if hasattr(node, 'lineno') and node.lineno in comment_lines_locations:
95+
for lineno in range(node.lineno, node.end_lineno + 1):
96+
# find all lines related to each node and mark those lines for removal
97+
lines_to_remove.add(lineno)
98+
8099
skip_next = False
81-
for i, token in enumerate(tokens):
100+
for token in tokens:
101+
if token.line in lines_to_remove:
102+
continue
82103
if skip_next:
83104
skip_next = False
84105
continue

tests/data/async/removals.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from common import (
2+
a, b , c # these should stick around
3+
)
4+
5+
# these imports should be removed
6+
from async_only import ( # unasync: remove
7+
async_a, async_b,
8+
async_c
9+
)
10+
11+
CONST = 'foo'
12+
ASYNC_CONST = 'bar' # unasync: remove
13+
14+
async def foo():
15+
print('this function should stick around')
16+
17+
async def async_only(): # unasync: remove
18+
print('this function will be removed entirely')
19+
20+
21+
class AsyncOnly: # unasync: remove
22+
async def foo(self):
23+
print('the entire class should be removed')
24+
25+
26+
class Foo:
27+
async def foobar(self):
28+
print('This method should stick around')
29+
30+
async def async_only_method(self): # unasync: remove
31+
print('only this method should be removed')
32+
33+
async def another_method(self):
34+
print('This line should stick around')
35+
await self.something("the content in this line should be removed") # unasync: remove
36+

tests/data/sync/removals.py

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from common import (
2+
a, b , c # these should stick around
3+
)
4+
5+
# these imports should be removed
6+
7+
CONST = 'foo'
8+
9+
def foo():
10+
print('this function should stick around')
11+
12+
13+
14+
15+
16+
class Foo:
17+
def foobar(self):
18+
print('This method should stick around')
19+
20+
21+
def another_method(self):
22+
print('This line should stick around')
23+

0 commit comments

Comments
 (0)