1
+ # -*- encoding: utf8 -*-
1
2
"""Top-level package for unasync."""
2
3
3
4
from __future__ import print_function
4
5
5
6
import collections
6
7
import errno
8
+ import io
7
9
import os
8
10
import sys
9
11
import tokenize as std_tokenize
34
36
"StopAsyncIteration" : "StopIteration" ,
35
37
}
36
38
39
+ _TYPE_COMMENT_PREFIX = "# type: "
40
+
41
+
42
+ if sys .version_info [0 ] == 2 : # PY2
43
+
44
+ def isidentifier (s ):
45
+ return all ([c .isalnum () or c == "_" for c in s ])
46
+
47
+ StringIO = io .BytesIO
48
+ else : # PY3
49
+
50
+ def isidentifier (s ):
51
+ return s .isidentifier ()
52
+
53
+ StringIO = io .StringIO
54
+
37
55
38
56
class Rule :
39
57
"""A single set of rules for 'unasync'ing file(s)"""
@@ -95,6 +113,31 @@ def _unasync_tokens(self, tokens):
95
113
elif toknum == std_tokenize .STRING :
96
114
left_quote , name , right_quote = tokval [0 ], tokval [1 :- 1 ], tokval [- 1 ]
97
115
tokval = left_quote + self ._unasync_name (name ) + right_quote
116
+ elif toknum == std_tokenize .COMMENT and tokval .startswith (
117
+ _TYPE_COMMENT_PREFIX
118
+ ):
119
+ type_decl , suffix = tokval [len (_TYPE_COMMENT_PREFIX ) :], ""
120
+ if "#" in type_decl :
121
+ type_decl , suffix = type_decl .split ("#" , 1 )
122
+ suffix = "#" + suffix
123
+ type_decl_stripped = type_decl .strip ()
124
+
125
+ # Do not process `type: ignore` or `type: ignore[…]` as these aren't actual identifiers
126
+ is_type_ignore = type_decl_stripped == "ignore"
127
+ is_type_ignore |= type_decl_stripped .startswith (
128
+ "ignore"
129
+ ) and not isidentifier (type_decl_stripped [0 :7 ])
130
+ if not is_type_ignore :
131
+ # Preserve trailing whitespace since the tokenizer won't
132
+ trailing_space_len = len (type_decl ) - len (type_decl .rstrip ())
133
+ if trailing_space_len > 0 :
134
+ suffix = type_decl [- trailing_space_len :] + suffix
135
+ type_decl = type_decl [:- trailing_space_len ]
136
+ type_decl = _untokenize (
137
+ self ._unasync_tokens (_tokenize (StringIO (type_decl )))
138
+ )
139
+
140
+ tokval = _TYPE_COMMENT_PREFIX + type_decl + suffix
98
141
if used_space is None :
99
142
used_space = space
100
143
yield (used_space , tokval )
@@ -133,7 +176,11 @@ def _get_tokens(f):
133
176
type_ , string , start , end , line = tok
134
177
yield Token (type_ , string , start , end , line )
135
178
else : # PY3
136
- for tok in std_tokenize .tokenize (f .readline ):
179
+ if isinstance (f , io .TextIOBase ):
180
+ gen = std_tokenize .generate_tokens (f .readline )
181
+ else :
182
+ gen = std_tokenize .tokenize (f .readline )
183
+ for tok in gen :
137
184
if tok .type == std_tokenize .ENCODING :
138
185
continue
139
186
yield tok
@@ -143,7 +190,10 @@ def _tokenize(f):
143
190
last_end = (1 , 0 )
144
191
for tok in _get_tokens (f ):
145
192
if last_end [0 ] < tok .start [0 ]:
146
- yield ("" , std_tokenize .STRING , " \\ \n " )
193
+ # Somehow Python 3.5 and below produce the ENDMARKER in a way that
194
+ # causes superfluous continuation lines to be generated
195
+ if tok .type != std_tokenize .ENDMARKER :
196
+ yield ("" , std_tokenize .STRING , " \\ \n " )
147
197
last_end = (tok .start [0 ], 0 )
148
198
149
199
space = ""
0 commit comments