[Python-modules-commits] [python-gnupg] 01/05: Import python-gnupg_0.3.8.orig.tar.gz
Elena Grandi
valhalla-guest at moszumanska.debian.org
Sun Nov 29 13:49:38 UTC 2015
This is an automated email from the git hooks/post-receive script.
valhalla-guest pushed a commit to branch master
in repository python-gnupg.
commit 16065427a552e386da63551fe79e7836133dfac5
Author: Elena Grandi <valhalla-d at trueelena.org>
Date: Tue Oct 13 10:26:13 2015 +0200
Import python-gnupg_0.3.8.orig.tar.gz
---
PKG-INFO | 6 ++--
README.rst | 51 ++++++++++++++++++++++++++-
gnupg.py | 105 ++++++++++++++++++++++++++++++++++++++++++++-----------
setup.py | 2 ++
test_gnupg.py | 52 ++++++++++++++++++++++++++-
test_pubring.gpg | Bin 0 -> 4399 bytes
test_secring.gpg | Bin 0 -> 6724 bytes
7 files changed, 191 insertions(+), 25 deletions(-)
diff --git a/PKG-INFO b/PKG-INFO
index 1f04c7c..09fab1f 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -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: vinay_sajip at 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.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 :: 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: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/README.rst b/README.rst
index 21e7b6f..8e0c387 100644
--- a/README.rst
+++ b/README.rst
@@ -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 @@ Change log
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) <vinay_sajip at yahoo.co.uk>
+
+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 --git a/gnupg.py b/gnupg.py
index 353f234..cfc6cb9 100644
--- a/gnupg.py
+++ b/gnupg.py
@@ -32,9 +32,9 @@ Modifications Copyright (C) 2008-2014 Vinay Sajip. All rights reserved.
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 @@ from subprocess import PIPE
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 @@ else:
# 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 @@ def _copy_data(instream, outstream):
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 @@ class Verify(object):
"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 @@ class ListKeys(SearchKeys):
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 @@ class ListKeys(SearchKeys):
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 @@ class ScanKeys(ListKeys):
# 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 @@ class Crypt(Verify, TextHandler):
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 @@ class GenKey(object):
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 @@ class Sign(TextHandler):
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 @@ class GPG(object):
'search': SearchKeys,
'sign': Sign,
'verify': Verify,
+ 'export': ExportResult,
}
"Encapsulate access to the gpg executable"
@@ -723,7 +776,14 @@ class GPG(object):
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 @@ class GPG(object):
# 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 @@ class GPG(object):
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 @@ class GPG(object):
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 @@ class GPG(object):
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 --git a/setup.py b/setup.py
index cc07c6e..936f1bb 100644
--- a/setup.py
+++ b/setup.py
@@ -31,6 +31,8 @@ It is intended for use with Python 2.4 or greater.",
"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 --git a/test_gnupg.py b/test_gnupg.py
index 2a33d08..f7133b6 100644
--- a/test_gnupg.py
+++ b/test_gnupg.py
@@ -17,7 +17,7 @@ import unittest
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 @@ class GPGTestCase(unittest.TestCase):
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 @@ class GPGTestCase(unittest.TestCase):
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 <barbara.brown at beta.com>',
+ 'Charlie Clark <charlie.clark at gamma.com>',
+ 'Donna Davis <donna.davis at delta.com>'])
+ 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 <donna.davis at delta.com>'])
+ 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 <barbara.brown at beta.com>',
+ 'Donna Davis <donna.davis at delta.com>'])
+ self.assertEqual(actual, expected)
+
def test_scan_keys(self):
"Test that external key files can be scanned"
expected = set([
@@ -515,6 +555,16 @@ class GPGTestCase(unittest.TestCase):
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:
diff --git a/test_pubring.gpg b/test_pubring.gpg
new file mode 100644
index 0000000..e127ffb
Binary files /dev/null and b/test_pubring.gpg differ
diff --git a/test_secring.gpg b/test_secring.gpg
new file mode 100644
index 0000000..8ea3ec3
Binary files /dev/null and b/test_secring.gpg differ
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-gnupg.git
More information about the Python-modules-commits
mailing list