Skip to content

Commit

Permalink
Merge branch 'master' into release/1.14.0
Browse files Browse the repository at this point in the history
  • Loading branch information
jaraco committed Sep 7, 2019
2 parents aabbff9 + 182e8af commit 9a730f3
Show file tree
Hide file tree
Showing 16 changed files with 257 additions and 16 deletions.
4 changes: 3 additions & 1 deletion AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ László Kiss Kollár <kiss.kollar.laszlo@gmail.com>
Frances Hocutt <frances.hocutt@gmail.com>
Tathagata Dasgupta <tathagatadg@gmail.com>
Wasim Thabraze <wasim@thabraze.me>
Varun Kamath <varunkamath18@gmail.com>
Varun Kamath <varunkamath18@gmail.com>
Brian Rutledge <bhrutledge@gmail.com>
Peter Stensmyr <peter.stensmyr@gmail.com> (http://www.peterstensmyr.com)
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ include README.rst
include AUTHORS
include .coveragerc

recursive-include tests *.py *.whl deprecated-pypirc
recursive-include tests *.py *.whl *.gz deprecated-pypirc
recursive-include docs *.bat *.empty *.py *.rst Makefile *.txt

prune docs/_build
Expand Down
5 changes: 1 addition & 4 deletions codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,4 @@ coverage:
fixes:
- .tox

comment:
layout: "header, diff, changes, sunburst, uncovered, tree"
branches: null
behavior: default
comment: off
7 changes: 5 additions & 2 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
=========
Changelog
=========
* :release:`1.14.0 <2019-06-12>`
* :release:`1.14.0 <2019-09-06>`
* :feature:`456` Better error handling and gpg2 fallback if gpg not available.
* :bug:`341` Fail more gracefully when encountering bad metadata
* :feature:`459` Show Warehouse URL after uploading a package
* :feature:`310` Now provide a more meaningful error on redirect during upload.
* :release:`1.13.0 <2019-02-13>`
* :bug:`452 major` Restore prompts while retaining support for suppressing
Expand Down Expand Up @@ -138,7 +141,7 @@ Changelog
ConnectionErrors
* :release:`1.6.4 <2015-10-27>`
* :bug:`145` Paths with hyphens in them break the Wheel regular expression.
* :bug:`146` Exception while accessing the ``respository`` key (sic)
* :bug:`146` Exception while accessing the ``repository`` key (sic)
when raising a redirect exception.
* :release:`1.6.3 <2015-10-05>`
* :bug:`137`, :bug:`140` Uploading signatures was broken due to the pull
Expand Down
Binary file added tests/fixtures/twine-1.5.0.tar.gz
Binary file not shown.
Binary file added tests/fixtures/twine-1.6.5-py2.py3-none-any.whl
Binary file not shown.
Binary file added tests/fixtures/twine-1.6.5.tar.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ def test_dispatch_to_subcommand(monkeypatch):

def test_catches_enoent():
with pytest.raises(SystemExit):
cli.dispatch(["non-existant-command"])
cli.dispatch(["non-existent-command"])
63 changes: 62 additions & 1 deletion tests/test_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.
from __future__ import unicode_literals
import platform
from twine import package
from twine import package, exceptions

import pretend
import pytest
Expand Down Expand Up @@ -59,6 +59,48 @@ def test_sign_file_with_identity(monkeypatch):
assert replaced_check_call.calls == [pretend.call(args)]


def test_run_gpg_raises_exception_if_no_gpgs(monkeypatch):
replaced_check_call = pretend.raiser(
package.FileNotFoundError('not found')
)
monkeypatch.setattr(package.subprocess, 'check_call', replaced_check_call)
gpg_args = ('gpg', '--detach-sign', '-a', 'pypircfile')

with pytest.raises(exceptions.InvalidSigningExecutable) as err:
package.PackageFile.run_gpg(gpg_args)

assert 'executables not available' in err.value.args[0]


def test_run_gpg_raises_exception_if_not_using_gpg(monkeypatch):
replaced_check_call = pretend.raiser(
package.FileNotFoundError('not found')
)
monkeypatch.setattr(package.subprocess, 'check_call', replaced_check_call)
gpg_args = ('not_gpg', '--detach-sign', '-a', 'pypircfile')

with pytest.raises(exceptions.InvalidSigningExecutable) as err:
package.PackageFile.run_gpg(gpg_args)

assert 'not_gpg executable not available' in err.value.args[0]


def test_run_gpg_falls_back_to_gpg2(monkeypatch):

def check_call(arg_list):
if arg_list[0] == 'gpg':
raise package.FileNotFoundError('gpg not found')

replaced_check_call = pretend.call_recorder(check_call)
monkeypatch.setattr(package.subprocess, 'check_call', replaced_check_call)
gpg_args = ('gpg', '--detach-sign', '-a', 'pypircfile')

package.PackageFile.run_gpg(gpg_args)

gpg2_args = replaced_check_call.calls[1].args
assert gpg2_args[0][0] == 'gpg2'


def test_package_signed_name_is_correct():
filename = 'tests/fixtures/deprecated-pypirc'

Expand Down Expand Up @@ -208,3 +250,22 @@ def test_no_blake2_hash_manager(monkeypatch):
hasher.hash()
hashes = TWINE_1_5_0_WHEEL_HEXDIGEST._replace(blake2=None)
assert hasher.hexdigest() == hashes


def test_pkginfo_returns_no_metadata(monkeypatch):
"""
Fail gracefully if pkginfo can't interpret the metadata (possibly due to
seeing a version number it doesn't support yet) and gives us back an
'empty' object with no metadata
"""

def EmptyDist(filename):
return pretend.stub(name=None, version=None)

monkeypatch.setattr(package, "DIST_TYPES", {"bdist_wheel": EmptyDist})
filename = 'tests/fixtures/twine-1.5.0-py2.py3-none-any.whl'

with pytest.raises(exceptions.InvalidDistribution) as err:
package.PackageFile.from_filename(filename, comment=None)

assert 'Invalid distribution metadata' in err.value.args[0]
61 changes: 60 additions & 1 deletion tests/test_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import requests

from twine import repository
from twine.utils import DEFAULT_REPOSITORY
from twine.utils import DEFAULT_REPOSITORY, TEST_REPOSITORY

import pretend
import pytest
Expand Down Expand Up @@ -188,3 +188,62 @@ def dictfunc():
)

