Hello community,

here is the log from the commit of package python-python-gnupg for 
openSUSE:Factory checked in at 2020-08-05 20:30:03
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-python-gnupg (Old)
 and      /work/SRC/openSUSE:Factory/.python-python-gnupg.new.3592 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-python-gnupg"

Wed Aug  5 20:30:03 2020 rev:9 rq:824526 version:0.4.6

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-python-gnupg/python-python-gnupg.changes  
2019-09-11 10:36:15.411281183 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-python-gnupg.new.3592/python-python-gnupg.changes
        2020-08-05 20:30:15.347125442 +0200
@@ -1,0 +2,9 @@
+Wed Aug  5 13:20:26 UTC 2020 - Marketa Calabkova <mcalabk...@suse.com>
+
+- Update to 0.4.6
+  * Fixed #128: Added ECC support by changing key generation parameters. (The 
Key-Length
+    value isn't added if a curve is specified.)
+  * More bugfixes.
+  * Support for Python versions 3.5 and under is discontinued, except for 
Python 2.7.
+
+-------------------------------------------------------------------

Old:
----
  python-gnupg-0.4.5.tar.gz

New:
----
  python-gnupg-0.4.6.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-python-gnupg.spec ++++++
--- /var/tmp/diff_new_pack.8Ef11b/_old  2020-08-05 20:30:17.179126398 +0200
+++ /var/tmp/diff_new_pack.8Ef11b/_new  2020-08-05 20:30:17.179126398 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-python-gnupg
 #
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %define         oldpython python
 Name:           python-python-gnupg
-Version:        0.4.5
+Version:        0.4.6
 Release:        0
 Summary:        A wrapper for the GNU Privacy Guard (GPG or GnuPG)
 License:        BSD-3-Clause

++++++ python-gnupg-0.4.5.tar.gz -> python-gnupg-0.4.6.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.4.5/MANIFEST.in 
new/python-gnupg-0.4.6/MANIFEST.in
--- old/python-gnupg-0.4.5/MANIFEST.in  1970-01-01 01:00:00.000000000 +0100
+++ new/python-gnupg-0.4.6/MANIFEST.in  2020-04-17 08:50:10.000000000 +0200
@@ -0,0 +1,6 @@
+include LICENSE.txt
+include README.rst
+include test_gnupg.py
+include messages.json
+include test_*ring.gpg
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.4.5/PKG-INFO 
new/python-gnupg-0.4.6/PKG-INFO
--- old/python-gnupg-0.4.5/PKG-INFO     2019-08-12 18:50:07.000000000 +0200
+++ new/python-gnupg-0.4.6/PKG-INFO     2020-04-17 13:30:43.000000000 +0200
@@ -1,12 +1,14 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
 Name: python-gnupg
-Version: 0.4.5
+Version: 0.4.6
 Summary: A wrapper for the Gnu Privacy Guard (GPG or GnuPG)
 Home-page: https://docs.red-dove.com/python-gnupg/
 Author: Vinay Sajip
 Author-email: vinay_sa...@red-dove.com
+Maintainer: Vinay Sajip
+Maintainer-email: vinay_sa...@red-dove.com
 License: Copyright (C) 2008-2019 by Vinay Sajip. All Rights Reserved. See 
LICENSE.txt for license.
-Download-URL: 
https://pypi.io/packages/source/p/python-gnupg/python-gnupg-0.4.5.tar.gz
+Download-URL: 
https://pypi.io/packages/source/p/python-gnupg/python-gnupg-0.4.6.tar.gz
 Description: This module allows easy access to GnuPG's key management, 
encryption and signature functionality from Python programs. It is intended for 
use with Python 2.4 or greater.
 Platform: No particular restrictions
 Classifier: Development Status :: 5 - Production/Stable
@@ -15,15 +17,9 @@
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 2.4
-Classifier: Programming Language :: Python :: 2.5
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3.2
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
 Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
 Classifier: Operating System :: OS Independent
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.4.5/README.rst 
new/python-gnupg-0.4.6/README.rst
--- old/python-gnupg-0.4.5/README.rst   2019-08-12 18:47:20.000000000 +0200
+++ new/python-gnupg-0.4.6/README.rst   2020-04-17 10:33:32.000000000 +0200
@@ -64,12 +64,33 @@
 .. note:: GCnn refers to an issue nn on Google Code.
 
 
-0.4.6 (future)
+0.4.7 (future)
 --------------
 
 Released: Not yet.
 
 
