From 5beed92e9b3e9342991cb5829cdc6fa6d67af721 Mon Sep 17 00:00:00 2001 From: Richard Si Date: Fri, 6 Dec 2024 20:29:22 -0500 Subject: [PATCH] Accommodate for recent pathname2url() changes upstream - UNC paths converted to URLs now start with two slashes, like earlier (yes, really) - Trailing slashes are now preserved on Windows, matching POSIX behaviour --- ...bb-2dcc-43d0-83ff-c762e7e55bf9.trivial.rst | 0 tests/lib/__init__.py | 17 ++++++++++++ tests/unit/test_collector.py | 20 ++++++++++++-- tests/unit/test_urls.py | 26 +++++++------------ 4 files changed, 45 insertions(+), 18 deletions(-) create mode 100644 news/ba0bd1bb-2dcc-43d0-83ff-c762e7e55bf9.trivial.rst diff --git a/news/ba0bd1bb-2dcc-43d0-83ff-c762e7e55bf9.trivial.rst b/news/ba0bd1bb-2dcc-43d0-83ff-c762e7e55bf9.trivial.rst new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py index df142ba4438..44fa4053b73 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -29,6 +29,7 @@ cast, ) from urllib.parse import urlparse, urlunparse +from urllib.request import pathname2url from zipfile import ZipFile import pytest @@ -1379,6 +1380,10 @@ def __call__( CertFactory = Callable[[], str] +# ------------------------------------------------------------------------- +# Accommodations for Windows path and URL changes in recent Python releases +# ------------------------------------------------------------------------- + # versions containing fix/backport from https://github.com/python/cpython/pull/113563 # which changed the behavior of `urllib.parse.urlun{parse,split}` url = "////path/to/file" @@ -1394,3 +1399,15 @@ def __call__( sys.platform != "win32" or has_new_urlun_behavior, reason="testing windows behavior for older CPython", ) + +# Trailing slashes are now preserved on Windows, matching POSIX behaviour. +# BPO: https://github.com/python/cpython/issues/126212 +does_pathname2url_preserve_trailing_slash = pathname2url("C:/foo/").endswith("/") +skip_needs_new_pathname2url_trailing_slash_behavior_win = pytest.mark.skipif( + sys.platform != "win32" or not does_pathname2url_preserve_trailing_slash, + reason="testing windows (pathname2url) behavior for newer CPython", +) +skip_needs_old_pathname2url_trailing_slash_behavior_win = pytest.mark.skipif( + sys.platform != "win32" or does_pathname2url_preserve_trailing_slash, + reason="testing windows (pathname2url) behavior for older CPython", +) diff --git a/tests/unit/test_collector.py b/tests/unit/test_collector.py index c527d5610a3..c7760a238f2 100644 --- a/tests/unit/test_collector.py +++ b/tests/unit/test_collector.py @@ -40,7 +40,9 @@ from tests.lib import ( TestData, make_test_link_collector, + skip_needs_new_pathname2url_trailing_slash_behavior_win, skip_needs_new_urlun_behavior_win, + skip_needs_old_pathname2url_trailing_slash_behavior_win, skip_needs_old_urlun_behavior_win, ) @@ -390,12 +392,26 @@ def test_clean_url_path_with_local_path(path: str, expected: str) -> None: pytest.param( "file:///T:/path/with spaces/", "file:///T:/path/with%20spaces", - marks=skip_needs_old_urlun_behavior_win, + marks=[ + skip_needs_old_urlun_behavior_win, + skip_needs_old_pathname2url_trailing_slash_behavior_win, + ], ), pytest.param( "file:///T:/path/with spaces/", "file://///T:/path/with%20spaces", - marks=skip_needs_new_urlun_behavior_win, + marks=[ + skip_needs_new_urlun_behavior_win, + skip_needs_old_pathname2url_trailing_slash_behavior_win, + ], + ), + pytest.param( + "file:///T:/path/with spaces/", + "file://///T:/path/with%20spaces/", + marks=[ + skip_needs_new_urlun_behavior_win, + skip_needs_new_pathname2url_trailing_slash_behavior_win, + ], ), # URL with Windows drive letter, running on non-windows # platform. The `:` after the drive should be quoted. diff --git a/tests/unit/test_urls.py b/tests/unit/test_urls.py index 539bf091fb2..0c145255080 100644 --- a/tests/unit/test_urls.py +++ b/tests/unit/test_urls.py @@ -6,11 +6,6 @@ from pip._internal.utils.urls import path_to_url, url_to_path -from tests.lib import ( - skip_needs_new_urlun_behavior_win, - skip_needs_old_urlun_behavior_win, -) - @pytest.mark.skipif("sys.platform == 'win32'") def test_path_to_url_unix() -> None: @@ -25,23 +20,22 @@ def test_path_to_url_unix() -> None: [ pytest.param("c:/tmp/file", "file:///C:/tmp/file", id="posix-path"), pytest.param("c:\\tmp\\file", "file:///C:/tmp/file", id="nt-path"), - pytest.param( - r"\\unc\as\path", - "file://unc/as/path", - marks=skip_needs_old_urlun_behavior_win, - id="unc-path", - ), - pytest.param( - r"\\unc\as\path", - "file:////unc/as/path", - marks=skip_needs_new_urlun_behavior_win, - ), ], ) def test_path_to_url_win(path: str, url: str) -> None: assert path_to_url(path) == url +@pytest.mark.skipif("sys.platform != 'win32'") +def test_unc_path_to_url_win() -> None: + # The two and four slash forms are both acceptable for our purposes. CPython's + # behaviour has changed several times here, so blindly accept either. + # - https://github.com/python/cpython/issues/78457 + # - https://github.com/python/cpython/issues/126205 + url = path_to_url(r"\\unc\as\path") + assert url in ["file://unc/as/path", "file:////unc/as/path"] + + @pytest.mark.skipif("sys.platform != 'win32'") def test_relative_path_to_url_win() -> None: resolved_path = os.path.join(os.getcwd(), "file")