repo.upload(package)


@pytest.mark.parametrize('package_meta,repository_url,release_urls', [
# Single package
(
[('fake', '2.12.0')],
DEFAULT_REPOSITORY,
{'https://pypi.org/project/fake/2.12.0/'},
),
# Single package to testpypi
(
[('fake', '2.12.0')],
TEST_REPOSITORY,
{'https://test.pypi.org/project/fake/2.12.0/'},
),
# Multiple packages (faking a wheel and an sdist)
(
[('fake', '2.12.0'), ('fake', '2.12.0')],
DEFAULT_REPOSITORY,
{'https://pypi.org/project/fake/2.12.0/'},
),
# Multiple releases
(
[('fake', '2.12.0'), ('fake', '2.12.1')],
DEFAULT_REPOSITORY,
{
'https://pypi.org/project/fake/2.12.0/',
'https://pypi.org/project/fake/2.12.1/',
},
),
# Not pypi
(
[('fake', '2.12.0')],
'http://devpi.example.com',
set(),
),
# No packages
(
[],
DEFAULT_REPOSITORY,
set(),
),
])
def test_release_urls(package_meta, repository_url, release_urls):
packages = [
pretend.stub(
safe_name=name,
metadata=pretend.stub(version=version),
)
for name, version in package_meta
]

repo = repository.Repository(
repository_url=repository_url,
username='username',
password='password',
)

assert repo.release_urls(packages) == release_urls
56 changes: 53 additions & 3 deletions tests/test_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,23 @@

import pretend
import pytest
from requests.exceptions import HTTPError

from twine.commands import upload
from twine import package, cli, exceptions
import twine

import helpers

SDIST_FIXTURE = 'tests/fixtures/twine-1.5.0.tar.gz'
WHEEL_FIXTURE = 'tests/fixtures/twine-1.5.0-py2.py3-none-any.whl'
RELEASE_URL = 'https://pypi.org/project/twine/1.5.0/'
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/'


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

