"""Path operations common to more than one OSDo not use directly. The OS specific modules import the appropriatefunctions from this module themselves."""importosimportstat__all__=['commonprefix','exists','getatime','getctime','getmtime','getsize','isdir','isfile','samefile','sameopenfile','samestat']# Does a path exist?# This is false for dangling symbolic links on systems that support them.defexists(path):"""Test whether a path exists. Returns False for broken symbolic links"""try:os.stat(path)except(OSError,ValueError):returnFalsereturnTrue# This follows symbolic links, so both islink() and isdir() can be true# for the same path on systems that support symlinks
[docs]defisfile(path):"""Test whether a path is a regular file"""try:st=os.stat(path)except(OSError,ValueError):returnFalsereturnstat.S_ISREG(st.st_mode)
# Is a path a directory?# This follows symbolic links, so both islink() and isdir()# can be true for the same path on systems that support symlinks
[docs]defisdir(s):"""Return true if the pathname refers to an existing directory."""try:st=os.stat(s)except(OSError,ValueError):returnFalsereturnstat.S_ISDIR(st.st_mode)
defgetsize(filename):"""Return the size of a file, reported by os.stat()."""returnos.stat(filename).st_sizedefgetmtime(filename):"""Return the last modification time of a file, reported by os.stat()."""returnos.stat(filename).st_mtimedefgetatime(filename):"""Return the last access time of a file, reported by os.stat()."""returnos.stat(filename).st_atimedefgetctime(filename):"""Return the metadata change time of a file, reported by os.stat()."""returnos.stat(filename).st_ctime# Return the longest prefix of all list elements.defcommonprefix(m):"Given a list of pathnames, returns the longest common leading component"ifnotm:return''# Some people pass in a list of pathname parts to operate in an OS-agnostic# fashion; don't try to translate in that case as that's an abuse of the# API and they are already doing what they need to be OS-agnostic and so# they most likely won't be using an os.PathLike object in the sublists.ifnotisinstance(m[0],(list,tuple)):m=tuple(map(os.fspath,m))s1=min(m)s2=max(m)fori,cinenumerate(s1):ifc!=s2[i]:returns1[:i]returns1# Are two stat buffers (obtained from stat, fstat or lstat)# describing the same file?defsamestat(s1,s2):"""Test whether two stat buffers reference the same file"""return(s1.st_ino==s2.st_inoands1.st_dev==s2.st_dev)# Are two filenames really pointing to the same file?defsamefile(f1,f2):"""Test whether two pathnames reference the same actual file or directory This is determined by the device number and i-node number and raises an exception if an os.stat() call on either pathname fails. """s1=os.stat(f1)s2=os.stat(f2)returnsamestat(s1,s2)# Are two open files really referencing the same file?# (Not necessarily the same file descriptor!)defsameopenfile(fp1,fp2):"""Test whether two open file objects reference the same file"""s1=os.fstat(fp1)s2=os.fstat(fp2)returnsamestat(s1,s2)# Split a path in root and extension.# The extension is everything starting at the last dot in the last# pathname component; the root is everything before that.# It is always true that root + ext == p.# Generic implementation of splitext, to be parametrized with# the separatorsdef_splitext(p,sep,altsep,extsep):"""Split the extension from a pathname. Extension is everything from the last dot to the end, ignoring leading dots. Returns "(root, ext)"; ext may be empty."""# NOTE: This code must work for text and bytes strings.sepIndex=p.rfind(sep)ifaltsep:altsepIndex=p.rfind(altsep)sepIndex=max(sepIndex,altsepIndex)dotIndex=p.rfind(extsep)ifdotIndex>sepIndex:# skip all leading dotsfilenameIndex=sepIndex+1whilefilenameIndex<dotIndex:ifp[filenameIndex:filenameIndex+1]!=extsep:returnp[:dotIndex],p[dotIndex:]filenameIndex+=1returnp,p[:0]def_check_arg_types(funcname,*args):hasstr=hasbytes=Falseforsinargs:ifisinstance(s,str):hasstr=Trueelifisinstance(s,bytes):hasbytes=Trueelse:raiseTypeError(f'{funcname}() argument must be str, bytes, or 'f'os.PathLike object, not {s.__class__.__name__!r}')fromNoneifhasstrandhasbytes:raiseTypeError("Can't mix strings and bytes in path components")fromNone