22import argparse
33import shlex
44import sys
5+ from typing import Generator
56from typing import List
7+ from typing import NamedTuple
68from typing import Optional
79from typing import Sequence
810from typing import Set
@@ -19,29 +21,38 @@ def check_executables(paths: List[str]) -> int:
1921 else : # pragma: win32 no cover
2022 retv = 0
2123 for path in paths :
22- if not _check_has_shebang (path ):
24+ if not has_shebang (path ):
2325 _message (path )
2426 retv = 1
2527
2628 return retv
2729
2830
29- def _check_git_filemode (paths : Sequence [str ]) -> int :
31+ class GitLsFile (NamedTuple ):
32+ mode : str
33+ filename : str
34+
35+
36+ def git_ls_files (paths : Sequence [str ]) -> Generator [GitLsFile , None , None ]:
3037 outs = cmd_output ('git' , 'ls-files' , '-z' , '--stage' , '--' , * paths )
31- seen : Set [str ] = set ()
3238 for out in zsplit (outs ):
33- metadata , path = out .split ('\t ' )
34- tagmode = metadata .split (' ' , 1 )[0 ]
39+ metadata , filename = out .split ('\t ' )
40+ mode , _ , _ = metadata .split ()
41+ yield GitLsFile (mode , filename )
3542
36- is_executable = any (b in EXECUTABLE_VALUES for b in tagmode [- 3 :])
37- if is_executable and not _check_has_shebang (path ):
38- _message (path )
39- seen .add (path )
43+
44+ def _check_git_filemode (paths : Sequence [str ]) -> int :
45+ seen : Set [str ] = set ()
46+ for ls_file in git_ls_files (paths ):
47+ is_executable = any (b in EXECUTABLE_VALUES for b in ls_file .mode [- 3 :])
48+ if is_executable and not has_shebang (ls_file .filename ):
49+ _message (ls_file .filename )
50+ seen .add (ls_file .filename )
4051
4152 return int (bool (seen ))
4253
4354
44- def _check_has_shebang (path : str ) -> int :
55+ def has_shebang (path : str ) -> int :
4556 with open (path , 'rb' ) as f :
4657 first_bytes = f .read (2 )
4758
0 commit comments