22
22
from stat import S_ISDIR
23
23
from stat import S_ISLNK
24
24
from stat import S_ISREG
25
+ from typing import Any
26
+ from typing import Callable
27
+ from typing import overload
28
+ from typing import TYPE_CHECKING
25
29
26
30
from . import error
27
31
32
+ if TYPE_CHECKING :
33
+ from typing import Literal
34
+
28
35
# Moved from local.py.
29
36
iswin32 = sys .platform == "win32" or (getattr (os , "_name" , False ) == "nt" )
30
37
@@ -96,7 +103,9 @@ def _evaluate(self, kw):
96
103
return False
97
104
return True
98
105
99
- def _stat (self ):
106
+ _statcache : Stat
107
+
108
+ def _stat (self ) -> Stat :
100
109
try :
101
110
return self ._statcache
102
111
except AttributeError :
@@ -129,7 +138,7 @@ def __init__(self, fil, rec, ignore, bf, sort):
129
138
if isinstance (fil , str ):
130
139
fil = FNMatcher (fil )
131
140
if isinstance (rec , str ):
132
- self .rec = FNMatcher (rec )
141
+ self .rec : Callable [[ LocalPath ], bool ] = FNMatcher (rec )
133
142
elif not hasattr (rec , "__call__" ) and rec :
134
143
self .rec = lambda path : True
135
144
else :
@@ -192,7 +201,17 @@ def map_as_list(func, iter):
192
201
193
202
194
203
class Stat :
195
- def __getattr__ (self , name ):
204
+ if TYPE_CHECKING :
205
+
206
+ @property
207
+ def size (self ) -> int :
208
+ ...
209
+
210
+ @property
211
+ def mtime (self ) -> float :
212
+ ...
213
+
214
+ def __getattr__ (self , name : str ) -> Any :
196
215
return getattr (self ._osstatresult , "st_" + name )
197
216
198
217
def __init__ (self , path , osstatresult ):
@@ -295,9 +314,10 @@ def chown(self, user, group, rec=0):
295
314
error .checked_call (os .chown , str (x ), uid , gid )
296
315
error .checked_call (os .chown , str (self ), uid , gid )
297
316
298
- def readlink (self ):
317
+ def readlink (self ) -> str :
299
318
"""Return value of a symbolic link."""
300
- return error .checked_call (os .readlink , self .strpath )
319
+ # https://github.com/python/mypy/issues/12278
320
+ return error .checked_call (os .readlink , self .strpath ) # type: ignore[arg-type,return-value]
301
321
302
322
def mklinkto (self , oldname ):
303
323
"""Posix style hard link to another name."""
@@ -659,32 +679,31 @@ def new(self, **kw):
659
679
obj .strpath = normpath ("%(dirname)s%(sep)s%(basename)s" % kw )
660
680
return obj
661
681
662
- def _getbyspec (self , spec ) :
682
+ def _getbyspec (self , spec : str ) -> list [ str ] :
663
683
"""See new for what 'spec' can be."""
664
684
res = []
665
685
parts = self .strpath .split (self .sep )
666
686
667
687
args = filter (None , spec .split ("," ))
668
- append = res .append
669
688
for name in args :
670
689
if name == "drive" :
671
- append (parts [0 ])
690
+ res . append (parts [0 ])
672
691
elif name == "dirname" :
673
- append (self .sep .join (parts [:- 1 ]))
692
+ res . append (self .sep .join (parts [:- 1 ]))
674
693
else :
675
694
basename = parts [- 1 ]
676
695
if name == "basename" :
677
- append (basename )
696
+ res . append (basename )
678
697
else :
679
698
i = basename .rfind ("." )
680
699
if i == - 1 :
681
700
purebasename , ext = basename , ""
682
701
else :
683
702
purebasename , ext = basename [:i ], basename [i :]
684
703
if name == "purebasename" :
685
- append (purebasename )
704
+ res . append (purebasename )
686
705
elif name == "ext" :
687
- append (ext )
706
+ res . append (ext )
688
707
else :
689
708
raise ValueError ("invalid part specification %r" % name )
690
709
return res
@@ -699,16 +718,16 @@ def dirpath(self, *args, **kwargs):
699
718
return path
700
719
return self .new (basename = "" ).join (* args , ** kwargs )
701
720
702
- def join (self , * args , ** kwargs ) :
721
+ def join (self , * args : os . PathLike [ str ], abs : bool = False ) -> LocalPath :
703
722
"""Return a new path by appending all 'args' as path
704
723
components. if abs=1 is used restart from root if any
705
724
of the args is an absolute path.
706
725
"""
707
726
sep = self .sep
708
727
strargs = [os .fspath (arg ) for arg in args ]
709
728
strpath = self .strpath
710
- if kwargs . get ( " abs" ) :
711
- newargs = []
729
+ if abs :
730
+ newargs : list [ str ] = []
712
731
for arg in reversed (strargs ):
713
732
if isabs (arg ):
714
733
strpath = arg
@@ -801,11 +820,11 @@ def listdir(self, fil=None, sort=None):
801
820
self ._sortlist (res , sort )
802
821
return res
803
822
804
- def size (self ):
823
+ def size (self ) -> int :
805
824
"""Return size of the underlying file object"""
806
825
return self .stat ().size
807
826
808
- def mtime (self ):
827
+ def mtime (self ) -> float :
809
828
"""Return last modification time of the path."""
810
829
return self .stat ().mtime
811
830
@@ -936,7 +955,15 @@ def ensure(self, *args, **kwargs):
936
955
p .open ("w" ).close ()
937
956
return p
938
957
939
- def stat (self , raising = True ):
958
+ @overload
959
+ def stat (self , raising : Literal [True ] = ...) -> Stat :
960
+ ...
961
+
962
+ @overload
963
+ def stat (self , raising : Literal [False ]) -> Stat | None :
964
+ ...
965
+
966
+ def stat (self , raising : bool = True ) -> Stat | None :
940
967
"""Return an os.stat() tuple."""
941
968
if raising :
942
969
return Stat (self , error .checked_call (os .stat , self .strpath ))
@@ -947,7 +974,7 @@ def stat(self, raising=True):
947
974
except Exception :
948
975
return None
949
976
950
- def lstat (self ):
977
+ def lstat (self ) -> Stat :
951
978
"""Return an os.lstat() tuple."""
952
979
return Stat (self , error .checked_call (os .lstat , self .strpath ))
953
980
@@ -1067,7 +1094,7 @@ def pyimport(self, modname=None, ensuresyspath=True):
1067
1094
if modname is None :
1068
1095
modname = self .purebasename
1069
1096
spec = importlib .util .spec_from_file_location (modname , str (self ))
1070
- if spec is None :
1097
+ if spec is None or spec . loader is None :
1071
1098
raise ImportError (
1072
1099
f"Can't find module { modname } at location { str (self )} "
1073
1100
)
@@ -1095,6 +1122,7 @@ def pyimport(self, modname=None, ensuresyspath=True):
1095
1122
return mod # we don't check anything as we might
1096
1123
# be in a namespace package ... too icky to check
1097
1124
modfile = mod .__file__
1125
+ assert modfile is not None
1098
1126
if modfile [- 4 :] in (".pyc" , ".pyo" ):
1099
1127
modfile = modfile [:- 1 ]
1100
1128
elif modfile .endswith ("$py.class" ):
@@ -1129,16 +1157,22 @@ def pyimport(self, modname=None, ensuresyspath=True):
1129
1157
raise
1130
1158
return mod
1131
1159
1132
- def sysexec (self , * argv , ** popen_opts ) :
1160
+ def sysexec (self , * argv : os . PathLike [ str ] , ** popen_opts : Any ) -> str :
1133
1161
"""Return stdout text from executing a system child process,
1134
1162
where the 'self' path points to executable.
1135
1163
The process is directly invoked and not through a system shell.
1136
1164
"""
1137
1165
from subprocess import Popen , PIPE
1138
1166
1139
- argv = map_as_list (str , argv )
1140
- popen_opts ["stdout" ] = popen_opts ["stderr" ] = PIPE
1141
- proc = Popen ([str (self )] + argv , ** popen_opts )
1167
+ popen_opts .pop ("stdout" , None )
1168
+ popen_opts .pop ("stderr" , None )
1169
+ proc = Popen (
1170
+ [str (self )] + [str (arg ) for arg in argv ],
1171
+ ** popen_opts ,
1172
+ stdout = PIPE ,
1173
+ stderr = PIPE ,
1174
+ )
1175
+ stdout : str | bytes
1142
1176
stdout , stderr = proc .communicate ()
1143
1177
ret = proc .wait ()
1144
1178
if isinstance (stdout , bytes ):
0 commit comments