Hello community,

here is the log from the commit of package python-gnupg for openSUSE:Factory 
checked in at 2015-10-30 13:43:04
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-gnupg (Old)
 and      /work/SRC/openSUSE:Factory/.python-gnupg.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-gnupg"

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-gnupg/python-gnupg.changes        
2015-05-10 10:46:24.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.python-gnupg.new/python-gnupg.changes   
2015-10-30 13:43:06.000000000 +0100
@@ -1,0 +2,28 @@
+Fri Oct 16 13:45:55 UTC 2015 - [email protected]
+
+- update to version 0.3.8
+  * Fixed #22: handled ``PROGRESS`` messages during verification and signing.
+  * Fixed #26: handled ``PINENTRY_LAUNCHED`` messages during verification,
+    decryption and key generation.
+  * Fixed #28: Allowed a default Name-Email to be computed even when neither of
+    ``LOGNAME`` and ``USERNAME`` are in the environment.
+  * Fixed #29: Included test files missing from the tarball in previous 
versions.
+  * Fixed #39: On Python 3.x, passing a text instead of a binary stream caused
+    file decryption to hang due to a ``UnicodeDecodeError``. This has now been
+    correctly handled: The decryption fails with a "no data" status.
+  * Fixed #41: Handled Unicode filenames correctly by encoding them on 2.x 
using
+    the file system encoding.
+  * Fixed #43: handled ``PINENTRY_LAUNCHED`` messages during key export. Thanks
+    to Ian Denhardt for looking into this.
+  * Hide the console window which appears on Windows when gpg is spawned.
+    Thanks to Kevin Bernard-Allies for the patch.
+  * Subkey fingerprints are now captured.
+  * The returned value from the ``list_keys`` method now has a new attribute,
+    ``key_map``, which is a dictionary mapping key and subkey fingerprints to
+    the corresponding key's dictionary. With this change, you don't need to
+    iterate over the (potentially large) returned list to search for a key with
+    a given fingerprint - the ``key_map`` dict will take you straight to the 
key
+    info, whether the fingerprint you have is for a key or a subkey. Thanks to
+    Nick Daly for the initial suggestion.
+
+-------------------------------------------------------------------

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

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

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

Other differences:
------------------
++++++ python-gnupg.spec ++++++
--- /var/tmp/diff_new_pack.nMqcQO/_old  2015-10-30 13:43:07.000000000 +0100
+++ /var/tmp/diff_new_pack.nMqcQO/_new  2015-10-30 13:43:07.000000000 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           python-gnupg
-Version:        0.3.7
+Version:        0.3.8
 Release:        0
 Url:            http://code.google.com/p/python-gnupg/
 Summary:        A wrapper for the Gnu Privacy Guard (GPG or GnuPG)

++++++ python-gnupg-0.3.7.tar.gz -> python-gnupg-0.3.8.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.3.7/PKG-INFO 
new/python-gnupg-0.3.8/PKG-INFO
--- old/python-gnupg-0.3.7/PKG-INFO     2014-12-07 20:45:57.000000000 +0100
+++ new/python-gnupg-0.3.8/PKG-INFO     2015-09-24 23:03:50.000000000 +0200
@@ -1,12 +1,12 @@
 Metadata-Version: 1.0
 Name: python-gnupg
-Version: 0.3.7
+Version: 0.3.8
 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: [email protected]
 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.7.tar.gz
+Download-URL: 
https://pypi.python.org/packages/source/p/python-gnupg/python-gnupg-0.3.8.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
@@ -22,5 +22,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: 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.7/README.rst 
new/python-gnupg-0.3.8/README.rst
--- old/python-gnupg-0.3.7/README.rst   2014-12-07 18:50:10.000000000 +0100
+++ new/python-gnupg-0.3.8/README.rst   2015-09-24 19:07:37.000000000 +0200
@@ -1,3 +1,10 @@
+.. image:: https://travis-ci.org/vsajip/python-gnupg.svg
+   :target: https://travis-ci.org/vsajip/python-gnupg
+
+.. image:: https://coveralls.io/repos/vsajip/python-gnupg/badge.svg
+   :target: https://coveralls.io/github/vsajip/python-gnupg
+
+
 What is it?
 ===========
 
