2222from stat import S_ISDIR
2323from stat import S_ISLNK
2424from 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
2529
2630from . import error
2731
32+ if TYPE_CHECKING :
33+ from typing import Literal
34+
2835# Moved from local.py.
2936iswin32 = sys .platform == "win32" or (getattr (os , "_name" , False ) == "nt" )
3037
@@ -96,7 +103,9 @@ def _evaluate(self, kw):
96103 return False
97104 return True
98105
99- def _stat (self ):
106+ _statcache : Stat
107+
108+ def _stat (self ) -> Stat :
100109 try :
101110 return self ._statcache
102111 except AttributeError :
@@ -129,7 +138,7 @@ def __init__(self, fil, rec, ignore, bf, sort):
129138 if isinstance (fil , str ):
130139 fil = FNMatcher (fil )
131140 if isinstance (rec , str ):
132- self .rec = FNMatcher (rec )
141+ self .rec : Callable [[ LocalPath ], bool ] = FNMatcher (rec )
133142 elif not hasattr (rec , "__call__" ) and rec :
134143 self .rec = lambda path : True
135144 else :
@@ -192,7 +201,17 @@ def map_as_list(func, iter):
192201
193202
194203class 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 :
196215 return getattr (self ._osstatresult , "st_" + name )
197216
198217 def __init__ (self , path , osstatresult ):
@@ -295,9 +314,10 @@ def chown(self, user, group, rec=0):
295314 error .checked_call (os .chown , str (x ), uid , gid )
296315 error .checked_call (os .chown , str (self ), uid , gid )
297316
298- def readlink (self ):
317+ def readlink (self ) -> str :
299318 """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]
301321
302322 def mklinkto (self , oldname ):
303323 """Posix style hard link to another name."""
@@ -659,32 +679,31 @@ def new(self, **kw):
659679 obj .strpath = normpath ("%(dirname)s%(sep)s%(basename)s" % kw )
660680 return obj
661681
662- def _getbyspec (self , spec ) :
682+ def _getbyspec (self , spec : str ) -> list [ str ] :
663683 """See new for what 'spec' can be."""
664684 res = []
665685 parts = self .strpath .split (self .sep )
666686
667687 args = filter (None , spec .split ("," ))
668- append = res .append
669688 for name in args :
670689 if name == "drive" :
671- append (parts [0 ])
690+ res . append (parts [0 ])
672691 elif name == "dirname" :
673- append (self .sep .join (parts [:- 1 ]))
692+ res . append (self .sep .join (parts [:- 1 ]))
674693 else :
675694 basename = parts [- 1 ]
676695 if name == "basename" :
677- append (basename )
696+ res . append (basename )
678697 else :
679698 i = basename .rfind ("." )
680699 if i == - 1 :
681700 purebasename , ext = basename , ""
682701 else :
683702 purebasename , ext = basename [:i ], basename [i :]
684703 if name == "purebasename" :
685- append (purebasename )
704+ res . append (purebasename )
686705 elif name == "ext" :
687- append (ext )
706+ res . append (ext )
688707 else :
689708 raise ValueError ("invalid part specification %r" % name )
690709 return res
@@ -699,16 +718,16 @@ def dirpath(self, *args, **kwargs):
699718 return path
700719 return self .new (basename = "" ).join (* args , ** kwargs )
701720
702- def join (self , * args , ** kwargs ) :
721+ def join (self , * args : os . PathLike [ str ], abs : bool = False ) -> LocalPath :
703722 """Return a new path by appending all 'args' as path
704723 components. if abs=1 is used restart from root if any
705724 of the args is an absolute path.
706725 """
707726 sep = self .sep
708727 strargs = [os .fspath (arg ) for arg in args ]
709728 strpath = self .strpath
710- if kwargs . get ( " abs" ) :
711- newargs = []
729+ if abs :
730+ newargs : list [ str ] = []
712731 for arg in reversed (strargs ):
713732 if isabs (arg ):
714733 strpath = arg
@@ -801,11 +820,11 @@ def listdir(self, fil=None, sort=None):
801820 self ._sortlist (res , sort )
802821 return res
803822
804- def size (self ):
823+ def size (self ) -> int :
805824 """Return size of the underlying file object"""
806825 return self .stat ().size
807826
808- def mtime (self ):
827+ def mtime (self ) -> float :
809828 """Return last modification time of the path."""
810829 return self .stat ().mtime
811830
@@ -936,7 +955,15 @@ def ensure(self, *args, **kwargs):
936955 p .open ("w" ).close ()
937956 return p
938957
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 :
940967 """Return an os.stat() tuple."""
941968 if raising :
942969 return Stat (self , error .checked_call (os .stat , self .strpath ))
@@ -947,7 +974,7 @@ def stat(self, raising=True):
947974 except Exception :
948975 return None
949976
950- def lstat (self ):
977+ def lstat (self ) -> Stat :
951978 """Return an os.lstat() tuple."""
952979 return Stat (self , error .checked_call (os .lstat , self .strpath ))
953980
@@ -1067,7 +1094,7 @@ def pyimport(self, modname=None, ensuresyspath=True):
10671094 if modname is None :
10681095 modname = self .purebasename
10691096 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 :
10711098 raise ImportError (
10721099 f"Can't find module { modname } at location { str (self )} "
10731100 )
@@ -1095,6 +1122,7 @@ def pyimport(self, modname=None, ensuresyspath=True):
10951122 return mod # we don't check anything as we might
10961123 # be in a namespace package ... too icky to check
10971124 modfile = mod .__file__
1125+ assert modfile is not None
10981126 if modfile [- 4 :] in (".pyc" , ".pyo" ):
10991127 modfile = modfile [:- 1 ]
11001128 elif modfile .endswith ("$py.class" ):
@@ -1129,16 +1157,22 @@ def pyimport(self, modname=None, ensuresyspath=True):
11291157 raise
11301158 return mod
11311159
1132- def sysexec (self , * argv , ** popen_opts ) :
1160+ def sysexec (self , * argv : os . PathLike [ str ] , ** popen_opts : Any ) -> str :
11331161 """Return stdout text from executing a system child process,
11341162 where the 'self' path points to executable.
11351163 The process is directly invoked and not through a system shell.
11361164 """
11371165 from subprocess import Popen , PIPE
11381166
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
11421176 stdout , stderr = proc .communicate ()
11431177 ret = proc .wait ()
11441178 if isinstance (stdout , bytes ):
0 commit comments