+0.4.6
+-----
+
+Released: 2020-04-17
+
+* Fixed #122: Updated documentation about gnupghome needing to be an existing
+  directory.
+
+* Fixed #123: Handled error conditions from gpg when calling trust_keys().
+
+* Fixed #124: Avoided an exception being raised when ImportResult.summary()
+  was called after a failed recv_keys().
+
+* Fixed #128: Added ECC support by changing key generation parameters. (The 
Key-Length
+  value isn't added if a curve is specified.)
+
+* Fixed #130: Provided a mechanism to provide more complete error messages.
+
+Support for Python versions 3.5 and under is discontinued, except for Python 
2.7.
+
+
 0.4.5
 -----
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.4.5/gnupg.py 
new/python-gnupg-0.4.6/gnupg.py
--- old/python-gnupg-0.4.5/gnupg.py     2019-08-12 18:47:20.000000000 +0200
+++ new/python-gnupg-0.4.6/gnupg.py     2020-04-17 10:35:35.000000000 +0200
@@ -32,9 +32,9 @@
 A unittest harness (test_gnupg.py) has also been added.
 """
 
-__version__ = "0.4.5"
+__version__ = "0.4.6"
 __author__ = "Vinay Sajip"
-__date__  = "$12-Aug-2019 15:58:03$"
+__date__  = "$17-Apr-2020 09:35:35$"
 
 try:
     from io import StringIO
@@ -349,17 +349,20 @@
                 # write to "/etc/foo" as a non-root user, a "permission denied"
                 # error will be sent as a non-status message.
                 message = 'error - %s' % value
-                parts = value.split()
-                if parts[-1].isdigit():
-                    code = int(parts[-1])
-                    system_error = bool(code & 0x8000)
-                    code = code & 0x7FFF
-                    if system_error:
-                        mapping = self.GPG_SYSTEM_ERROR_CODES
+                operation, code = value.rsplit(' ', 1)
+                if code.isdigit():
+                    code = int(code) & 0xFFFFFF  # lose the error source
+                    if self.gpg.error_map and code in self.gpg.error_map:
+                        message = '%s: %s' % (operation, 
self.gpg.error_map[code])
                     else:
-                        mapping = self.GPG_ERROR_CODES
-                    if code in mapping:
-                        message = mapping[code]
+                        system_error = bool(code & 0x8000)
+                        code = code & 0x7FFF
+                        if system_error:
+                            mapping = self.GPG_SYSTEM_ERROR_CODES
+                        else:
+                            mapping = self.GPG_ERROR_CODES
+                        if code in mapping:
+                            message = '%s: %s' % (operation, mapping[code])
                 if not self.status:
                     self.status = message
         elif key in ("DECRYPTION_INFO", "PLAINTEXT", "PLAINTEXT_LENGTH",
@@ -376,11 +379,10 @@
             sec_dups not_imported'''.split()
     def __init__(self, gpg):
         self.gpg = gpg
-        self.imported = []
         self.results = []
         self.fingerprints = []
         for result in self.counts:
-            setattr(self, result, None)
+            setattr(self, result, 0)
 
     def __nonzero__(self):
         if self.not_imported: return False
@@ -450,11 +452,11 @@
             logger.debug('message ignored: %s, %s', key, value)
 
     def summary(self):
-        l = []
-        l.append('%d imported' % self.imported)
+        result = []
+        result.append('%d imported' % self.imported)
         if self.not_imported:  # pragma: no cover
-            l.append('%d not imported' % self.not_imported)
-        return ', '.join(l)
+            result.append('%d not imported' % self.not_imported)
+        return ', '.join(result)
 
 ESCAPE_PATTERN = re.compile(r'\\x([0-9a-f][0-9a-f])', re.I)
 BASIC_ESCAPES = {
@@ -737,6 +739,10 @@
     __bool__ = __nonzero__
 
 
+class TrustResult(DeleteResult):
+    pass
+
+
 class Sign(TextHandler):
     "Handle status messages for --sign"
     def __init__(self, gpg):
@@ -779,6 +785,8 @@
 
 class GPG(object):
 
+    error_map = None
+
     decode_errors = 'strict'
 
     result_map = {
@@ -791,6 +799,7 @@
         'scan': ScanKeys,
         'search': SearchKeys,
         'sign': Sign,
+        'trust': TrustResult,
         'verify': Verify,
         'export': ExportResult,
     }
@@ -990,7 +999,8 @@
         rr.start()
 
         stdout = process.stdout
-        dr = threading.Thread(target=self._read_data, args=(stdout, result, 
self.on_data))
+        dr = threading.Thread(target=self._read_data, args=(stdout, result,
+                              self.on_data))
         dr.setDaemon(True)
         logger.debug('stdout reader: %r', dr)
         dr.start()
@@ -1000,8 +1010,9 @@
         if writer is not None:
             writer.join()
         process.wait()
-        if process.returncode != 0:
-            logger.warning('gpg returned a non-zero error code: %d', 
process.returncode)
+        rc = process.returncode
+        if rc != 0:
+            logger.warning('gpg returned a non-zero error code: %d', rc)
         if stdin is not None:
             try:
                 stdin.close()
@@ -1456,7 +1467,8 @@
             if str(val).strip():    # skip empty strings
                 parms[key] = val
         parms.setdefault('Key-Type','RSA')
-        parms.setdefault('Key-Length',2048)
+        if 'key_curve' not in kwargs:
+            parms.setdefault('Key-Length',2048)
         parms.setdefault('Name-Real', "Autogenerated Key")
         logname = (os.environ.get('LOGNAME') or os.environ.get('USERNAME') or
                    'unspecified')
@@ -1623,9 +1635,12 @@
             logger.debug('writing ownertrust info: %s', s);
             os.write(fd, s.encode(self.encoding))
             os.close(fd)
-            result = self.result_map['delete'](self)
+            result = self.result_map['trust'](self)
             p = self._open_subprocess(['--import-ownertrust', fn])
             self._collect_output(p, result, stdin=p.stdin)
+            if p.returncode != 0:
+                raise ValueError('gpg returned an error - return code %d' %
+                                 p.returncode)
         finally:
             os.remove(fn)
         return result
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.4.5/messages.json 
new/python-gnupg-0.4.6/messages.json
--- old/python-gnupg-0.4.5/messages.json        1970-01-01 01:00:00.000000000 
+0100
+++ new/python-gnupg-0.4.6/messages.json        2020-04-17 08:02:24.000000000 
+0200
@@ -0,0 +1,565 @@
+{
+  "0000": "Success",
+  "0001": "General error",
+  "0002": "Unknown packet",
+  "0003": "Unknown version in packet",
+  "0004": "Invalid public key algorithm",
+  "0005": "Invalid digest algorithm",
+  "0006": "Bad public key",
+  "0007": "Bad secret key",
+  "0008": "Bad signature",
+  "0009": "No public key",
+  "000A": "Checksum error",
+  "000B": "Bad passphrase",
+  "000C": "Invalid cipher algorithm",
+  "000D": "Cannot open keyring",
+  "000E": "Invalid packet",
+  "000F": "Invalid armor",
+  "0010": "No user ID",
+  "0011": "No secret key",
+  "0012": "Wrong secret key used",
+  "0013": "Bad session key",
+  "0014": "Unknown compression algorithm",
+  "0015": "Number is not prime",
+  "0016": "Invalid encoding method",
+  "0017": "Invalid encryption scheme",
+  "0018": "Invalid signature scheme",
+  "0019": "Invalid attribute",
+  "001A": "No value",
+  "001B": "Not found",
+  "001C": "Value not found",
+  "001D": "Syntax error",
+  "001E": "Bad MPI value",
+  "001F": "Invalid passphrase",
+  "0020": "Invalid signature class",
+  "0021": "Resources exhausted",
+  "0022": "Invalid keyring",
+  "0023": "Trust DB error",
+  "0024": "Bad certificate",
+  "0025": "Invalid user ID",
+  "0026": "Unexpected error",
+  "0027": "Time conflict",
+  "0028": "Keyserver error",
+  "0029": "Wrong public key algorithm",
+  "002A": "Tribute to D. A.",
+  "002B": "Weak encryption key",
+  "002C": "Invalid key length",
+  "002D": "Invalid argument",
+  "002E": "Syntax error in URI",
+  "002F": "Invalid URI",
+  "0030": "Network error",
+  "0031": "Unknown host",
+  "0032": "Selftest failed",
+  "0033": "Data not encrypted",
+  "0034": "Data not processed",
+  "0035": "Unusable public key",
+  "0036": "Unusable secret key",
+  "0037": "Invalid value",
+  "0038": "Bad certificate chain",
+  "0039": "Missing certificate",
+  "003A": "No data",
+  "003B": "Bug",
+  "003C": "Not supported",
+  "003D": "Invalid operation code",
+  "003E": "Timeout",
+  "003F": "Internal error",
+  "0040": "EOF (gcrypt)",
+  "0041": "Invalid object",
+  "0042": "Provided object is too short",
+  "0043": "Provided object is too large",
+  "0044": "Missing item in object",
+  "0045": "Not implemented",
+  "0046": "Conflicting use",
+  "0047": "Invalid cipher mode",
+  "0048": "Invalid flag",
+  "0049": "Invalid handle",
+  "004A": "Result truncated",
+  "004B": "Incomplete line",
+  "004C": "Invalid response",
+  "004D": "No agent running",
+  "004E": "Agent error",
+  "004F": "Invalid data",
+  "0050": "Unspecific Assuan server fault",
+  "0051": "General Assuan error",
+  "0052": "Invalid session key",
+  "0053": "Invalid S-expression",
+  "0054": "Unsupported algorithm",
+  "0055": "No pinentry",
+  "0056": "pinentry error",
+  "0057": "Bad PIN",
+  "0058": "Invalid name",
+  "0059": "Bad data",
+  "005A": "Invalid parameter",
+  "005B": "Wrong card",
+  "005C": "No dirmngr",
+  "005D": "dirmngr error",
+  "005E": "Certificate revoked",
+  "005F": "No CRL known",
+  "0060": "CRL too old",
+  "0061": "Line too long",
+  "0062": "Not trusted",
+  "0063": "Operation cancelled",
+  "0064": "Bad CA certificate",
+  "0065": "Certificate expired",
+  "0066": "Certificate too young",
+  "0067": "Unsupported certificate",
+  "0068": "Unknown S-expression",
+  "0069": "Unsupported protection",
+  "006A": "Corrupted protection",
+  "006B": "Ambiguous name",
+  "006C": "Card error",
+  "006D": "Card reset required",
+  "006E": "Card removed",
+  "006F": "Invalid card",
+  "0070": "Card not present",
+  "0071": "No PKCS15 application",
+  "0072": "Not confirmed",
+  "0073": "Configuration error",
+  "0074": "No policy match",
+  "0075": "Invalid index",
+  "0076": "Invalid ID",
+  "0077": "No SmartCard daemon",
+  "0078": "SmartCard daemon error",
+  "0079": "Unsupported protocol",
+  "007A": "Bad PIN method",
+  "007B": "Card not initialized",
+  "007C": "Unsupported operation",
+  "007D": "Wrong key usage",
+  "007E": "Nothing found",
+  "007F": "Wrong blob type",
+  "0080": "Missing value",
+  "0081": "Hardware problem",
+  "0082": "PIN blocked",
+  "0083": "Conditions of use not satisfied",
+  "0084": "PINs are not synced",
+  "0085": "Invalid CRL",
+  "0086": "BER error",
+  "0087": "Invalid BER",
+  "0088": "Element not found",
+  "0089": "Identifier not found",
+  "008A": "Invalid tag",
+  "008B": "Invalid length",
+  "008C": "Invalid key info",
+  "008D": "Unexpected tag",
+  "008E": "Not DER encoded",
+  "008F": "No CMS object",
+  "0090": "Invalid CMS object",
+  "0091": "Unknown CMS object",
+  "0092": "Unsupported CMS object",
+  "0093": "Unsupported encoding",
+  "0094": "Unsupported CMS version",
+  "0095": "Unknown algorithm",
+  "0096": "Invalid crypto engine",
+  "0097": "Public key not trusted",
+  "0098": "Decryption failed",
+  "0099": "Key expired",
+  "009A": "Signature expired",
+  "009B": "Encoding problem",
+  "009C": "Invalid state",
+  "009D": "Duplicated value",
+  "009E": "Missing action",
+  "009F": "ASN.1 module not found",
+  "00A0": "Invalid OID string",
+  "00A1": "Invalid time",
+  "00A2": "Invalid CRL object",
+  "00A3": "Unsupported CRL version",
+  "00A4": "Invalid certificate object",
+  "00A5": "Unknown name",
+  "00A6": "A locale function failed",
+  "00A7": "Not locked",
+  "00A8": "Protocol violation",
+  "00A9": "Invalid MAC",
+  "00AA": "Invalid request",
+  "00AB": "Unknown extension",
+  "00AC": "Unknown critical extension",
+  "00AD": "Locked",
+  "00AE": "Unknown option",
+  "00AF": "Unknown command",
+  "00B0": "Not operational",
+  "00B1": "No passphrase given",
+  "00B2": "No PIN given",
+  "00B3": "Not enabled",
+  "00B4": "No crypto engine",
+  "00B5": "Missing key",
+  "00B6": "Too many objects",
+  "00B7": "Limit reached",
+  "00B8": "Not initialized",
+  "00B9": "Missing issuer certificate",
+  "00BA": "No keyserver available",
+  "00BB": "Invalid elliptic curve",
+  "00BC": "Unknown elliptic curve",
+  "00BD": "Duplicated key",
+  "00BE": "Ambiguous result",
+  "00BF": "No crypto context",
+  "00C0": "Wrong crypto context",
+  "00C1": "Bad crypto context",
+  "00C2": "Conflict in the crypto context",
+  "00C3": "Broken public key",
+  "00C4": "Broken secret key",
+  "00C5": "Invalid MAC algorithm",
+  "00C6": "Operation fully cancelled",
+  "00C7": "Operation not yet finished",
+  "00C8": "Buffer too short",
+  "00C9": "Invalid length specifier in S-expression",
+  "00CA": "String too long in S-expression",
+  "00CB": "Unmatched parentheses in S-expression",
+  "00CC": "S-expression not canonical",
+  "00CD": "Bad character in S-expression",
+  "00CE": "Bad quotation in S-expression",
+  "00CF": "Zero prefix in S-expression",
+  "00D0": "Nested display hints in S-expression",
+  "00D1": "Unmatched display hints",
+  "00D2": "Unexpected reserved punctuation in S-expression",
+  "00D3": "Bad hexadecimal character in S-expression",
+  "00D4": "Odd hexadecimal numbers in S-expression",
+  "00D5": "Bad octal character in S-expression",
+  "00D9": "All subkeys are expired or revoked",
+  "00DA": "Database is corrupted",
+  "00DB": "Server indicated a failure",
+  "00DC": "No name",
+  "00DD": "No key",
+  "00DE": "Legacy key",
+  "00DF": "Request too short",
+  "00E0": "Request too long",
+  "00E1": "Object is in termination state",
+  "00E2": "No certificate chain",
+  "00E3": "Certificate is too large",
+  "00E4": "Invalid record",
+  "00E5": "The MAC does not verify",
+  "00E6": "Unexpected message",
+  "00E7": "Compression or decompression failed",
+  "00E8": "A counter would wrap",
+  "00E9": "Fatal alert message received",
+  "00EA": "No cipher algorithm",
+  "00EB": "Missing client certificate",
+  "00EC": "Close notification received",
+  "00ED": "Ticket expired",
+  "00EE": "Bad ticket",
+  "00EF": "Unknown identity",
+  "00F0": "Bad certificate message in handshake",
+  "00F1": "Bad certificate request message in handshake",
+  "00F2": "Bad certificate verify message in handshake",
+  "00F3": "Bad change cipher message in handshake",
+  "00F4": "Bad client hello message in handshake",
+  "00F5": "Bad server hello message in handshake",
+  "00F6": "Bad server hello done message in handshake",
+  "00F7": "Bad finished message in handshake",
+  "00F8": "Bad server key exchange message in handshake",
+  "00F9": "Bad client key exchange message in handshake",
+  "00FA": "Bogus string",
+  "00FB": "Forbidden",
+  "00FC": "Key disabled",
+  "00FD": "Not possible with a card based key",
+  "00FE": "Invalid lock object",
+  "00FF": "True",
+  "0100": "False",
+  "0101": "General IPC error",
+  "0102": "IPC accept call failed",
+  "0103": "IPC connect call failed",
+  "0104": "Invalid IPC response",
+  "0105": "Invalid value passed to IPC",
+  "0106": "Incomplete line passed to IPC",
+  "0107": "Line passed to IPC too long",
+  "0108": "Nested IPC commands",
+  "0109": "No data callback in IPC",
+  "010A": "No inquire callback in IPC",
+  "010B": "Not an IPC server",
+  "010C": "Not an IPC client",
+  "010D": "Problem starting IPC server",
+  "010E": "IPC read error",
+  "010F": "IPC write error",
+  "0111": "Too much data for IPC layer",
+  "0112": "Unexpected IPC command",
+  "0113": "Unknown IPC command",
+  "0114": "IPC syntax error",
+  "0115": "IPC call has been cancelled",
+  "0116": "No input source for IPC",
+  "0117": "No output source for IPC",
+  "0118": "IPC parameter error",
+  "0119": "Unknown IPC inquire",
+  "012C": "Crypto engine too old",
+  "012D": "Screen or window too small",
+  "012E": "Screen or window too large",
+  "012F": "Required environment variable not set",
+  "0130": "User ID already exists",
+  "0131": "Name already exists",
+  "0132": "Duplicated name",
+  "0133": "Object is too young",
+  "0134": "Object is too old",
+  "0135": "Unknown flag",
+  "0136": "Invalid execution order",
+  "0137": "Already fetched",
+  "0138": "Try again later",
+  "0139": "Wrong name",
+  "013A": "Not authenticated",
+  "013B": "Bad authentication",
+  "013C": "No Keybox daemon running",
+  "013D": "Keybox daemon error",
+  "013E": "Service is not running",
+  "013F": "Service error",
+  "029A": "System bug detected",
+  "02C7": "Unknown DNS error",
+  "02C8": "Invalid DNS section",
+  "02C9": "Invalid textual address form",
+  "02CA": "Missing DNS query packet",
+  "02CB": "Missing DNS answer packet",
+  "02CC": "Connection closed in DNS",
+  "02CD": "Verification failed in DNS",
+  "02CE": "DNS Timeout",
+  "02D1": "General LDAP error",
+  "02D2": "General LDAP attribute error",
+  "02D3": "General LDAP name error",
+  "02D4": "General LDAP security error",
+  "02D5": "General LDAP service error",
+  "02D6": "General LDAP update error",
+  "02D7": "Experimental LDAP error code",
+  "02D8": "Private LDAP error code",
+  "02D9": "Other general LDAP error",
+  "02EE": "LDAP connecting failed (X)",
+  "02EF": "LDAP referral limit exceeded",
+  "02F0": "LDAP client loop",
+  "02F2": "No LDAP results returned",
+  "02F3": "LDAP control not found",
+  "02F4": "Not supported by LDAP",
+  "02F5": "LDAP connect error",
+  "02F6": "Out of memory in LDAP",
+  "02F7": "Bad parameter to an LDAP routine",
+  "02F8": "User cancelled LDAP operation",
+  "02F9": "Bad LDAP search filter",
+  "02FA": "Unknown LDAP authentication method",
+  "02FB": "Timeout in LDAP",
+  "02FC": "LDAP decoding error",
+  "02FD": "LDAP encoding error",
+  "02FE": "LDAP local error",
+  "02FF": "Cannot contact LDAP server",
+  "0300": "LDAP success",
+  "0301": "LDAP operations error",
+  "0302": "LDAP protocol error",
+  "0303": "Time limit exceeded in LDAP",
+  "0304": "Size limit exceeded in LDAP",
+  "0305": "LDAP compare false",
+  "0306": "LDAP compare true",
+  "0307": "LDAP authentication method not supported",
+  "0308": "Strong(er) LDAP authentication required",
+  "0309": "Partial LDAP results+referral received",
+  "030A": "LDAP referral",
+  "030B": "Administrative LDAP limit exceeded",
+  "030C": "Critical LDAP extension is unavailable",
+  "030D": "Confidentiality required by LDAP",
+  "030E": "LDAP SASL bind in progress",
+  "0310": "No such LDAP attribute",
+  "0311": "Undefined LDAP attribute type",
+  "0312": "Inappropriate matching in LDAP",
+  "0313": "Constraint violation in LDAP",
+  "0314": "LDAP type or value exists",
+  "0315": "Invalid syntax in LDAP",
+  "0320": "No such LDAP object",
+  "0321": "LDAP alias problem",
+  "0322": "Invalid DN syntax in LDAP",
+  "0323": "LDAP entry is a leaf",
+  "0324": "LDAP alias dereferencing problem",
+  "032F": "LDAP proxy authorization failure (X)",
+  "0330": "Inappropriate LDAP authentication",
+  "0331": "Invalid LDAP credentials",
+  "0332": "Insufficient access for LDAP",
+  "0333": "LDAP server is busy",
+  "0334": "LDAP server is unavailable",
+  "0335": "LDAP server is unwilling to perform",
+  "0336": "Loop detected by LDAP",
+  "0340": "LDAP naming violation",
+  "0341": "LDAP object class violation",
+  "0342": "LDAP operation not allowed on non-leaf",
+  "0343": "LDAP operation not allowed on RDN",
+  "0344": "Already exists (LDAP)",
+  "0345": "Cannot modify LDAP object class",
+  "0346": "LDAP results too large",
+  "0347": "LDAP operation affects multiple DSAs",
+  "034C": "Virtual LDAP list view error",
+  "0350": "Other LDAP error",
+  "0371": "Resources exhausted in LCUP",
+  "0372": "Security violation in LCUP",
+  "0373": "Invalid data in LCUP",
+  "0374": "Unsupported scheme in LCUP",
+  "0375": "Reload required in LCUP",
+  "0376": "LDAP cancelled",
+  "0377": "No LDAP operation to cancel",
+  "0378": "Too late to cancel LDAP",
+  "0379": "Cannot cancel LDAP",
+  "037A": "LDAP assertion failed",
+  "037B": "Proxied authorization denied by LDAP",
+  "0400": "User defined error code 1",
+  "0401": "User defined error code 2",
+  "0402": "User defined error code 3",
+  "0403": "User defined error code 4",
+  "0404": "User defined error code 5",
+  "0405": "User defined error code 6",
+  "0406": "User defined error code 7",
+  "0407": "User defined error code 8",
+  "0408": "User defined error code 9",
+  "0409": "User defined error code 10",
+  "040A": "User defined error code 11",
+  "040B": "User defined error code 12",
+  "040C": "User defined error code 13",
+  "040D": "User defined error code 14",
+  "040E": "User defined error code 15",
+  "040F": "User defined error code 16",
+  "05DC": "SQL success",
+  "05DD": "SQL error",
+  "05DE": "Internal logic error in SQL library",
+  "05DF": "Access permission denied (SQL)",
+  "05E0": "SQL abort was requested",
+  "05E1": "SQL database file is locked",
+  "05E2": "An SQL table in the database is locked",
+  "05E3": "SQL library ran out of core",
+  "05E4": "Attempt to write a readonly SQL database",
+  "05E5": "SQL operation terminated by interrupt",
+  "05E6": "I/O error during SQL operation",
+  "05E7": "SQL database disk image is malformed",
+  "05E8": "Unknown opcode in SQL file control",
+  "05E9": "Insertion failed because SQL database is full",
+  "05EA": "Unable to open the SQL database file",
+  "05EB": "SQL database lock protocol error",
+  "05EC": "(internal SQL code: empty)",
+  "05ED": "SQL database schema changed",
+  "05EE": "String or blob exceeds size limit (SQL)",
+  "05EF": "SQL abort due to constraint violation",
+  "05F0": "Data type mismatch (SQL)",
+  "05F1": "SQL library used incorrectly",
+  "05F2": "SQL library uses unsupported OS features",
+  "05F3": "Authorization denied (SQL)",
+  "05F4": "(unused SQL code: format)",
+  "05F5": "SQL bind parameter out of range",
+  "05F6": "File opened that is not an SQL database file",
+  "05F7": "Notifications from SQL logger",
+  "05F8": "Warnings from SQL logger",
+  "0640": "SQL has another row ready",
+  "0641": "SQL has finished executing",
+  "3FFD": "System error w/o errno",
+  "3FFF": "End of file",
+  "8000": "Argument list too long",
+  "8001": "Permission denied",
+  "8002": "Address already in use",
+  "8003": "Cannot assign requested address",
+  "8004": "Advertise error",
+  "8005": "Address family not supported by protocol",
+  "8006": "Resource temporarily unavailable",
+  "8007": "Operation already in progress",
+  "800A": "Invalid exchange",
+  "800B": "Bad file descriptor",
+  "800C": "File descriptor in bad state",
+  "800D": "Bad message",
+  "800E": "Invalid request descriptor",
+  "8010": "Invalid request code",
+  "8011": "Invalid slot",
+  "8012": "Bad font file format",
+  "8013": "Device or resource busy",
+  "8014": "Operation canceled",
+  "8015": "No child processes",
+  "8016": "Channel number out of range",
+  "8017": "Communication error on send",
+  "8018": "Software caused connection abort",
+  "8019": "Connection refused",
+  "801A": "Connection reset by peer",
+  "801C": "Resource deadlock avoided",
+  "801D": "Resource deadlock avoided",
+  "801E": "Destination address required",
+  "8020": "Numerical argument out of domain",
+  "8021": "RFS specific error",
+  "8022": "Disk quota exceeded",
+  "8023": "File exists",
+  "8024": "Bad address",
+  "8025": "File too large",
+  "8029": "Host is down",
+  "802A": "No route to host",
+  "802B": "Identifier removed",
+  "802D": "Invalid or incomplete multibyte or wide character",
+  "802E": "Operation now in progress",
+  "802F": "Interrupted system call",
+  "8030": "Invalid argument",
+  "8031": "Input/output error",
+  "8032": "Transport endpoint is already connected",
+  "8033": "Is a directory",
+  "8034": "Is a named type file",
+  "8035": "Level 2 halted",
+  "8036": "Level 2 not synchronized",
+  "8037": "Level 3 halted",
+  "8038": "Level 3 reset",
+  "8039": "Can not access a needed shared library",
+  "803A": "Accessing a corrupted shared library",
+  "803B": "Cannot exec a shared library directly",
+  "803C": "Attempting to link in too many shared libraries",
+  "803D": ".lib section in a.out corrupted",
+  "803E": "Link number out of range",
+  "803F": "Too many levels of symbolic links",
+  "8040": "Wrong medium type",
+  "8041": "Too many open files",
+  "8042": "Too many links",
+  "8043": "Message too long",
+  "8044": "Multihop attempted",
+  "8045": "File name too long",
+  "8046": "No XENIX semaphores available",
+  "8048": "Network is down",
+  "8049": "Network dropped connection on reset",
+  "804A": "Network is unreachable",
+  "804B": "Too many open files in system",
+  "804C": "No anode",
+  "804D": "No buffer space available",
+  "804E": "No CSI structure available",
+  "804F": "No data available",
+  "8050": "No such device",
+  "8051": "No such file or directory",
+  "8052": "Exec format error",
+  "8053": "No locks available",
+  "8054": "Link has been severed",
+  "8055": "No medium found",
+  "8056": "Cannot allocate memory",
+  "8057": "No message of desired type",
+  "8058": "Machine is not on the network",
+  "8059": "Package not installed",
+  "805A": "Protocol not available",
+  "805B": "No space left on device",
+  "805C": "Out of streams resources",
+  "805D": "Device not a stream",
+  "805E": "Function not implemented",
+  "805F": "Block device required",
+  "8060": "Transport endpoint is not connected",
+  "8061": "Not a directory",
+  "8062": "Directory not empty",
+  "8063": "Not a XENIX named type file",
+  "8064": "Socket operation on non-socket",
+  "8065": "Operation not supported",
+  "8066": "Inappropriate ioctl for device",
+  "8067": "Name not unique on network",
+  "8068": "No such device or address",
+  "8069": "Operation not supported",
+  "806A": "Value too large for defined data type",
+  "806B": "Operation not permitted",
+  "806C": "Protocol family not supported",
+  "806D": "Broken pipe",
+  "8072": "Protocol error",
+  "8073": "Protocol not supported",
+  "8074": "Protocol wrong type for socket",
+  "8075": "Numerical result out of range",
+  "8076": "Remote address changed",
+  "8077": "Object is remote",
+  "8078": "Remote I/O error",
+  "8079": "Interrupted system call should be restarted",
+  "807A": "Read-only file system",
+  "807C": "Cannot send after transport endpoint shutdown",
+  "807D": "Socket type not supported",
+  "807E": "Illegal seek",
+  "807F": "No such process",
+  "8080": "Srmount error",
+  "8081": "Stale file handle",
+  "8082": "Streams pipe error",
+  "8083": "Timer expired",
+  "8084": "Connection timed out",
+  "8085": "Too many references: cannot splice",
+  "8086": "Text file busy",
+  "8087": "Structure needs cleaning",
+  "8088": "Protocol driver not attached",
+  "8089": "Too many users",
+  "808A": "Resource temporarily unavailable",
+  "808B": "Invalid cross-device link",
+  "808C": "Exchange full"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.4.5/python_gnupg.egg-info/PKG-INFO 
new/python-gnupg-0.4.6/python_gnupg.egg-info/PKG-INFO
--- old/python-gnupg-0.4.5/python_gnupg.egg-info/PKG-INFO       1970-01-01 
01:00:00.000000000 +0100
+++ new/python-gnupg-0.4.6/python_gnupg.egg-info/PKG-INFO       2020-04-17 
13:30:43.000000000 +0200
@@ -0,0 +1,25 @@
+Metadata-Version: 1.2
+Name: python-gnupg
+Version: 0.4.6
+Summary: A wrapper for the Gnu Privacy Guard (GPG or GnuPG)
+Home-page: https://docs.red-dove.com/python-gnupg/
+Author: Vinay Sajip
+Author-email: vinay_sa...@red-dove.com
+Maintainer: Vinay Sajip
+Maintainer-email: vinay_sa...@red-dove.com
+License: Copyright (C) 2008-2019 by Vinay Sajip. All Rights Reserved. See 
LICENSE.txt for license.
+Download-URL: 
https://pypi.io/packages/source/p/python-gnupg/python-gnupg-0.4.6.tar.gz
+Description: This module allows easy access to GnuPG's key management, 
encryption and signature functionality from Python programs. It is intended for 
use with Python 2.4 or greater.
+Platform: No particular restrictions
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Operating System :: OS Independent
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.4.5/python_gnupg.egg-info/SOURCES.txt 
new/python-gnupg-0.4.6/python_gnupg.egg-info/SOURCES.txt
--- old/python-gnupg-0.4.5/python_gnupg.egg-info/SOURCES.txt    1970-01-01 
01:00:00.000000000 +0100
+++ new/python-gnupg-0.4.6/python_gnupg.egg-info/SOURCES.txt    2020-04-17 
13:30:43.000000000 +0200
@@ -0,0 +1,14 @@
+LICENSE.txt
+MANIFEST.in
+README.rst
+gnupg.py
+messages.json
+setup.cfg
+setup.py
+test_gnupg.py
+test_pubring.gpg
+test_secring.gpg
+python_gnupg.egg-info/PKG-INFO
+python_gnupg.egg-info/SOURCES.txt
+python_gnupg.egg-info/dependency_links.txt
+python_gnupg.egg-info/top_level.txt
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-gnupg-0.4.5/python_gnupg.egg-info/dependency_links.txt 
new/python-gnupg-0.4.6/python_gnupg.egg-info/dependency_links.txt
--- old/python-gnupg-0.4.5/python_gnupg.egg-info/dependency_links.txt   
1970-01-01 01:00:00.000000000 +0100
+++ new/python-gnupg-0.4.6/python_gnupg.egg-info/dependency_links.txt   
2020-04-17 13:30:43.000000000 +0200
@@ -0,0 +1 @@
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-gnupg-0.4.5/python_gnupg.egg-info/top_level.txt 
new/python-gnupg-0.4.6/python_gnupg.egg-info/top_level.txt
--- old/python-gnupg-0.4.5/python_gnupg.egg-info/top_level.txt  1970-01-01 
01:00:00.000000000 +0100
+++ new/python-gnupg-0.4.6/python_gnupg.egg-info/top_level.txt  2020-04-17 
13:30:43.000000000 +0200
@@ -0,0 +1 @@
+gnupg
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.4.5/setup.cfg 
new/python-gnupg-0.4.6/setup.cfg
--- old/python-gnupg-0.4.5/setup.cfg    1970-01-01 01:00:00.000000000 +0100
+++ new/python-gnupg-0.4.6/setup.cfg    2020-04-17 13:30:43.000000000 +0200
@@ -0,0 +1,7 @@
+[bdist_wheel]
+universal = 1
+
+[egg_info]
+tag_build = 
+tag_date = 0
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.4.5/setup.py 
new/python-gnupg-0.4.6/setup.py
--- old/python-gnupg-0.4.5/setup.py     2019-07-16 09:21:35.000000000 +0200
+++ new/python-gnupg-0.4.6/setup.py     2020-04-17 13:29:40.000000000 +0200
@@ -1,4 +1,4 @@
-from distutils.core import setup
+from setuptools import setup
 
 from gnupg import __version__ as version
 
@@ -24,16 +24,10 @@
         "Programming Language :: Python",
         "Programming Language :: Python :: 2",
         "Programming Language :: Python :: 3",
-        "Programming Language :: Python :: 2.4",
-        "Programming Language :: Python :: 2.5",
-        "Programming Language :: Python :: 2.6",
         "Programming Language :: Python :: 2.7",
-        "Programming Language :: Python :: 3.2",
-        "Programming Language :: Python :: 3.3",
-        "Programming Language :: Python :: 3.4",
-        "Programming Language :: Python :: 3.5",
         "Programming Language :: Python :: 3.6",
         "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8",
         "Operating System :: OS Independent",
         "Topic :: Software Development :: Libraries :: Python Modules"
     ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.4.5/test_gnupg.py 
new/python-gnupg-0.4.6/test_gnupg.py
--- old/python-gnupg-0.4.5/test_gnupg.py        2019-08-12 18:47:20.000000000 
+0200
+++ new/python-gnupg-0.4.6/test_gnupg.py        2020-04-17 10:36:09.000000000 
+0200
@@ -2,9 +2,11 @@
 """
 A test harness for gnupg.py.
 
