Hello community,

here is the log from the commit of package python3-gnupg for openSUSE:Factory 
checked in at 2016-09-21 18:48:58
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python3-gnupg (Old)
 and      /work/SRC/openSUSE:Factory/.python3-gnupg.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python3-gnupg"

Changes:
--------
--- /work/SRC/openSUSE:Factory/python3-gnupg/python3-gnupg.changes      
2016-05-25 21:24:18.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.python3-gnupg.new/python3-gnupg.changes 
2016-09-21 18:49:00.000000000 +0200
@@ -1,0 +2,19 @@
+Sun Sep 18 15:52:25 UTC 2016 - a...@gmx.de
+
+- update to version 0.3.9:
+  * Fixed #38: You can now request information about signatures
+    against keys. Thanks to SunDwarf for the suggestion and patch,
+    which was used as a basis for this change.
+  * Fixed #49: When exporting keys, no attempt is made to decode the
+    output when armor=False is specified.
+  * Fixed #53: A FAILURE message caused by passing an incorrect
+    passphrase is handled.
+  * Handled EXPORTED and EXPORT_RES messages while exporting
+    keys. Thanks to Marcel Pörner for the patch.
+  * Fixed #54: Improved error message shown when gpg is not available.
+  * Fixed #55: Added support for KEY_CONSIDERED while verifying.
+  * Avoided encoding problems with filenames under Windows. Thanks to
+    Kévin Bernard-Allies for the patch.
+  * Fixed #57: Used a better mechanism for comparing keys.
+
+-------------------------------------------------------------------
@@ -6 +24,0 @@
-

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

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

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

Other differences:
------------------
++++++ python3-gnupg.spec ++++++
--- /var/tmp/diff_new_pack.hS2SdC/_old  2016-09-21 18:49:02.000000000 +0200
+++ /var/tmp/diff_new_pack.hS2SdC/_new  2016-09-21 18:49:02.000000000 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           python3-gnupg
-Version:        0.3.8
+Version:        0.3.9
 Release:        0
 Url:            https://bitbucket.org/vinay.sajip/python-gnupg
 Summary:        A wrapper for the Gnu Privacy Guard (GPG or GnuPG)

++++++ python-gnupg-0.3.8.tar.gz -> python-gnupg-0.3.9.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.3.8/PKG-INFO 
new/python-gnupg-0.3.9/PKG-INFO
--- old/python-gnupg-0.3.8/PKG-INFO     2015-09-24 23:03:50.000000000 +0200
+++ new/python-gnupg-0.3.9/PKG-INFO     2016-09-10 09:41:21.000000000 +0200
@@ -1,12 +1,12 @@
-Metadata-Version: 1.0
+Metadata-Version: 1.1
 Name: python-gnupg
-Version: 0.3.8
+Version: 0.3.9
 Summary: A wrapper for the Gnu Privacy Guard (GPG or GnuPG)
 Home-page: http://packages.python.org/python-gnupg/index.html
 Author: Vinay Sajip
 Author-email: vinay_sa...@red-dove.com
-License: Copyright (C) 2008-2014 by Vinay Sajip. All Rights Reserved. See 
LICENSE.txt for license.
-Download-URL: 
https://pypi.python.org/packages/source/p/python-gnupg/python-gnupg-0.3.8.tar.gz
+License: Copyright (C) 2008-2016 by Vinay Sajip. All Rights Reserved. See 
LICENSE.txt for license.
+Download-URL: 
https://pypi.python.org/packages/source/p/python-gnupg/python-gnupg-0.3.9.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
@@ -23,6 +23,5 @@
 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: 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.3.8/README.rst 
new/python-gnupg-0.3.9/README.rst
--- old/python-gnupg-0.3.8/README.rst   2015-09-24 19:07:37.000000000 +0200
+++ new/python-gnupg-0.3.9/README.rst   2016-09-10 09:37:53.000000000 +0200
@@ -54,11 +54,39 @@
 
 N.B: GCnn refers to an issue nn on Google Code.
 
-0.3.9 (future)
+0.4.0 (future)
 --------------
 
 Released: Not yet
 
+0.3.9
+-----
+
+Released: 2016-09-10
+
+* Fixed #38: You can now request information about signatures against
+  keys. Thanks to SunDwarf for the suggestion and patch, which was used
+  as a basis for this change.
+
+* Fixed #49: When exporting keys, no attempt is made to decode the output when
+  armor=False is specified.
+
+* Fixed #53: A ``FAILURE`` message caused by passing an incorrect passphrase
+  is handled.
+
+* Handled ``EXPORTED`` and ``EXPORT_RES`` messages while exporting keys. Thanks
+  to Marcel Pörner for the patch.
+
+* Fixed #54: Improved error message shown when gpg is not available.
+
+* Fixed #55: Added support for ``KEY_CONSIDERED`` while verifying.
+
+* Avoided encoding problems with filenames under Windows. Thanks to Kévin
+  Bernard-Allies for the patch.
+
+* Fixed #57: Used a better mechanism for comparing keys.
+
+
 0.3.8
 -----
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.3.8/gnupg.py 
new/python-gnupg-0.3.9/gnupg.py
--- old/python-gnupg-0.3.8/gnupg.py     2015-09-24 19:03:56.000000000 +0200
+++ new/python-gnupg-0.3.9/gnupg.py     2016-09-10 09:38:35.000000000 +0200
@@ -27,18 +27,18 @@
 and so does not work on Windows). Renamed to gnupg.py to avoid confusion with
 the previous versions.
 
