summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--NEWS1
-rw-r--r--alot/commands/utils.py2
-rw-r--r--alot/crypto.py236
-rw-r--r--alot/db/envelope.py9
-rw-r--r--alot/db/utils.py2
-rw-r--r--alot/errors.py2
-rw-r--r--alot/utils/configobj.py2
-rw-r--r--docs/source/conf.py2
-rw-r--r--docs/source/installation.rst6
-rwxr-xr-xsetup.py2
-rw-r--r--tests/crypto_test.py88
-rw-r--r--tests/utilities.py6
13 files changed, 190 insertions, 170 deletions
diff --git a/.travis.yml b/.travis.yml
index cb863a5d..1436aec8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -123,7 +123,7 @@ install:
# alot when rebuilding the documentation. Notmuch would have to be
# installed by hand in order to get a recent enough version on travis.
printf '%s = None\n' NotmuchError NullPointerError > notmuch.py
- touch gpgme.py
+ touch gpg.py
# install sphinx for building the html docs
pip install sphinx
else
diff --git a/NEWS b/NEWS
index a919a4ed..ffd6b09e 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,7 @@ next:
* bug fix: GPG signatures are acutally verified
* feature: option to use linewise focussing in thread mode
* feature: add support to move to next or previous message matching a notmuch query in a thread buffer
+* feature: Convert from deprecated pygppme module to upstream gpg wrappers
0.5:
* save command prompt, recipient and sender history across program restarts
diff --git a/alot/commands/utils.py b/alot/commands/utils.py
index f8e5067d..ddfeec41 100644
--- a/alot/commands/utils.py
+++ b/alot/commands/utils.py
@@ -66,7 +66,7 @@ def _get_keys(ui, encrypt_keyids, block_error=False, signed_only=False):
to the key)
:type signed_only: bool
:returns: the available keys indexed by their key hash
- :rtype: dict(str->gpgme.Key)
+ :rtype: dict(str->gpg key object)
"""
keys = {}
diff --git a/alot/crypto.py b/alot/crypto.py
index 4da32eea..3700aeef 100644
--- a/alot/crypto.py
+++ b/alot/crypto.py
@@ -1,43 +1,13 @@
+# encoding=utf-8
# Copyright (C) 2011-2012 Patrick Totzke <patricktotzke@gmail.com>
+# Copyright © 2017 Dylan Baker <dylan@pnwbakers.com>
# This file is released under the GNU GPL, version 3 or a later revision.
# For further details see the COPYING file
from __future__ import absolute_import
-import os
-from cStringIO import StringIO
-import gpgme
-from .errors import GPGProblem, GPGCode
-
-
-def _hash_algo_name(hash_algo):
- """
- Re-implements GPGME's hash_algo_name as long as pygpgme doesn't wrap that
- function.
+import gpg
- :param hash_algo: GPGME hash_algo
- :rtype: str
- """
- mapping = {
- gpgme.MD_MD5: "MD5",
- gpgme.MD_SHA1: "SHA1",
- gpgme.MD_RMD160: "RIPEMD160",
- gpgme.MD_MD2: "MD2",
- gpgme.MD_TIGER: "TIGER192",
- gpgme.MD_HAVAL: "HAVAL",
- gpgme.MD_SHA256: "SHA256",
- gpgme.MD_SHA384: "SHA384",
- gpgme.MD_SHA512: "SHA512",
- gpgme.MD_MD4: "MD4",
- gpgme.MD_CRC32: "CRC32",
- gpgme.MD_CRC32_RFC1510: "CRC32RFC1510",
- gpgme.MD_CRC24_RFC2440: "CRC24RFC2440",
- }
- if hash_algo in mapping:
- return mapping[hash_algo]
- else:
- raise GPGProblem(("Invalid hash_algo passed to hash_algo_name."
- " Please report this as a bug in alot."),
- code=GPGCode.INVALID_HASH)
+from .errors import GPGProblem, GPGCode
def RFC3156_micalg_from_algo(hash_algo):
@@ -47,12 +17,16 @@ def RFC3156_micalg_from_algo(hash_algo):
GPGME returns hash algorithm names such as "SHA256", but RFC3156 says that
programs need to use names such as "pgp-sha256" instead.
- :param hash_algo: GPGME hash_algo
+ :param str hash_algo: GPGME hash_algo
+ :returns: the lowercase name of of the algorithm with "pgp-" prepended
:rtype: str
"""
# hash_algo will be something like SHA256, but we need pgp-sha256.
- hash_algo = _hash_algo_name(hash_algo)
- return 'pgp-' + hash_algo.lower()
+ algo = gpg.core.hash_algo_name(hash_algo)
+ if algo is None:
+ raise GPGProblem('Unknown hash algorithm {}'.format(algo),
+ code=GPGCode.INVALID_HASH_ALGORITHM)
+ return 'pgp-' + algo.lower()
def get_key(keyid, validate=False, encrypt=False, sign=False,
@@ -81,69 +55,94 @@ def get_key(keyid, validate=False, encrypt=False, sign=False,
:param signed_only: only return keys whose uid is signed (trusted to
belong to the key)
:type signed_only: bool
- :rtype: gpgme.Key
+ :returns: A gpg key matching the given parameters
+ :rtype: gpg.gpgme._gpgme_key
+ :raises ~alot.errors.GPGProblem: if the keyid is ambiguous
+ :raises ~alot.errors.GPGProblem: if there is no key that matches the
+ parameters
+ :raises ~alot.errors.GPGProblem: if a key is found, but signed_only is true
+ and the key is unused
"""
- ctx = gpgme.Context()
+ ctx = gpg.core.Context()
try:
key = ctx.get_key(keyid)
if validate:
validate_key(key, encrypt=encrypt, sign=sign)
- except gpgme.GpgmeError as e:
- if e.code == gpgme.ERR_AMBIGUOUS_NAME:
+ except gpg.errors.KeyNotFound:
+ raise GPGProblem('Cannot find key for "{}".'.format(keyid),
+ code=GPGCode.NOT_FOUND)
+ except gpg.errors.GPGMEError as e:
+ if e.getcode() == gpg.errors.AMBIGUOUS_NAME:
# When we get here it means there were multiple keys returned by
# gpg for given keyid. Unfortunately gpgme returns invalid and
# expired keys together with valid keys. If only one key is valid
# for given operation maybe we can still return it instead of
# raising exception
- keys = list_keys(hint=keyid)
+
valid_key = None
- for k in keys:
- try:
- validate_key(k, encrypt=encrypt, sign=sign)
- except GPGProblem:
- # if the key is invalid for given action skip it
- continue
-
- if valid_key:
- # we have already found one valid key and now we find
- # another? We really received an ambiguous keyid
+
+ # Catching exceptions for list_keys
+ try:
+ for k in list_keys(hint=keyid):
+ try:
+ validate_key(k, encrypt=encrypt, sign=sign)
+ except GPGProblem:
+ # if the key is invalid for given action skip it
+ continue
+
+ if valid_key:
+ # we have already found one valid key and now we find
+ # another? We really received an ambiguous keyid
+ raise GPGProblem(
+ "More than one key found matching this filter. "
+ "Please be more specific "
+ "(use a key ID like 4AC8EE1D).",
+ code=GPGCode.AMBIGUOUS_NAME)
+ valid_key = k
+ except gpg.errors.GPGMEError as e:
+ # This if will be triggered if there is no key matching at all.
+ if e.getcode() == gpg.errors.AMBIGUOUS_NAME:
raise GPGProblem(
- "More than one key found matching this filter. Please "
- "be more specific (use a key ID like 4AC8EE1D).",
- code=GPGCode.AMBIGUOUS_NAME)
- valid_key = k
+ 'Can not find any key for "{}".'.format(keyid),
+ code=GPGCode.NOT_FOUND)
+ raise
if not valid_key:
# there were multiple keys found but none of them are valid for
# given action (we don't have private key, they are expired
- # etc)
+ # etc), or there was no key at all
raise GPGProblem(
'Can not find usable key for "{}".'.format(keyid),
code=GPGCode.NOT_FOUND)
return valid_key
- elif e.code == gpgme.ERR_INV_VALUE or e.code == gpgme.ERR_EOF:
- raise GPGProblem('Can not find key for "{}".'.format(keyid),
- code=GPGCode.NOT_FOUND)
+ elif e.getcode() == gpg.errors.INV_VALUE:
+ raise GPGProblem(
+ 'Can not find usable key for "{}".'.format(keyid),
+ code=GPGCode.NOT_FOUND)
else:
raise e
if signed_only and not check_uid_validity(key, keyid):
- raise GPGProblem('Can not find a trusworthy key for "{}".'.format(keyid),
+ raise GPGProblem('Cannot find a trusworthy key for "{}".'.format(keyid),
code=GPGCode.NOT_FOUND)
return key
def list_keys(hint=None, private=False):
"""
- Returns a iterator of all keys containing the fingerprint, or all keys if
+ Returns a generator of all keys containing the fingerprint, or all keys if
hint is None.
+ The generator may raise exceptions of :class:gpg.errors.GPGMEError, and it
+ is the caller's responsibility to handle them.
+
:param hint: Part of a fingerprint to usee to search
:type hint: str or None
:param private: Whether to return public keys or secret keys
:type private: bool
- :rtype: :class:`gpgme.KeyIter`
+ :returns: A generator that yields keys.
+ :rtype: Generator[gpg.gpgme.gpgme_key_t, None, None]
"""
- ctx = gpgme.Context()
+ ctx = gpg.core.Context()
return ctx.keylist(hint, private)
@@ -154,81 +153,69 @@ def detached_signature_for(plaintext_str, key=None):
A detached signature in GPG speak is a separate blob of data containing
a signature for the specified plaintext.
- :param plaintext_str: text to sign
- :param key: gpgme_key_t object representing the key to use
- :rtype: tuple of gpgme.NewSignature array and str
+ :param str plaintext_str: text to sign
+ :param key: key to sign with
+ :type key: gpg.gpgme._gpgme_key
+ :returns: A list of signature and the signed blob of data
+ :rtype: tuple[list[gpg.results.NewSignature], str]
"""
- ctx = gpgme.Context()
- ctx.armor = True
+ ctx = gpg.core.Context(armor=True)
if key is not None:
ctx.signers = [key]
- plaintext_data = StringIO(plaintext_str)
- signature_data = StringIO()
- sigs = ctx.sign(plaintext_data, signature_data, gpgme.SIG_MODE_DETACH)
- signature_data.seek(0, os.SEEK_SET)
- signature = signature_data.read()
- return sigs, signature
+ (sigblob, sign_result) = ctx.sign(plaintext_str, mode=gpg.constants.SIG_MODE_DETACH)
+ return sign_result.signatures, sigblob
def encrypt(plaintext_str, keys=None):
- """
- Encrypts the given plaintext string and returns a PGP/MIME compatible
- string
+ """Encrypt data and return the encrypted form.
- :param plaintext_str: the mail to encrypt
- :param key: gpgme_key_t object representing the key to use
- :rtype: a string holding the encrypted mail
+ :param str plaintext_str: the mail to encrypt
+ :param key: optionally, a list of keys to encrypt with
+ :type key: list[gpg.gpgme.gpgme_key_t] or None
+ :returns: encrypted mail
+ :rtype: str
"""
- plaintext_data = StringIO(plaintext_str)
- encrypted_data = StringIO()
- ctx = gpgme.Context()
- ctx.armor = True
- ctx.encrypt(keys, gpgme.ENCRYPT_ALWAYS_TRUST, plaintext_data,
- encrypted_data)
- encrypted_data.seek(0, os.SEEK_SET)
- encrypted = encrypted_data.read()
- return encrypted
+ ctx = gpg.core.Context(armor=True)
+ out = ctx.encrypt(plaintext_str, recipients=keys, always_trust=True)[0]
+ return out
def verify_detached(message, signature):
- '''Verifies whether the message is authentic by checking the
- signature.
+ """Verifies whether the message is authentic by checking the signature.
- :param message: the message as `str`
- :param signature: a `str` containing an OpenPGP signature
- :returns: a list of :class:`gpgme.Signature`
+ :param str message: The message to be verified, in canonical form.
+ :param str signature: the OpenPGP signature to verify
+ :returns: a list of signatures
+ :rtype: list[gpg.results.Signature]
:raises: :class:`~alot.errors.GPGProblem` if the verification fails
- '''
- message_data = StringIO(message)
- signature_data = StringIO(signature)
- ctx = gpgme.Context()
-
- status = ctx.verify(signature_data, message_data, None)
- if isinstance(status[0].status, gpgme.GpgmeError):
- raise GPGProblem(status[0].status.message, code=status[0].status.code)
- return status
+ """
+ ctx = gpg.core.Context()
+ try:
+ verify_results = ctx.verify(message, signature)[1]
+ return verify_results.signatures
+ except gpg.errors.BadSignatures as e:
+ raise GPGProblem(str(e), code=GPGCode.BAD_SIGNATURE)
+ except gpg.errors.GPGMEError as e:
+ raise GPGProblem(e.message, code=e.getcode())
def decrypt_verify(encrypted):
- '''Decrypts the given ciphertext string and returns both the
+ """Decrypts the given ciphertext string and returns both the
signatures (if any) and the plaintext.
- :param encrypted: the mail to decrypt
- :returns: a tuple (sigs, plaintext) with sigs being a list of a
- :class:`gpgme.Signature` and plaintext is a `str` holding
- the decrypted mail
+ :param str encrypted: the mail to decrypt
+ :returns: the signatures and decrypted plaintext data
+ :rtype: tuple[list[gpg.resuit.Signature], str]
:raises: :class:`~alot.errors.GPGProblem` if the decryption fails
- '''
- encrypted_data = StringIO(encrypted)
- plaintext_data = StringIO()
- ctx = gpgme.Context()
+ """
+ ctx = gpg.core.Context()
try:
- sigs = ctx.decrypt_verify(encrypted_data, plaintext_data)
- except gpgme.GpgmeError as e:
- raise GPGProblem(e.message, code=e.code)
+ (plaintext, _, verify_result) = ctx.decrypt(encrypted, verify=True)
+ except gpg.errors.GPGMEError as e:
+ raise GPGProblem(e.message, code=e.getcode())
+ # what if the signature is bad?
- plaintext_data.seek(0, os.SEEK_SET)
- return sigs, plaintext_data.read()
+ return verify_result.signatures, plaintext
def hash_key(key):
@@ -253,12 +240,16 @@ def validate_key(key, sign=False, encrypt=False):
signing or encrypting. Raise GPGProblem otherwise.
:param key: the GPG key to check
- :type key: gpgme.Key
+ :type key: gpg.gpgme._gpgme_key
:param sign: whether the key should be able to sign
:type sign: bool
:param encrypt: whether the key should be able to encrypt
:type encrypt: bool
-
+ :raises ~alot.errors.GPGProblem: If the key is revoked, expired, or invalid
+ :raises ~alot.errors.GPGProblem: If encrypt is true and the key cannot be
+ used to encrypt
+ :raises ~alot.errors.GPGProblem: If sign is true and th key cannot be used
+ to encrypt
"""
if key.revoked:
raise GPGProblem('The key "{}" is revoked.'.format(key.uids[0].uid),
@@ -285,16 +276,15 @@ def check_uid_validity(key, email):
email is assumed to belong to the key.
:param key: the GPG key to which the email should belong
- :type key: gpgme.Key
+ :type key: gpg.gpgme._gpgme_key
:param email: the email address that should belong to the key
:type email: str
:returns: whether the key can be assumed to belong to the given email
:rtype: bool
-
"""
for key_uid in key.uids:
if email == key_uid.email and not key_uid.revoked and \
not key_uid.invalid and \
- key_uid.validity >= gpgme.VALIDITY_FULL:
+ key_uid.validity >= gpg.constants.validity.FULL:
return True
return False
diff --git a/alot/db/envelope.py b/alot/db/envelope.py
index 0a6d4f58..3dc12278 100644
--- a/alot/db/envelope.py
+++ b/alot/db/envelope.py
@@ -13,8 +13,7 @@ from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
import email.charset as charset
-
-import gpgme
+import gpg
from .attachment import Attachment
from .utils import encode_header
@@ -197,8 +196,8 @@ class Envelope(object):
raise GPGProblem("Could not sign message (GPGME "
"did not return a signature)",
code=GPGCode.KEY_CANNOT_SIGN)
- except gpgme.GpgmeError as e:
- if e.code == gpgme.ERR_BAD_PASSPHRASE:
+ except gpg.errors.GPGMEError as e:
+ if e.getcode() == gpg.errors.BAD_PASSPHRASE:
# If GPG_AGENT_INFO is unset or empty, the user just does
# not have gpg-agent running (properly).
if os.environ.get('GPG_AGENT_INFO', '').strip() == '':
@@ -237,7 +236,7 @@ class Envelope(object):
try:
encrypted_str = crypto.encrypt(plaintext,
self.encrypt_keys.values())
- except gpgme.GpgmeError as e:
+ except gpg.errors.GPGMEError as e:
raise GPGProblem(str(e), code=GPGCode.KEY_CANNOT_ENCRYPT)
outer_msg = MIMEMultipart('encrypted',
diff --git a/alot/db/utils.py b/alot/db/utils.py
index 3cff108a..994e85b2 100644
--- a/alot/db/utils.py
+++ b/alot/db/utils.py
@@ -35,7 +35,7 @@ def add_signature_headers(mail, sigs, error_msg):
verification was successful.
:param mail: :class:`email.message.Message` the message to entitle
- :param sigs: list of :class:`gpgme.Signature`
+ :param sigs: list of :class:`gpg.results.Signature`
:param error_msg: `str` containing an error message, the empty
string indicating no error
'''
diff --git a/alot/errors.py b/alot/errors.py
index 9860f411..c312e674 100644
--- a/alot/errors.py
+++ b/alot/errors.py
@@ -13,6 +13,8 @@ class GPGCode(object):
KEY_CANNOT_ENCRYPT = 7
KEY_CANNOT_SIGN = 8
INVALID_HASH = 9
+ INVALID_HASH_ALGORITHM = 10
+ BAD_SIGNATURE = 11
class GPGProblem(Exception):
diff --git a/alot/utils/configobj.py b/alot/utils/configobj.py
index aba61c3d..5e04409a 100644
--- a/alot/utils/configobj.py
+++ b/alot/utils/configobj.py
@@ -136,7 +136,7 @@ def force_list(value, min=None, max=None):
def gpg_key(value):
"""
test if value points to a known gpg key
- and return that key as :class:`pyme.pygpgme._gpgme_key`.
+ and return that key as a gpg key object.
"""
try:
return crypto.get_key(value)
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 5ebab550..e096cc89 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -33,7 +33,7 @@ MOCK_MODULES = ['twisted', 'twisted.internet',
'urwid',
'urwidtrees',
'magic',
- 'gpgme',
+ 'gpg',
'configobj',
'validate',
'argparse']
diff --git a/docs/source/installation.rst b/docs/source/installation.rst
index 2110315b..170c384b 100644
--- a/docs/source/installation.rst
+++ b/docs/source/installation.rst
@@ -13,7 +13,7 @@ A full list of dependencies is below:
* `libnotmuch <http://notmuchmail.org/>`_ and it's python bindings, ≥ `0.13`
* `urwid <http://excess.org/urwid/>`_ toolkit, ≥ `1.1.0`
* `urwidtrees <https://github.com/pazz/urwidtrees>`_, ≥ `1.0`
-* `PyGPGME <https://launchpad.net/pygpgme>`_ ≥ `0.2`
+* `gpg <https://pypi.python.org/pypi/gpg>`_
.. note:: urwidtrees was only recently detached from alot and is not widely
available as a separate package. You can install it e.g., via
@@ -26,11 +26,11 @@ A full list of dependencies is below:
On debian/ubuntu the rest are packaged as::
- python-setuptools python-magic python-configobj python-twisted python-notmuch python-urwid python-gpgme
+ python-setuptools python-magic python-configobj python-twisted python-notmuch python-urwid python-gpg
On fedora/redhat these are packaged as::
- python-setuptools python-magic python-configobj python-twisted python-notmuch python-urwid pygpgme
+ python-setuptools python-magic python-configobj python-twisted python-notmuch python-urwid python-gpg
Alot uses `mailcap <http://en.wikipedia.org/wiki/Mailcap>`_ to look up mime-handler for inline
rendering and opening of attachments. For a full description of the maicap protocol consider the
diff --git a/setup.py b/setup.py
index ecfb33af..7df797f2 100755
--- a/setup.py
+++ b/setup.py
@@ -32,7 +32,7 @@ setup(name='alot',
'twisted>=10.2.0',
'python-magic',
'configobj>=4.7.0',
- 'pygpgme>=0.2'],
+ 'gpg'],
tests_require=[
'mock',
],
diff --git a/tests/crypto_test.py b/tests/crypto_test.py
index e51adaeb..1ee5404f 100644
--- a/tests/crypto_test.py
+++ b/tests/crypto_test.py
@@ -10,7 +10,7 @@ import subprocess
import tempfile
import unittest
-import gpgme
+import gpg
import mock
from alot import crypto
@@ -43,15 +43,13 @@ def setUpModule():
mock_home.start()
MOD_CLEAN.add_cleanup(mock_home.stop)
- ctx = gpgme.Context()
- ctx.armor = True
-
- # Add the public and private keys. They have no password
- search_dir = os.path.join(os.path.dirname(__file__), 'static/gpg-keys')
- for each in os.listdir(search_dir):
- if os.path.splitext(each)[1] == '.gpg':
- with open(os.path.join(search_dir, each)) as f:
- ctx.import_(f)
+ with gpg.core.Context(armor=True) as ctx:
+ # Add the public and private keys. They have no password
+ search_dir = os.path.join(os.path.dirname(__file__), 'static/gpg-keys')
+ for each in os.listdir(search_dir):
+ if os.path.splitext(each)[1] == '.gpg':
+ with open(os.path.join(search_dir, each)) as f:
+ ctx.op_import(f)
@MOD_CLEAN.wrap_teardown
@@ -66,29 +64,54 @@ def tearDownModule():
os.kill(int(pid), signal.SIGKILL)
+def make_key(revoked=False, expired=False, invalid=False, can_encrypt=True,
+ can_sign=True):
+ # This is ugly
+ mock_key = mock.create_autospec(gpg._gpgme._gpgme_key)
+ mock_key.uids = [mock.Mock(uid=u'mocked')]
+ mock_key.revoked = revoked
+ mock_key.expired = expired
+ mock_key.invalid = invalid
+ mock_key.can_encrypt = can_encrypt
+ mock_key.can_sign = can_sign
+
+ return mock_key
+
+
+def make_uid(email, revoked=False, invalid=False,
+ validity=gpg.constants.validity.FULL):
+ uid = mock.Mock()
+ uid.email = email
+ uid.revoked = revoked
+ uid.invalid = invalid
+ uid.validity = validity
+
+ return uid
+
+
class TestHashAlgorithmHelper(unittest.TestCase):
"""Test cases for the helper function RFC3156_canonicalize."""
def test_returned_string_starts_with_pgp(self):
- result = crypto.RFC3156_micalg_from_algo(gpgme.MD_MD5)
+ result = crypto.RFC3156_micalg_from_algo(gpg.constants.md.MD5)
self.assertTrue(result.startswith('pgp-'))
def test_returned_string_is_lower_case(self):
- result = crypto.RFC3156_micalg_from_algo(gpgme.MD_MD5)
+ result = crypto.RFC3156_micalg_from_algo(gpg.constants.md.MD5)
self.assertTrue(result.islower())
def test_raises_for_unknown_hash_name(self):
with self.assertRaises(GPGProblem):
- crypto.RFC3156_micalg_from_algo(gpgme.MD_NONE)
+ crypto.RFC3156_micalg_from_algo(gpg.constants.md.NONE)
class TestDetachedSignatureFor(unittest.TestCase):
def test_valid_signature_generated(self):
- ctx = gpgme.Context()
to_sign = "this is some text.\nit is more than nothing.\n"
- _, detached = crypto.detached_signature_for(to_sign, ctx.get_key(FPR))
+ with gpg.core.Context() as ctx:
+ _, detached = crypto.detached_signature_for(to_sign, ctx.get_key(FPR))
with tempfile.NamedTemporaryFile(delete=False) as f:
f.write(detached)
@@ -108,9 +131,9 @@ class TestDetachedSignatureFor(unittest.TestCase):
class TestVerifyDetached(unittest.TestCase):
def test_verify_signature_good(self):
- ctx = gpgme.Context()
to_sign = "this is some text.\nIt's something\n."
- _, detached = crypto.detached_signature_for(to_sign, ctx.get_key(FPR))
+ with gpg.core.Context() as ctx:
+ _, detached = crypto.detached_signature_for(to_sign, ctx.get_key(FPR))
try:
crypto.verify_detached(to_sign, detached)
@@ -118,10 +141,10 @@ class TestVerifyDetached(unittest.TestCase):
raise AssertionError
def test_verify_signature_bad(self):
- ctx = gpgme.Context()
to_sign = "this is some text.\nIt's something\n."
similar = "this is some text.\r\n.It's something\r\n."
- _, detached = crypto.detached_signature_for(to_sign, ctx.get_key(FPR))
+ with gpg.core.Context() as ctx:
+ _, detached = crypto.detached_signature_for(to_sign, ctx.get_key(FPR))
with self.assertRaises(GPGProblem):
crypto.verify_detached(similar, detached)
@@ -218,7 +241,7 @@ class TestCheckUIDValidity(unittest.TestCase):
key = utilities.make_key()
key.uids[0] = utilities.make_uid(
mock.sentinel.EMAIL,
- validity=gpgme.VALIDITY_UNDEFINED)
+ validity=gpg.constants.validity.UNDEFINED)
ret = crypto.check_uid_validity(key, mock.sentinel.EMAIL)
self.assertFalse(ret)
@@ -251,16 +274,16 @@ class TestGetKey(unittest.TestCase):
def test_plain(self):
# Test the uid of the only identity attached to the key we generated.
- ctx = gpgme.Context()
- expected = ctx.get_key(FPR).uids[0].uid
+ with gpg.core.Context() as ctx:
+ expected = ctx.get_key(FPR).uids[0].uid
actual = crypto.get_key(FPR).uids[0].uid
self.assertEqual(expected, actual)
def test_validate(self):
# Since we already test validation we're only going to test validate
# once.
- ctx = gpgme.Context()
- expected = ctx.get_key(FPR).uids[0].uid
+ with gpg.core.Context() as ctx:
+ expected = ctx.get_key(FPR).uids[0].uid
actual = crypto.get_key(FPR, validate=True, encrypt=True, sign=True).uids[0].uid
self.assertEqual(expected, actual)
@@ -289,10 +312,15 @@ class TestGetKey(unittest.TestCase):
@staticmethod
def _context_mock():
- error = gpgme.GpgmeError()
- error.code = gpgme.ERR_AMBIGUOUS_NAME
+ class CustomError(gpg.errors.GPGMEError):
+ """A custom GPGMEError class that always has an errors code of
+ AMBIGUOUS_NAME.
+ """
+ def getcode(self):
+ return gpg.errors.AMBIGUOUS_NAME
+
context_mock = mock.Mock()
- context_mock.get_key = mock.Mock(side_effect=error)
+ context_mock.get_key = mock.Mock(side_effect=CustomError)
return context_mock
@@ -300,7 +328,7 @@ class TestGetKey(unittest.TestCase):
invalid_key = utilities.make_key(invalid=True)
valid_key = utilities.make_key()
- with mock.patch('alot.crypto.gpgme.Context',
+ with mock.patch('alot.crypto.gpg.core.Context',
mock.Mock(return_value=self._context_mock())), \
mock.patch('alot.crypto.list_keys',
mock.Mock(return_value=[valid_key, invalid_key])):
@@ -308,7 +336,7 @@ class TestGetKey(unittest.TestCase):
self.assertIs(key, valid_key)
def test_ambiguous_two_valid(self):
- with mock.patch('alot.crypto.gpgme.Context',
+ with mock.patch('alot.crypto.gpg.core.Context',
mock.Mock(return_value=self._context_mock())), \
mock.patch('alot.crypto.list_keys',
mock.Mock(return_value=[utilities.make_key(),
@@ -318,7 +346,7 @@ class TestGetKey(unittest.TestCase):
self.assertEqual(cm.exception.code, GPGCode.AMBIGUOUS_NAME)
def test_ambiguous_no_valid(self):
- with mock.patch('alot.crypto.gpgme.Context',
+ with mock.patch('alot.crypto.gpg.core.Context',
mock.Mock(return_value=self._context_mock())), \
mock.patch('alot.crypto.list_keys',
mock.Mock(return_value=[
diff --git a/tests/utilities.py b/tests/utilities.py
index 45433c99..402feb38 100644
--- a/tests/utilities.py
+++ b/tests/utilities.py
@@ -21,7 +21,7 @@ from __future__ import absolute_import
import functools
import unittest
-import gpgme
+import gpg
import mock
@@ -149,7 +149,7 @@ class ModuleCleanup(object):
def make_uid(email, uid=u'mocked', revoked=False, invalid=False,
- validity=gpgme.VALIDITY_FULL):
+ validity=gpg.constants.validity.FULL):
uid_ = mock.Mock()
uid_.email = email
uid_.uid = uid
@@ -162,7 +162,7 @@ def make_uid(email, uid=u'mocked', revoked=False, invalid=False,
def make_key(revoked=False, expired=False, invalid=False, can_encrypt=True,
can_sign=True):
- mock_key = mock.create_autospec(gpgme.Key)
+ mock_key = mock.Mock()
mock_key.uids = [make_uid(u'foo@example.com')]
mock_key.revoked = revoked
mock_key.expired = expired