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
