blob: 82d3f09154b1a071d5d422a6124febfc8dbc8bb4 [file] [log] [blame]
Tarek Ziadéc3399782010-02-23 05:39:18 +00001"""Utility functions for copying and archiving files and directory trees.
Guido van Rossum9d0a3df1997-04-29 14:45:19 +00002
Guido van Rossum959fa011999-08-18 20:03:17 +00003XXX The functions here don't copy the resource fork or other metadata on Mac.
Guido van Rossum9d0a3df1997-04-29 14:45:19 +00004
5"""
Guido van Rossumc6360141990-10-13 19:23:40 +00006
Guido van Rossumc96207a1992-03-31 18:55:40 +00007import os
Guido van Rossum83c03e21999-02-23 23:07:51 +00008import sys
Guido van Rossum9d0a3df1997-04-29 14:45:19 +00009import stat
Brett Cannon1c3fa182004-06-19 21:11:35 +000010from os.path import abspath
Georg Brandl2ee470f2008-07-16 12:55:28 +000011import fnmatch
Tarek Ziadé396fad72010-02-23 05:30:31 +000012import collections
Antoine Pitrou910bd512010-03-22 20:11:09 +000013import errno
Tarek Ziadé6ac91722010-04-28 17:51:36 +000014import tarfile
Tarek Ziadé396fad72010-02-23 05:30:31 +000015
16try:
Tarek Ziadéffa155a2010-04-29 13:34:35 +000017 import bz2
Florent Xicluna54540ec2011-11-04 08:29:17 +010018 del bz2
Tarek Ziadéffa155a2010-04-29 13:34:35 +000019 _BZ2_SUPPORTED = True
20except ImportError:
21 _BZ2_SUPPORTED = False
22
23try:
Tarek Ziadé396fad72010-02-23 05:30:31 +000024 from pwd import getpwnam
25except ImportError:
26 getpwnam = None
27
28try:
29 from grp import getgrnam
30except ImportError:
31 getgrnam = None
Guido van Rossumc6360141990-10-13 19:23:40 +000032
Tarek Ziadéc3399782010-02-23 05:39:18 +000033__all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2",
34 "copytree", "move", "rmtree", "Error", "SpecialFileError",
35 "ExecError", "make_archive", "get_archive_formats",
Tarek Ziadé6ac91722010-04-28 17:51:36 +000036 "register_archive_format", "unregister_archive_format",
37 "get_unpack_formats", "register_unpack_format",
Éric Araujoc5efe652011-08-21 14:30:00 +020038 "unregister_unpack_format", "unpack_archive",
Brian Curtinc57a3452012-06-22 16:00:30 -050039 "ignore_patterns", "chown", "which"]
Éric Araujoe4d5b8e2011-08-08 16:51:11 +020040 # disk_usage is added later, if available on the platform
Martin v. Löwise9ce0b02002-10-07 13:23:24 +000041
Neal Norwitz4ce69a52005-09-01 00:45:28 +000042class Error(EnvironmentError):
Martin v. Löwise9ce0b02002-10-07 13:23:24 +000043 pass
Guido van Rossumc6360141990-10-13 19:23:40 +000044
Antoine Pitrou7fff0962009-05-01 21:09:44 +000045class SpecialFileError(EnvironmentError):
46 """Raised when trying to do a kind of operation (e.g. copying) which is
47 not supported on a special file (e.g. a named pipe)"""
48
Tarek Ziadé396fad72010-02-23 05:30:31 +000049class ExecError(EnvironmentError):
50 """Raised when a command could not be executed"""
51
Tarek Ziadé6ac91722010-04-28 17:51:36 +000052class ReadError(EnvironmentError):
53 """Raised when an archive cannot be read"""
54
55class RegistryError(Exception):
56 """Raised when a registery operation with the archiving
57 and unpacking registeries fails"""
58
59
Georg Brandl6aa2d1f2008-08-12 08:35:52 +000060try:
61 WindowsError
62except NameError:
63 WindowsError = None
64
Greg Stein42bb8b32000-07-12 09:55:30 +000065def copyfileobj(fsrc, fdst, length=16*1024):
66 """copy data from file-like object fsrc to file-like object fdst"""
67 while 1:
68 buf = fsrc.read(length)
69 if not buf:
70 break
71 fdst.write(buf)
72
Johannes Gijsbers46f14592004-08-14 13:30:02 +000073def _samefile(src, dst):
74 # Macintosh, Unix.
Tarek Ziadé1eab9cc2010-04-19 21:19:57 +000075 if hasattr(os.path, 'samefile'):
Johannes Gijsbersf9a098e2004-08-14 14:51:01 +000076 try:
77 return os.path.samefile(src, dst)
78 except OSError:
79 return False
Johannes Gijsbers46f14592004-08-14 13:30:02 +000080
81 # All other platforms: check for same pathname.
82 return (os.path.normcase(os.path.abspath(src)) ==
83 os.path.normcase(os.path.abspath(dst)))
Tim Peters495ad3c2001-01-15 01:36:40 +000084
Antoine Pitrou78091e62011-12-29 18:54:15 +010085def copyfile(src, dst, symlinks=False):
86 """Copy data from src to dst.
87
88 If optional flag `symlinks` is set and `src` is a symbolic link, a new
89 symlink will be created instead of copying the file it points to.
90
91 """
Johannes Gijsbers46f14592004-08-14 13:30:02 +000092 if _samefile(src, dst):
Collin Winterce36ad82007-08-30 01:19:48 +000093 raise Error("`%s` and `%s` are the same file" % (src, dst))
Johannes Gijsbers46f14592004-08-14 13:30:02 +000094
Antoine Pitrou7fff0962009-05-01 21:09:44 +000095 for fn in [src, dst]:
96 try:
97 st = os.stat(fn)
98 except OSError:
99 # File most likely does not exist
100 pass
Benjamin Petersonc0d98aa2009-06-05 19:13:27 +0000101 else:
102 # XXX What about other special files? (sockets, devices...)
103 if stat.S_ISFIFO(st.st_mode):
104 raise SpecialFileError("`%s` is a named pipe" % fn)
Tarek Ziadéb01142b2010-05-05 22:43:04 +0000105
Antoine Pitrou78091e62011-12-29 18:54:15 +0100106 if symlinks and os.path.islink(src):
107 os.symlink(os.readlink(src), dst)
108 else:
109 with open(src, 'rb') as fsrc:
110 with open(dst, 'wb') as fdst:
111 copyfileobj(fsrc, fdst)
Brian Curtin0d0a1de2012-06-18 18:41:07 -0500112 return dst
Guido van Rossumc6360141990-10-13 19:23:40 +0000113
Antoine Pitrou78091e62011-12-29 18:54:15 +0100114def copymode(src, dst, symlinks=False):
115 """Copy mode bits from src to dst.
Guido van Rossumc6360141990-10-13 19:23:40 +0000116
Antoine Pitrou78091e62011-12-29 18:54:15 +0100117 If the optional flag `symlinks` is set, symlinks aren't followed if and
118 only if both `src` and `dst` are symlinks. If `lchmod` isn't available (eg.
119 Linux), in these cases, this method does nothing.
120
121 """
122 if symlinks and os.path.islink(src) and os.path.islink(dst):
123 if hasattr(os, 'lchmod'):
124 stat_func, chmod_func = os.lstat, os.lchmod
125 else:
126 return
127 elif hasattr(os, 'chmod'):
128 stat_func, chmod_func = os.stat, os.chmod
129 else:
130 return
131
132 st = stat_func(src)
133 chmod_func(dst, stat.S_IMODE(st.st_mode))
134
135def copystat(src, dst, symlinks=False):
136 """Copy all stat info (mode bits, atime, mtime, flags) from src to dst.
137
138 If the optional flag `symlinks` is set, symlinks aren't followed if and
139 only if both `src` and `dst` are symlinks.
140
141 """
Larry Hastings9cf065c2012-06-22 16:30:09 -0700142 def _nop(*args, ns=None, follow_symlinks=None):
Antoine Pitrou78091e62011-12-29 18:54:15 +0100143 pass
144
Larry Hastings9cf065c2012-06-22 16:30:09 -0700145 # follow symlinks (aka don't not follow symlinks)
146 follow = not (symlinks and os.path.islink(src) and os.path.islink(dst))
147 if follow:
148 # use the real function if it exists
149 def lookup(name):
150 return getattr(os, name, _nop)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100151 else:
Larry Hastings9cf065c2012-06-22 16:30:09 -0700152 # use the real function only if it exists
153 # *and* it supports follow_symlinks
154 def lookup(name):
155 fn = getattr(os, name, _nop)
156 if fn in os.supports_follow_symlinks:
157 return fn
158 return _nop
Antoine Pitrou78091e62011-12-29 18:54:15 +0100159
Larry Hastings9cf065c2012-06-22 16:30:09 -0700160 st = lookup("stat")(src, follow_symlinks=follow)
Walter Dörwald294bbf32002-06-06 09:48:13 +0000161 mode = stat.S_IMODE(st.st_mode)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700162 lookup("utime")(dst, ns=(st.st_atime_ns, st.st_mtime_ns),
163 follow_symlinks=follow)
164 try:
165 lookup("chmod")(dst, mode, follow_symlinks=follow)
166 except NotImplementedError:
167 # if we got a NotImplementedError, it's because
168 # * follow_symlinks=False,
169 # * lchown() is unavailable, and
170 # * either
171 # * fchownat() is unvailable or
172 # * fchownat() doesn't implement AT_SYMLINK_NOFOLLOW.
173 # (it returned ENOSUP.)
174 # therefore we're out of options--we simply cannot chown the
175 # symlink. give up, suppress the error.
176 # (which is what shutil always did in this circumstance.)
177 pass
Antoine Pitrou78091e62011-12-29 18:54:15 +0100178 if hasattr(st, 'st_flags'):
Antoine Pitrou910bd512010-03-22 20:11:09 +0000179 try:
Larry Hastings9cf065c2012-06-22 16:30:09 -0700180 lookup("chflags")(dst, st.st_flags, follow_symlinks=follow)
Antoine Pitrou910bd512010-03-22 20:11:09 +0000181 except OSError as why:
Ned Deilybaf75712012-05-10 17:05:19 -0700182 for err in 'EOPNOTSUPP', 'ENOTSUP':
183 if hasattr(errno, err) and why.errno == getattr(errno, err):
184 break
185 else:
Antoine Pitrou910bd512010-03-22 20:11:09 +0000186 raise
Guido van Rossum9d0a3df1997-04-29 14:45:19 +0000187
Antoine Pitrou424246f2012-05-12 19:02:01 +0200188if hasattr(os, 'listxattr'):
189 def _copyxattr(src, dst, symlinks=False):
190 """Copy extended filesystem attributes from `src` to `dst`.
191
192 Overwrite existing attributes.
193
194 If the optional flag `symlinks` is set, symlinks won't be followed.
195
196 """
Antoine Pitrou424246f2012-05-12 19:02:01 +0200197
Larry Hastings9cf065c2012-06-22 16:30:09 -0700198 for name in os.listxattr(src, follow_symlinks=symlinks):
Antoine Pitrou424246f2012-05-12 19:02:01 +0200199 try:
Larry Hastings9cf065c2012-06-22 16:30:09 -0700200 value = os.getxattr(src, name, follow_symlinks=symlinks)
201 os.setxattr(dst, name, value, follow_symlinks=symlinks)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200202 except OSError as e:
203 if e.errno not in (errno.EPERM, errno.ENOTSUP, errno.ENODATA):
204 raise
205else:
206 def _copyxattr(*args, **kwargs):
207 pass
208
Antoine Pitrou78091e62011-12-29 18:54:15 +0100209def copy(src, dst, symlinks=False):
Brian Curtin0d0a1de2012-06-18 18:41:07 -0500210 """Copy data and mode bits ("cp src dst"). Return the file's destination.
Tim Peters495ad3c2001-01-15 01:36:40 +0000211
Guido van Rossum9d0a3df1997-04-29 14:45:19 +0000212 The destination may be a directory.
213
Antoine Pitrou78091e62011-12-29 18:54:15 +0100214 If the optional flag `symlinks` is set, symlinks won't be followed. This
215 resembles GNU's "cp -P src dst".
216
Guido van Rossum9d0a3df1997-04-29 14:45:19 +0000217 """
Guido van Rossuma2baf461997-04-29 14:06:46 +0000218 if os.path.isdir(dst):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000219 dst = os.path.join(dst, os.path.basename(src))
Antoine Pitrou78091e62011-12-29 18:54:15 +0100220 copyfile(src, dst, symlinks=symlinks)
221 copymode(src, dst, symlinks=symlinks)
Brian Curtin0d0a1de2012-06-18 18:41:07 -0500222 return dst
Guido van Rossumc6360141990-10-13 19:23:40 +0000223
Antoine Pitrou78091e62011-12-29 18:54:15 +0100224def copy2(src, dst, symlinks=False):
Brian Curtin0d0a1de2012-06-18 18:41:07 -0500225 """Copy data and all stat info ("cp -p src dst"). Return the file's
226 destination."
Guido van Rossum9d0a3df1997-04-29 14:45:19 +0000227
228 The destination may be a directory.
229
Antoine Pitrou78091e62011-12-29 18:54:15 +0100230 If the optional flag `symlinks` is set, symlinks won't be followed. This
231 resembles GNU's "cp -P src dst".
232
Guido van Rossum9d0a3df1997-04-29 14:45:19 +0000233 """
Guido van Rossuma2baf461997-04-29 14:06:46 +0000234 if os.path.isdir(dst):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000235 dst = os.path.join(dst, os.path.basename(src))
Antoine Pitrou78091e62011-12-29 18:54:15 +0100236 copyfile(src, dst, symlinks=symlinks)
237 copystat(src, dst, symlinks=symlinks)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200238 _copyxattr(src, dst, symlinks=symlinks)
Brian Curtin0d0a1de2012-06-18 18:41:07 -0500239 return dst
Guido van Rossumc6360141990-10-13 19:23:40 +0000240
Georg Brandl2ee470f2008-07-16 12:55:28 +0000241def ignore_patterns(*patterns):
242 """Function that can be used as copytree() ignore parameter.
Guido van Rossum9d0a3df1997-04-29 14:45:19 +0000243
Georg Brandl2ee470f2008-07-16 12:55:28 +0000244 Patterns is a sequence of glob-style patterns
245 that are used to exclude files"""
246 def _ignore_patterns(path, names):
247 ignored_names = []
248 for pattern in patterns:
249 ignored_names.extend(fnmatch.filter(names, pattern))
250 return set(ignored_names)
251 return _ignore_patterns
252
Tarek Ziadéfb437512010-04-20 08:57:33 +0000253def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2,
254 ignore_dangling_symlinks=False):
Tarek Ziadé5340db32010-04-19 22:30:51 +0000255 """Recursively copy a directory tree.
Guido van Rossum9d0a3df1997-04-29 14:45:19 +0000256
257 The destination directory must not already exist.
Neal Norwitza4c93b62003-02-23 21:36:32 +0000258 If exception(s) occur, an Error is raised with a list of reasons.
Guido van Rossum9d0a3df1997-04-29 14:45:19 +0000259
260 If the optional symlinks flag is true, symbolic links in the
261 source tree result in symbolic links in the destination tree; if
262 it is false, the contents of the files pointed to by symbolic
Tarek Ziadéfb437512010-04-20 08:57:33 +0000263 links are copied. If the file pointed by the symlink doesn't
264 exist, an exception will be added in the list of errors raised in
265 an Error exception at the end of the copy process.
266
267 You can set the optional ignore_dangling_symlinks flag to true if you
Tarek Ziadé8c26c7d2010-04-23 13:03:50 +0000268 want to silence this exception. Notice that this has no effect on
269 platforms that don't support os.symlink.
Guido van Rossum9d0a3df1997-04-29 14:45:19 +0000270
Georg Brandl2ee470f2008-07-16 12:55:28 +0000271 The optional ignore argument is a callable. If given, it
272 is called with the `src` parameter, which is the directory
273 being visited by copytree(), and `names` which is the list of
274 `src` contents, as returned by os.listdir():
275
276 callable(src, names) -> ignored_names
277
278 Since copytree() is called recursively, the callable will be
279 called once for each directory that is copied. It returns a
280 list of names relative to the `src` directory that should
281 not be copied.
282
Tarek Ziadé5340db32010-04-19 22:30:51 +0000283 The optional copy_function argument is a callable that will be used
284 to copy each file. It will be called with the source path and the
285 destination path as arguments. By default, copy2() is used, but any
286 function that supports the same signature (like copy()) can be used.
Guido van Rossum9d0a3df1997-04-29 14:45:19 +0000287
288 """
Guido van Rossuma2baf461997-04-29 14:06:46 +0000289 names = os.listdir(src)
Georg Brandl2ee470f2008-07-16 12:55:28 +0000290 if ignore is not None:
291 ignored_names = ignore(src, names)
292 else:
293 ignored_names = set()
294
Johannes Gijsberse4172ea2005-01-08 12:31:29 +0000295 os.makedirs(dst)
Martin v. Löwise9ce0b02002-10-07 13:23:24 +0000296 errors = []
Guido van Rossuma2baf461997-04-29 14:06:46 +0000297 for name in names:
Georg Brandl2ee470f2008-07-16 12:55:28 +0000298 if name in ignored_names:
299 continue
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000300 srcname = os.path.join(src, name)
301 dstname = os.path.join(dst, name)
302 try:
Tarek Ziadéfb437512010-04-20 08:57:33 +0000303 if os.path.islink(srcname):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000304 linkto = os.readlink(srcname)
Tarek Ziadéfb437512010-04-20 08:57:33 +0000305 if symlinks:
Antoine Pitrou78091e62011-12-29 18:54:15 +0100306 # We can't just leave it to `copy_function` because legacy
307 # code with a custom `copy_function` may rely on copytree
308 # doing the right thing.
Tarek Ziadéfb437512010-04-20 08:57:33 +0000309 os.symlink(linkto, dstname)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100310 copystat(srcname, dstname, symlinks=symlinks)
Tarek Ziadéfb437512010-04-20 08:57:33 +0000311 else:
312 # ignore dangling symlink if the flag is on
313 if not os.path.exists(linkto) and ignore_dangling_symlinks:
314 continue
315 # otherwise let the copy occurs. copy2 will raise an error
316 copy_function(srcname, dstname)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000317 elif os.path.isdir(srcname):
Tarek Ziadé5340db32010-04-19 22:30:51 +0000318 copytree(srcname, dstname, symlinks, ignore, copy_function)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000319 else:
Antoine Pitrou7fff0962009-05-01 21:09:44 +0000320 # Will raise a SpecialFileError for unsupported file types
Tarek Ziadé5340db32010-04-19 22:30:51 +0000321 copy_function(srcname, dstname)
Georg Brandla1be88e2005-08-31 22:48:45 +0000322 # catch the Error from the recursive copytree so that we can
323 # continue with other files
Guido van Rossumb940e112007-01-10 16:19:56 +0000324 except Error as err:
Georg Brandla1be88e2005-08-31 22:48:45 +0000325 errors.extend(err.args[0])
Antoine Pitrou7fff0962009-05-01 21:09:44 +0000326 except EnvironmentError as why:
327 errors.append((srcname, dstname, str(why)))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000328 try:
329 copystat(src, dst)
Guido van Rossumb940e112007-01-10 16:19:56 +0000330 except OSError as why:
Georg Brandl6aa2d1f2008-08-12 08:35:52 +0000331 if WindowsError is not None and isinstance(why, WindowsError):
332 # Copying file access times may fail on Windows
333 pass
334 else:
335 errors.extend((src, dst, str(why)))
Martin v. Löwise9ce0b02002-10-07 13:23:24 +0000336 if errors:
Collin Winterce36ad82007-08-30 01:19:48 +0000337 raise Error(errors)
Brian Curtin0d0a1de2012-06-18 18:41:07 -0500338 return dst
Guido van Rossumd7673291998-02-06 21:38:09 +0000339
Barry Warsaw234d9a92003-01-24 17:36:15 +0000340def rmtree(path, ignore_errors=False, onerror=None):
Guido van Rossumd7673291998-02-06 21:38:09 +0000341 """Recursively delete a directory tree.
342
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000343 If ignore_errors is set, errors are ignored; otherwise, if onerror
344 is set, it is called to handle the error with arguments (func,
345 path, exc_info) where func is os.listdir, os.remove, or os.rmdir;
346 path is the argument to that function that caused it to fail; and
347 exc_info is a tuple returned by sys.exc_info(). If ignore_errors
348 is false and onerror is None, an exception is raised.
349
Guido van Rossumd7673291998-02-06 21:38:09 +0000350 """
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000351 if ignore_errors:
352 def onerror(*args):
Barry Warsaw234d9a92003-01-24 17:36:15 +0000353 pass
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000354 elif onerror is None:
355 def onerror(*args):
356 raise
Christian Heimes9bd667a2008-01-20 15:14:11 +0000357 try:
358 if os.path.islink(path):
359 # symlinks to directories are forbidden, see bug #1669
360 raise OSError("Cannot call rmtree on a symbolic link")
361 except OSError:
362 onerror(os.path.islink, path, sys.exc_info())
363 # can't continue even if onerror hook returns
364 return
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000365 names = []
366 try:
367 names = os.listdir(path)
Éric Araujocfcc9772011-08-10 20:54:33 +0200368 except os.error:
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000369 onerror(os.listdir, path, sys.exc_info())
370 for name in names:
371 fullname = os.path.join(path, name)
372 try:
373 mode = os.lstat(fullname).st_mode
374 except os.error:
375 mode = 0
376 if stat.S_ISDIR(mode):
377 rmtree(fullname, ignore_errors, onerror)
Barry Warsaw234d9a92003-01-24 17:36:15 +0000378 else:
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000379 try:
380 os.remove(fullname)
Éric Araujocfcc9772011-08-10 20:54:33 +0200381 except os.error:
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000382 onerror(os.remove, fullname, sys.exc_info())
383 try:
384 os.rmdir(path)
385 except os.error:
386 onerror(os.rmdir, path, sys.exc_info())
Guido van Rossumd7673291998-02-06 21:38:09 +0000387
Martin v. Löwise9ce0b02002-10-07 13:23:24 +0000388
Christian Heimesada8c3b2008-03-18 18:26:33 +0000389def _basename(path):
390 # A basename() variant which first strips the trailing slash, if present.
391 # Thus we always get the last component of the path, even for directories.
392 return os.path.basename(path.rstrip(os.path.sep))
393
394def move(src, dst):
395 """Recursively move a file or directory to another location. This is
Brian Curtin0d0a1de2012-06-18 18:41:07 -0500396 similar to the Unix "mv" command. Return the file or directory's
397 destination.
Christian Heimesada8c3b2008-03-18 18:26:33 +0000398
399 If the destination is a directory or a symlink to a directory, the source
400 is moved inside the directory. The destination path must not already
401 exist.
402
403 If the destination already exists but is not a directory, it may be
404 overwritten depending on os.rename() semantics.
405
406 If the destination is on our current filesystem, then rename() is used.
Antoine Pitrou0a08d7a2012-01-06 20:16:19 +0100407 Otherwise, src is copied to the destination and then removed. Symlinks are
408 recreated under the new name if os.rename() fails because of cross
409 filesystem renames.
410
Martin v. Löwise9ce0b02002-10-07 13:23:24 +0000411 A lot more could be done here... A look at a mv.c shows a lot of
412 the issues this implementation glosses over.
413
414 """
Christian Heimesada8c3b2008-03-18 18:26:33 +0000415 real_dst = dst
416 if os.path.isdir(dst):
Ronald Oussorenf51738b2011-05-06 10:23:04 +0200417 if _samefile(src, dst):
418 # We might be on a case insensitive filesystem,
419 # perform the rename anyway.
420 os.rename(src, dst)
421 return
422
Christian Heimesada8c3b2008-03-18 18:26:33 +0000423 real_dst = os.path.join(dst, _basename(src))
424 if os.path.exists(real_dst):
425 raise Error("Destination path '%s' already exists" % real_dst)
Martin v. Löwise9ce0b02002-10-07 13:23:24 +0000426 try:
Christian Heimesada8c3b2008-03-18 18:26:33 +0000427 os.rename(src, real_dst)
Éric Araujocfcc9772011-08-10 20:54:33 +0200428 except OSError:
Antoine Pitrou0a08d7a2012-01-06 20:16:19 +0100429 if os.path.islink(src):
430 linkto = os.readlink(src)
431 os.symlink(linkto, real_dst)
432 os.unlink(src)
433 elif os.path.isdir(src):
Benjamin Peterson247a9b82009-02-20 04:09:19 +0000434 if _destinsrc(src, dst):
Collin Winterce36ad82007-08-30 01:19:48 +0000435 raise Error("Cannot move a directory '%s' into itself '%s'." % (src, dst))
Christian Heimesada8c3b2008-03-18 18:26:33 +0000436 copytree(src, real_dst, symlinks=True)
Martin v. Löwise9ce0b02002-10-07 13:23:24 +0000437 rmtree(src)
438 else:
Christian Heimesada8c3b2008-03-18 18:26:33 +0000439 copy2(src, real_dst)
Martin v. Löwise9ce0b02002-10-07 13:23:24 +0000440 os.unlink(src)
Brian Curtin0d0a1de2012-06-18 18:41:07 -0500441 return real_dst
Brett Cannon1c3fa182004-06-19 21:11:35 +0000442
Benjamin Peterson247a9b82009-02-20 04:09:19 +0000443def _destinsrc(src, dst):
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +0000444 src = abspath(src)
445 dst = abspath(dst)
446 if not src.endswith(os.path.sep):
447 src += os.path.sep
448 if not dst.endswith(os.path.sep):
449 dst += os.path.sep
450 return dst.startswith(src)
Tarek Ziadé396fad72010-02-23 05:30:31 +0000451
452def _get_gid(name):
453 """Returns a gid, given a group name."""
454 if getgrnam is None or name is None:
455 return None
456 try:
457 result = getgrnam(name)
458 except KeyError:
459 result = None
460 if result is not None:
461 return result[2]
462 return None
463
464def _get_uid(name):
465 """Returns an uid, given a user name."""
466 if getpwnam is None or name is None:
467 return None
468 try:
469 result = getpwnam(name)
470 except KeyError:
471 result = None
472 if result is not None:
473 return result[2]
474 return None
475
476def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
477 owner=None, group=None, logger=None):
478 """Create a (possibly compressed) tar file from all the files under
479 'base_dir'.
480
Tarek Ziadé5e2be872010-04-20 21:40:47 +0000481 'compress' must be "gzip" (the default), "bzip2", or None.
Tarek Ziadé396fad72010-02-23 05:30:31 +0000482
483 'owner' and 'group' can be used to define an owner and a group for the
484 archive that is being built. If not provided, the current owner and group
485 will be used.
486
Éric Araujo4433a5f2010-12-15 20:26:30 +0000487 The output tar file will be named 'base_name' + ".tar", possibly plus
Tarek Ziadé5e2be872010-04-20 21:40:47 +0000488 the appropriate compression extension (".gz", or ".bz2").
Tarek Ziadé396fad72010-02-23 05:30:31 +0000489
490 Returns the output filename.
491 """
Tarek Ziadéffa155a2010-04-29 13:34:35 +0000492 tar_compression = {'gzip': 'gz', None: ''}
493 compress_ext = {'gzip': '.gz'}
494
495 if _BZ2_SUPPORTED:
496 tar_compression['bzip2'] = 'bz2'
497 compress_ext['bzip2'] = '.bz2'
Tarek Ziadé396fad72010-02-23 05:30:31 +0000498
499 # flags for compression program, each element of list will be an argument
Éric Araujoc1b7e7f2011-09-18 23:12:30 +0200500 if compress is not None and compress not in compress_ext:
Tarek Ziadéffa155a2010-04-29 13:34:35 +0000501 raise ValueError("bad value for 'compress', or compression format not "
502 "supported : {0}".format(compress))
Tarek Ziadé396fad72010-02-23 05:30:31 +0000503
Tarek Ziadé5e2be872010-04-20 21:40:47 +0000504 archive_name = base_name + '.tar' + compress_ext.get(compress, '')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000505 archive_dir = os.path.dirname(archive_name)
Tarek Ziadé5e2be872010-04-20 21:40:47 +0000506
Tarek Ziadé396fad72010-02-23 05:30:31 +0000507 if not os.path.exists(archive_dir):
Éric Araujoac4e58e2011-01-29 20:32:11 +0000508 if logger is not None:
Éric Araujo43a7ee12011-08-19 02:55:11 +0200509 logger.info("creating %s", archive_dir)
Tarek Ziadé396fad72010-02-23 05:30:31 +0000510 if not dry_run:
511 os.makedirs(archive_dir)
512
Tarek Ziadé396fad72010-02-23 05:30:31 +0000513 # creating the tarball
Tarek Ziadé396fad72010-02-23 05:30:31 +0000514 if logger is not None:
515 logger.info('Creating tar archive')
516
517 uid = _get_uid(owner)
518 gid = _get_gid(group)
519
520 def _set_uid_gid(tarinfo):
521 if gid is not None:
522 tarinfo.gid = gid
523 tarinfo.gname = group
524 if uid is not None:
525 tarinfo.uid = uid
526 tarinfo.uname = owner
527 return tarinfo
528
529 if not dry_run:
530 tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress])
531 try:
532 tar.add(base_dir, filter=_set_uid_gid)
533 finally:
534 tar.close()
535
Tarek Ziadé396fad72010-02-23 05:30:31 +0000536 return archive_name
537
Tarek Ziadée2124162010-04-21 13:35:21 +0000538def _call_external_zip(base_dir, zip_filename, verbose=False, dry_run=False):
Tarek Ziadé396fad72010-02-23 05:30:31 +0000539 # XXX see if we want to keep an external call here
540 if verbose:
541 zipoptions = "-r"
542 else:
543 zipoptions = "-rq"
544 from distutils.errors import DistutilsExecError
545 from distutils.spawn import spawn
546 try:
547 spawn(["zip", zipoptions, zip_filename, base_dir], dry_run=dry_run)
548 except DistutilsExecError:
549 # XXX really should distinguish between "couldn't find
550 # external 'zip' command" and "zip failed".
551 raise ExecError("unable to create zip file '%s': "
552 "could neither import the 'zipfile' module nor "
553 "find a standalone zip utility") % zip_filename
554
555def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None):
556 """Create a zip file from all the files under 'base_dir'.
557
Éric Araujo4433a5f2010-12-15 20:26:30 +0000558 The output zip file will be named 'base_name' + ".zip". Uses either the
Tarek Ziadé396fad72010-02-23 05:30:31 +0000559 "zipfile" Python module (if available) or the InfoZIP "zip" utility
560 (if installed and found on the default search path). If neither tool is
561 available, raises ExecError. Returns the name of the output zip
562 file.
563 """
564 zip_filename = base_name + ".zip"
565 archive_dir = os.path.dirname(base_name)
566
567 if not os.path.exists(archive_dir):
568 if logger is not None:
569 logger.info("creating %s", archive_dir)
570 if not dry_run:
571 os.makedirs(archive_dir)
572
573 # If zipfile module is not available, try spawning an external 'zip'
574 # command.
575 try:
576 import zipfile
577 except ImportError:
578 zipfile = None
579
580 if zipfile is None:
Tarek Ziadée2124162010-04-21 13:35:21 +0000581 _call_external_zip(base_dir, zip_filename, verbose, dry_run)
Tarek Ziadé396fad72010-02-23 05:30:31 +0000582 else:
583 if logger is not None:
584 logger.info("creating '%s' and adding '%s' to it",
585 zip_filename, base_dir)
586
587 if not dry_run:
588 zip = zipfile.ZipFile(zip_filename, "w",
589 compression=zipfile.ZIP_DEFLATED)
590
591 for dirpath, dirnames, filenames in os.walk(base_dir):
592 for name in filenames:
593 path = os.path.normpath(os.path.join(dirpath, name))
594 if os.path.isfile(path):
595 zip.write(path, path)
596 if logger is not None:
597 logger.info("adding '%s'", path)
598 zip.close()
599
600 return zip_filename
601
602_ARCHIVE_FORMATS = {
603 'gztar': (_make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"),
Tarek Ziadé396fad72010-02-23 05:30:31 +0000604 'tar': (_make_tarball, [('compress', None)], "uncompressed tar file"),
Éric Araujoc1b7e7f2011-09-18 23:12:30 +0200605 'zip': (_make_zipfile, [], "ZIP file")
Tarek Ziadé396fad72010-02-23 05:30:31 +0000606 }
607
Tarek Ziadéffa155a2010-04-29 13:34:35 +0000608if _BZ2_SUPPORTED:
609 _ARCHIVE_FORMATS['bztar'] = (_make_tarball, [('compress', 'bzip2')],
610 "bzip2'ed tar-file")
611
Tarek Ziadé396fad72010-02-23 05:30:31 +0000612def get_archive_formats():
613 """Returns a list of supported formats for archiving and unarchiving.
614
615 Each element of the returned sequence is a tuple (name, description)
616 """
617 formats = [(name, registry[2]) for name, registry in
618 _ARCHIVE_FORMATS.items()]
619 formats.sort()
620 return formats
621
622def register_archive_format(name, function, extra_args=None, description=''):
623 """Registers an archive format.
624
625 name is the name of the format. function is the callable that will be
626 used to create archives. If provided, extra_args is a sequence of
627 (name, value) tuples that will be passed as arguments to the callable.
628 description can be provided to describe the format, and will be returned
629 by the get_archive_formats() function.
630 """
631 if extra_args is None:
632 extra_args = []
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200633 if not callable(function):
Tarek Ziadé396fad72010-02-23 05:30:31 +0000634 raise TypeError('The %s object is not callable' % function)
635 if not isinstance(extra_args, (tuple, list)):
636 raise TypeError('extra_args needs to be a sequence')
637 for element in extra_args:
Éric Araujoc1b7e7f2011-09-18 23:12:30 +0200638 if not isinstance(element, (tuple, list)) or len(element) !=2:
Tarek Ziadé396fad72010-02-23 05:30:31 +0000639 raise TypeError('extra_args elements are : (arg_name, value)')
640
641 _ARCHIVE_FORMATS[name] = (function, extra_args, description)
642
643def unregister_archive_format(name):
644 del _ARCHIVE_FORMATS[name]
645
646def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
647 dry_run=0, owner=None, group=None, logger=None):
648 """Create an archive file (eg. zip or tar).
649
650 'base_name' is the name of the file to create, minus any format-specific
Tarek Ziadé5e2be872010-04-20 21:40:47 +0000651 extension; 'format' is the archive format: one of "zip", "tar", "bztar"
652 or "gztar".
Tarek Ziadé396fad72010-02-23 05:30:31 +0000653
654 'root_dir' is a directory that will be the root directory of the
655 archive; ie. we typically chdir into 'root_dir' before creating the
656 archive. 'base_dir' is the directory where we start archiving from;
657 ie. 'base_dir' will be the common prefix of all files and
658 directories in the archive. 'root_dir' and 'base_dir' both default
659 to the current directory. Returns the name of the archive file.
660
661 'owner' and 'group' are used when creating a tar archive. By default,
662 uses the current owner and group.
663 """
664 save_cwd = os.getcwd()
665 if root_dir is not None:
666 if logger is not None:
667 logger.debug("changing into '%s'", root_dir)
668 base_name = os.path.abspath(base_name)
669 if not dry_run:
670 os.chdir(root_dir)
671
672 if base_dir is None:
673 base_dir = os.curdir
674
675 kwargs = {'dry_run': dry_run, 'logger': logger}
676
677 try:
678 format_info = _ARCHIVE_FORMATS[format]
679 except KeyError:
680 raise ValueError("unknown archive format '%s'" % format)
681
682 func = format_info[0]
683 for arg, val in format_info[1]:
684 kwargs[arg] = val
685
686 if format != 'zip':
687 kwargs['owner'] = owner
688 kwargs['group'] = group
689
690 try:
691 filename = func(base_name, base_dir, **kwargs)
692 finally:
693 if root_dir is not None:
694 if logger is not None:
695 logger.debug("changing back to '%s'", save_cwd)
696 os.chdir(save_cwd)
697
698 return filename
Tarek Ziadé6ac91722010-04-28 17:51:36 +0000699
700
701def get_unpack_formats():
702 """Returns a list of supported formats for unpacking.
703
704 Each element of the returned sequence is a tuple
705 (name, extensions, description)
706 """
707 formats = [(name, info[0], info[3]) for name, info in
708 _UNPACK_FORMATS.items()]
709 formats.sort()
710 return formats
711
712def _check_unpack_options(extensions, function, extra_args):
713 """Checks what gets registered as an unpacker."""
714 # first make sure no other unpacker is registered for this extension
715 existing_extensions = {}
716 for name, info in _UNPACK_FORMATS.items():
717 for ext in info[0]:
718 existing_extensions[ext] = name
719
720 for extension in extensions:
721 if extension in existing_extensions:
722 msg = '%s is already registered for "%s"'
723 raise RegistryError(msg % (extension,
724 existing_extensions[extension]))
725
Florent Xicluna5d1155c2011-10-28 14:45:05 +0200726 if not callable(function):
Tarek Ziadé6ac91722010-04-28 17:51:36 +0000727 raise TypeError('The registered function must be a callable')
728
729
730def register_unpack_format(name, extensions, function, extra_args=None,
731 description=''):
732 """Registers an unpack format.
733
734 `name` is the name of the format. `extensions` is a list of extensions
735 corresponding to the format.
736
737 `function` is the callable that will be
738 used to unpack archives. The callable will receive archives to unpack.
739 If it's unable to handle an archive, it needs to raise a ReadError
740 exception.
741
742 If provided, `extra_args` is a sequence of
743 (name, value) tuples that will be passed as arguments to the callable.
744 description can be provided to describe the format, and will be returned
745 by the get_unpack_formats() function.
746 """
747 if extra_args is None:
748 extra_args = []
749 _check_unpack_options(extensions, function, extra_args)
750 _UNPACK_FORMATS[name] = extensions, function, extra_args, description
751
752def unregister_unpack_format(name):
753 """Removes the pack format from the registery."""
754 del _UNPACK_FORMATS[name]
755
756def _ensure_directory(path):
757 """Ensure that the parent directory of `path` exists"""
758 dirname = os.path.dirname(path)
759 if not os.path.isdir(dirname):
760 os.makedirs(dirname)
761
762def _unpack_zipfile(filename, extract_dir):
763 """Unpack zip `filename` to `extract_dir`
764 """
765 try:
766 import zipfile
767 except ImportError:
768 raise ReadError('zlib not supported, cannot unpack this archive.')
769
770 if not zipfile.is_zipfile(filename):
771 raise ReadError("%s is not a zip file" % filename)
772
773 zip = zipfile.ZipFile(filename)
774 try:
775 for info in zip.infolist():
776 name = info.filename
777
778 # don't extract absolute paths or ones with .. in them
779 if name.startswith('/') or '..' in name:
780 continue
781
782 target = os.path.join(extract_dir, *name.split('/'))
783 if not target:
784 continue
785
786 _ensure_directory(target)
787 if not name.endswith('/'):
788 # file
789 data = zip.read(info.filename)
Éric Araujoc1b7e7f2011-09-18 23:12:30 +0200790 f = open(target, 'wb')
Tarek Ziadé6ac91722010-04-28 17:51:36 +0000791 try:
792 f.write(data)
793 finally:
794 f.close()
795 del data
796 finally:
797 zip.close()
798
799def _unpack_tarfile(filename, extract_dir):
800 """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir`
801 """
802 try:
803 tarobj = tarfile.open(filename)
804 except tarfile.TarError:
805 raise ReadError(
806 "%s is not a compressed or uncompressed tar file" % filename)
807 try:
808 tarobj.extractall(extract_dir)
809 finally:
810 tarobj.close()
811
812_UNPACK_FORMATS = {
813 'gztar': (['.tar.gz', '.tgz'], _unpack_tarfile, [], "gzip'ed tar-file"),
Tarek Ziadé6ac91722010-04-28 17:51:36 +0000814 'tar': (['.tar'], _unpack_tarfile, [], "uncompressed tar file"),
815 'zip': (['.zip'], _unpack_zipfile, [], "ZIP file")
816 }
817
Tarek Ziadéffa155a2010-04-29 13:34:35 +0000818if _BZ2_SUPPORTED:
819 _UNPACK_FORMATS['bztar'] = (['.bz2'], _unpack_tarfile, [],
820 "bzip2'ed tar-file")
821
Tarek Ziadé6ac91722010-04-28 17:51:36 +0000822def _find_unpack_format(filename):
823 for name, info in _UNPACK_FORMATS.items():
824 for extension in info[0]:
825 if filename.endswith(extension):
826 return name
827 return None
828
829def unpack_archive(filename, extract_dir=None, format=None):
830 """Unpack an archive.
831
832 `filename` is the name of the archive.
833
834 `extract_dir` is the name of the target directory, where the archive
835 is unpacked. If not provided, the current working directory is used.
836
837 `format` is the archive format: one of "zip", "tar", or "gztar". Or any
838 other registered format. If not provided, unpack_archive will use the
839 filename extension and see if an unpacker was registered for that
840 extension.
841
842 In case none is found, a ValueError is raised.
843 """
844 if extract_dir is None:
845 extract_dir = os.getcwd()
846
847 if format is not None:
848 try:
849 format_info = _UNPACK_FORMATS[format]
850 except KeyError:
851 raise ValueError("Unknown unpack format '{0}'".format(format))
852
Nick Coghlanabf202d2011-03-16 13:52:20 -0400853 func = format_info[1]
854 func(filename, extract_dir, **dict(format_info[2]))
Tarek Ziadé6ac91722010-04-28 17:51:36 +0000855 else:
856 # we need to look at the registered unpackers supported extensions
857 format = _find_unpack_format(filename)
858 if format is None:
859 raise ReadError("Unknown archive format '{0}'".format(filename))
860
861 func = _UNPACK_FORMATS[format][1]
862 kwargs = dict(_UNPACK_FORMATS[format][2])
863 func(filename, extract_dir, **kwargs)
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +0200864
Éric Araujoe4d5b8e2011-08-08 16:51:11 +0200865
866if hasattr(os, 'statvfs'):
867
868 __all__.append('disk_usage')
869 _ntuple_diskusage = collections.namedtuple('usage', 'total used free')
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +0200870
871 def disk_usage(path):
Éric Araujoe4d5b8e2011-08-08 16:51:11 +0200872 """Return disk usage statistics about the given path.
873
Sandro Tosif8ae4fa2012-04-23 20:07:15 +0200874 Returned value is a named tuple with attributes 'total', 'used' and
Éric Araujoe4d5b8e2011-08-08 16:51:11 +0200875 'free', which are the amount of total, used and free space, in bytes.
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +0200876 """
Éric Araujoe4d5b8e2011-08-08 16:51:11 +0200877 st = os.statvfs(path)
878 free = st.f_bavail * st.f_frsize
879 total = st.f_blocks * st.f_frsize
880 used = (st.f_blocks - st.f_bfree) * st.f_frsize
881 return _ntuple_diskusage(total, used, free)
882
883elif os.name == 'nt':
884
885 import nt
886 __all__.append('disk_usage')
887 _ntuple_diskusage = collections.namedtuple('usage', 'total used free')
888
889 def disk_usage(path):
890 """Return disk usage statistics about the given path.
891
892 Returned valus is a named tuple with attributes 'total', 'used' and
893 'free', which are the amount of total, used and free space, in bytes.
894 """
895 total, free = nt._getdiskusage(path)
896 used = total - free
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +0200897 return _ntuple_diskusage(total, used, free)
Sandro Tosid902a142011-08-22 23:28:27 +0200898
Éric Araujo0ac4a5d2011-09-01 08:31:51 +0200899
Sandro Tosid902a142011-08-22 23:28:27 +0200900def chown(path, user=None, group=None):
901 """Change owner user and group of the given path.
902
903 user and group can be the uid/gid or the user/group names, and in that case,
904 they are converted to their respective uid/gid.
905 """
906
907 if user is None and group is None:
908 raise ValueError("user and/or group must be set")
909
910 _user = user
911 _group = group
912
913 # -1 means don't change it
914 if user is None:
915 _user = -1
916 # user can either be an int (the uid) or a string (the system username)
917 elif isinstance(user, str):
918 _user = _get_uid(user)
919 if _user is None:
920 raise LookupError("no such user: {!r}".format(user))
921
922 if group is None:
923 _group = -1
924 elif not isinstance(group, int):
925 _group = _get_gid(group)
926 if _group is None:
927 raise LookupError("no such group: {!r}".format(group))
928
929 os.chown(path, _user, _group)
Antoine Pitroubcf2b592012-02-08 23:28:36 +0100930
931def get_terminal_size(fallback=(80, 24)):
932 """Get the size of the terminal window.
933
934 For each of the two dimensions, the environment variable, COLUMNS
935 and LINES respectively, is checked. If the variable is defined and
936 the value is a positive integer, it is used.
937
938 When COLUMNS or LINES is not defined, which is the common case,
939 the terminal connected to sys.__stdout__ is queried
940 by invoking os.get_terminal_size.
941
942 If the terminal size cannot be successfully queried, either because
943 the system doesn't support querying, or because we are not
944 connected to a terminal, the value given in fallback parameter
945 is used. Fallback defaults to (80, 24) which is the default
946 size used by many terminal emulators.
947
948 The value returned is a named tuple of type os.terminal_size.
949 """
950 # columns, lines are the working values
951 try:
952 columns = int(os.environ['COLUMNS'])
953 except (KeyError, ValueError):
954 columns = 0
955
956 try:
957 lines = int(os.environ['LINES'])
958 except (KeyError, ValueError):
959 lines = 0
960
961 # only query if necessary
962 if columns <= 0 or lines <= 0:
963 try:
964 size = os.get_terminal_size(sys.__stdout__.fileno())
965 except (NameError, OSError):
966 size = os.terminal_size(fallback)
967 if columns <= 0:
968 columns = size.columns
969 if lines <= 0:
970 lines = size.lines
971
972 return os.terminal_size((columns, lines))
Brian Curtinc57a3452012-06-22 16:00:30 -0500973
974def which(cmd, mode=os.F_OK | os.X_OK, path=None):
Brian Curtine3f39402012-06-22 21:14:34 -0500975 """Given a command, mode, and a path string, return the path which
Brian Curtin21935362012-06-22 22:48:06 -0500976 conforms to the given mode on the PATH, or None if there is no such file.
977 `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result of
978 os.environ.get("PATH"), or can be overridden with a custom search path."""
Brian Curtinc57a3452012-06-22 16:00:30 -0500979 # Check that a given file can be accessed with the correct mode.
980 # Additionally check that `file` is not a directory, as on Windows
981 # directories pass the os.access check.
982 def _access_check(fn, mode):
983 if (os.path.exists(fn) and os.access(fn, mode)
984 and not os.path.isdir(fn)):
985 return True
986 return False
987
988 # Short circuit. If we're given a full path which matches the mode
989 # and it exists, we're done here.
990 if _access_check(cmd, mode):
991 return cmd
992
993 path = (path or os.environ.get("PATH", os.defpath)).split(os.pathsep)
994
995 if sys.platform == "win32":
996 # The current directory takes precedence on Windows.
997 if not os.curdir in path:
998 path.insert(0, os.curdir)
999
1000 # PATHEXT is necessary to check on Windows.
1001 pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
1002 # See if the given file matches any of the expected path extensions.
1003 # This will allow us to short circuit when given "python.exe".
1004 matches = [cmd for ext in pathext if cmd.lower().endswith(ext.lower())]
1005 # If it does match, only test that one, otherwise we have to try others.
1006 files = [cmd + ext.lower() for ext in pathext] if not matches else [cmd]
1007 else:
1008 # On other platforms you don't have things like PATHEXT to tell you
1009 # what file suffixes are executable, so just pass on cmd as-is.
1010 files = [cmd]
1011
1012 seen = set()
1013 for dir in path:
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001014 dir = os.path.normcase(dir)
Brian Curtinc57a3452012-06-22 16:00:30 -05001015 if not dir in seen:
1016 seen.add(dir)
1017 for thefile in files:
1018 name = os.path.join(dir, thefile)
1019 if _access_check(name, mode):
1020 return name
1021 return None