@@ -100,7 +100,57 @@ def _unasync_file(self, filepath):
100100    def  _unasync_tokens (self , tokens ):
101101        # TODO __await__, ...? 
102102        used_space  =  None 
103+         context  =  None   # Can be `None`, `"func_decl"`, `"func_name"`, `"arg_list"`, `"arg_list_end"`, `"return_type"` 
104+         brace_depth  =  0 
105+         typing_ctx  =  False 
106+ 
103107        for  space , toknum , tokval  in  tokens :
108+             # Update context state tracker 
109+             if  context  is  None  and  toknum  ==  std_tokenize .NAME  and  tokval  ==  "def" :
110+                 context  =  "func_decl" 
111+             elif  context  ==  "func_decl"  and  toknum  ==  std_tokenize .NAME :
112+                 context  =  "func_name" 
113+             elif  context  ==  "func_name"  and  toknum  ==  std_tokenize .OP  and  tokval  ==  "(" :
114+                 context  =  "arg_list" 
115+             elif  context  ==  "arg_list" :
116+                 if  toknum  ==  std_tokenize .OP  and  tokval  in  ("(" , "[" ):
117+                     brace_depth  +=  1 
118+                 elif  (
119+                     toknum  ==  std_tokenize .OP 
120+                     and  tokval  in  (")" , "]" )
121+                     and  brace_depth  >=  1 
122+                 ):
123+                     brace_depth  -=  1 
124+                 elif  toknum  ==  std_tokenize .OP  and  tokval  ==  ")" :
125+                     context  =  "arg_list_end" 
126+                 elif  toknum  ==  std_tokenize .OP  and  tokval  ==  ":"  and  brace_depth  <  1 :
127+                     typing_ctx  =  True 
128+                 elif  toknum  ==  std_tokenize .OP  and  tokval  ==  ","  and  brace_depth  <  1 :
129+                     typing_ctx  =  False 
130+             elif  (
131+                 context  ==  "arg_list_end" 
132+                 and  toknum  ==  std_tokenize .OP 
133+                 and  tokval  ==  "->" 
134+             ):
135+                 context  =  "return_type" 
136+                 typing_ctx  =  True 
137+             elif  context  ==  "return_type" :
138+                 if  toknum  ==  std_tokenize .OP  and  tokval  in  ("(" , "[" ):
139+                     brace_depth  +=  1 
140+                 elif  (
141+                     toknum  ==  std_tokenize .OP 
142+                     and  tokval  in  (")" , "]" )
143+                     and  brace_depth  >=  1 
144+                 ):
145+                     brace_depth  -=  1 
146+                 elif  toknum  ==  std_tokenize .OP  and  tokval  ==  ":" :
147+                     context  =  None 
148+                     typing_ctx  =  False 
149+             else :  # Something unexpected happend - reset state 
150+                 context  =  None 
151+                 brace_depth  =  0 
152+                 typing_ctx  =  False 
153+ 
104154            if  tokval  in  ["async" , "await" ]:
105155                # When removing async or await, we want to use the whitespace that 
106156                # was before async/await before the next token so that 
@@ -111,8 +161,34 @@ def _unasync_tokens(self, tokens):
111161                if  toknum  ==  std_tokenize .NAME :
112162                    tokval  =  self ._unasync_name (tokval )
113163                elif  toknum  ==  std_tokenize .STRING :
114-                     left_quote , name , right_quote  =  tokval [0 ], tokval [1 :- 1 ], tokval [- 1 ]
115-                     tokval  =  left_quote  +  self ._unasync_name (name ) +  right_quote 
164+                     # Strings in typing context are forward-references and should be unasyncified 
165+                     quote  =  "" 
166+                     prefix  =  "" 
167+                     while  ord (tokval [0 ]) in  range (ord ("a" ), ord ("z" ) +  1 ):
168+                         prefix  +=  tokval [0 ]
169+                         tokval  =  tokval [1 :]
170+ 
171+                     if  tokval .startswith ('"""' ) and  tokval .endswith ('"""' ):
172+                         quote  =  '"""'   # Broken syntax highlighters workaround: """ 
173+                     elif  tokval .startswith ("'''" ) and  tokval .endswith ("'''" ):
174+                         quote  =  "'''"   # Broken syntax highlighters wokraround: ''' 
175+                     elif  tokval .startswith ('"' ) and  tokval .endswith ('"' ):
176+                         quote  =  '"' 
177+                     elif  tokval .startswith ("'" ) and  tokval .endswith (
178+                         "'" 
179+                     ):  # pragma: no branch 
180+                         quote  =  "'" 
181+                     assert  (
182+                         len (quote ) >  0 
183+                     ), "Quoting style of string {0!r} unknown" .format (tokval )
184+                     stringval  =  tokval [len (quote ) : - len (quote )]
185+                     if  typing_ctx :
186+                         stringval  =  _untokenize (
187+                             self ._unasync_tokens (_tokenize (StringIO (stringval )))
188+                         )
189+                     else :
190+                         stringval  =  self ._unasync_name (stringval )
191+                     tokval  =  prefix  +  quote  +  stringval  +  quote 
116192                elif  toknum  ==  std_tokenize .COMMENT  and  tokval .startswith (
117193                    _TYPE_COMMENT_PREFIX 
118194                ):
@@ -193,7 +269,7 @@ def _tokenize(f):
193269            # Somehow Python 3.5 and below produce the ENDMARKER in a way that 
194270            # causes superfluous continuation lines to be generated 
195271            if  tok .type  !=  std_tokenize .ENDMARKER :
196-                 yield  ("" , std_tokenize .STRING , "  \\ \n " )
272+                 yield  ("  " , std_tokenize .NEWLINE , "\\ \n " )
197273            last_end  =  (tok .start [0 ], 0 )
198274
199275        space  =  "" 
0 commit comments