-Modifications Copyright (C) 2008-2014 Vinay Sajip. All rights reserved.
+Modifications Copyright (C) 2008-2016 Vinay Sajip. All rights reserved.
 
 A unittest harness (test_gnupg.py) has also been added.
 """
 
-__version__ = "0.3.8"
+__version__ = "0.3.9"
 __author__ = "Vinay Sajip"
-__date__  = "$24-Sep-2015 18:03:55$"
+__date__  = "$10-Sep-2016 08:38:35$"
 
 try:
     from io import StringIO
-except ImportError:
+except ImportError:  # pragma: no cover
     from cStringIO import StringIO
 
 import codecs
@@ -53,7 +53,7 @@
 import threading
 
 STARTUPINFO = None
-if os.name == 'nt':
+if os.name == 'nt':  # pragma: no cover
     try:
         from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW, SW_HIDE
     except ImportError:
@@ -80,7 +80,7 @@
     logger.addHandler(NullHandler())
 
 # We use the test below because it works for Jython as well as CPython
-if os.path.__name__ == 'ntpath':
+if os.path.__name__ == 'ntpath':  # pragma: no cover
     # On Windows, we don't need shell quoting, other than worrying about
     # paths with spaces in them.
     def shell_quote(s):
@@ -105,7 +105,7 @@
                  command shells
         :rtype: The passed-in type
         """
-        if not isinstance(s, string_types):
+        if not isinstance(s, string_types):  # pragma: no cover
             raise TypeError('Expected string type, got %s' % type(s))
         if not s:
             result = "''"
@@ -138,7 +138,7 @@
     sent = 0
     if hasattr(sys.stdin, 'encoding'):
         enc = sys.stdin.encoding
-    else:
+    else:  # pragma: no cover
         enc = 'ascii'
     while True:
         # See issue #39: read can fail when e.g. a text stream is provided
@@ -151,10 +151,10 @@
         if not data:
             break
         sent += len(data)
-        logger.debug("sending chunk (%d): %r", sent, data[:256])
+        # logger.debug("sending chunk (%d): %r", sent, data[:256])
         try:
             outstream.write(data)
-        except UnicodeError:
+        except UnicodeError:  # pragma: no cover
             outstream.write(data.encode(enc))
         except:
             # Can sometimes get 'broken pipe' errors even when the data has all
@@ -163,7 +163,7 @@
             break
     try:
         outstream.close()
-    except IOError:
+    except IOError:  # pragma: no cover
         logger.warning('Exception occurred while closing: ignored', exc_info=1)
     logger.debug("closed output, %d bytes sent", sent)
 
@@ -187,7 +187,7 @@
     try:
         from io import BytesIO
         rv = BytesIO(s)
-    except ImportError:
+    except ImportError:  # pragma: no cover
         rv = StringIO(s)
     return rv
 
@@ -245,20 +245,21 @@
                      "DECRYPTION_OKAY", "INV_SGNR", "FILE_START", "FILE_ERROR",
                      "FILE_DONE", "PKA_TRUST_GOOD", "PKA_TRUST_BAD", "BADMDC",
                      "GOODMDC", "NO_SGNR", "NOTATION_NAME", "NOTATION_DATA",
-                     "PROGRESS", "PINENTRY_LAUNCHED", "NEWSIG"):
+                     "PROGRESS", "PINENTRY_LAUNCHED", "NEWSIG",
+                     "KEY_CONSIDERED"):
             pass
-        elif key == "BADSIG":
+        elif key == "BADSIG":  # pragma: no cover
             self.valid = False
             self.status = 'signature bad'
             self.key_id, self.username = value.split(None, 1)
-        elif key == "ERRSIG":
+        elif key == "ERRSIG":  # pragma: no cover
             self.valid = False
             (self.key_id,
              algo, hash_algo,
              cls,
              self.timestamp) = value.split()[:5]
             self.status = 'signature error'
-        elif key == "EXPSIG":
+        elif key == "EXPSIG":  # pragma: no cover
             self.valid = False
             self.status = 'signature expired'
             self.key_id, self.username = value.split(None, 1)
@@ -277,21 +278,21 @@
         elif key == "SIG_ID":
             (self.signature_id,
              self.creation_date, self.timestamp) = value.split()
-        elif key == "DECRYPTION_FAILED":
+        elif key == "DECRYPTION_FAILED":  # pragma: no cover
             self.valid = False
             self.key_id = value
             self.status = 'decryption failed'
-        elif key == "NO_PUBKEY":
+        elif key == "NO_PUBKEY":  # pragma: no cover
             self.valid = False
             self.key_id = value
             self.status = 'no public key'
-        elif key in ("KEYEXPIRED", "SIGEXPIRED", "KEYREVOKED"):
+        elif key in ("KEYEXPIRED", "SIGEXPIRED", "KEYREVOKED"):  # pragma: no 
cover
             # these are useless in verify, since they are spit out for any
             # pub/subkeys on the key, not just the one doing the signing.
             # if we want to check for signatures with expired key,
             # the relevant flag is EXPKEYSIG or REVKEYSIG.
             pass
-        elif key in ("EXPKEYSIG", "REVKEYSIG"):
+        elif key in ("EXPKEYSIG", "REVKEYSIG"):  # pragma: no cover
             # signed with expired or revoked key
             self.valid = False
             self.key_id = value.split()[0]
@@ -300,10 +301,14 @@
             else:
                 self.key_status = 'signing key was revoked'
             self.status = self.key_status
