From eb3f4b08cca20a9fff91ccdbd1eef26f52d5e544 Mon Sep 17 00:00:00 2001 From: Bibek Date: Wed, 25 Mar 2026 14:01:12 -0400 Subject: [PATCH 1/3] fix(publish): normalize legacy repository URL trailing slash (#6687) - Simplify _normalize_legacy_repository_url per maintainer suggestion - Fix broken call_args assertions in test, use assert_called_once_with - Add parametrized test cases: already-normalized URL and non-legacy URL --- src/poetry/publishing/publisher.py | 5 ++++ tests/publishing/test_publisher.py | 42 ++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/poetry/publishing/publisher.py b/src/poetry/publishing/publisher.py index f752a0d5ee4..b6f36ff5b06 100644 --- a/src/poetry/publishing/publisher.py +++ b/src/poetry/publishing/publisher.py @@ -17,6 +17,10 @@ logger = logging.getLogger(__name__) +def _normalize_legacy_repository_url(url: str) -> str: + if url.endswith("/legacy"): + url += "/" + return url class Publisher: """ @@ -52,6 +56,7 @@ def publish( url = self._poetry.config.get(f"repositories.{repository_name}.url") if url is None: raise RuntimeError(f"Repository {repository_name} is not defined") + url = _normalize_legacy_repository_url(url) if not (username and password): # Check if we have a token first diff --git a/tests/publishing/test_publisher.py b/tests/publishing/test_publisher.py index 53f30e75f78..956d0434d2e 100644 --- a/tests/publishing/test_publisher.py +++ b/tests/publishing/test_publisher.py @@ -210,3 +210,45 @@ def test_publish_read_from_environment_variable( ("https://foo.bar",), {"cert": True, "client_cert": None, "dry_run": False, "skip_existing": False}, ] + +@pytest.mark.parametrize( + ("configured_url", "expected_url"), + [ + # Missing trailing slash — should be added + ("https://test.pypi.org/legacy", "https://test.pypi.org/legacy/"), + # Already has trailing slash — should not double up + ("https://test.pypi.org/legacy/", "https://test.pypi.org/legacy/"), + # Non-legacy URL — should be unchanged + ("https://test.pypi.org/simple", "https://test.pypi.org/simple"), + ], +) +def test_publish_normalizes_legacy_repository_url( + fixture_dir: FixtureDirGetter, + mocker: MockerFixture, + config: Config, + configured_url: str, + expected_url: str, +) -> None: + uploader_auth = mocker.patch("poetry.publishing.uploader.Uploader.auth") + uploader_upload = mocker.patch("poetry.publishing.uploader.Uploader.upload") + + poetry = Factory().create_poetry(fixture_dir("sample_project")) + poetry._config = config + poetry.config.merge( + { + "repositories": {"testpypi": {"url": configured_url}}, + "http-basic": {"testpypi": {"username": "foo", "password": "bar"}}, + } + ) + + publisher = Publisher(poetry, NullIO()) + publisher.publish("testpypi", None, None) + + uploader_auth.assert_called_once_with("foo", "bar") + uploader_upload.assert_called_once_with( + expected_url, + cert=True, + client_cert=None, + dry_run=False, + skip_existing=False, + ) \ No newline at end of file From c04dc133f68c600c784c8e67a13715b67c16fc17 Mon Sep 17 00:00:00 2001 From: Bibek Date: Wed, 25 Mar 2026 14:12:54 -0400 Subject: [PATCH 2/3] updated docs linking to the fix --- docs/repositories.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/repositories.md b/docs/repositories.md index 07bf2ef14d1..9c3b5c40054 100644 --- a/docs/repositories.md +++ b/docs/repositories.md @@ -447,6 +447,8 @@ typically different to the same one provided by the repository for the simple AP in the example of [Test PyPI](https://test.pypi.org/), both the host (`test.pypi.org`) as well as the path (`/legacy`) are different to its simple API (`https://test.pypi.org/simple`). +While the examples show URLs with a trailing slash (e.g., `https://test.pypi.org/legacy/`), Poetry automatically normalizes legacy repository URLs by adding a trailing slash if one is missing. This means both `https://test.pypi.org/legacy` and `https://test.pypi.org/legacy/` will work correctly when publishing. + {{% /note %}} ## Configuring Credentials From 941a9c207515fa900a18d31becd1b0013ce358f4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 18:27:43 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/poetry/publishing/publisher.py | 2 ++ tests/publishing/test_publisher.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/poetry/publishing/publisher.py b/src/poetry/publishing/publisher.py index b6f36ff5b06..a31464355dc 100644 --- a/src/poetry/publishing/publisher.py +++ b/src/poetry/publishing/publisher.py @@ -17,11 +17,13 @@ logger = logging.getLogger(__name__) + def _normalize_legacy_repository_url(url: str) -> str: if url.endswith("/legacy"): url += "/" return url + class Publisher: """ Registers and publishes packages to remote repositories. diff --git a/tests/publishing/test_publisher.py b/tests/publishing/test_publisher.py index 956d0434d2e..b40c9048d07 100644 --- a/tests/publishing/test_publisher.py +++ b/tests/publishing/test_publisher.py @@ -211,6 +211,7 @@ def test_publish_read_from_environment_variable( {"cert": True, "client_cert": None, "dry_run": False, "skip_existing": False}, ] + @pytest.mark.parametrize( ("configured_url", "expected_url"), [ @@ -251,4 +252,4 @@ def test_publish_normalizes_legacy_repository_url( client_cert=None, dry_run=False, skip_existing=False, - ) \ No newline at end of file + )