-Copyright (C) 2008-2018 Vinay Sajip. All rights reserved.
+Copyright (C) 2008-2020 Vinay Sajip. All rights reserved.
 """
 import doctest
+import io
+import json
 import logging
 import os.path
 import os
@@ -28,7 +30,7 @@
 import gnupg
 
 __author__ = "Vinay Sajip"
-__date__  = "$12-Aug-2019 15:58:32$"
+__date__  = "$17-Apr-2020 09:36:09$"
 
 ALL_TESTS = True
 
@@ -136,6 +138,40 @@
 -----END PGP PUBLIC KEY BLOCK-----
 """
 
+SECRET_KEY = """
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+
+lQPGBFztd1UBCACiHhlEJIGfXNEiUX4GwamgdLOkJ3mbn5OyV4M/Ie3YvvHxveq/
+TFYbuV63iuDVhNXpDUNmGsTq4vFaMsseLl7eESw8UTa3XklHHjh56kw0AVkJA75A
+Xq/VshFobLNxYZdtlOVkKe1a3uJVKs+BqFjhavEjQyhkpWvBY51OzCSc2AN/aQZA
+F3AltZ8luIHZPs8zVbgH90WIpze+vzAd9FyXD0wV6gylGSifHj8zIhac80evQgD9
+50De7EPnSdgZSNwnlrhQtAIB5UnTETxXk34/W0Rq+BKn6SuchtaP7hXIHC0+B0C7
+zBzPYKMQ7vXc/hceNwSGtgovhaQPCcv1byFBABEBAAH+BwMCUNdAVY/RMdJg1q5n
+FQOyVZl2tvd3krExjGYvhabwijbPz+TrVkPhKqdkp4Hbf3oXV/bcbQhG2dld4Ooc
++xtEpTqYw08bNDuk4NEAvggasUkgssHZccDmHySGfA9U8C7B0Hj8xT4SifnuVNL+
+xp9iv1BS03s+UIEVZ2rGjDQy7/G/U6/ZpLqFg+C113VQs6yz0VMsnnAQOMgN0+gQ
+aZb6VNPR7nZ5+/hRlx0DgXu++lei9HTmHRz+ZvbbYjeU9nj10eANhO0lEvlgtyXa
+v4Y5ERwk86gbkSRGtN88qVK/+GXK60Q33EoGMlwPZrfFGx+N5QuPEnCjT1vvz7E3
+HhCpe4u5Idusgui+tDkxq8BEz6iTGMO1hcb75MDdIQBhJzeJ7OIxyBfqLReF4+Ut
+eNwy0wpN3xuEeYvP4ZIe7hj74WWIuKq2+lesPm4eWRPoaQ5MZXmEwbjr29e++V7D
+EkHgCYio6TVwrHA0LRSNfm8VVBV2cdsqFOLLutudHoC8BnjetEetmYaA99u0Pevz
+NscYwfaWLNW/d5FGyPUb+GQFYzmQWUfUzpg9hu7U79uA0kOwC+4nK6LEalILtoHn
+YO3PvvcCEnpWBlDhCR3n0zkNQCulvQKS/ww5q/MDNqvibKiMJHJ1xP89tEU3lnHl
+qgwHVmleqUR+yzdg5lo96Yey5yaDdhK5ZR1TFC4qK4Igcn2+WG109659bJUGpEre
+Vktu530JutX38ZoyKdHO0uPs/ft/hgBhNd6MKmh7eejo84Wn6/lxkfMydkfKm5QY
+dMHF3Ew+l7aACAs3l95V0YDNzA0FyOFkb/tqxyx8dP+O2NdZQZSvG+yxDav05bCq
+kwz+7H7sJnUj1JJtUgPTL9yVH+LyUhL8AU13UKVjBFJ4VL5+KDD9KwPkk6aN7zDW
+Qv0g8Cc7A8H0tB5BdXRvZ2VuZXJhdGVkIEtleSA8dXNlcjFAdGVzdD6JATgEEwEI
+ACIFAlztd1UCGy8GCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEGP32fXSIJgg
+IXgH/3o1rUzbjjz1sMoBwRv4qLmgeqlB2YJSVzLWOn4AcrHbxup5O9nJkqG+YFwH
+OFmytuiPDKmA4ZXww8f+2rHXdDuwI5SWnfhuPpV863BulIhtjwiwqD9eIzQ9LX79
+K7hXRJ4I0AkYEbDHOWlLHZCrjul/ZaS10QRVR21EYICha2I8tvxsRMPp0I93XnuB
+T+z7ykRxRjpMv6MfhWVcw5B0s7lPedLhcx657HfY49t36/CIZ9/zMKsduX7cTOAh
+tO8f06R3yfjxLRD8y89frVP3+tGMvt2yGOd5TT0zht5yYcG6QkiHlfdgXqeE8nsU
+2392Xn/RETq6xCj3kG6K3wbWqh0=
+=2A5s
+-----END PGP PRIVATE KEY BLOCK-----
+"""
 
 def is_list_with_len(o, n):
     return isinstance(o, list) and len(o) == n