-        elif key == "UNEXPECTED":
+        elif key in ("UNEXPECTED", "FAILURE"):  # pragma: no cover
             self.valid = False
             self.key_id = value
-            self.status = 'unexpected data'
+            if key == "UNEXPECTED":
+                self.status = 'unexpected data'
+            else:
+                # N.B. there might be other reasons
+                self.status = 'incorrect passphrase'
         else:
             raise ValueError("Unknown status message: %r" % key)
 
@@ -349,7 +354,7 @@
         if key == "IMPORTED":
             # this duplicates info we already see in import_ok & import_problem
             pass
-        elif key == "NODATA":
+        elif key == "NODATA":  # pragma: no cover
             self.results.append({'fingerprint': None,
                 'problem': '0', 'text': 'No valid data found'})
         elif key == "IMPORT_OK":
@@ -362,7 +367,7 @@
             self.results.append({'fingerprint': fingerprint,
                 'ok': reason, 'text': reasontext})
             self.fingerprints.append(fingerprint)
-        elif key == "IMPORT_PROBLEM":
+        elif key == "IMPORT_PROBLEM":  # pragma: no cover
             try:
                 reason, fingerprint = value.split()
             except:
@@ -374,19 +379,19 @@
             import_res = value.split()
             for i, count in enumerate(self.counts):
                 setattr(self, count, int(import_res[i]))
-        elif key == "KEYEXPIRED":
+        elif key == "KEYEXPIRED":  # pragma: no cover
             self.results.append({'fingerprint': None,
                 'problem': '0', 'text': 'Key expired'})
-        elif key == "SIGEXPIRED":
+        elif key == "SIGEXPIRED":  # pragma: no cover
             self.results.append({'fingerprint': None,
                 'problem': '0', 'text': 'Signature expired'})
-        else:
+        else:  # pragma: no cover
             raise ValueError("Unknown status message: %r" % key)
 
     def summary(self):
         l = []
         l.append('%d imported' % self.imported)
-        if self.not_imported:
+        if self.not_imported:  # pragma: no cover
             l.append('%d not imported' % self.not_imported)
         return ', '.join(l)
 
@@ -429,6 +434,7 @@
         for i, var in enumerate(self.FIELDS):
             result[var] = args[i]
         result['uids'] = []
+        result['sigs'] = []
         return result
 
     def pub(self, args):
@@ -443,11 +449,11 @@
         self.curkey['uids'].append(uid)
         self.uids.append(uid)
 
-    def handle_status(self, key, value):
+    def handle_status(self, key, value):  # pragma: no cover
         pass
 
 class ListKeys(SearchKeys):
-    ''' Handle status messages for --list-keys.
+    ''' Handle status messages for --list-keys, --list-sigs.
 
         Handle pub and uid (relating the latter to the former).
 
@@ -455,7 +461,6 @@
 
         crt = X.509 certificate
         crs = X.509 certificate and private key available
-        ssb = secret subkey (secondary key)
         uat = user attribute (same as user id except for field 10).
         sig = signature
         rev = revocation signature