stub_response = pretend.stub(
Expand All @@ -36,16 +42,56 @@ def test_successful_upload(make_settings):

stub_repository = pretend.stub(
upload=lambda package: stub_response,
close=lambda: None
close=lambda: None,
release_urls=lambda packages: {RELEASE_URL, NEW_RELEASE_URL}
)

upload_settings.create_repository = lambda: stub_repository

result = upload.upload(upload_settings, [WHEEL_FIXTURE])
result = upload.upload(upload_settings, [
WHEEL_FIXTURE, SDIST_FIXTURE, NEW_SDIST_FIXTURE, NEW_WHEEL_FIXTURE
])

# A truthy result means the upload failed
assert result is None

captured = capsys.readouterr()
assert captured.out.count(RELEASE_URL) == 1
assert captured.out.count(NEW_RELEASE_URL) == 1


@pytest.mark.parametrize('verbose', [False, True])
def test_exception_for_http_status(verbose, make_settings, capsys):
upload_settings = make_settings()
upload_settings.verbose = verbose

stub_response = pretend.stub(
is_redirect=False,
status_code=403,
text="Invalid or non-existent authentication information",
raise_for_status=pretend.raiser(HTTPError)
)

stub_repository = pretend.stub(
upload=lambda package: stub_response,
close=lambda: None,
)

upload_settings.create_repository = lambda: stub_repository

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

captured = capsys.readouterr()
assert RELEASE_URL not in captured.out

if verbose:
assert stub_response.text in captured.out
assert '--verbose' not in captured.out
else:
assert stub_response.text not in captured.out
assert '--verbose' in captured.out


def test_get_config_old_format(make_settings, pypirc):
try:
Expand Down Expand Up @@ -115,6 +161,7 @@ def test_prints_skip_message_for_uploaded_package(make_settings, capsys):
stub_repository = pretend.stub(
# Short-circuit the upload, so no need for a stub response
package_is_uploaded=lambda package: True,
release_urls=lambda packages: {},
close=lambda: None
)

Expand All @@ -127,6 +174,7 @@ def test_prints_skip_message_for_uploaded_package(make_settings, capsys):

captured = capsys.readouterr()
assert "Skipping twine-1.5.0-py2.py3-none-any.whl" in captured.out
assert RELEASE_URL not in captured.out


def test_prints_skip_message_for_response(make_settings, capsys):
Expand All @@ -140,6 +188,7 @@ def test_prints_skip_message_for_response(make_settings, capsys):
stub_repository = pretend.stub(
# Do the upload, triggering the error response
package_is_uploaded=lambda package: False,
release_urls=lambda packages: {},
upload=lambda package: stub_response,
close=lambda: None
)
Expand All @@ -153,6 +202,7 @@ def test_prints_skip_message_for_response(make_settings, capsys):

captured = capsys.readouterr()
assert "Skipping twine-1.5.0-py2.py3-none-any.whl" in captured.out
assert RELEASE_URL not in captured.out


def test_skip_existing_skips_files_already_on_PyPI(monkeypatch):
Expand Down
2 changes: 1 addition & 1 deletion twine/commands/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
}


# Regular expression used to capture and reformat doctuils warnings into
# Regular expression used to capture and reformat docutils warnings into
# something that a human can understand. This is loosely borrowed from
# Sphinx: https://github.com/sphinx-doc/sphinx/blob
# /c35eb6fade7a3b4a6de4183d1dd4196f04a5edaf/sphinx/util/docutils.py#L199
Expand Down
10 changes: 10 additions & 0 deletions twine/commands/upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def upload(upload_settings, dists):
print("Uploading distributions to {}".format(repository_url))

repository = upload_settings.create_repository()
uploaded_packages = []

for filename in uploads:
package = PackageFile.from_filename(filename, upload_settings.comment)
Expand Down Expand Up @@ -100,8 +101,17 @@ def upload(upload_settings, dists):
if skip_upload(resp, upload_settings.skip_existing, package):
print(skip_message)
continue

utils.check_status_code(resp, upload_settings.verbose)

uploaded_packages.append(package)

release_urls = repository.release_urls(uploaded_packages)
if release_urls:
print('\nView at:')
for url in release_urls:
print(url)

# Bug 28. Try to silence a ResourceWarning by clearing the connection
# pool.
repository.close()
Expand Down
6 changes: 6 additions & 0 deletions twine/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ class InvalidSigningConfiguration(TwineException):
pass


class InvalidSigningExecutable(TwineException):
"""Signing executable must be installed on system."""

pass


class InvalidConfiguration(TwineException):
"""Raised when configuration is invalid."""

Expand Down
Loading

0 comments on commit 9a730f3

Please sign in to comment.