@@ -47,16 +54,58 @@
 
 N.B: GCnn refers to an issue nn on Google Code.
 
-0.3.8 (future)
+0.3.9 (future)
 --------------
 
 Released: Not yet
 
+0.3.8
+-----
+
+Released: 2015-09-24
+
+* Fixed #22: handled ``PROGRESS`` messages during verification and signing.
+
+* Fixed #26: handled ``PINENTRY_LAUNCHED`` messages during verification,
+  decryption and key generation.
+
+* Fixed #28: Allowed a default Name-Email to be computed even when neither of
+  ``LOGNAME`` and ``USERNAME`` are in the environment.
+
+* Fixed #29: Included test files missing from the tarball in previous versions.
+
+* Fixed #39: On Python 3.x, passing a text instead of a binary stream caused
+  file decryption to hang due to a ``UnicodeDecodeError``. This has now been
+  correctly handled: The decryption fails with a "no data" status.
+
+* Fixed #41: Handled Unicode filenames correctly by encoding them on 2.x using
+  the file system encoding.
+
+* Fixed #43: handled ``PINENTRY_LAUNCHED`` messages during key export. Thanks
+  to Ian Denhardt for looking into this.
+
+* Hide the console window which appears on Windows when gpg is spawned.
+  Thanks to Kévin Bernard-Allies for the patch.
+
+* Subkey fingerprints are now captured.
+
+* The returned value from the ``list_keys`` method now has a new attribute,
+  ``key_map``, which is a dictionary mapping key and subkey fingerprints to
+  the corresponding key's dictionary. With this change, you don't need to
+  iterate over the (potentially large) returned list to search for a key with
+  a given fingerprint - the ``key_map`` dict will take you straight to the key
+  info, whether the fingerprint you have is for a key or a subkey. Thanks to
+  Nick Daly for the initial suggestion.
+
 0.3.7
 -----
 
 Released: 2014-12-07
 
+Signed with PGP key: Vinay Sajip (CODE SIGNING KEY) <[email protected]>
+
+Key Fingerprint    : CA74 9061 914E AC13 8E66 EADB 9147 B477 339A 9B86
+
 * Added an ``output`` keyword parameter to the ``sign`` and
   ``sign_file`` methods, to allow writing the signature to a file.
   Thanks to Jannis Leidel for the patch.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.3.7/gnupg.py 
