|
32 | 32 | # Import ssl from compat so the initial import occurs in only one place.
|
33 | 33 | from pip._internal.utils.compat import HAS_TLS, ssl
|
34 | 34 | from pip._internal.utils.encoding import auto_decode
|
35 |
| -from pip._internal.utils.filesystem import check_path_owner, is_socket |
| 35 | +from pip._internal.utils.filesystem import check_path_owner, copytree |
36 | 36 | from pip._internal.utils.glibc import libc_ver
|
37 | 37 | from pip._internal.utils.marker_files import write_delete_marker_file
|
38 | 38 | from pip._internal.utils.misc import (
|
|
46 | 46 | display_path,
|
47 | 47 | format_size,
|
48 | 48 | get_installed_version,
|
| 49 | + path_to_display, |
49 | 50 | path_to_url,
|
50 | 51 | remove_auth_from_url,
|
51 | 52 | rmtree,
|
|
60 | 61 |
|
61 | 62 | if MYPY_CHECK_RUNNING:
|
62 | 63 | from typing import (
|
63 |
| - Dict, IO, List, Optional, Text, Tuple, Union |
| 64 | + Dict, IO, Optional, Text, Tuple, Union |
64 | 65 | )
|
65 | 66 | from optparse import Values
|
66 | 67 | from pip._internal.models.link import Link
|
@@ -963,25 +964,36 @@ def ignore(d, names):
|
963 | 964 | # See discussion at https://github.com/pypa/pip/pull/6770
|
964 | 965 | return ['.tox', '.nox'] if d == source else []
|
965 | 966 |
|
| 967 | + def ignore_special_file_errors(error_details): |
| 968 | + # Copying special files is not supported, so we skip errors related to |
| 969 | + # them. This is a convenience to support users that may have tools |
| 970 | + # creating e.g. socket files in their source directory. |
| 971 | + src, dest, error = error_details |
| 972 | + |
| 973 | + if not isinstance(error, shutil.SpecialFileError): |
| 974 | + # Then it is some other kind of error that we do want to report. |
| 975 | + return True |
| 976 | + |
| 977 | + # SpecialFileError may be raised due to either the source or |
| 978 | + # destination. If the destination was the cause then we would actually |
| 979 | + # care, but since the destination directory is deleted prior to |
| 980 | + # copy we ignore all of them assuming it is caused by the source. |
| 981 | + logger.warning( |
| 982 | + "Ignoring special file error '%s' encountered copying %s to %s.", |
| 983 | + str(error), |
| 984 | + path_to_display(src), |
| 985 | + path_to_display(dest), |
| 986 | + ) |
| 987 | + |
966 | 988 | try:
|
967 |
| - shutil.copytree(source, target, ignore=ignore, symlinks=True) |
| 989 | + copytree(source, target, ignore=ignore, symlinks=True) |
968 | 990 | except shutil.Error as e:
|
969 |
| - errors = e.args[0] # type: List[Tuple[str, str, shutil.Error]] |
970 |
| - # Users may have locally-created socket files in the source |
971 |
| - # directory. Copying socket files is not supported, so we skip errors |
972 |
| - # related to them, for convenience. |
973 |
| - |
974 |
| - # Copy list to avoid mutation while iterating. |
975 |
| - errors_copy = errors[:] |
976 |
| - for i, err in reversed(list(enumerate(errors_copy))): |
977 |
| - src, _dest, _error = err |
978 |
| - if is_socket(src): |
979 |
| - # Remove errors related to sockets to prevent distractions if |
980 |
| - # we end up re-raising. |
981 |
| - errors.pop(i) |
982 |
| - |
983 |
| - if errors: |
984 |
| - raise |
| 991 | + errors = e.args[0] |
| 992 | + normal_file_errors = list( |
| 993 | + filter(ignore_special_file_errors, errors) |
| 994 | + ) |
| 995 | + if normal_file_errors: |
| 996 | + raise shutil.Error(normal_file_errors) |
985 | 997 |
|
986 | 998 |
|
987 | 999 | def unpack_file_url(
|
|
0 commit comments