@@ -465,7 +470,7 @@
     '''
 
     UID_INDEX = 9
-    FIELDS = 'type trust length algo keyid date expires dummy ownertrust 
uid'.split()
+    FIELDS = 'type trust length algo keyid date expires dummy ownertrust uid 
sig'.split()
 
     def __init__(self, gpg):
         super(ListKeys, self).__init__(gpg)
@@ -485,7 +490,7 @@
 
     def fpr(self, args):
         fp = args[9]
-        if fp in self.key_map:
+        if fp in self.key_map:  # pragma: no cover
             raise ValueError('Unexpected fingerprint collision: %s' % fp)
         if not self.in_subkey:
             self.curkey['fingerprint'] = fp
@@ -500,6 +505,14 @@
         self.curkey['subkeys'].append(subkey)
         self.in_subkey = True
 
+    def ssb(self, args):
+        subkey = [args[4], None]    # keyid, type
+        self.curkey['subkeys'].append(subkey)
+        self.in_subkey = True
+
+    def sig(self, args):
+        # keyid, uid, sigclass
+        self.curkey['sigs'].append((args[4], args[9], args[10]))
 
 class ScanKeys(ListKeys):
     ''' Handle status messages for --with-fingerprint.'''
@@ -563,13 +576,13 @@
         elif key == "END_ENCRYPTION":
             self.status = 'encryption ok'
             self.ok = True
-        elif key == "INV_RECP":
+        elif key == "INV_RECP":  # pragma: no cover
             self.status = 'invalid recipient'
-        elif key == "KEYEXPIRED":
+        elif key == "KEYEXPIRED":  # pragma: no cover
             self.status = 'key expired'
-        elif key == "SIG_CREATED":
+        elif key == "SIG_CREATED":  # pragma: no cover
             self.status = 'sig created'
-        elif key == "SIGEXPIRED":
+        elif key == "SIGEXPIRED":  # pragma: no cover
             self.status = 'sig expired'
         else:
             Verify.handle_status(self, key, value)
@@ -605,7 +618,11 @@
     For now, just use an existing class to base it on - if needed, we
     can override handle_status for more specific message handling.
     """
-    pass
+    def handle_status(self, key, value):
+        if key in ("EXPORTED", "EXPORT_RES"):
+            pass
+        else:
+            super(ExportResult, self).handle_status(key, value)
 
 class DeleteResult(object):
     "Handle status messages for --delete-key and --delete-secret-key"
@@ -623,10 +640,10 @@
     }
 
     def handle_status(self, key, value):
-        if key == "DELETE_PROBLEM":
+        if key == "DELETE_PROBLEM":  # pragma: no cover
             self.status = self.problem_reason.get(value,
                                                   "Unknown error: %r" % value)
-        else:
+        else:  # pragma: no cover
             raise ValueError("Unknown status message: %r" % key)
 
     def __nonzero__(self):
@@ -655,16 +672,16 @@
                    "SC_OP_FAILURE", "SC_OP_SUCCESS", "PROGRESS",
                    "PINENTRY_LAUNCHED"):
             pass
-        elif key in ("KEYEXPIRED", "SIGEXPIRED"):
+        elif key in ("KEYEXPIRED", "SIGEXPIRED"):  # pragma: no cover
             self.status = 'key expired'
-        elif key == "KEYREVOKED":
+        elif key == "KEYREVOKED":  # pragma: no cover
             self.status = 'key revoked'
         elif key == "SIG_CREATED":
             (self.type,
              algo, self.hash_algo, cls,
              self.timestamp, self.fingerprint
              ) = value.split()
-        else:
+        else:  # pragma: no cover
             raise ValueError("Unknown status message: %r" % key)
 
 VERSION_RE = re.compile(r'gpg \(GnuPG\) (\d+(\.\d+)*)'.encode('ascii'), re.I)
@@ -720,7 +737,7 @@
         self.secret_keyring = secret_keyring
         self.verbose = verbose
         self.use_agent = use_agent
-        if isinstance(options, str):
+        if isinstance(options, str):  # pragma: no cover
             options = [options]
         self.options = options
         # Changed in 0.3.7 to use Latin-1 encoding rather than
@@ -730,14 +747,19 @@
         self.encoding = 'latin-1'
         if gnupghome and not os.path.isdir(self.gnupghome):
             os.makedirs(self.gnupghome,0x1C0)
-        p = self._open_subprocess(["--version"])
+        try:
+            p = self._open_subprocess(["--version"])
+        except OSError:
+            msg = 'Unable to run gpg - it may not be available.'
+            logger.exception(msg)
+            raise OSError(msg)
         result = self.result_map['verify'](self) # any result will do for this
         self._collect_output(p, result, stdin=p.stdin)
-        if p.returncode != 0:
+        if p.returncode != 0:  # pragma: no cover
             raise ValueError("Error invoking gpg: %s: %s" % (p.returncode,
                                                              result.stderr))
         m = VERSION_RE.match(result.data)
-        if not m:
+        if not m:  # pragma: no cover
             self.version = None
         else:
             dot = '.'.encode('ascii')
@@ -761,7 +783,7 @@
                 cmd.extend(['--secret-keyring', no_quote(fn)])
         if passphrase:
             cmd.extend(['--batch', '--passphrase-fd', '0'])
-        if self.use_agent:
+        if self.use_agent:  # pragma: no cover
             cmd.append('--use-agent')
         if self.options:
             cmd.extend(self.options)
@@ -772,13 +794,13 @@
         # Internal method: open a pipe to a GPG subprocess and return
         # the file objects for communicating with it.
         cmd = self.make_args(args, passphrase)
-        if self.verbose:
+        if self.verbose:  # pragma: no cover
             pcmd = ' '.join(cmd)
             print(pcmd)
         logger.debug("%s", cmd)
         if not STARTUPINFO:
             si = None
-        else:
+        else:  # pragma: no cover
             si = STARTUPINFO()
             si.dwFlags = STARTF_USESHOWWINDOW
             si.wShowWindow = SW_HIDE
@@ -798,7 +820,7 @@
                 break
             lines.append(line)
             line = line.rstrip()
-            if self.verbose:
+            if self.verbose:  # pragma: no cover
                 print(line)
             logger.debug("%s", line)
             if line[0:9] == '[GNUPG:] ':
@@ -855,7 +877,7 @@
         if stdin is not None:
             try:
                 stdin.close()
-            except IOError:
+            except IOError:  # pragma: no cover
                 pass
         stderr.close()
         stdout.close()
@@ -865,7 +887,7 @@
         # Handle a basic data call - pass data to GPG, handle the output
         # including status information. Garbage In, Garbage Out :)
         p = self._open_subprocess(args, passphrase is not None)
-        if not binary:
+        if not binary:  # pragma: no cover
             stdin = codecs.getwriter(self.encoding)(p.stdin)
         else:
             stdin = p.stdin
@@ -890,13 +912,13 @@
         if os.path.exists(output):
             # We need to avoid an overwrite confirmation message
             args.extend(['--batch', '--yes'])
-        args.extend(['--output', output])
+        args.extend(['--output', no_quote(output)])
 
     def sign_file(self, file, keyid=None, passphrase=None, clearsign=True,
                   detach=False, binary=False, output=None):
         """sign file"""
         logger.debug("sign_file: %s", file)
-        if binary:
+        if binary:  # pragma: no cover
             args = ['-s']
         else:
             args = ['-sa']
@@ -920,7 +942,7 @@
             if passphrase:
                 _write_passphrase(stdin, passphrase, self.encoding)
             writer = _threaded_copy_data(file, stdin)
-        except IOError:
+        except IOError:  # pragma: no cover
             logging.exception("error writing message")
             writer = None
         self._collect_output(p, result, writer, stdin)
@@ -1078,9 +1100,9 @@
 
     def delete_keys(self, fingerprints, secret=False):
         which='key'
-        if secret:
+        if secret:  # pragma: no cover
             which='secret-key'
-        if _is_sequence(fingerprints):
+        if _is_sequence(fingerprints):  # pragma: no cover
             fingerprints = [no_quote(s) for s in fingerprints]
         else:
             fingerprints = [no_quote(fingerprints)]
@@ -1103,7 +1125,7 @@
         args = ['--export%s' % which]
         if armor:
             args.append('--armor')
-        if minimal:
+        if minimal:  # pragma: no cover
             args.extend(['--export-options','export-minimal'])
         args.extend(keyids)
         p = self._open_subprocess(args)
@@ -1113,7 +1135,11 @@
         result = self.result_map['export'](self)
         self._collect_output(p, result, stdin=p.stdin)
         logger.debug('export_keys result: %r', result.data)
-        return result.data.decode(self.encoding, self.decode_errors)
+        # Issue #49: Return bytes if armor not specified, else text
+        result = result.data
+        if armor:
+            result = result.decode(self.encoding, self.decode_errors)
+        return result
 
     def _get_list_output(self, p, kind):
         # Get the response information
@@ -1121,22 +1147,22 @@
         self._collect_output(p, result, stdin=p.stdin)
         lines = result.data.decode(self.encoding,
                                    self.decode_errors).splitlines()
-        valid_keywords = 'pub uid sec fpr sub'.split()
+        valid_keywords = 'pub uid sec fpr sub ssb sig'.split()
         for line in lines:
-            if self.verbose:
+            if self.verbose:  # pragma: no cover
                 print(line)
             logger.debug("line: %r", line.rstrip())
-            if not line:
+            if not line:  # pragma: no cover
                 break
             L = line.strip().split(':')
-            if not L:
+            if not L:  # pragma: no cover
                 continue
             keyword = L[0]
             if keyword in valid_keywords:
                 getattr(result, keyword)(L)
         return result
 
-    def list_keys(self, secret=False, keys=None):
+    def list_keys(self, secret=False, keys=None, sigs=False):
         """ list the keys currently in the keyring
 
         >>> import shutil
@@ -1153,7 +1179,10 @@
 
         """
 
-        which='keys'
+        if sigs:
+            which = 'sigs'
+        else:
+            which='keys'
         if secret:
             which='secret-keys'
         args = ['--list-%s' % which, '--fixed-list-mode',
@@ -1208,13 +1237,13 @@
                                    self.decode_errors).splitlines()
         valid_keywords = ['pub', 'uid']
         for line in lines:
-            if self.verbose:
+            if self.verbose:  # pragma: no cover
                 print(line)
             logger.debug('line: %r', line.rstrip())
             if not line:    # sometimes get blank lines on Windows
                 continue
             L = line.strip().split(':')
-            if not L:
+            if not L:  # pragma: no cover
                 continue
             keyword = L[0]
             if keyword in valid_keywords:
@@ -1312,11 +1341,11 @@
             args.append('--armor')
         if output:  # write the output to a file with the specified name
             self.set_output_without_confirmation(args, output)
-        if sign is True:
+        if sign is True:  # pragma: no cover
             args.append('--sign')
-        elif sign:
+        elif sign:  # pragma: no cover
             args.extend(['--sign', '--default-key', no_quote(sign)])
-        if always_trust:
+        if always_trust:  # pragma: no cover
             args.append('--always-trust')
         result = self.result_map['crypt'](self)
         self._handle_io(args, file, result, passphrase=passphrase, binary=True)
@@ -1380,7 +1409,7 @@
         args = ["--decrypt"]
         if output:  # write the output to a file with the specified name
             self.set_output_without_confirmation(args, output)
-        if always_trust:
+        if always_trust:  # pragma: no cover
             args.append("--always-trust")
         result = self.result_map['crypt'](self)
         self._handle_io(args, file, result, passphrase, binary=True)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.3.8/setup.py 
new/python-gnupg-0.3.9/setup.py
--- old/python-gnupg-0.3.8/setup.py     2015-09-24 19:18:52.000000000 +0200
+++ new/python-gnupg-0.3.9/setup.py     2016-09-09 00:28:05.000000000 +0200
@@ -7,7 +7,7 @@
     long_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.",
-    license="""Copyright (C) 2008-2014 by Vinay Sajip. All Rights Reserved. 
See LICENSE.txt for license.""",
+    license="""Copyright (C) 2008-2016 by Vinay Sajip. All Rights Reserved. 
See LICENSE.txt for license.""",
     version=version,
     author="Vinay Sajip",
     author_email="vinay_sa...@red-dove.com",
@@ -32,7 +32,6 @@
         "Programming Language :: Python :: 3.3",
         "Programming Language :: Python :: 3.4",
         "Programming Language :: Python :: 3.5",
-        "Programming Language :: Python :: 3.6",
         "Operating System :: OS Independent",
         "Topic :: Software Development :: Libraries :: Python Modules"
     ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.3.8/test_gnupg.py 
new/python-gnupg-0.3.9/test_gnupg.py
--- old/python-gnupg-0.3.8/test_gnupg.py        2015-09-24 19:05:16.000000000 
+0200
+++ new/python-gnupg-0.3.9/test_gnupg.py        2016-09-10 09:38:57.000000000 
+0200
@@ -2,12 +2,13 @@
 """
 A test harness for gnupg.py.
 
-Copyright (C) 2008-2014 Vinay Sajip. All rights reserved.
+Copyright (C) 2008-2016 Vinay Sajip. All rights reserved.
 """
 import doctest
 import logging
 import os.path
 import os
+import re
 import shutil
 import stat
 import sys
@@ -17,7 +18,7 @@
 import gnupg
 
 __author__ = "Vinay Sajip"
-__date__  = "$24-Sep-2015 18:05:15$"
+__date__  = "$10-Sep-2016 08:38:57$"
 
 ALL_TESTS = True
 
@@ -80,16 +81,71 @@
 =sqld
 -----END PGP PUBLIC KEY BLOCK-----"""
 
+SIGNED_KEYS="""-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2
+
+mI0EVcnKUQEEAKWazmfM0kbvDdw7Kos2NARaX67c8iJ3GOBimUvYLj4VR3Mqrm34
+ZdLlS8jCmid+qoisefvGW5uw5Q3gIs0mdEdUpFKlXNiIja/Dg/FHjjJPPCjfzDTh
+Q03EYA7QvOnXZXhYPBqK7NitsNXW4lPnIJdanLx7yMuL+2Xb+tF39mwnABEBAAG0
+LUpvc2h1YSBDYWx2ZXJ0IChBIHRlc3QgdXNlcikgPGpjQGV4YW1wbGUuY29tPoi3
+BBMBCAAhBQJVycpRAhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJELxvNQ+z
+0EB2jcED/0lHKaEkyd6cj0Zckf9luIkZ4Hno/vRCquTI7c3aPjS3qmE8mOvKSBCV
++SamPdRM7DdjkdBrrKy2HtiDqbM+1/CdXuQka2SlJWyLCJe48+KWfBpqlY3N4t53
+JjHRitDB+hC8njWTV5prli6EgsBPAF+ZkO0iZhlsMmWdDWgqDpGRiJwEEAEIAAYF
+AlXJym8ACgkQBXzPZYwHT9oiiQQAvPF8ubwRopnXIMDQgSxKyFDM1MI1w/wb4Okd
+/MkMeZSmdcHJ6pEymp5bYciCBuLW+jw0vZWza3YloO/HtuppnF6A9a1UvYcp/diI
+O5qkQqYPlui1PJl7hQ014ioniMfOcC4X/r6PDbC78Pczje0Yh9AOqNGeCyNyNdlc
+pjaHb0m4jQRVycpRAQQAo9JjW75F5wTVVO552cGCZWqZvDyBt9+IkoK9Bc+ggdn5
+6R8QVCihYuaSzcSEN84zHaR3MmGKHraCmCSlfe7w0d41Dlns0P03KMdIZOGrm045
+F8TXdSSPQOv5tA4bz3k2lGD0zB8l4NUWFaZ5fzw2i73FF4O/FwCU8xd/JCKVPkkA
+EQEAAYifBBgBCAAJBQJVycpRAhsMAAoJELxvNQ+z0EB2xLYD/i3tKirQlVB+32WP
+wggstqDp1BlUBmDb+4Gndpg4l7omJTTyOsF26SbYgXZqAdEd5T/UfpEla0DKiBYh
+2/CFYXadkgX/ME+GTetTmD4hHoBNmdXau92buXsIXkwh+JR+RC3cl2U6tWb/MIRd
+zvJiok8W8/FT/QrEjIa2etN2d+KR
+=nNBX
+-----END PGP PUBLIC KEY BLOCK-----
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2
+
+mI0EVcnKNgEEANIVlIUyRXWHP/ljdMEA8B5NxecRCKusUIPxeapk2do5UCZgR1q8
+5wOP4K/+W3Uj85ylOOCNTFYKRozAHsPMAmQ38W93DZYqFbG6d7rwMvz4pVe0wUtj
+SBINoKnoEDZwx3erxFKOkp/5fF3NoYSIx9a0Ds21ESk0TAuH5Tg934YhABEBAAG0
+MVdpbnN0b24gU21pdGggKEEgdGVzdCB1c2VyKSA8d2luc3RvbkBleGFtcGxlLmNv
+bT6ItwQTAQgAIQUCVcnKNgIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRAF
+fM9ljAdP2h05A/4vmnxV1MwcOhJTHZys5g2/j5UoZG7V7lPGpJaojSAIVzYXZtwT
+5A7OY8Nl21kIY6gnZlgbTRpHN8Qq2wRKAyW5o6wQvuN16CW4bmGjoHYRGPqkeM0w
+G40W/v88JXrYDNNe/68g4pnPsZ3J0oMLbRvCaDQQHXBuZNJrT1sOxl9Of7iNBFXJ
+yjYBBACmHbs0PdOF8NEGc+fEtmdKOSKOkrcvg1wTu1KFFTBFEbseHOCNpx+R6lfO
+ZiZmHGdKeJhTherfjHaY5jmvyDWq5TLZXK61quNsWxmY2zJ0SRwrIG/CWi4bMi5t
+JNc23vMumkz4X5g7x0Ea7xEWkcYBn0H6sZDAtb8d8mrlWkMekQARAQABiJ8EGAEI
+AAkFAlXJyjYCGwwACgkQBXzPZYwHT9pQIwP8D9/VroykSE2J3gy0S6HC287jXqXF
+0zWejUAQtWUSSRx4esqfLE8lfae6+LDHO8D0Bf6YUJmu7ATOZP2/TIas7JrNvXWc
+NKWl2MHEAGUYq8utCjZ3dKKhaV7UvcY4PyLIpFteNkOz4wFe6C0Mm+1NYwokIFyh
+zPBq9eFk7Xx9Wrc=
+=HT6N
+-----END PGP PUBLIC KEY BLOCK-----
+"""
+
+
 def is_list_with_len(o, n):
     return isinstance(o, list) and len(o) == n
 
+BASE64_PATTERN = 
re.compile(r'^(?:[A-Z0-9+/]{4})*(?:[A-Z0-9+/]{2}==|[A-Z0-9+/]{3}=)?$', re.I)
+
+def get_key_data(s):
+    lines = s.split('\n')
+    result = ''
+    for line in lines:
+        m = BASE64_PATTERN.match(line)
+        if m:
+            result += line
+    return result
+
 def compare_keys(k1, k2):
     "Compare ASCII keys"
-    k1 = k1.split('\n')
-    k2 = k2.split('\n')
-    del k1[1] # remove version lines
-    del k2[1]
-    return k1 != k2
+    # See issue #57: we need to compare only the actual key data,
+    # ignoring things like spurious blank lines
+    return get_key_data(k1) != get_key_data(k2)
 
 class GPGTestCase(unittest.TestCase):
     def setUp(self):
@@ -102,12 +158,12 @@
         self.gpg = gpg = gnupg.GPG(gnupghome=hd, gpgbinary=GPGBINARY)
         v = gpg.version
         if v:
-            if v >= (2,):
+            if v >= (2,):  # pragma: no cover
                 gpg.options = ['--debug-quick-random']
             else:
                 gpg.options = ['--quick-random']
         self.test_fn = test_fn = 'random_binary_data'
-        if not os.path.exists(test_fn):
+        if not os.path.exists(test_fn):  # pragma: no cover
             data_file = open(test_fn, 'wb')
             data_file.write(os.urandom(5120 * 1024))
             data_file.close()
@@ -237,14 +293,36 @@
         for _, _, sfp in key_info['subkeys']:
             self.assertTrue(sfp in public_keys.key_map)
             self.assertTrue(public_keys.key_map[sfp] is key_info)
+
+        # now test with sigs=True
+        public_keys_sigs = self.gpg.list_keys(sigs=True)
+        self.assertTrue(is_list_with_len(public_keys_sigs, 1),
+                        "1-element list expected")
+        key_info = public_keys_sigs[0]
+        fp = key_info['fingerprint']
+        self.assertTrue(fp in public_keys_sigs.key_map)
+        self.assertTrue(public_keys_sigs.key_map[fp] is key_info)
+        self.assertTrue(is_list_with_len(key_info['sigs'], 2))
+        for siginfo in key_info['sigs']:
+            self.assertTrue(len(siginfo), 3)
+        for _, _, sfp in key_info['subkeys']:
+            self.assertTrue(sfp in public_keys_sigs.key_map)
+            self.assertTrue(public_keys_sigs.key_map[sfp] is key_info)
+
         private_keys = self.gpg.list_keys(secret=True)
         self.assertTrue(is_list_with_len(private_keys, 1),
                         "1-element list expected")
+        self.assertEqual(len(private_keys.fingerprints), 1)
         # Now do the same test, but using keyring and secret_keyring arguments
+        pkn = 'pubring.gpg'
+        skn = 'secring.gpg'
         hd = os.path.join(os.getcwd(), 'keys')
+        if os.name == 'posix':
+            pkn = os.path.join(hd, pkn)
+            skn = os.path.join(hd, skn)
         gpg = gnupg.GPG(gnupghome=hd, gpgbinary=GPGBINARY,
-                        keyring=os.path.join(hd, 'pubring.gpg'),
-                        secret_keyring=os.path.join(hd, 'secring.gpg'))
+                        keyring=pkn, secret_keyring=skn)
+        logger.debug('Using keyring and secret_keyring arguments')
         public_keys_2 = gpg.list_keys()
         self.assertEqual(public_keys_2, public_keys)
         private_keys_2 = gpg.list_keys(secret=True)
@@ -283,6 +361,20 @@
                         'Donna Davis <donna.da...@delta.com>'])
         self.assertEqual(actual, expected)
 
+    def test_list_signatures(self):
+        logger.debug("test_list_signatures begins")
+        imported = self.gpg.import_keys(SIGNED_KEYS)
+        keys = self.gpg.list_keys(keys=["18897CA2"])
+        self.assertTrue(is_list_with_len(keys, 1), "importing test signed key")
+        sigs = self.gpg.list_keys(keys=["18897CA2"], sigs=True)[0]['sigs']
+        logger.debug("testing self-signature")
+        self.assertTrue(('BC6F350FB3D04076', 'Joshua Calvert (A test user) 
<j...@example.com>', '13x') in sigs)
+        logger.debug("testing subkey self-signature")
+        self.assertTrue(('BC6F350FB3D04076', 'Joshua Calvert (A test user) 
<j...@example.com>', '18x') in sigs)
+        logger.debug("testing other signature")
+        self.assertTrue(('057CCF658C074FDA', 'Winston Smith (A test user) 
<wins...@example.com>', '10x') in sigs)
+        logger.debug("test_list_signatures ends")
+
     def test_scan_keys(self):
         "Test that external key files can be scanned"
         expected = set([
@@ -315,7 +407,7 @@
         edata = str(gpg.encrypt(data, barbara))
         self.assertNotEqual(data, edata, "Data must have changed")
         ddata = gpg.decrypt(edata, passphrase="bbrown")
-        if data != ddata.data:
+        if data != ddata.data:  # pragma: no cover
             logger.debug("was: %r", data)
             logger.debug("new: %r", ddata.data)
         self.assertEqual(data, ddata.data, "Round-trip must work")
@@ -357,15 +449,18 @@
                         "Exported key should be public")
         ascii = ascii.replace("\r", "").strip()
         match = compare_keys(ascii, KEYS_TO_IMPORT)
-        if match:
+        if match:  # pragma: no cover
             logger.debug("was: %r", KEYS_TO_IMPORT)
             logger.debug("now: %r", ascii)
         self.assertEqual(0, match, "Keys must match")
         #Generate a key so we can test exporting private keys
         key = self.do_key_generation()
         ascii = gpg.export_keys(key.fingerprint, True)
+        self.assertTrue(isinstance(ascii, gnupg.text_type))
         self.assertTrue(ascii.find("PGP PRIVATE KEY BLOCK") >= 0,
                         "Exported key should be private")
+        binary = gpg.export_keys(key.fingerprint, True, armor=False)
+        self.assertFalse(isinstance(binary, gnupg.text_type))
         logger.debug("test_import_and_export ends")
 
     def test_import_only(self):
@@ -384,7 +479,7 @@
                         "Exported key should be public")
         ascii = ascii.replace("\r", "").strip()
         match = compare_keys(ascii, KEYS_TO_IMPORT)
-        if match:
+        if match:  # pragma: no cover
             logger.debug("was: %r", KEYS_TO_IMPORT)
             logger.debug("now: %r", ascii)
         self.assertEqual(0, match, "Keys must match")
@@ -406,7 +501,7 @@
         self.assertTrue(sig, "Good passphrase should succeed")
         self.assertTrue(sig.hash_algo)
         verified = self.gpg.verify(sig.data)
-        if key.fingerprint != verified.fingerprint:
+        if key.fingerprint != verified.fingerprint:  # pragma: no cover
             logger.debug("key: %r", key.fingerprint)
             logger.debug("ver: %r", verified.fingerprint)
         self.assertEqual(key.fingerprint, verified.fingerprint,
@@ -422,10 +517,11 @@
         try:
             file = gnupg._make_binary_stream(sig.data, self.gpg.encoding)
             verified = self.gpg.verify_file(file)
-        except UnicodeDecodeError: #happens in Python 2.6
+        except UnicodeDecodeError:  # pragma: no cover
+            # sometimes happens in Python 2.6
             from io import BytesIO
             verified = self.gpg.verify_file(BytesIO(sig.data))
-        if key.fingerprint != verified.fingerprint:
+        if key.fingerprint != verified.fingerprint:  # pragma: no cover
             logger.debug("key: %r", key.fingerprint)
             logger.debug("ver: %r", verified.fingerprint)
         self.assertEqual(key.fingerprint, verified.fingerprint,
@@ -439,10 +535,11 @@
         try:
             file = gnupg._make_binary_stream(sig.data, self.gpg.encoding)
             verified = self.gpg.verify_file(file, self.test_fn)
-        except UnicodeDecodeError: #happens in Python 2.6
+        except UnicodeDecodeError:  # pragma: no cover
+            # sometimes happens in Python 2.6
             from io import BytesIO
             verified = self.gpg.verify_file(BytesIO(sig.data))
-        if key.fingerprint != verified.fingerprint:
+        if key.fingerprint != verified.fingerprint:  # pragma: no cover
             logger.debug("key: %r", key.fingerprint)
             logger.debug("ver: %r", verified.fingerprint)
         self.assertEqual(key.fingerprint, verified.fingerprint,
@@ -458,7 +555,7 @@
             verified = self.gpg.verify_data(fn, data)
         finally:
             os.unlink(fn)
-        if key.fingerprint != verified.fingerprint:
+        if key.fingerprint != verified.fingerprint:  # pragma: no cover
             logger.debug("key: %r", key.fingerprint)
             logger.debug("ver: %r", verified.fingerprint)
         self.assertEqual(key.fingerprint, verified.fingerprint,
@@ -551,7 +648,7 @@
             ddata = dfile.read()
             dfile.close()
             data = data.encode(self.gpg.encoding)
-            if ddata != data:
+            if ddata != data:  # pragma: no cover
                 logger.debug("was: %r", data)
                 logger.debug("new: %r", ddata)
             self.assertEqual(data, ddata, "Round-trip must work")
@@ -631,7 +728,7 @@
         finally:
             shutil.rmtree(workdir)
 
-    def disabled_test_signing_with_uid(self):
+    def disabled_test_signing_with_uid(self):  # pragma: no cover
         "Test that signing with uids works. On hold for now."
         logger.debug("test_signing_with_uid begins")
         key = self.generate_key("Andrew", "Able", "alpha.com")
@@ -653,6 +750,7 @@
                    'test_filenames_with_spaces']),
     'key' : set(['test_deletion', 'test_import_and_export',
                  'test_list_keys_after_generation',
+                 'test_list_signatures',
                  'test_key_generation_with_invalid_key_type',
                  'test_key_generation_with_escapes',
                  'test_key_generation_with_empty_value',
@@ -671,7 +769,7 @@
     if not args or args == ['--no-doctests']:
         result = unittest.TestLoader().loadTestsFromTestCase(GPGTestCase)
         want_doctests = not args
-    else:
+    else:  # pragma: no cover
         tests = set()
         want_doctests = False
         for arg in args:
@@ -688,7 +786,7 @@
 
 def init_logging():
     logging.basicConfig(level=logging.DEBUG, filename="test_gnupg.log",
-                        filemode="w", format="%(asctime)s %(levelname)-5s 
%(name)-10s %(threadName)-10s %(message)s")
+                        filemode="w", format="%(asctime)s %(levelname)-5s 
%(name)-10s %(threadName)-10s %(lineno)4d %(message)s")
 
 def main():
     init_logging()


Reply via email to