Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add helpful error message for incorrect PyPI URL #509

Merged
merged 14 commits into from
Oct 30, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions tests/test_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
NEW_SDIST_FIXTURE = 'tests/fixtures/twine-1.6.5.tar.gz'
NEW_WHEEL_FIXTURE = 'tests/fixtures/twine-1.6.5-py2.py3-none-any.whl'
NEW_RELEASE_URL = 'https://pypi.org/project/twine/1.6.5/'
DEFAULT_REPOSITORY = "https://upload.pypi.org/legacy/"
TEST_REPOSITORY = "https://test.pypi.org/legacy/"


def test_successful_upload(make_settings, capsys):
Expand Down Expand Up @@ -285,3 +287,20 @@ def none_upload(*args, **settings_kwargs):
assert "pypipassword" == upload_settings.password
assert "pypiuser" == upload_settings.username
assert "/foo/bar.crt" == upload_settings.cacert


def test_check_status_code(make_settings, capsys):
upload_settings = make_settings()

# override default upload_settings
upload_settings.repository_config['repository'] = \
"https://upload.pypi.org"

with pytest.raises(HTTPError):
upload.upload(upload_settings, [
WHEEL_FIXTURE, SDIST_FIXTURE, NEW_SDIST_FIXTURE, NEW_WHEEL_FIXTURE
])

captured = capsys.readouterr()
assert captured.out.count(DEFAULT_REPOSITORY) == 1
assert captured.out.count(TEST_REPOSITORY) == 1
42 changes: 40 additions & 2 deletions twine/commands/upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@
from twine.package import PackageFile
from twine import exceptions
from twine import settings
from twine import utils

from requests.exceptions import HTTPError


DEFAULT_REPOSITORY = "https://upload.pypi.org/legacy/"
TEST_REPOSITORY = "https://test.pypi.org/legacy/"


def skip_upload(response, skip_existing, package):
Expand Down Expand Up @@ -48,6 +53,39 @@ def skip_upload(response, skip_existing, package):
(response.status_code == 403 and msg_403 in response.text)))


def check_status_code(response, verbose):
"""
Additional safety net to catch response code 410 in case the
UploadToDeprecatedPyPIDetected exception breaks.
Also includes a check for response code 405 and prints helpful error
message guiding users to the right repository endpoints.
"""
if (response.status_code == 410 and
response.url.startswith(("https://pypi.python.org",
"https://testpypi.python.org"))):
print("It appears you're uploading to pypi.python.org (or "
"testpypi.python.org). You've received a 410 error response. "
"Uploading to those sites is deprecated. The new sites are "
"pypi.org and test.pypi.org. Try using "
"https://upload.pypi.org/legacy/ "
"(or https://test.pypi.org/legacy/) to upload your packages "
"instead. These are the default URLs for Twine now. More at "
"https://packaging.python.org/guides/migrating-to-pypi-org/ ")
elif response.status_code == 405 and "pypi.org" in response.url:
print(f"You probably want one of these two URLs: {DEFAULT_REPOSITORY} "
f"or {TEST_REPOSITORY}.")
try:
response.raise_for_status()
except HTTPError as err:
if response.text:
if verbose:
print('Content received from server:\n{}'.format(
response.text))
else:
print('NOTE: Try --verbose to see response content.')
raise err


def upload(upload_settings, dists):
dists = _find_dists(dists)

Expand Down Expand Up @@ -99,7 +137,7 @@ def upload(upload_settings, dists):
print(skip_message)
continue

utils.check_status_code(resp, upload_settings.verbose)
check_status_code(resp, upload_settings.verbose)

uploaded_packages.append(package)

Expand Down
29 changes: 0 additions & 29 deletions twine/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import configparser
from urllib.parse import urlparse, urlunparse

from requests.exceptions import HTTPError

try:
import keyring # noqa
Expand Down Expand Up @@ -129,34 +128,6 @@ def normalize_repository_url(url):
return urlunparse(parsed)


def check_status_code(response, verbose):
"""
Shouldn't happen, thanks to the UploadToDeprecatedPyPIDetected
exception, but this is in case that breaks and it does.
"""
if (response.status_code == 410 and
response.url.startswith(("https://pypi.python.org",
"https://testpypi.python.org"))):
print("It appears you're uploading to pypi.python.org (or "
"testpypi.python.org). You've received a 410 error response. "
"Uploading to those sites is deprecated. The new sites are "
"pypi.org and test.pypi.org. Try using "
"https://upload.pypi.org/legacy/ "
"(or https://test.pypi.org/legacy/) to upload your packages "
"instead. These are the default URLs for Twine now. More at "
"https://packaging.python.org/guides/migrating-to-pypi-org/ ")
try:
response.raise_for_status()
except HTTPError as err:
if response.text:
if verbose:
print('Content received from server:\n{}'.format(
response.text))
else:
print('NOTE: Try --verbose to see response content.')
raise err


def get_userpass_value(cli_value, config, key, prompt_strategy=None):
"""Gets the username / password from config.

Expand Down