• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""
2Path operations common to more than one OS
3Do not use directly.  The OS specific modules import the appropriate
4functions from this module themselves.
5"""
6import os
7import stat
8
9__all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime',
10           'getsize', 'isdir', 'isfile', 'samefile', 'sameopenfile',
11           'samestat']
12
13
14# Does a path exist?
15# This is false for dangling symbolic links on systems that support them.
16def exists(path):
17    """Test whether a path exists.  Returns False for broken symbolic links"""
18    try:
19        os.stat(path)
20    except (OSError, ValueError):
21        return False
22    return True
23
24
25# This follows symbolic links, so both islink() and isdir() can be true
26# for the same path on systems that support symlinks
27def isfile(path):
28    """Test whether a path is a regular file"""
29    try:
30        st = os.stat(path)
31    except (OSError, ValueError):
32        return False
33    return stat.S_ISREG(st.st_mode)
34
35
36# Is a path a directory?
37# This follows symbolic links, so both islink() and isdir()
38# can be true for the same path on systems that support symlinks
39def isdir(s):
40    """Return true if the pathname refers to an existing directory."""
41    try:
42        st = os.stat(s)
43    except (OSError, ValueError):
44        return False
45    return stat.S_ISDIR(st.st_mode)
46
47
48def getsize(filename):
49    """Return the size of a file, reported by os.stat()."""
50    return os.stat(filename).st_size
51
52
53def getmtime(filename):
54    """Return the last modification time of a file, reported by os.stat()."""
55    return os.stat(filename).st_mtime
56
57
58def getatime(filename):
59    """Return the last access time of a file, reported by os.stat()."""
60    return os.stat(filename).st_atime
61
62
63def getctime(filename):
64    """Return the metadata change time of a file, reported by os.stat()."""
65    return os.stat(filename).st_ctime
66
67
68# Return the longest prefix of all list elements.
69def commonprefix(m):
70    "Given a list of pathnames, returns the longest common leading component"
71    if not m: return ''
72    # Some people pass in a list of pathname parts to operate in an OS-agnostic
73    # fashion; don't try to translate in that case as that's an abuse of the
74    # API and they are already doing what they need to be OS-agnostic and so
75    # they most likely won't be using an os.PathLike object in the sublists.
76    if not isinstance(m[0], (list, tuple)):
77        m = tuple(map(os.fspath, m))
78    s1 = min(m)
79    s2 = max(m)
80    for i, c in enumerate(s1):
81        if c != s2[i]:
82            return s1[:i]
83    return s1
84
85# Are two stat buffers (obtained from stat, fstat or lstat)
86# describing the same file?
87def samestat(s1, s2):
88    """Test whether two stat buffers reference the same file"""
89    return (s1.st_ino == s2.st_ino and
90            s1.st_dev == s2.st_dev)
91
92
93# Are two filenames really pointing to the same file?
94def samefile(f1, f2):
95    """Test whether two pathnames reference the same actual file or directory
96
97    This is determined by the device number and i-node number and
98    raises an exception if an os.stat() call on either pathname fails.
99    """
100    s1 = os.stat(f1)
101    s2 = os.stat(f2)
102    return samestat(s1, s2)
103
104
105# Are two open files really referencing the same file?
106# (Not necessarily the same file descriptor!)
107def sameopenfile(fp1, fp2):
108    """Test whether two open file objects reference the same file"""
109    s1 = os.fstat(fp1)
110    s2 = os.fstat(fp2)
111    return samestat(s1, s2)
112
113
114# Split a path in root and extension.
115# The extension is everything starting at the last dot in the last
116# pathname component; the root is everything before that.
117# It is always true that root + ext == p.
118
119# Generic implementation of splitext, to be parametrized with
120# the separators
121def _splitext(p, sep, altsep, extsep):
122    """Split the extension from a pathname.
123
124    Extension is everything from the last dot to the end, ignoring
125    leading dots.  Returns "(root, ext)"; ext may be empty."""
126    # NOTE: This code must work for text and bytes strings.
127
128    sepIndex = p.rfind(sep)
129    if altsep:
130        altsepIndex = p.rfind(altsep)
131        sepIndex = max(sepIndex, altsepIndex)
132
133    dotIndex = p.rfind(extsep)
134    if dotIndex > sepIndex:
135        # skip all leading dots
136        filenameIndex = sepIndex + 1
137        while filenameIndex < dotIndex:
138            if p[filenameIndex:filenameIndex+1] != extsep:
139                return p[:dotIndex], p[dotIndex:]
140            filenameIndex += 1
141
142    return p, p[:0]
143
144def _check_arg_types(funcname, *args):
145    hasstr = hasbytes = False
146    for s in args:
147        if isinstance(s, str):
148            hasstr = True
149        elif isinstance(s, bytes):
150            hasbytes = True
151        else:
152            raise TypeError(f'{funcname}() argument must be str, bytes, or '
153                            f'os.PathLike object, not {s.__class__.__name__!r}') from None
154    if hasstr and hasbytes:
155        raise TypeError("Can't mix strings and bytes in path components") from None
156