@@ -157,19 +193,30 @@
     # ignoring things like spurious blank lines
     return get_key_data(k1) != get_key_data(k2)
 
-AGENT_CONFIG = b'''allow-loopback-pinentry
+AGENT_CONFIG = '''allow-loopback-pinentry
 log-file socket:///tmp/S.my-gnupg-log
 verbose
 debug ipc
 '''
 
+ENABLE_TOFU = 'ENABLE_TOFU' in os.environ
+
+if ENABLE_TOFU:
+    GPG_CONFIG = 'trust-model tofu+pgp\ntofu-default-policy unknown\n'
+
+
 def prepare_homedir(hd):
     if not os.path.isdir(hd):
         os.makedirs(hd)
     os.chmod(hd, 0x1C0)
     fn = os.path.join(hd, 'gpg-agent.conf')
-    with open(fn, 'wb') as f:
+    with open(fn, 'w') as f:
         f.write(AGENT_CONFIG)
+    if ENABLE_TOFU:
+        fn = os.path.join(hd, 'gpg.conf')
+        with open(fn, 'w') as f:
+            f.write(GPG_CONFIG)
+
 
 class GPGTestCase(unittest.TestCase):
     def setUp(self):
@@ -305,8 +352,8 @@
         self.assertEqual(uid, 'Test Name (Funny chars: '
                               '\r\n\x0c\x0b\x00\x08) <test.n...@example.com>')
 
-    def test_key_generation_with_empty_value(self):
-        "Test that key generation handles empty values"
+    def test_key_generation_input(self):
+        "Test that key generation input handles empty values, curves etc."
         params = {
             'key_type': ' ',
             'key_length': 2048,
@@ -316,6 +363,18 @@
         params['key_type'] = 'DSA'
         cmd = self.gpg.gen_key_input(**params)
         self.assertTrue('Key-Type: DSA\n' in cmd)
+        params = {
+            'key_type': 'ECDSA',
+            'key_curve': 'nistp384',
+            'subkey_type': 'ECDH',
+            'subkey_curve': 'nistp384',
+            'name_comment': 'NIST P-384',
+        }
+        cmd = self.gpg.gen_key_input(**params)
+        for s in ('Key-Type: ECDSA', 'Key-Curve: nistp384', 'Subkey-Type: 
ECDH',
+                  'Subkey-Curve: nistp384', 'Name-Comment: NIST P-384'):
+            self.assertTrue('%s\n' %s in cmd)
+        self.assertFalse('Key-Length: ' in cmd)
 
     def test_list_keys_after_generation(self):
         "Test that after key generation, the generated key is available"
@@ -448,6 +507,11 @@
             for key in keys:
                 self.assertEqual(key['ownertrust'], expected)
         self.assertRaises(ValueError, gpg.trust_keys, fingerprints, 
'TRUST_FOOBAR')
+        self.assertRaises(ValueError, gpg.trust_keys, 'NO_SUCH_FINGERPRINT', 
'TRUST_NEVER')
+        # gpg should raise an error for the following - but it doesn't!
+        # self.assertRaises(ValueError, gpg.trust_keys,
+                         # 'BADF00DBADF00DBADF00DBADF00DBADF00DBADF0',
+                         # 'TRUST_NEVER')
 
     def test_list_signatures(self):
         logger.debug("test_list_signatures begins")
@@ -599,6 +663,19 @@
         binary = gpg.export_keys(key.fingerprint, True, armor=False,
                                  passphrase=passphrase)
         self.assertFalse(isinstance(binary, gnupg.text_type))
+        # import a secret key, and confirm that it's found in the list of
+        # secret keys.
+        result = gpg.import_keys(SECRET_KEY)
+        self.assertEqual(result.summary(), '1 imported')
+        private_keys = gpg.list_keys(secret=True)
+        self.assertTrue(is_list_with_len(private_keys, 2))
+        found = False
+        for pk in private_keys:
+            if pk['keyid'].endswith('D2209820'):
+                found = True
+                break
+        self.assertTrue(found)
+        self.assertEqual(pk['uids'][0], 'Autogenerated Key <user1@test>')
         logger.debug("test_import_and_export ends")
 
     def test_import_only(self):
@@ -846,8 +923,8 @@
         encfno, encfname = tempfile.mkstemp()
         os.close(encfno)
         cases = (
-            ('/dev/null/foo', 'not a directory'),
-            ('/etc/foo', 'permission denied'),
+            ('/dev/null/foo', 'encrypt: not a directory'),
+            ('/etc/foo', 'encrypt: permission denied'),
         )
         key = self.generate_key("Barbara", "Brown", "beta.com")
         barbara = key.fingerprint
@@ -861,6 +938,31 @@
             if edata.status:
                 self.assertEqual(edata.status, message)
 
+        # now try with custom error map, if available
+        if os.path.exists('messages.json'):
+            with open('messages.json') as f:
+                mdata = json.load(f)
+            messages = {}
+            for k, v in mdata.items():
+                messages[int(k, 16)] = v
+
+            self.gpg.error_map = messages
+
+            cases = (
+                ('/dev/null/foo', 'encrypt: Not a directory'),
+                ('/etc/foo', 'encrypt: Permission denied'),
+            )
+
+            for badout, message in cases:
+                stream = gnupg._make_binary_stream(data, self.gpg.encoding)
+                edata = self.gpg.encrypt_file(stream,
+                                              barbara, armor=False, 
output=badout)
+                # on GnuPG 1.4, you sometimes don't get any FAILURE messages, 
in
+                # which case status will not be set
+                if edata.status:
+                    self.assertEqual(edata.status, message)
+
+
     def test_filenames_with_spaces(self):       # See Issue #16
         "Test that filenames with spaces are correctly handled"
         logger.debug("test_filename_with_spaces begins")
@@ -1011,6 +1113,10 @@
             self.assertFalse(result)
         logger.debug("test_doctest_import_keys ends")
 
+    def test_recv_keys_no_server(self):
+        result = self.gpg.recv_keys('foo.bar.baz', '92905378')
+        self.assertEqual(result.summary(), '0 imported')
+
 
 TEST_GROUPS = {
     'sign' : set(['test_signature_verification',
@@ -1023,14 +1129,14 @@
                  'test_list_signatures',
                  'test_key_generation_with_invalid_key_type',
                  'test_key_generation_with_escapes',
-                 'test_key_generation_with_empty_value',
+                 'test_key_generation_input',
                  'test_key_generation_with_colons',
                  'test_search_keys', 'test_scan_keys', 'test_key_trust']),
     'import' : set(['test_import_only', 'test_doctest_import_keys']),
     'basic' : set(['test_environment', 'test_list_keys_initial',
                    'test_nogpg', 'test_make_args',
                    'test_quote_with_shell']),
-    'test': set(['test_encryption_and_decryption']),
+    'test': set(['test_invalid_outputs']),
 }
 
 def suite(args=None):


Reply via email to