new/python-gnupg-0.3.8/gnupg.py
--- old/python-gnupg-0.3.7/gnupg.py     2014-12-07 19:46:19.000000000 +0100
+++ new/python-gnupg-0.3.8/gnupg.py     2015-09-24 19:03:56.000000000 +0200
@@ -32,9 +32,9 @@
 A unittest harness (test_gnupg.py) has also been added.
 """
 
-__version__ = "0.3.7"
+__version__ = "0.3.8"
 __author__ = "Vinay Sajip"
-__date__  = "$07-Dec-2014 18:46:17$"
+__date__  = "$24-Sep-2015 18:03:55$"
 
 try:
     from io import StringIO
@@ -52,6 +52,13 @@
 import sys
 import threading
 
+STARTUPINFO = None
+if os.name == 'nt':
+    try:
+        from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW, SW_HIDE
+    except ImportError:
+        STARTUPINFO = None
+
 try:
     import logging.NullHandler as NullHandler
 except ImportError:
@@ -112,8 +119,18 @@
 
 # Now that we use shell=False, we shouldn't need to quote arguments.
 # Use no_quote instead of shell_quote to remind us of where quoting
-# was needed.
+# was needed. However, note that we still need, on 2.x, to encode any
+# Unicode argument with the file system encoding - see Issue #41 and
+# Python issue #1759845 ("subprocess.call fails with unicode strings in
+# command line").
+
+# Allows the encoding used to be overridden in special cases by setting
+# this module attribute appropriately.
+fsencoding = sys.getfilesystemencoding()
+
 def no_quote(s):
+    if not _py3k and isinstance(s, text_type):
+        s = s.encode(fsencoding)
     return s
 
 def _copy_data(instream, outstream):
@@ -124,7 +141,13 @@
     else:
         enc = 'ascii'
     while True:
-        data = instream.read(1024)
+        # See issue #39: read can fail when e.g. a text stream is provided
+        # for what is actually a binary file
+        try:
+            data = instream.read(1024)
+        except UnicodeError:
+            logger.warning('Exception occurred while reading', exc_info=1)
+            break
         if not data:
             break
         sent += len(data)
@@ -221,7 +244,8 @@
                      "PLAINTEXT_LENGTH", "POLICY_URL", "DECRYPTION_INFO",
                      "DECRYPTION_OKAY", "INV_SGNR", "FILE_START", "FILE_ERROR",
                      "FILE_DONE", "PKA_TRUST_GOOD", "PKA_TRUST_BAD", "BADMDC",
-                     "GOODMDC", "NO_SGNR", "NOTATION_NAME", "NOTATION_DATA"):
+                     "GOODMDC", "NO_SGNR", "NOTATION_NAME", "NOTATION_DATA",
+                     "PROGRESS", "PINENTRY_LAUNCHED", "NEWSIG"):
             pass
         elif key == "BADSIG":
             self.valid = False
@@ -443,6 +467,11 @@
     UID_INDEX = 9
     FIELDS = 'type trust length algo keyid date expires dummy ownertrust 
uid'.split()
 
+    def __init__(self, gpg):
+        super(ListKeys, self).__init__(gpg)
+        self.in_subkey = False
+        self.key_map = {}
+
     def key(self, args):
         self.curkey = curkey = self.get_fields(args)
         if curkey['uid']:
@@ -450,16 +479,26 @@
         del curkey['uid']
         curkey['subkeys'] = []
         self.append(curkey)
+        self.in_subkey = False
 
     pub = sec = key
 
     def fpr(self, args):
-        self.curkey['fingerprint'] = args[9]
-        self.fingerprints.append(args[9])
+        fp = args[9]
+        if fp in self.key_map:
+            raise ValueError('Unexpected fingerprint collision: %s' % fp)
+        if not self.in_subkey:
+            self.curkey['fingerprint'] = fp
+            self.fingerprints.append(fp)
+            self.key_map[fp] = self.curkey
+        else:
+            self.curkey['subkeys'][-1].append(fp)
+            self.key_map[fp] = self.curkey
 
     def sub(self, args):
-        subkey = [args[4], args[11]]
+        subkey = [args[4], args[11]]    # keyid, type
         self.curkey['subkeys'].append(subkey)
+        self.in_subkey = True
 
 
 class ScanKeys(ListKeys):
@@ -470,6 +509,7 @@
         # use the last value args[-1] instead of args[11]
         subkey = [args[4], args[-1]]
         self.curkey['subkeys'].append(subkey)
+        self.in_subkey = True
 
 class TextHandler(object):
     def _as_text(self):
@@ -501,10 +541,12 @@
     def handle_status(self, key, value):
         if key in ("ENC_TO", "USERID_HINT", "GOODMDC", "END_DECRYPTION",
                    "BEGIN_SIGNING", "NO_SECKEY", "ERROR", "NODATA", "PROGRESS",
-                   "CARDCTRL", "BADMDC", "SC_OP_FAILURE", "SC_OP_SUCCESS"):
+                   "CARDCTRL", "BADMDC", "SC_OP_FAILURE", "SC_OP_SUCCESS",
+                   "PINENTRY_LAUNCHED"):
             # in the case of ERROR, this is because a more specific error
             # message will have come first
-            pass
+            if key == "NODATA":
+                self.status = "no data was provided"
         elif key in ("NEED_PASSPHRASE", "BAD_PASSPHRASE", "GOOD_PASSPHRASE",
                      "MISSING_PASSPHRASE", "DECRYPTION_FAILED",
                      "KEY_NOT_CREATED", "NEED_PASSPHRASE_PIN"):
@@ -549,13 +591,22 @@
         return self.fingerprint or ''
 
     def handle_status(self, key, value):
-        if key in ("PROGRESS", "GOOD_PASSPHRASE", "NODATA", "KEY_NOT_CREATED"):
+        if key in ("PROGRESS", "GOOD_PASSPHRASE", "NODATA", "KEY_NOT_CREATED",
+                   "PINENTRY_LAUNCHED"):
             pass
         elif key == "KEY_CREATED":
             (self.type,self.fingerprint) = value.split()
         else:
             raise ValueError("Unknown status message: %r" % key)
 
+class ExportResult(GenKey):
+    """Handle status messages for --export[-secret-key].
+
+    For now, just use an existing class to base it on - if needed, we
+    can override handle_status for more specific message handling.
+    """
+    pass
+
 class DeleteResult(object):
     "Handle status messages for --delete-key and --delete-secret-key"
     def __init__(self, gpg):
@@ -601,7 +652,8 @@
         if key in ("USERID_HINT", "NEED_PASSPHRASE", "BAD_PASSPHRASE",
                    "GOOD_PASSPHRASE", "BEGIN_SIGNING", "CARDCTRL", "INV_SGNR",
                    "NO_SGNR", "MISSING_PASSPHRASE", "NEED_PASSPHRASE_PIN",
-                   "SC_OP_FAILURE", "SC_OP_SUCCESS"):
+                   "SC_OP_FAILURE", "SC_OP_SUCCESS", "PROGRESS",
+                   "PINENTRY_LAUNCHED"):
             pass
         elif key in ("KEYEXPIRED", "SIGEXPIRED"):
             self.status = 'key expired'
@@ -633,6 +685,7 @@
         'search': SearchKeys,
         'sign': Sign,
         'verify': Verify,
+        'export': ExportResult,
     }
 
     "Encapsulate access to the gpg executable"
@@ -723,7 +776,14 @@
             pcmd = ' '.join(cmd)
             print(pcmd)
         logger.debug("%s", cmd)
-        return Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE)
+        if not STARTUPINFO:
+            si = None
+        else:
+            si = STARTUPINFO()
+            si.dwFlags = STARTF_USESHOWWINDOW
+            si.wShowWindow = SW_HIDE
+        return Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE,
+                     startupinfo=si)
 
     def _read_response(self, stream, result):
         # Internal method: reads all the stderr output from GPG, taking notice
@@ -1050,7 +1110,7 @@
         # gpg --export produces no status-fd output; stdout will be
         # empty in case of failure
         #stdout, stderr = p.communicate()
-        result = self.result_map['delete'](self) # any result will do
+        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)
@@ -1076,7 +1136,7 @@
                 getattr(result, keyword)(L)
         return result
 
-    def list_keys(self, secret=False):
+    def list_keys(self, secret=False, keys=None):
         """ list the keys currently in the keyring
 
         >>> import shutil
