Skip to content

Commit

Permalink
Merge pull request pypa#484 from allhailwesttexas/handle-missing-gpg
Browse files Browse the repository at this point in the history
Handle missing GPG executable graciously
  • Loading branch information
sigmavirus24 committed Aug 8, 2019
2 parents 579f3fe + 87957a8 commit 84f1a69
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 1 deletion.
42 changes: 42 additions & 0 deletions tests/test_package.py
Original file line number Diff line number Diff line change
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
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
29 changes: 28 additions & 1 deletion twine/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
from twine.wininst import WinInst
from twine import exceptions

try:
FileNotFoundError = FileNotFoundError
except NameError:
FileNotFoundError = IOError # Py2


DIST_TYPES = {
"bdist_wheel": Wheel,
"bdist_wininst": WinInst,
Expand Down Expand Up @@ -174,10 +180,31 @@ def sign(self, sign_with, identity):
if identity:
gpg_args += ("--local-user", identity)
gpg_args += ("-a", self.filename)
subprocess.check_call(gpg_args)
self.run_gpg(gpg_args)

self.add_gpg_signature(self.signed_filename, self.signed_basefilename)

@classmethod
def run_gpg(cls, gpg_args):
try:
subprocess.check_call(gpg_args)
return
except FileNotFoundError:
if gpg_args[0] != "gpg":
raise exceptions.InvalidSigningExecutable(
"{} executable not available.".format(gpg_args[0]))

print("gpg executable not available. Attempting fallback to gpg2.")
try:
subprocess.check_call(("gpg2",) + gpg_args[1:])
except FileNotFoundError:
print("gpg2 executable not available.")
raise exceptions.InvalidSigningExecutable(
"'gpg' or 'gpg2' executables not available. "
"Try installing one of these or specifying an executable "
"with the --sign-with flag."
)


Hexdigest = collections.namedtuple('Hexdigest', ['md5', 'sha2', 'blake2'])

Expand Down

0 comments on commit 84f1a69

Please sign in to comment.