diff --git a/news/13346.bugfix.rst b/news/13346.bugfix.rst new file mode 100644 index 00000000000..b2b9413e7b4 --- /dev/null +++ b/news/13346.bugfix.rst @@ -0,0 +1 @@ +Provide a hint if a system error is raised involving long filenames or path segments on Windows. diff --git a/src/pip/_internal/commands/install.py b/src/pip/_internal/commands/install.py index 056eb8866a0..5cb9e6f3b00 100644 --- a/src/pip/_internal/commands/install.py +++ b/src/pip/_internal/commands/install.py @@ -7,6 +7,7 @@ import shutil import site from optparse import SUPPRESS_HELP, Values +from pathlib import Path from pip._vendor.packaging.utils import canonicalize_name from pip._vendor.requests.exceptions import InvalidProxyURL @@ -774,9 +775,23 @@ def create_os_error_message( ) parts.append(".\n") + # Windows raises EINVAL when a file*name* or path segment exceeds 255 + # characters, even if long path support is enabled. + if ( + WINDOWS + and error.errno in (errno.EINVAL, errno.ENOENT) + and error.filename + and any(len(part) > 255 for part in Path(error.filename).parts) + ): + parts.append( + "HINT: This error might be caused by a file or folder name exceeding " + "255 characters, which is a Windows limitation even if long paths " + "are enabled.\n " + ) + # Suggest the user to enable Long Paths if path length is # more than 260 - if ( + elif ( WINDOWS and error.errno == errno.ENOENT and error.filename diff --git a/tests/unit/test_command_install.py b/tests/unit/test_command_install.py index e28edf6449c..db288cf0f4c 100644 --- a/tests/unit/test_command_install.py +++ b/tests/unit/test_command_install.py @@ -1,4 +1,5 @@ import errno +import sys from unittest import mock import pytest @@ -120,6 +121,36 @@ def test_most_cases( "Consider checking your local proxy configuration" ' with "pip config debug".\n', ), + # Testing both long path error (ENOENT) + # and long file/folder name error (EINVAL) on Windows + pytest.param( + OSError(errno.ENOENT, "No such file or directory", f"C:/foo/{'/a/'*261}"), + False, + False, + "Could not install packages due to an OSError: " + f"[Errno 2] No such file or directory: 'C:/foo/{'/a/'*261}'\n" + "HINT: This error might have occurred since " + "this system does not have Windows Long Path " + "support enabled. You can find information on " + "how to enable this at " + "https://pip.pypa.io/warnings/enable-long-paths\n", + marks=pytest.mark.skipif( + sys.platform != "win32", reason="Windows-specific filename length test" + ), + ), + pytest.param( + OSError(errno.EINVAL, "No such file or directory", f"C:/foo/{'a'*256}"), + False, + False, + "Could not install packages due to an OSError: " + f"[Errno 22] No such file or directory: 'C:/foo/{'a'*256}'\n" + "HINT: This error might be caused by a file or folder name exceeding " + "255 characters, which is a Windows limitation even if long paths " + "are enabled.\n", + marks=pytest.mark.skipif( + sys.platform != "win32", reason="Windows-specific filename length test" + ), + ), ], ) def test_create_os_error_message(