@@ -1096,8 +1156,13 @@
         which='keys'
         if secret:
             which='secret-keys'
-        args = ["--list-%s" % which, "--fixed-list-mode", "--fingerprint",
-                "--with-colons"]
+        args = ['--list-%s' % which, '--fixed-list-mode',
+                '--fingerprint', '--fingerprint',   # get subkey FPs, too
+                '--with-colons']
+        if keys:
+            if isinstance(keys, string_types):
+                keys = [keys]
+            args.extend(keys)
         p = self._open_subprocess(args)
         return self._get_list_output(p, 'list')
 
@@ -1187,10 +1252,8 @@
         parms.setdefault('Key-Type','RSA')
         parms.setdefault('Key-Length',2048)
         parms.setdefault('Name-Real', "Autogenerated Key")
-        try:
-            logname = os.environ['LOGNAME']
-        except KeyError:
-            logname = os.environ['USERNAME']
+        logname = (os.environ.get('LOGNAME') or os.environ.get('USERNAME') or
+                   'unspecified')
         hostname = socket.gethostname()
         parms.setdefault('Name-Email', "%s@%s" % (logname.replace(' ', '_'),
                                                   hostname))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.3.7/setup.py 
new/python-gnupg-0.3.8/setup.py
--- old/python-gnupg-0.3.7/setup.py     2014-11-03 10:47:28.000000000 +0100
+++ new/python-gnupg-0.3.8/setup.py     2015-09-24 19:18:52.000000000 +0200
@@ -31,6 +31,8 @@
         "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",
         "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.7/test_gnupg.py 
