• 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', 'isdevdrive', 'isdir', 'isfile', 'isjunction', 'islink',
11           'lexists', 'samefile', 'sameopenfile', '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# Being true for dangling symbolic links is also useful.
26def lexists(path):
27    """Test whether a path exists.  Returns True for broken symbolic links"""
28    try:
29        os.lstat(path)
30    except (OSError, ValueError):
31        return False
32    return True
33
34# This follows symbolic links, so both islink() and isdir() can be true
35# for the same path on systems that support symlinks
36def isfile(path):
37    """Test whether a path is a regular file"""
38    try:
39        st = os.stat(path)
40    except (OSError, ValueError):
41        return False
42    return stat.S_ISREG(st.st_mode)
43
44
45# Is a path a directory?
46# This follows symbolic links, so both islink() and isdir()
47# can be true for the same path on systems that support symlinks
48def isdir(s):
49    """Return true if the pathname refers to an existing directory."""
50    try:
51        st = os.stat(s)
52    except (OSError, ValueError):
53        return False
54    return stat.S_ISDIR(st.st_mode)
55
56
57# Is a path a symbolic link?
58# This will always return false on systems where os.lstat doesn't exist.
59
60def islink(path):
61    """Test whether a path is a symbolic link"""
62    try:
63        st = os.lstat(path)
64    except (OSError, ValueError, AttributeError):
65        return False
66    return stat.S_ISLNK(st.st_mode)
67
68
69# Is a path a junction?
70def isjunction(path):
71    """Test whether a path is a junction
72    Junctions are not supported on the current platform"""
73    os.fspath(path)
74    return False
75
76
77def isdevdrive(path):
78    """Determines whether the specified path is on a Windows Dev Drive.
79    Dev Drives are not supported on the current platform"""
80    os.fspath(path)
81    return False
82
83
84def getsize(filename):
85    """Return the size of a file, reported by os.stat()."""
86    return os.stat(filename).st_size
87
88
89def getmtime(filename):
90    """Return the last modification time of a file, reported by os.stat()."""
91    return os.stat(filename).st_mtime
92
93
94def getatime(filename):
95    """Return the last access time of a file, reported by os.stat()."""
96    return os.stat(filename).st_atime
97
98
99def getctime(filename):
100    """Return the metadata change time of a file, reported by os.stat()."""
101    return os.stat(filename).st_ctime
102
103
104# Return the longest prefix of all list elements.
105def commonprefix(m):
106    "Given a list of pathnames, returns the longest common leading component"
107    if not m: return ''
108    # Some people pass in a list of pathname parts to operate in an OS-agnostic
109    # fashion; don't try to translate in that case as that's an abuse of the
110    # API and they are already doing what they need to be OS-agnostic and so
111    # they most likely won't be using an os.PathLike object in the sublists.
112    if not isinstance(m[0], (list, tuple)):
113        m = tuple(map(os.fspath, m))
114    s1 = min(m)
115    s2 = max(m)
116    for i, c in enumerate(s1):
117        if c != s2[i]:
118            return s1[:i]
119    return s1
120
121# Are two stat buffers (obtained from stat, fstat or lstat)
122# describing the same file?
123def samestat(s1, s2):
124    """Test whether two stat buffers reference the same file"""
125    return (s1.st_ino == s2.st_ino and
126            s1.st_dev == s2.st_dev)
127
128
129# Are two filenames really pointing to the same file?
130def samefile(f1, f2):
131    """Test whether two pathnames reference the same actual file or directory
132
133    This is determined by the device number and i-node number and
134    raises an exception if an os.stat() call on either pathname fails.
135    """
136    s1 = os.stat(f1)
137    s2 = os.stat(f2)
138    return samestat(s1, s2)
139
140
141# Are two open files really referencing the same file?
142# (Not necessarily the same file descriptor!)
143def sameopenfile(fp1, fp2):
144    """Test whether two open file objects reference the same file"""
145    s1 = os.fstat(fp1)
146    s2 = os.fstat(fp2)
147    return samestat(s1, s2)
148
149
150# Split a path in root and extension.
151# The extension is everything starting at the last dot in the last
152# pathname component; the root is everything before that.
153# It is always true that root + ext == p.
154
155# Generic implementation of splitext, to be parametrized with
156# the separators
157def _splitext(p, sep, altsep, extsep):
158    """Split the extension from a pathname.
159
160    Extension is everything from the last dot to the end, ignoring
161    leading dots.  Returns "(root, ext)"; ext may be empty."""
162    # NOTE: This code must work for text and bytes strings.
163
164    sepIndex = p.rfind(sep)
165    if altsep:
166        altsepIndex = p.rfind(altsep)
167        sepIndex = max(sepIndex, altsepIndex)
168
169    dotIndex = p.rfind(extsep)
170    if dotIndex > sepIndex:
171        # skip all leading dots
172        filenameIndex = sepIndex + 1
173        while filenameIndex < dotIndex:
174            if p[filenameIndex:filenameIndex+1] != extsep:
175                return p[:dotIndex], p[dotIndex:]
176            filenameIndex += 1
177
178    return p, p[:0]
179
180def _check_arg_types(funcname, *args):
181    hasstr = hasbytes = False
182    for s in args:
183        if isinstance(s, str):
184            hasstr = True
185        elif isinstance(s, bytes):
186            hasbytes = True
187        else:
188            raise TypeError(f'{funcname}() argument must be str, bytes, or '
189                            f'os.PathLike object, not {s.__class__.__name__!r}') from None
190    if hasstr and hasbytes:
191        raise TypeError("Can't mix strings and bytes in path components") from None
192