77import asyncio
88import builtins
99import logging
10- import os
1110from functools import partial
1211from io import StringIO
13- from pathlib import Path
1412
1513import reorder_python_imports
1614from autoflake import _main as autoflake_main
1715from isort .main import main as isort_main
1816from pyupgrade ._main import main as pyupgrade_main
1917
18+ from .const import FileStatus
19+ from .utils import (
20+ async_check_uncommitted_changes , async_restore_files ,
21+ check_comment_between_imports , check_files_exist )
22+
2023logger = logging .getLogger ("typing-update" )
2124
2225
2326async def typing_update (
2427 loop : asyncio .AbstractEventLoop ,
2528 filename : str ,
2629 args : argparse .Namespace ,
30+ file_status : FileStatus ,
2731) -> tuple [int , str ]:
2832 """Update typing syntax.
2933
@@ -67,7 +71,7 @@ async def typing_update(
6771 None , autoflake_partial ,
6872 [None , '-c' , filename ],
6973 )
70- if not args .full_reorder :
74+ if not args .full_reorder and FileStatus . COMMENT_TYPING not in file_status :
7175 # -> No unused imports, revert changes
7276 return 2 , filename
7377 except SystemExit :
@@ -103,56 +107,6 @@ async def typing_update(
103107 return 0 , filename
104108
105109
106- async def async_restore_files (file_list : list [str ]) -> None :
107- if not file_list :
108- return
109- process = await asyncio .create_subprocess_shell (
110- f"git restore -- { ' ' .join (file_list )} " ,
111- )
112- await process .communicate ()
113-
114-
115- def check_files_exist (file_list : list [str ]) -> list [str ]:
116- """Check if all files exist. Return False if not."""
117- file_errors : list [str ] = []
118- cwd = Path (os .getcwd ())
119- for file_ in file_list :
120- if cwd .joinpath (file_ ).is_file () is False :
121- file_errors .append (file_ )
122- return sorted (file_errors )
123-
124-
125- async def async_check_uncommitted_changes (file_list : list [str ]) -> bool :
126- """Check for uncommitted changes.
127-
128- Returns:
129- False: if changes still need to be committed
130- """
131- process = await asyncio .create_subprocess_shell (
132- "git diff-index --name-only HEAD --" ,
133- stdout = asyncio .subprocess .PIPE ,
134- )
135- stdout , _ = await process .communicate ()
136- files_uncommitted : set [str ] = {file_ for item in stdout .decode ().split ('\n ' )
137- if (file_ := item .strip ())}
138- return not any (True for file_ in file_list if file_ in files_uncommitted )
139-
140-
141- async def async_check_changes (file_list : list [str ]) -> list [str ]:
142- """Check if comment was change in files.
143-
144- That is a sing we can't update it automatically.
145- """
146- if not file_list :
147- return []
148- process = await asyncio .create_subprocess_shell (
149- f"git diff -G\" ^#|^from.*#|^import.*#\" --name-only -- { ' ' .join (file_list )} " ,
150- stdout = asyncio .subprocess .PIPE ,
151- )
152- stdout , _ = await process .communicate ()
153- return sorted ([file_ for file_ in stdout .decode ().strip ().split ('\n ' ) if file_ ])
154-
155-
156110async def async_run (args : argparse .Namespace ) -> int :
157111 """Update Python typing syntax.
158112
@@ -175,17 +129,26 @@ async def async_run(args: argparse.Namespace) -> int:
175129 print ("Abort! Commit all changes to '.py' files before running again." )
176130 return 11
177131
132+ filenames : dict [str , FileStatus ] = {}
133+ for filename in args .filenames :
134+ with open (filename ) as fp :
135+ result = check_comment_between_imports (fp )
136+ filenames [filename ] = result
137+
138+ if args .only_force :
139+ filenames = {filename : file_status for filename , file_status in filenames .items ()
140+ if file_status != FileStatus .CLEAR }
141+
178142 loop = asyncio .get_running_loop ()
179143 files_updated : list [str ] = []
180144 files_no_changes : list [str ] = []
181- files_with_comments : list [str ] = []
182145
183146 # Mock builtin print to omit output
184147 original_print = builtins .print
185148 builtins .print = lambda * args , ** kwargs : None
186149
187150 return_values = await asyncio .gather (
188- * [typing_update (loop , filename , args ) for filename in args . filenames ])
151+ * [typing_update (loop , filename , args , file_status ) for filename , file_status in filenames . items () ])
189152 for status , filename in return_values :
190153 if status == 0 :
191154 files_updated .append (filename )
@@ -195,6 +158,13 @@ async def async_run(args: argparse.Namespace) -> int:
195158 builtins .print = original_print
196159 await async_restore_files (files_no_changes )
197160
161+ if args .limit > 0 and len (files_updated ) > args .limit :
162+ print (
163+ f"Limit applied! Only updated the first { args .limit } "
164+ f"of { len (files_updated )} files" )
165+ async_restore_files (files_updated [args .limit :])
166+ files_updated = files_updated [:args .limit ]
167+
198168 if args .check is True :
199169 await async_restore_files (files_updated )
200170 if files_updated :
@@ -204,9 +174,13 @@ async def async_run(args: argparse.Namespace) -> int:
204174 return 1
205175 return 0
206176
207- files_with_comments = await async_check_changes (files_updated )
177+ files_updated_set : set [str ] = set (files_updated )
178+ files_with_comments = sorted ([
179+ filename for filename , file_status in filenames .items ()
180+ if FileStatus .COMMENT in file_status and filename in files_updated_set
181+ ])
208182 if files_with_comments :
209- if args .force :
183+ if args .force or args . only_force :
210184 print ("Force mode selected!" )
211185 print ("Make sure to double check:" )
212186 for file_ in files_with_comments :
@@ -218,12 +192,17 @@ async def async_run(args: argparse.Namespace) -> int:
218192 await async_restore_files (files_with_comments )
219193
220194 print ("---" )
221- print (f"All files: { len (args . filenames )} " )
195+ print (f"All files: { len (filenames )} " )
222196 print (f"No changes: { len (files_no_changes )} " )
223197 print (f"Files updated: { len (files_updated ) - len (files_with_comments )} " )
224198 print (f"Files (no automatic update): { len (files_with_comments )} " )
225199
226- if not files_with_comments and not args .force and args .verbose == 0 :
200+ if (
201+ not files_with_comments
202+ and not args .force
203+ and not args .only_force
204+ and args .verbose == 0
205+ ):
227206 return 0
228207 if files_with_comments :
229208 return 2
0 commit comments