new/python-gnupg-0.3.8/test_gnupg.py
--- old/python-gnupg-0.3.7/test_gnupg.py        2014-12-07 19:46:41.000000000 
+0100
+++ new/python-gnupg-0.3.8/test_gnupg.py        2015-09-24 19:05:16.000000000 
+0200
@@ -17,7 +17,7 @@
 import gnupg
 
 __author__ = "Vinay Sajip"
-__date__  = "$07-Dec-2014 18:46:40$"
+__date__  = "$24-Sep-2015 18:05:15$"
 
 ALL_TESTS = True
 
@@ -230,6 +230,13 @@
         public_keys = self.gpg.list_keys()
         self.assertTrue(is_list_with_len(public_keys, 1),
                         "1-element list expected")
+        key_info = public_keys[0]
+        fp = key_info['fingerprint']
+        self.assertTrue(fp in public_keys.key_map)
+        self.assertTrue(public_keys.key_map[fp] is key_info)
+        for _, _, sfp in key_info['subkeys']:
+            self.assertTrue(sfp in public_keys.key_map)
+            self.assertTrue(public_keys.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")
@@ -243,6 +250,39 @@
         private_keys_2 = gpg.list_keys(secret=True)
         self.assertEqual(private_keys_2, private_keys)
 
+        # generate additional keys so that we can test listing a subset of
+        # keys
+        def get_names(key_map):
+            result = set()
+            for info in key_map.values():
+                for uid in info['uids']:
+                    uid = uid.replace(' (A test user (insecure!))', '')
+                    result.add(uid)
+            return result
+
+        result = self.generate_key("Charlie", "Clark", "gamma.com")
+        self.assertNotEqual(None, result, "Non-null result")
+        result = self.generate_key("Donna", "Davis", "delta.com")
+        self.assertNotEqual(None, result, "Non-null result")
+        public_keys = gpg.list_keys()
+        self.assertEqual(len(public_keys), 3)
+        actual = get_names(public_keys.key_map)
+        expected = set(['Barbara Brown <[email protected]>',
+                        'Charlie Clark <[email protected]>',
+                        'Donna Davis <[email protected]>'])
+        self.assertEqual(actual, expected)
+        # specify a single key as a string
+        public_keys = gpg.list_keys(keys='Donna Davis')
+        actual = get_names(public_keys.key_map)
+        expected = set(['Donna Davis <[email protected]>'])
+        self.assertEqual(actual, expected)
+        # specify multiple keys
+        public_keys = gpg.list_keys(keys=['Donna', 'Barbara'])
+        actual = get_names(public_keys.key_map)
+        expected = set(['Barbara Brown <[email protected]>',
+                        'Donna Davis <[email protected]>'])
+        self.assertEqual(actual, expected)
+
     def test_scan_keys(self):
         "Test that external key files can be scanned"
         expected = set([
@@ -515,6 +555,16 @@
                 logger.debug("was: %r", data)
                 logger.debug("new: %r", ddata)
             self.assertEqual(data, ddata, "Round-trip must work")
+
+            # Try opening the encrypted file in text mode (Issue #39)
+            # this doesn't fail in 2.x
+            if gnupg._py3k:
+                efile = open(encfname, 'r')
+                ddata = self.gpg.decrypt_file(efile, passphrase="bbrown",
+                                              output=decfname)
+                self.assertFalse(ddata)
+                self.assertEqual(ddata.status, "no data was provided")
+                efile.close()
         finally:
             for fn in (encfname, decfname):
                 if os.name == 'posix' and mode is not None:
Files old/python-gnupg-0.3.7/test_pubring.gpg and 
new/python-gnupg-0.3.8/test_pubring.gpg differ
Files old/python-gnupg-0.3.7/test_secring.gpg and 
new/python-gnupg-0.3.8/test_secring.gpg differ


Reply via email to