1"""Utility functions for copying and archiving files and directory trees. 2 3XXX The functions here don't copy the resource fork or other metadata on Mac. 4 5""" 6 7import os 8import sys 9import stat 10import fnmatch 11import collections 12import errno 13 14try: 15 import zlib 16 del zlib 17 _ZLIB_SUPPORTED = True 18except ImportError: 19 _ZLIB_SUPPORTED = False 20 21try: 22 import bz2 23 del bz2 24 _BZ2_SUPPORTED = True 25except ImportError: 26 _BZ2_SUPPORTED = False 27 28try: 29 import lzma 30 del lzma 31 _LZMA_SUPPORTED = True 32except ImportError: 33 _LZMA_SUPPORTED = False 34 35try: 36 from pwd import getpwnam 37except ImportError: 38 getpwnam = None 39 40try: 41 from grp import getgrnam 42except ImportError: 43 getgrnam = None 44 45_WINDOWS = os.name == 'nt' 46posix = nt = None 47if os.name == 'posix': 48 import posix 49elif _WINDOWS: 50 import nt 51 52COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 64 * 1024 53_USE_CP_SENDFILE = hasattr(os, "sendfile") and sys.platform.startswith("linux") 54_HAS_FCOPYFILE = posix and hasattr(posix, "_fcopyfile") # macOS 55 56# CMD defaults in Windows 10 57_WIN_DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC" 58 59__all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2", 60 "copytree", "move", "rmtree", "Error", "SpecialFileError", 61 "ExecError", "make_archive", "get_archive_formats", 62 "register_archive_format", "unregister_archive_format", 63 "get_unpack_formats", "register_unpack_format", 64 "unregister_unpack_format", "unpack_archive", 65 "ignore_patterns", "chown", "which", "get_terminal_size", 66 "SameFileError"] 67 # disk_usage is added later, if available on the platform 68 69class Error(OSError): 70 pass 71 72class SameFileError(Error): 73 """Raised when source and destination are the same file.""" 74 75class SpecialFileError(OSError): 76 """Raised when trying to do a kind of operation (e.g. copying) which is 77 not supported on a special file (e.g. a named pipe)""" 78 79class ExecError(OSError): 80 """Raised when a command could not be executed""" 81 82class ReadError(OSError): 83 """Raised when an archive cannot be read""" 84 85class RegistryError(Exception): 86 """Raised when a registry operation with the archiving 87 and unpacking registries fails""" 88 89class _GiveupOnFastCopy(Exception): 90 """Raised as a signal to fallback on using raw read()/write() 91 file copy when fast-copy functions fail to do so. 92 """ 93 94def _fastcopy_fcopyfile(fsrc, fdst, flags): 95 """Copy a regular file content or metadata by using high-performance 96 fcopyfile(3) syscall (macOS). 97 """ 98 try: 99 infd = fsrc.fileno() 100 outfd = fdst.fileno() 101 except Exception as err: 102 raise _GiveupOnFastCopy(err) # not a regular file 103 104 try: 105 posix._fcopyfile(infd, outfd, flags) 106 except OSError as err: 107 err.filename = fsrc.name 108 err.filename2 = fdst.name 109 if err.errno in {errno.EINVAL, errno.ENOTSUP}: 110 raise _GiveupOnFastCopy(err) 111 else: 112 raise err from None 113 114def _fastcopy_sendfile(fsrc, fdst): 115 """Copy data from one regular mmap-like fd to another by using 116 high-performance sendfile(2) syscall. 117 This should work on Linux >= 2.6.33 only. 118 """ 119 # Note: copyfileobj() is left alone in order to not introduce any 120 # unexpected breakage. Possible risks by using zero-copy calls 121 # in copyfileobj() are: 122 # - fdst cannot be open in "a"(ppend) mode 123 # - fsrc and fdst may be open in "t"(ext) mode 124 # - fsrc may be a BufferedReader (which hides unread data in a buffer), 125 # GzipFile (which decompresses data), HTTPResponse (which decodes 126 # chunks). 127 # - possibly others (e.g. encrypted fs/partition?) 128 global _USE_CP_SENDFILE 129 try: 130 infd = fsrc.fileno() 131 outfd = fdst.fileno() 132 except Exception as err: 133 raise _GiveupOnFastCopy(err) # not a regular file 134 135 # Hopefully the whole file will be copied in a single call. 136 # sendfile() is called in a loop 'till EOF is reached (0 return) 137 # so a bufsize smaller or bigger than the actual file size 138 # should not make any difference, also in case the file content 139 # changes while being copied. 140 try: 141 blocksize = max(os.fstat(infd).st_size, 2 ** 23) # min 8MiB 142 except OSError: 143 blocksize = 2 ** 27 # 128MiB 144 # On 32-bit architectures truncate to 1GiB to avoid OverflowError, 145 # see bpo-38319. 146 if sys.maxsize < 2 ** 32: 147 blocksize = min(blocksize, 2 ** 30) 148 149 offset = 0 150 while True: 151 try: 152 sent = os.sendfile(outfd, infd, offset, blocksize) 153 except OSError as err: 154 # ...in oder to have a more informative exception. 155 err.filename = fsrc.name 156 err.filename2 = fdst.name 157 158 if err.errno == errno.ENOTSOCK: 159 # sendfile() on this platform (probably Linux < 2.6.33) 160 # does not support copies between regular files (only 161 # sockets). 162 _USE_CP_SENDFILE = False 163 raise _GiveupOnFastCopy(err) 164 165 if err.errno == errno.ENOSPC: # filesystem is full 166 raise err from None 167 168 # Give up on first call and if no data was copied. 169 if offset == 0 and os.lseek(outfd, 0, os.SEEK_CUR) == 0: 170 raise _GiveupOnFastCopy(err) 171 172 raise err 173 else: 174 if sent == 0: 175 break # EOF 176 offset += sent 177 178def _copyfileobj_readinto(fsrc, fdst, length=COPY_BUFSIZE): 179 """readinto()/memoryview() based variant of copyfileobj(). 180 *fsrc* must support readinto() method and both files must be 181 open in binary mode. 182 """ 183 # Localize variable access to minimize overhead. 184 fsrc_readinto = fsrc.readinto 185 fdst_write = fdst.write 186 with memoryview(bytearray(length)) as mv: 187 while True: 188 n = fsrc_readinto(mv) 189 if not n: 190 break 191 elif n < length: 192 with mv[:n] as smv: 193 fdst.write(smv) 194 else: 195 fdst_write(mv) 196 197def copyfileobj(fsrc, fdst, length=0): 198 """copy data from file-like object fsrc to file-like object fdst""" 199 # Localize variable access to minimize overhead. 200 if not length: 201 length = COPY_BUFSIZE 202 fsrc_read = fsrc.read 203 fdst_write = fdst.write 204 while True: 205 buf = fsrc_read(length) 206 if not buf: 207 break 208 fdst_write(buf) 209 210def _samefile(src, dst): 211 # Macintosh, Unix. 212 if isinstance(src, os.DirEntry) and hasattr(os.path, 'samestat'): 213 try: 214 return os.path.samestat(src.stat(), os.stat(dst)) 215 except OSError: 216 return False 217 218 if hasattr(os.path, 'samefile'): 219 try: 220 return os.path.samefile(src, dst) 221 except OSError: 222 return False 223 224 # All other platforms: check for same pathname. 225 return (os.path.normcase(os.path.abspath(src)) == 226 os.path.normcase(os.path.abspath(dst))) 227 228def _stat(fn): 229 return fn.stat() if isinstance(fn, os.DirEntry) else os.stat(fn) 230 231def _islink(fn): 232 return fn.is_symlink() if isinstance(fn, os.DirEntry) else os.path.islink(fn) 233 234def copyfile(src, dst, *, follow_symlinks=True): 235 """Copy data from src to dst in the most efficient way possible. 236 237 If follow_symlinks is not set and src is a symbolic link, a new 238 symlink will be created instead of copying the file it points to. 239 240 """ 241 sys.audit("shutil.copyfile", src, dst) 242 243 if _samefile(src, dst): 244 raise SameFileError("{!r} and {!r} are the same file".format(src, dst)) 245 246 file_size = 0 247 for i, fn in enumerate([src, dst]): 248 try: 249 st = _stat(fn) 250 except OSError: 251 # File most likely does not exist 252 pass 253 else: 254 # XXX What about other special files? (sockets, devices...) 255 if stat.S_ISFIFO(st.st_mode): 256 fn = fn.path if isinstance(fn, os.DirEntry) else fn 257 raise SpecialFileError("`%s` is a named pipe" % fn) 258 if _WINDOWS and i == 0: 259 file_size = st.st_size 260 261 if not follow_symlinks and _islink(src): 262 os.symlink(os.readlink(src), dst) 263 else: 264 with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst: 265 # macOS 266 if _HAS_FCOPYFILE: 267 try: 268 _fastcopy_fcopyfile(fsrc, fdst, posix._COPYFILE_DATA) 269 return dst 270 except _GiveupOnFastCopy: 271 pass 272 # Linux 273 elif _USE_CP_SENDFILE: 274 try: 275 _fastcopy_sendfile(fsrc, fdst) 276 return dst 277 except _GiveupOnFastCopy: 278 pass 279 # Windows, see: 280 # https://github.com/python/cpython/pull/7160#discussion_r195405230 281 elif _WINDOWS and file_size > 0: 282 _copyfileobj_readinto(fsrc, fdst, min(file_size, COPY_BUFSIZE)) 283 return dst 284 285 copyfileobj(fsrc, fdst) 286 287 return dst 288 289def copymode(src, dst, *, follow_symlinks=True): 290 """Copy mode bits from src to dst. 291 292 If follow_symlinks is not set, symlinks aren't followed if and only 293 if both `src` and `dst` are symlinks. If `lchmod` isn't available 294 (e.g. Linux) this method does nothing. 295 296 """ 297 sys.audit("shutil.copymode", src, dst) 298 299 if not follow_symlinks and _islink(src) and os.path.islink(dst): 300 if hasattr(os, 'lchmod'): 301 stat_func, chmod_func = os.lstat, os.lchmod 302 else: 303 return 304 else: 305 stat_func, chmod_func = _stat, os.chmod 306 307 st = stat_func(src) 308 chmod_func(dst, stat.S_IMODE(st.st_mode)) 309 310if hasattr(os, 'listxattr'): 311 def _copyxattr(src, dst, *, follow_symlinks=True): 312 """Copy extended filesystem attributes from `src` to `dst`. 313 314 Overwrite existing attributes. 315 316 If `follow_symlinks` is false, symlinks won't be followed. 317 318 """ 319 320 try: 321 names = os.listxattr(src, follow_symlinks=follow_symlinks) 322 except OSError as e: 323 if e.errno not in (errno.ENOTSUP, errno.ENODATA, errno.EINVAL): 324 raise 325 return 326 for name in names: 327 try: 328 value = os.getxattr(src, name, follow_symlinks=follow_symlinks) 329 os.setxattr(dst, name, value, follow_symlinks=follow_symlinks) 330 except OSError as e: 331 if e.errno not in (errno.EPERM, errno.ENOTSUP, errno.ENODATA, 332 errno.EINVAL): 333 raise 334else: 335 def _copyxattr(*args, **kwargs): 336 pass 337 338def copystat(src, dst, *, follow_symlinks=True): 339 """Copy file metadata 340 341 Copy the permission bits, last access time, last modification time, and 342 flags from `src` to `dst`. On Linux, copystat() also copies the "extended 343 attributes" where possible. The file contents, owner, and group are 344 unaffected. `src` and `dst` are path-like objects or path names given as 345 strings. 346 347 If the optional flag `follow_symlinks` is not set, symlinks aren't 348 followed if and only if both `src` and `dst` are symlinks. 349 """ 350 sys.audit("shutil.copystat", src, dst) 351 352 def _nop(*args, ns=None, follow_symlinks=None): 353 pass 354 355 # follow symlinks (aka don't not follow symlinks) 356 follow = follow_symlinks or not (_islink(src) and os.path.islink(dst)) 357 if follow: 358 # use the real function if it exists 359 def lookup(name): 360 return getattr(os, name, _nop) 361 else: 362 # use the real function only if it exists 363 # *and* it supports follow_symlinks 364 def lookup(name): 365 fn = getattr(os, name, _nop) 366 if fn in os.supports_follow_symlinks: 367 return fn 368 return _nop 369 370 if isinstance(src, os.DirEntry): 371 st = src.stat(follow_symlinks=follow) 372 else: 373 st = lookup("stat")(src, follow_symlinks=follow) 374 mode = stat.S_IMODE(st.st_mode) 375 lookup("utime")(dst, ns=(st.st_atime_ns, st.st_mtime_ns), 376 follow_symlinks=follow) 377 # We must copy extended attributes before the file is (potentially) 378 # chmod()'ed read-only, otherwise setxattr() will error with -EACCES. 379 _copyxattr(src, dst, follow_symlinks=follow) 380 try: 381 lookup("chmod")(dst, mode, follow_symlinks=follow) 382 except NotImplementedError: 383 # if we got a NotImplementedError, it's because 384 # * follow_symlinks=False, 385 # * lchown() is unavailable, and 386 # * either 387 # * fchownat() is unavailable or 388 # * fchownat() doesn't implement AT_SYMLINK_NOFOLLOW. 389 # (it returned ENOSUP.) 390 # therefore we're out of options--we simply cannot chown the 391 # symlink. give up, suppress the error. 392 # (which is what shutil always did in this circumstance.) 393 pass 394 if hasattr(st, 'st_flags'): 395 try: 396 lookup("chflags")(dst, st.st_flags, follow_symlinks=follow) 397 except OSError as why: 398 for err in 'EOPNOTSUPP', 'ENOTSUP': 399 if hasattr(errno, err) and why.errno == getattr(errno, err): 400 break 401 else: 402 raise 403 404def copy(src, dst, *, follow_symlinks=True): 405 """Copy data and mode bits ("cp src dst"). Return the file's destination. 406 407 The destination may be a directory. 408 409 If follow_symlinks is false, symlinks won't be followed. This 410 resembles GNU's "cp -P src dst". 411 412 If source and destination are the same file, a SameFileError will be 413 raised. 414 415 """ 416 if os.path.isdir(dst): 417 dst = os.path.join(dst, os.path.basename(src)) 418 copyfile(src, dst, follow_symlinks=follow_symlinks) 419 copymode(src, dst, follow_symlinks=follow_symlinks) 420 return dst 421 422def copy2(src, dst, *, follow_symlinks=True): 423 """Copy data and metadata. Return the file's destination. 424 425 Metadata is copied with copystat(). Please see the copystat function 426 for more information. 427 428 The destination may be a directory. 429 430 If follow_symlinks is false, symlinks won't be followed. This 431 resembles GNU's "cp -P src dst". 432 """ 433 if os.path.isdir(dst): 434 dst = os.path.join(dst, os.path.basename(src)) 435 copyfile(src, dst, follow_symlinks=follow_symlinks) 436 copystat(src, dst, follow_symlinks=follow_symlinks) 437 return dst 438 439def ignore_patterns(*patterns): 440 """Function that can be used as copytree() ignore parameter. 441 442 Patterns is a sequence of glob-style patterns 443 that are used to exclude files""" 444 def _ignore_patterns(path, names): 445 ignored_names = [] 446 for pattern in patterns: 447 ignored_names.extend(fnmatch.filter(names, pattern)) 448 return set(ignored_names) 449 return _ignore_patterns 450 451def _copytree(entries, src, dst, symlinks, ignore, copy_function, 452 ignore_dangling_symlinks, dirs_exist_ok=False): 453 if ignore is not None: 454 ignored_names = ignore(os.fspath(src), [x.name for x in entries]) 455 else: 456 ignored_names = set() 457 458 os.makedirs(dst, exist_ok=dirs_exist_ok) 459 errors = [] 460 use_srcentry = copy_function is copy2 or copy_function is copy 461 462 for srcentry in entries: 463 if srcentry.name in ignored_names: 464 continue 465 srcname = os.path.join(src, srcentry.name) 466 dstname = os.path.join(dst, srcentry.name) 467 srcobj = srcentry if use_srcentry else srcname 468 try: 469 is_symlink = srcentry.is_symlink() 470 if is_symlink and os.name == 'nt': 471 # Special check for directory junctions, which appear as 472 # symlinks but we want to recurse. 473 lstat = srcentry.stat(follow_symlinks=False) 474 if lstat.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT: 475 is_symlink = False 476 if is_symlink: 477 linkto = os.readlink(srcname) 478 if symlinks: 479 # We can't just leave it to `copy_function` because legacy 480 # code with a custom `copy_function` may rely on copytree 481 # doing the right thing. 482 os.symlink(linkto, dstname) 483 copystat(srcobj, dstname, follow_symlinks=not symlinks) 484 else: 485 # ignore dangling symlink if the flag is on 486 if not os.path.exists(linkto) and ignore_dangling_symlinks: 487 continue 488 # otherwise let the copy occur. copy2 will raise an error 489 if srcentry.is_dir(): 490 copytree(srcobj, dstname, symlinks, ignore, 491 copy_function, dirs_exist_ok=dirs_exist_ok) 492 else: 493 copy_function(srcobj, dstname) 494 elif srcentry.is_dir(): 495 copytree(srcobj, dstname, symlinks, ignore, copy_function, 496 dirs_exist_ok=dirs_exist_ok) 497 else: 498 # Will raise a SpecialFileError for unsupported file types 499 copy_function(srcobj, dstname) 500 # catch the Error from the recursive copytree so that we can 501 # continue with other files 502 except Error as err: 503 errors.extend(err.args[0]) 504 except OSError as why: 505 errors.append((srcname, dstname, str(why))) 506 try: 507 copystat(src, dst) 508 except OSError as why: 509 # Copying file access times may fail on Windows 510 if getattr(why, 'winerror', None) is None: 511 errors.append((src, dst, str(why))) 512 if errors: 513 raise Error(errors) 514 return dst 515 516def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, 517 ignore_dangling_symlinks=False, dirs_exist_ok=False): 518 """Recursively copy a directory tree and return the destination directory. 519 520 dirs_exist_ok dictates whether to raise an exception in case dst or any 521 missing parent directory already exists. 522 523 If exception(s) occur, an Error is raised with a list of reasons. 524 525 If the optional symlinks flag is true, symbolic links in the 526 source tree result in symbolic links in the destination tree; if 527 it is false, the contents of the files pointed to by symbolic 528 links are copied. If the file pointed by the symlink doesn't 529 exist, an exception will be added in the list of errors raised in 530 an Error exception at the end of the copy process. 531 532 You can set the optional ignore_dangling_symlinks flag to true if you 533 want to silence this exception. Notice that this has no effect on 534 platforms that don't support os.symlink. 535 536 The optional ignore argument is a callable. If given, it 537 is called with the `src` parameter, which is the directory 538 being visited by copytree(), and `names` which is the list of 539 `src` contents, as returned by os.listdir(): 540 541 callable(src, names) -> ignored_names 542 543 Since copytree() is called recursively, the callable will be 544 called once for each directory that is copied. It returns a 545 list of names relative to the `src` directory that should 546 not be copied. 547 548 The optional copy_function argument is a callable that will be used 549 to copy each file. It will be called with the source path and the 550 destination path as arguments. By default, copy2() is used, but any 551 function that supports the same signature (like copy()) can be used. 552 553 """ 554 sys.audit("shutil.copytree", src, dst) 555 with os.scandir(src) as itr: 556 entries = list(itr) 557 return _copytree(entries=entries, src=src, dst=dst, symlinks=symlinks, 558 ignore=ignore, copy_function=copy_function, 559 ignore_dangling_symlinks=ignore_dangling_symlinks, 560 dirs_exist_ok=dirs_exist_ok) 561 562if hasattr(os.stat_result, 'st_file_attributes'): 563 # Special handling for directory junctions to make them behave like 564 # symlinks for shutil.rmtree, since in general they do not appear as 565 # regular links. 566 def _rmtree_isdir(entry): 567 try: 568 st = entry.stat(follow_symlinks=False) 569 return (stat.S_ISDIR(st.st_mode) and not 570 (st.st_file_attributes & stat.FILE_ATTRIBUTE_REPARSE_POINT 571 and st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT)) 572 except OSError: 573 return False 574 575 def _rmtree_islink(path): 576 try: 577 st = os.lstat(path) 578 return (stat.S_ISLNK(st.st_mode) or 579 (st.st_file_attributes & stat.FILE_ATTRIBUTE_REPARSE_POINT 580 and st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT)) 581 except OSError: 582 return False 583else: 584 def _rmtree_isdir(entry): 585 try: 586 return entry.is_dir(follow_symlinks=False) 587 except OSError: 588 return False 589 590 def _rmtree_islink(path): 591 return os.path.islink(path) 592 593# version vulnerable to race conditions 594def _rmtree_unsafe(path, onerror): 595 try: 596 with os.scandir(path) as scandir_it: 597 entries = list(scandir_it) 598 except OSError: 599 onerror(os.scandir, path, sys.exc_info()) 600 entries = [] 601 for entry in entries: 602 fullname = entry.path 603 if _rmtree_isdir(entry): 604 try: 605 if entry.is_symlink(): 606 # This can only happen if someone replaces 607 # a directory with a symlink after the call to 608 # os.scandir or entry.is_dir above. 609 raise OSError("Cannot call rmtree on a symbolic link") 610 except OSError: 611 onerror(os.path.islink, fullname, sys.exc_info()) 612 continue 613 _rmtree_unsafe(fullname, onerror) 614 else: 615 try: 616 os.unlink(fullname) 617 except OSError: 618 onerror(os.unlink, fullname, sys.exc_info()) 619 try: 620 os.rmdir(path) 621 except OSError: 622 onerror(os.rmdir, path, sys.exc_info()) 623 624# Version using fd-based APIs to protect against races 625def _rmtree_safe_fd(topfd, path, onerror): 626 try: 627 with os.scandir(topfd) as scandir_it: 628 entries = list(scandir_it) 629 except OSError as err: 630 err.filename = path 631 onerror(os.scandir, path, sys.exc_info()) 632 return 633 for entry in entries: 634 fullname = os.path.join(path, entry.name) 635 try: 636 is_dir = entry.is_dir(follow_symlinks=False) 637 except OSError: 638 is_dir = False 639 else: 640 if is_dir: 641 try: 642 orig_st = entry.stat(follow_symlinks=False) 643 is_dir = stat.S_ISDIR(orig_st.st_mode) 644 except OSError: 645 onerror(os.lstat, fullname, sys.exc_info()) 646 continue 647 if is_dir: 648 try: 649 dirfd = os.open(entry.name, os.O_RDONLY, dir_fd=topfd) 650 except OSError: 651 onerror(os.open, fullname, sys.exc_info()) 652 else: 653 try: 654 if os.path.samestat(orig_st, os.fstat(dirfd)): 655 _rmtree_safe_fd(dirfd, fullname, onerror) 656 try: 657 os.rmdir(entry.name, dir_fd=topfd) 658 except OSError: 659 onerror(os.rmdir, fullname, sys.exc_info()) 660 else: 661 try: 662 # This can only happen if someone replaces 663 # a directory with a symlink after the call to 664 # os.scandir or stat.S_ISDIR above. 665 raise OSError("Cannot call rmtree on a symbolic " 666 "link") 667 except OSError: 668 onerror(os.path.islink, fullname, sys.exc_info()) 669 finally: 670 os.close(dirfd) 671 else: 672 try: 673 os.unlink(entry.name, dir_fd=topfd) 674 except OSError: 675 onerror(os.unlink, fullname, sys.exc_info()) 676 677_use_fd_functions = ({os.open, os.stat, os.unlink, os.rmdir} <= 678 os.supports_dir_fd and 679 os.scandir in os.supports_fd and 680 os.stat in os.supports_follow_symlinks) 681 682def rmtree(path, ignore_errors=False, onerror=None): 683 """Recursively delete a directory tree. 684 685 If ignore_errors is set, errors are ignored; otherwise, if onerror 686 is set, it is called to handle the error with arguments (func, 687 path, exc_info) where func is platform and implementation dependent; 688 path is the argument to that function that caused it to fail; and 689 exc_info is a tuple returned by sys.exc_info(). If ignore_errors 690 is false and onerror is None, an exception is raised. 691 692 """ 693 sys.audit("shutil.rmtree", path) 694 if ignore_errors: 695 def onerror(*args): 696 pass 697 elif onerror is None: 698 def onerror(*args): 699 raise 700 if _use_fd_functions: 701 # While the unsafe rmtree works fine on bytes, the fd based does not. 702 if isinstance(path, bytes): 703 path = os.fsdecode(path) 704 # Note: To guard against symlink races, we use the standard 705 # lstat()/open()/fstat() trick. 706 try: 707 orig_st = os.lstat(path) 708 except Exception: 709 onerror(os.lstat, path, sys.exc_info()) 710 return 711 try: 712 fd = os.open(path, os.O_RDONLY) 713 except Exception: 714 onerror(os.open, path, sys.exc_info()) 715 return 716 try: 717 if os.path.samestat(orig_st, os.fstat(fd)): 718 _rmtree_safe_fd(fd, path, onerror) 719 try: 720 os.rmdir(path) 721 except OSError: 722 onerror(os.rmdir, path, sys.exc_info()) 723 else: 724 try: 725 # symlinks to directories are forbidden, see bug #1669 726 raise OSError("Cannot call rmtree on a symbolic link") 727 except OSError: 728 onerror(os.path.islink, path, sys.exc_info()) 729 finally: 730 os.close(fd) 731 else: 732 try: 733 if _rmtree_islink(path): 734 # symlinks to directories are forbidden, see bug #1669 735 raise OSError("Cannot call rmtree on a symbolic link") 736 except OSError: 737 onerror(os.path.islink, path, sys.exc_info()) 738 # can't continue even if onerror hook returns 739 return 740 return _rmtree_unsafe(path, onerror) 741 742# Allow introspection of whether or not the hardening against symlink 743# attacks is supported on the current platform 744rmtree.avoids_symlink_attacks = _use_fd_functions 745 746def _basename(path): 747 # A basename() variant which first strips the trailing slash, if present. 748 # Thus we always get the last component of the path, even for directories. 749 sep = os.path.sep + (os.path.altsep or '') 750 return os.path.basename(path.rstrip(sep)) 751 752def move(src, dst, copy_function=copy2): 753 """Recursively move a file or directory to another location. This is 754 similar to the Unix "mv" command. Return the file or directory's 755 destination. 756 757 If the destination is a directory or a symlink to a directory, the source 758 is moved inside the directory. The destination path must not already 759 exist. 760 761 If the destination already exists but is not a directory, it may be 762 overwritten depending on os.rename() semantics. 763 764 If the destination is on our current filesystem, then rename() is used. 765 Otherwise, src is copied to the destination and then removed. Symlinks are 766 recreated under the new name if os.rename() fails because of cross 767 filesystem renames. 768 769 The optional `copy_function` argument is a callable that will be used 770 to copy the source or it will be delegated to `copytree`. 771 By default, copy2() is used, but any function that supports the same 772 signature (like copy()) can be used. 773 774 A lot more could be done here... A look at a mv.c shows a lot of 775 the issues this implementation glosses over. 776 777 """ 778 sys.audit("shutil.move", src, dst) 779 real_dst = dst 780 if os.path.isdir(dst): 781 if _samefile(src, dst): 782 # We might be on a case insensitive filesystem, 783 # perform the rename anyway. 784 os.rename(src, dst) 785 return 786 787 real_dst = os.path.join(dst, _basename(src)) 788 if os.path.exists(real_dst): 789 raise Error("Destination path '%s' already exists" % real_dst) 790 try: 791 os.rename(src, real_dst) 792 except OSError: 793 if os.path.islink(src): 794 linkto = os.readlink(src) 795 os.symlink(linkto, real_dst) 796 os.unlink(src) 797 elif os.path.isdir(src): 798 if _destinsrc(src, dst): 799 raise Error("Cannot move a directory '%s' into itself" 800 " '%s'." % (src, dst)) 801 if (_is_immutable(src) 802 or (not os.access(src, os.W_OK) and os.listdir(src) 803 and sys.platform == 'darwin')): 804 raise PermissionError("Cannot move the non-empty directory " 805 "'%s': Lacking write permission to '%s'." 806 % (src, src)) 807 copytree(src, real_dst, copy_function=copy_function, 808 symlinks=True) 809 rmtree(src) 810 else: 811 copy_function(src, real_dst) 812 os.unlink(src) 813 return real_dst 814 815def _destinsrc(src, dst): 816 src = os.path.abspath(src) 817 dst = os.path.abspath(dst) 818 if not src.endswith(os.path.sep): 819 src += os.path.sep 820 if not dst.endswith(os.path.sep): 821 dst += os.path.sep 822 return dst.startswith(src) 823 824def _is_immutable(src): 825 st = _stat(src) 826 immutable_states = [stat.UF_IMMUTABLE, stat.SF_IMMUTABLE] 827 return hasattr(st, 'st_flags') and st.st_flags in immutable_states 828 829def _get_gid(name): 830 """Returns a gid, given a group name.""" 831 if getgrnam is None or name is None: 832 return None 833 try: 834 result = getgrnam(name) 835 except KeyError: 836 result = None 837 if result is not None: 838 return result[2] 839 return None 840 841def _get_uid(name): 842 """Returns an uid, given a user name.""" 843 if getpwnam is None or name is None: 844 return None 845 try: 846 result = getpwnam(name) 847 except KeyError: 848 result = None 849 if result is not None: 850 return result[2] 851 return None 852 853def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, 854 owner=None, group=None, logger=None): 855 """Create a (possibly compressed) tar file from all the files under 856 'base_dir'. 857 858 'compress' must be "gzip" (the default), "bzip2", "xz", or None. 859 860 'owner' and 'group' can be used to define an owner and a group for the 861 archive that is being built. If not provided, the current owner and group 862 will be used. 863 864 The output tar file will be named 'base_name' + ".tar", possibly plus 865 the appropriate compression extension (".gz", ".bz2", or ".xz"). 866 867 Returns the output filename. 868 """ 869 if compress is None: 870 tar_compression = '' 871 elif _ZLIB_SUPPORTED and compress == 'gzip': 872 tar_compression = 'gz' 873 elif _BZ2_SUPPORTED and compress == 'bzip2': 874 tar_compression = 'bz2' 875 elif _LZMA_SUPPORTED and compress == 'xz': 876 tar_compression = 'xz' 877 else: 878 raise ValueError("bad value for 'compress', or compression format not " 879 "supported : {0}".format(compress)) 880 881 import tarfile # late import for breaking circular dependency 882 883 compress_ext = '.' + tar_compression if compress else '' 884 archive_name = base_name + '.tar' + compress_ext 885 archive_dir = os.path.dirname(archive_name) 886 887 if archive_dir and not os.path.exists(archive_dir): 888 if logger is not None: 889 logger.info("creating %s", archive_dir) 890 if not dry_run: 891 os.makedirs(archive_dir) 892 893 # creating the tarball 894 if logger is not None: 895 logger.info('Creating tar archive') 896 897 uid = _get_uid(owner) 898 gid = _get_gid(group) 899 900 def _set_uid_gid(tarinfo): 901 if gid is not None: 902 tarinfo.gid = gid 903 tarinfo.gname = group 904 if uid is not None: 905 tarinfo.uid = uid 906 tarinfo.uname = owner 907 return tarinfo 908 909 if not dry_run: 910 tar = tarfile.open(archive_name, 'w|%s' % tar_compression) 911 try: 912 tar.add(base_dir, filter=_set_uid_gid) 913 finally: 914 tar.close() 915 916 return archive_name 917 918def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None): 919 """Create a zip file from all the files under 'base_dir'. 920 921 The output zip file will be named 'base_name' + ".zip". Returns the 922 name of the output zip file. 923 """ 924 import zipfile # late import for breaking circular dependency 925 926 zip_filename = base_name + ".zip" 927 archive_dir = os.path.dirname(base_name) 928 929 if archive_dir and not os.path.exists(archive_dir): 930 if logger is not None: 931 logger.info("creating %s", archive_dir) 932 if not dry_run: 933 os.makedirs(archive_dir) 934 935 if logger is not None: 936 logger.info("creating '%s' and adding '%s' to it", 937 zip_filename, base_dir) 938 939 if not dry_run: 940 with zipfile.ZipFile(zip_filename, "w", 941 compression=zipfile.ZIP_DEFLATED) as zf: 942 path = os.path.normpath(base_dir) 943 if path != os.curdir: 944 zf.write(path, path) 945 if logger is not None: 946 logger.info("adding '%s'", path) 947 for dirpath, dirnames, filenames in os.walk(base_dir): 948 for name in sorted(dirnames): 949 path = os.path.normpath(os.path.join(dirpath, name)) 950 zf.write(path, path) 951 if logger is not None: 952 logger.info("adding '%s'", path) 953 for name in filenames: 954 path = os.path.normpath(os.path.join(dirpath, name)) 955 if os.path.isfile(path): 956 zf.write(path, path) 957 if logger is not None: 958 logger.info("adding '%s'", path) 959 960 return zip_filename 961 962_ARCHIVE_FORMATS = { 963 'tar': (_make_tarball, [('compress', None)], "uncompressed tar file"), 964} 965 966if _ZLIB_SUPPORTED: 967 _ARCHIVE_FORMATS['gztar'] = (_make_tarball, [('compress', 'gzip')], 968 "gzip'ed tar-file") 969 _ARCHIVE_FORMATS['zip'] = (_make_zipfile, [], "ZIP file") 970 971if _BZ2_SUPPORTED: 972 _ARCHIVE_FORMATS['bztar'] = (_make_tarball, [('compress', 'bzip2')], 973 "bzip2'ed tar-file") 974 975if _LZMA_SUPPORTED: 976 _ARCHIVE_FORMATS['xztar'] = (_make_tarball, [('compress', 'xz')], 977 "xz'ed tar-file") 978 979def get_archive_formats(): 980 """Returns a list of supported formats for archiving and unarchiving. 981 982 Each element of the returned sequence is a tuple (name, description) 983 """ 984 formats = [(name, registry[2]) for name, registry in 985 _ARCHIVE_FORMATS.items()] 986 formats.sort() 987 return formats 988 989def register_archive_format(name, function, extra_args=None, description=''): 990 """Registers an archive format. 991 992 name is the name of the format. function is the callable that will be 993 used to create archives. If provided, extra_args is a sequence of 994 (name, value) tuples that will be passed as arguments to the callable. 995 description can be provided to describe the format, and will be returned 996 by the get_archive_formats() function. 997 """ 998 if extra_args is None: 999 extra_args = [] 1000 if not callable(function): 1001 raise TypeError('The %s object is not callable' % function) 1002 if not isinstance(extra_args, (tuple, list)): 1003 raise TypeError('extra_args needs to be a sequence') 1004 for element in extra_args: 1005 if not isinstance(element, (tuple, list)) or len(element) !=2: 1006 raise TypeError('extra_args elements are : (arg_name, value)') 1007 1008 _ARCHIVE_FORMATS[name] = (function, extra_args, description) 1009 1010def unregister_archive_format(name): 1011 del _ARCHIVE_FORMATS[name] 1012 1013def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, 1014 dry_run=0, owner=None, group=None, logger=None): 1015 """Create an archive file (eg. zip or tar). 1016 1017 'base_name' is the name of the file to create, minus any format-specific 1018 extension; 'format' is the archive format: one of "zip", "tar", "gztar", 1019 "bztar", or "xztar". Or any other registered format. 1020 1021 'root_dir' is a directory that will be the root directory of the 1022 archive; ie. we typically chdir into 'root_dir' before creating the 1023 archive. 'base_dir' is the directory where we start archiving from; 1024 ie. 'base_dir' will be the common prefix of all files and 1025 directories in the archive. 'root_dir' and 'base_dir' both default 1026 to the current directory. Returns the name of the archive file. 1027 1028 'owner' and 'group' are used when creating a tar archive. By default, 1029 uses the current owner and group. 1030 """ 1031 sys.audit("shutil.make_archive", base_name, format, root_dir, base_dir) 1032 save_cwd = os.getcwd() 1033 if root_dir is not None: 1034 if logger is not None: 1035 logger.debug("changing into '%s'", root_dir) 1036 base_name = os.path.abspath(base_name) 1037 if not dry_run: 1038 os.chdir(root_dir) 1039 1040 if base_dir is None: 1041 base_dir = os.curdir 1042 1043 kwargs = {'dry_run': dry_run, 'logger': logger} 1044 1045 try: 1046 format_info = _ARCHIVE_FORMATS[format] 1047 except KeyError: 1048 raise ValueError("unknown archive format '%s'" % format) from None 1049 1050 func = format_info[0] 1051 for arg, val in format_info[1]: 1052 kwargs[arg] = val 1053 1054 if format != 'zip': 1055 kwargs['owner'] = owner 1056 kwargs['group'] = group 1057 1058 try: 1059 filename = func(base_name, base_dir, **kwargs) 1060 finally: 1061 if root_dir is not None: 1062 if logger is not None: 1063 logger.debug("changing back to '%s'", save_cwd) 1064 os.chdir(save_cwd) 1065 1066 return filename 1067 1068 1069def get_unpack_formats(): 1070 """Returns a list of supported formats for unpacking. 1071 1072 Each element of the returned sequence is a tuple 1073 (name, extensions, description) 1074 """ 1075 formats = [(name, info[0], info[3]) for name, info in 1076 _UNPACK_FORMATS.items()] 1077 formats.sort() 1078 return formats 1079 1080def _check_unpack_options(extensions, function, extra_args): 1081 """Checks what gets registered as an unpacker.""" 1082 # first make sure no other unpacker is registered for this extension 1083 existing_extensions = {} 1084 for name, info in _UNPACK_FORMATS.items(): 1085 for ext in info[0]: 1086 existing_extensions[ext] = name 1087 1088 for extension in extensions: 1089 if extension in existing_extensions: 1090 msg = '%s is already registered for "%s"' 1091 raise RegistryError(msg % (extension, 1092 existing_extensions[extension])) 1093 1094 if not callable(function): 1095 raise TypeError('The registered function must be a callable') 1096 1097 1098def register_unpack_format(name, extensions, function, extra_args=None, 1099 description=''): 1100 """Registers an unpack format. 1101 1102 `name` is the name of the format. `extensions` is a list of extensions 1103 corresponding to the format. 1104 1105 `function` is the callable that will be 1106 used to unpack archives. The callable will receive archives to unpack. 1107 If it's unable to handle an archive, it needs to raise a ReadError 1108 exception. 1109 1110 If provided, `extra_args` is a sequence of 1111 (name, value) tuples that will be passed as arguments to the callable. 1112 description can be provided to describe the format, and will be returned 1113 by the get_unpack_formats() function. 1114 """ 1115 if extra_args is None: 1116 extra_args = [] 1117 _check_unpack_options(extensions, function, extra_args) 1118 _UNPACK_FORMATS[name] = extensions, function, extra_args, description 1119 1120def unregister_unpack_format(name): 1121 """Removes the pack format from the registry.""" 1122 del _UNPACK_FORMATS[name] 1123 1124def _ensure_directory(path): 1125 """Ensure that the parent directory of `path` exists""" 1126 dirname = os.path.dirname(path) 1127 if not os.path.isdir(dirname): 1128 os.makedirs(dirname) 1129 1130def _unpack_zipfile(filename, extract_dir): 1131 """Unpack zip `filename` to `extract_dir` 1132 """ 1133 import zipfile # late import for breaking circular dependency 1134 1135 if not zipfile.is_zipfile(filename): 1136 raise ReadError("%s is not a zip file" % filename) 1137 1138 zip = zipfile.ZipFile(filename) 1139 try: 1140 for info in zip.infolist(): 1141 name = info.filename 1142 1143 # don't extract absolute paths or ones with .. in them 1144 if name.startswith('/') or '..' in name: 1145 continue 1146 1147 target = os.path.join(extract_dir, *name.split('/')) 1148 if not target: 1149 continue 1150 1151 _ensure_directory(target) 1152 if not name.endswith('/'): 1153 # file 1154 data = zip.read(info.filename) 1155 f = open(target, 'wb') 1156 try: 1157 f.write(data) 1158 finally: 1159 f.close() 1160 del data 1161 finally: 1162 zip.close() 1163 1164def _unpack_tarfile(filename, extract_dir): 1165 """Unpack tar/tar.gz/tar.bz2/tar.xz `filename` to `extract_dir` 1166 """ 1167 import tarfile # late import for breaking circular dependency 1168 try: 1169 tarobj = tarfile.open(filename) 1170 except tarfile.TarError: 1171 raise ReadError( 1172 "%s is not a compressed or uncompressed tar file" % filename) 1173 try: 1174 tarobj.extractall(extract_dir) 1175 finally: 1176 tarobj.close() 1177 1178_UNPACK_FORMATS = { 1179 'tar': (['.tar'], _unpack_tarfile, [], "uncompressed tar file"), 1180 'zip': (['.zip'], _unpack_zipfile, [], "ZIP file"), 1181} 1182 1183if _ZLIB_SUPPORTED: 1184 _UNPACK_FORMATS['gztar'] = (['.tar.gz', '.tgz'], _unpack_tarfile, [], 1185 "gzip'ed tar-file") 1186 1187if _BZ2_SUPPORTED: 1188 _UNPACK_FORMATS['bztar'] = (['.tar.bz2', '.tbz2'], _unpack_tarfile, [], 1189 "bzip2'ed tar-file") 1190 1191if _LZMA_SUPPORTED: 1192 _UNPACK_FORMATS['xztar'] = (['.tar.xz', '.txz'], _unpack_tarfile, [], 1193 "xz'ed tar-file") 1194 1195def _find_unpack_format(filename): 1196 for name, info in _UNPACK_FORMATS.items(): 1197 for extension in info[0]: 1198 if filename.endswith(extension): 1199 return name 1200 return None 1201 1202def unpack_archive(filename, extract_dir=None, format=None): 1203 """Unpack an archive. 1204 1205 `filename` is the name of the archive. 1206 1207 `extract_dir` is the name of the target directory, where the archive 1208 is unpacked. If not provided, the current working directory is used. 1209 1210 `format` is the archive format: one of "zip", "tar", "gztar", "bztar", 1211 or "xztar". Or any other registered format. If not provided, 1212 unpack_archive will use the filename extension and see if an unpacker 1213 was registered for that extension. 1214 1215 In case none is found, a ValueError is raised. 1216 """ 1217 sys.audit("shutil.unpack_archive", filename, extract_dir, format) 1218 1219 if extract_dir is None: 1220 extract_dir = os.getcwd() 1221 1222 extract_dir = os.fspath(extract_dir) 1223 filename = os.fspath(filename) 1224 1225 if format is not None: 1226 try: 1227 format_info = _UNPACK_FORMATS[format] 1228 except KeyError: 1229 raise ValueError("Unknown unpack format '{0}'".format(format)) from None 1230 1231 func = format_info[1] 1232 func(filename, extract_dir, **dict(format_info[2])) 1233 else: 1234 # we need to look at the registered unpackers supported extensions 1235 format = _find_unpack_format(filename) 1236 if format is None: 1237 raise ReadError("Unknown archive format '{0}'".format(filename)) 1238 1239 func = _UNPACK_FORMATS[format][1] 1240 kwargs = dict(_UNPACK_FORMATS[format][2]) 1241 func(filename, extract_dir, **kwargs) 1242 1243 1244if hasattr(os, 'statvfs'): 1245 1246 __all__.append('disk_usage') 1247 _ntuple_diskusage = collections.namedtuple('usage', 'total used free') 1248 _ntuple_diskusage.total.__doc__ = 'Total space in bytes' 1249 _ntuple_diskusage.used.__doc__ = 'Used space in bytes' 1250 _ntuple_diskusage.free.__doc__ = 'Free space in bytes' 1251 1252 def disk_usage(path): 1253 """Return disk usage statistics about the given path. 1254 1255 Returned value is a named tuple with attributes 'total', 'used' and 1256 'free', which are the amount of total, used and free space, in bytes. 1257 """ 1258 st = os.statvfs(path) 1259 free = st.f_bavail * st.f_frsize 1260 total = st.f_blocks * st.f_frsize 1261 used = (st.f_blocks - st.f_bfree) * st.f_frsize 1262 return _ntuple_diskusage(total, used, free) 1263 1264elif _WINDOWS: 1265 1266 __all__.append('disk_usage') 1267 _ntuple_diskusage = collections.namedtuple('usage', 'total used free') 1268 1269 def disk_usage(path): 1270 """Return disk usage statistics about the given path. 1271 1272 Returned values is a named tuple with attributes 'total', 'used' and 1273 'free', which are the amount of total, used and free space, in bytes. 1274 """ 1275 total, free = nt._getdiskusage(path) 1276 used = total - free 1277 return _ntuple_diskusage(total, used, free) 1278 1279 1280def chown(path, user=None, group=None): 1281 """Change owner user and group of the given path. 1282 1283 user and group can be the uid/gid or the user/group names, and in that case, 1284 they are converted to their respective uid/gid. 1285 """ 1286 sys.audit('shutil.chown', path, user, group) 1287 1288 if user is None and group is None: 1289 raise ValueError("user and/or group must be set") 1290 1291 _user = user 1292 _group = group 1293 1294 # -1 means don't change it 1295 if user is None: 1296 _user = -1 1297 # user can either be an int (the uid) or a string (the system username) 1298 elif isinstance(user, str): 1299 _user = _get_uid(user) 1300 if _user is None: 1301 raise LookupError("no such user: {!r}".format(user)) 1302 1303 if group is None: 1304 _group = -1 1305 elif not isinstance(group, int): 1306 _group = _get_gid(group) 1307 if _group is None: 1308 raise LookupError("no such group: {!r}".format(group)) 1309 1310 os.chown(path, _user, _group) 1311 1312def get_terminal_size(fallback=(80, 24)): 1313 """Get the size of the terminal window. 1314 1315 For each of the two dimensions, the environment variable, COLUMNS 1316 and LINES respectively, is checked. If the variable is defined and 1317 the value is a positive integer, it is used. 1318 1319 When COLUMNS or LINES is not defined, which is the common case, 1320 the terminal connected to sys.__stdout__ is queried 1321 by invoking os.get_terminal_size. 1322 1323 If the terminal size cannot be successfully queried, either because 1324 the system doesn't support querying, or because we are not 1325 connected to a terminal, the value given in fallback parameter 1326 is used. Fallback defaults to (80, 24) which is the default 1327 size used by many terminal emulators. 1328 1329 The value returned is a named tuple of type os.terminal_size. 1330 """ 1331 # columns, lines are the working values 1332 try: 1333 columns = int(os.environ['COLUMNS']) 1334 except (KeyError, ValueError): 1335 columns = 0 1336 1337 try: 1338 lines = int(os.environ['LINES']) 1339 except (KeyError, ValueError): 1340 lines = 0 1341 1342 # only query if necessary 1343 if columns <= 0 or lines <= 0: 1344 try: 1345 size = os.get_terminal_size(sys.__stdout__.fileno()) 1346 except (AttributeError, ValueError, OSError): 1347 # stdout is None, closed, detached, or not a terminal, or 1348 # os.get_terminal_size() is unsupported 1349 size = os.terminal_size(fallback) 1350 if columns <= 0: 1351 columns = size.columns 1352 if lines <= 0: 1353 lines = size.lines 1354 1355 return os.terminal_size((columns, lines)) 1356 1357 1358# Check that a given file can be accessed with the correct mode. 1359# Additionally check that `file` is not a directory, as on Windows 1360# directories pass the os.access check. 1361def _access_check(fn, mode): 1362 return (os.path.exists(fn) and os.access(fn, mode) 1363 and not os.path.isdir(fn)) 1364 1365 1366def which(cmd, mode=os.F_OK | os.X_OK, path=None): 1367 """Given a command, mode, and a PATH string, return the path which 1368 conforms to the given mode on the PATH, or None if there is no such 1369 file. 1370 1371 `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result 1372 of os.environ.get("PATH"), or can be overridden with a custom search 1373 path. 1374 1375 """ 1376 # If we're given a path with a directory part, look it up directly rather 1377 # than referring to PATH directories. This includes checking relative to the 1378 # current directory, e.g. ./script 1379 if os.path.dirname(cmd): 1380 if _access_check(cmd, mode): 1381 return cmd 1382 return None 1383 1384 use_bytes = isinstance(cmd, bytes) 1385 1386 if path is None: 1387 path = os.environ.get("PATH", None) 1388 if path is None: 1389 try: 1390 path = os.confstr("CS_PATH") 1391 except (AttributeError, ValueError): 1392 # os.confstr() or CS_PATH is not available 1393 path = os.defpath 1394 # bpo-35755: Don't use os.defpath if the PATH environment variable is 1395 # set to an empty string 1396 1397 # PATH='' doesn't match, whereas PATH=':' looks in the current directory 1398 if not path: 1399 return None 1400 1401 if use_bytes: 1402 path = os.fsencode(path) 1403 path = path.split(os.fsencode(os.pathsep)) 1404 else: 1405 path = os.fsdecode(path) 1406 path = path.split(os.pathsep) 1407 1408 if sys.platform == "win32": 1409 # The current directory takes precedence on Windows. 1410 curdir = os.curdir 1411 if use_bytes: 1412 curdir = os.fsencode(curdir) 1413 if curdir not in path: 1414 path.insert(0, curdir) 1415 1416 # PATHEXT is necessary to check on Windows. 1417 pathext_source = os.getenv("PATHEXT") or _WIN_DEFAULT_PATHEXT 1418 pathext = [ext for ext in pathext_source.split(os.pathsep) if ext] 1419 1420 if use_bytes: 1421 pathext = [os.fsencode(ext) for ext in pathext] 1422 # See if the given file matches any of the expected path extensions. 1423 # This will allow us to short circuit when given "python.exe". 1424 # If it does match, only test that one, otherwise we have to try 1425 # others. 1426 if any(cmd.lower().endswith(ext.lower()) for ext in pathext): 1427 files = [cmd] 1428 else: 1429 files = [cmd + ext for ext in pathext] 1430 else: 1431 # On other platforms you don't have things like PATHEXT to tell you 1432 # what file suffixes are executable, so just pass on cmd as-is. 1433 files = [cmd] 1434 1435 seen = set() 1436 for dir in path: 1437 normdir = os.path.normcase(dir) 1438 if not normdir in seen: 1439 seen.add(normdir) 1440 for thefile in files: 1441 name = os.path.join(dir, thefile) 1442 if _access_check(name, mode): 1443 return name 1444 return None 1445