Skip to content

Commit 6010fd2

Browse files
committed
Add unasync: remove feature
1 parent 9b15d0e commit 6010fd2

File tree

3 files changed

+85
-0
lines changed

3 files changed

+85
-0
lines changed

src/unasync/__init__.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22

33
from __future__ import print_function
44

5+
import ast
56
import collections
67
import errno
78
import os
89
import sys
910
import tokenize as std_tokenize
11+
from tokenize_rt import reversed_enumerate
12+
from tokenize_rt import src_to_tokens
13+
from tokenize_rt import tokens_to_src
1014

1115
from setuptools.command import build_py as orig
1216

@@ -74,11 +78,33 @@ def _unasync_file(self, filepath):
7478
tokens = _tokenize(f)
7579
tokens = self._unasync_tokens(tokens)
7680
result = _untokenize(tokens)
81+
result = self._unasync_remove(contents=result, filename=filepath)
7782
outfilepath = filepath.replace(self.fromdir, self.todir)
7883
_makedirs_existok(os.path.dirname(outfilepath))
7984
with open(outfilepath, "w", **write_kwargs) as f:
8085
print(result, file=f, end="")
8186

87+
def _unasync_remove(self, contents, filename):
88+
tree = ast.parse(contents, filename=filename)
89+
tokens = src_to_tokens(contents)
90+
comment_lines_locations = []
91+
for tok in tokens:
92+
# find line numbers where "unasync: remove" comments are found
93+
if tok.name == 'COMMENT' and 'unasync: remove' in tok.src: # XXX: maybe make this a little more strict
94+
comment_lines_locations.append(tok.line)
95+
lines_to_remove = set()
96+
for node in ast.walk(tree):
97+
# find nodes whose line number (start line) intersect with unasync: remove comments
98+
if hasattr(node, 'lineno') and node.lineno in comment_lines_locations:
99+
for lineno in range(node.lineno, node.end_lineno + 1):
100+
# find all lines related to each node and mark those lines for removal
101+
lines_to_remove.add(lineno)
102+
for i, tok in reversed_enumerate(tokens):
103+
if tok.line in lines_to_remove:
104+
tokens.pop(i)
105+
new_contents = tokens_to_src(tokens)
106+
return new_contents
107+
82108
def _unasync_tokens(self, tokens):
83109
# TODO __await__, ...?
84110
used_space = None

tests/data/async/removals.py

Lines changed: 36 additions & 0 deletions
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

Lines changed: 23 additions & 0 deletions
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)