Hello community,

here is the log from the commit of package python-gnupg for openSUSE:Factory 
checked in at 2015-05-10 10:46:22
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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        
2013-12-16 07:09:00.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.python-gnupg.new/python-gnupg.changes   
2015-05-10 10:46:24.000000000 +0200
@@ -1,0 +2,44 @@
+Wed May  6 12:08:06 UTC 2015 - benoit.mo...@gmx.fr
+
+- update to version 0.3.7:
+  * Added an output keyword parameter to the sign and sign_file
+    methods, to allow writing the signature to a file
+  * Allowed specifying True for the sign keyword parameter, which
+    allows use of the default key for signing and avoids having to
+    specify a key id when it's desired to use the default
+  * Used a uniform approach with subprocess on Windows and POSIX:
+    shell=True is not used on either
+  * When signing/verifying, the status is updated to reflect any
+    expired or revoked keys or signatures
+  * Handled 'NOTATION_NAME' and 'NOTATION_DATA' during verification
+  * Fixed #1, #16, #18, #20: Quoting approach changed, since now
+    shell=False
+  * Fixed #14: Handled 'NEED_PASSPHRASE_PIN' message
+  * Fixed #8: Added a scan_keys method to allow scanning of keys
+    without the need to import into a keyring
+  * Fixed #5: Added '0x' prefix when searching for keys
+  * Fixed #4: Handled 'PROGRESS' message during encryption
+  * Fixed #3: Changed default encoding to Latin-1
+  * Fixed #2: Raised ValueError if no recipients were specified for
+    an asymmetric encryption request
+  * Handled 'UNEXPECTED' message during verification
+  * Replaced old range(len(X)) idiom with enumerate()
+  * Refactored ListKeys / SearchKeys classes to maximise use of
+    common functions
+  * Fixed GC94: Added export-minimal and armor options when
+    exporting keys
+- additional changes from version 0.3.6:
+  * Fixed GC82: Enabled fast random tests on gpg as well as gpg2
+  * Fixed GC85: Avoided deleting temporary file to preserve its
+    permissions
+  * Fixed GC87: Avoided writing passphrase to log
+  * Fixed GC95: Added verify_data() method to allow verification of
+    signatures in memory
+  * Fixed GC96: Regularised end-of-line characters
+  * Fixed GC98: Rectified problems with earlier fix for shell
+    injection
+- point the source URL to pypi
+- fix end of line of README.rst
+- rename LICENSE.txt and README.rst to follow upstream
+
+-------------------------------------------------------------------

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

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

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

Other differences:
------------------
++++++ python-gnupg.spec ++++++
--- /var/tmp/diff_new_pack.Exeb3E/_old  2015-05-10 10:46:24.000000000 +0200
+++ /var/tmp/diff_new_pack.Exeb3E/_new  2015-05-10 10:46:24.000000000 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-gnupg
 #
-# Copyright (c) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.
+# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,13 +17,13 @@
 
 
 Name:           python-gnupg
-Version:        0.3.5
+Version:        0.3.7
 Release:        0
 Url:            http://code.google.com/p/python-gnupg/
 Summary:        A wrapper for the Gnu Privacy Guard (GPG or GnuPG)
 License:        BSD-3-Clause
 Group:          Development/Languages/Python
-Source:         python-gnupg-%{version}.tar.gz
+Source:         
https://pypi.python.org/packages/source/p/python-gnupg/python-gnupg-%{version}.tar.gz
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 BuildRequires:  python-devel
 %if 0%{?suse_version} && 0%{?suse_version} <= 1110
@@ -39,6 +39,8 @@
 
 %prep
 %setup -q -n python-gnupg-%{version}
+# fix end of line
+sed -i 's/\r//' README.rst
 
 %build
 python setup.py build
@@ -48,7 +50,7 @@
 
 %files
 %defattr(-,root,root,-)
-%doc LICENSE README
+%doc LICENSE.txt README.rst
 %{python_sitelib}/*
 
 %changelog

++++++ python-gnupg-0.3.5.tar.gz -> python-gnupg-0.3.7.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.3.5/LICENSE 
new/python-gnupg-0.3.7/LICENSE
--- old/python-gnupg-0.3.5/LICENSE      2013-02-08 15:09:00.000000000 +0100
+++ new/python-gnupg-0.3.7/LICENSE      1970-01-01 01:00:00.000000000 +0100
@@ -1,26 +0,0 @@
-Copyright (c) 2008-2013 by Vinay Sajip.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-    * Redistributions of source code must retain the above copyright notice,
-      this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above copyright notice,
-      this list of conditions and the following disclaimer in the documentation
-      and/or other materials provided with the distribution.
-    * The name(s) of the copyright holder(s) may not be used to endorse or
-      promote products derived from this software without specific prior
-      written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) "AS IS" AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.3.5/LICENSE.txt 
new/python-gnupg-0.3.7/LICENSE.txt
--- old/python-gnupg-0.3.5/LICENSE.txt  1970-01-01 01:00:00.000000000 +0100
+++ new/python-gnupg-0.3.7/LICENSE.txt  2014-02-05 23:03:58.000000000 +0100
@@ -0,0 +1,26 @@
+Copyright (c) 2008-2014 by Vinay Sajip.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * The name(s) of the copyright holder(s) may not be used to endorse or
+      promote products derived from this software without specific prior
+      written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.3.5/PKG-INFO 
new/python-gnupg-0.3.7/PKG-INFO
--- old/python-gnupg-0.3.5/PKG-INFO     2013-08-30 19:11:27.000000000 +0200
+++ new/python-gnupg-0.3.7/PKG-INFO     2014-12-07 20:45:57.000000000 +0100
@@ -1,12 +1,12 @@
 Metadata-Version: 1.0
 Name: python-gnupg
-Version: 0.3.5
+Version: 0.3.7
 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-2013 by Vinay Sajip. All Rights Reserved. See 
LICENSE for license.
-Download-URL: 
http://python-gnupg.googlecode.com/files/python-gnupg-0.3.5.tar.gz
+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
 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
@@ -19,8 +19,8 @@
 Classifier: Programming Language :: Python :: 2.5
 Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3.0
-Classifier: Programming Language :: Python :: 3.1
 Classifier: Programming Language :: Python :: 3.2
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
 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.5/README 
new/python-gnupg-0.3.7/README
--- old/python-gnupg-0.3.5/README       2009-07-03 23:39:13.000000000 +0200
+++ new/python-gnupg-0.3.7/README       1970-01-01 01:00:00.000000000 +0100
@@ -1,5 +0,0 @@
-To install this package from a source distribution, do the following.
-
-1. Extract all the files in the distribution archive to some directory on your 
system.
-2. In that directory, run "python setup.py install".
-3. Optionally, run "python test_gnupg.py" to ensure that the package is 
working as expected.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.3.5/README.rst 
new/python-gnupg-0.3.7/README.rst
--- old/python-gnupg-0.3.5/README.rst   1970-01-01 01:00:00.000000000 +0100
+++ new/python-gnupg-0.3.7/README.rst   2014-12-07 18:50:10.000000000 +0100
@@ -0,0 +1,276 @@
+What is it?
+===========
+
+The GNU Privacy Guard (gpg, or gpg.exe on Windows) is a command-line program
+which provides support for programmatic access via spawning a separate process
+to run it and then communicating with that process from your program.
+
+This project, ``python-gnupg``, implements a Python library which takes care
+of the internal details and allows its users to generate and manage keys,
+encrypt and decrypt data, and sign and verify messages.
+
+Installation
+============
+
+Installing from PyPI
+--------------------
+
+You can install this package from the Python Package Index (pyPI) by running::
+
+    pip install python-gnupg
+
+
+Installing from a source distribution archive
+---------------------------------------------
+To install this package from a source distribution archive, do the following:
+
+1. Extract all the files in the distribution archive to some directory on your
+   system.
+2. In that directory, run ``python setup.py install``.
+3. Optionally, run ``python test_gnupg.py`` to ensure that the package is
+   working as expected.
+
+Credits
+=======
+
+* The developers of the GNU Privacy Guard.
+* The original version of this module was developed by Andrew Kuchling.
+* It was improved by Richard Jones.
+* It was further improved by Steve Traugott.
+
+The present incarnation, based on the earlier versions, uses the ``subprocess``
+module and so works on Windows as well as Unix/Linux platforms. It's not,
+however, 100% backwards-compatible with earlier incarnations.
+
+Change log
+==========
+
+N.B: GCnn refers to an issue nn on Google Code.
+
+0.3.8 (future)
+--------------
+
+Released: Not yet
+
+0.3.7
+-----
+
+Released: 2014-12-07
+
+* 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.
+
+* Allowed specifying ``True`` for the ``sign`` keyword parameter,
+  which allows use of the default key for signing and avoids having to
+  specify a key id when it's desired to use the default. Thanks to
+  Fabian Beutel for the patch.
+
+* Used a uniform approach with subprocess on Windows and POSIX: shell=True
+  is not used on either.
+
+* When signing/verifying, the status is updated to reflect any expired or
+  revoked keys or signatures.
+
+* Handled 'NOTATION_NAME' and 'NOTATION_DATA' during verification.
+
+* Fixed #1, #16, #18, #20: Quoting approach changed, since now shell=False.
+
+* Fixed #14: Handled 'NEED_PASSPHRASE_PIN' message.
+
+* Fixed #8: Added a scan_keys method to allow scanning of keys without the
+  need to import into a keyring. Thanks to Venzen Khaosan for the suggestion.
+
+* Fixed #5: Added '0x' prefix when searching for keys. Thanks to Aaron Toponce
+  for the report.
+
+* Fixed #4: Handled 'PROGRESS' message during encryption. Thanks to Daniel
+  Mills for the report.
+
+* Fixed #3: Changed default encoding to Latin-1.
+
+* Fixed #2: Raised ValueError if no recipients were specified
+  for an asymmetric encryption request.
+
+* Handled 'UNEXPECTED' message during verification. Thanks to
+  David Andersen for the patch.
+
+* Replaced old range(len(X)) idiom with enumerate().
+
+* Refactored ``ListKeys`` / ``SearchKeys`` classes to maximise use of common
+  functions.
+
+* Fixed GC94: Added ``export-minimal`` and ``armor`` options when exporting
+  keys. This addition was inadvertently left out of 0.3.6.
+
+0.3.6
+-----
+
+Released: 2014-02-05
+
+* Fixed GC82: Enabled fast random tests on gpg as well as gpg2.
+* Fixed GC85: Avoided deleting temporary file to preserve its permissions.
+* Fixed GC87: Avoided writing passphrase to log.
+* Fixed GC95: Added ``verify_data()`` method to allow verification of
+  signatures in memory.
+* Fixed GC96: Regularised end-of-line characters.
+* Fixed GC98: Rectified problems with earlier fix for shell injection.
+
+0.3.5
+-----
+
+Released: 2013-08-30
+
+* Added improved shell quoting to guard against shell injection.
+* Fixed GC76: Added ``search_keys()`` and ``send_keys()`` methods.
+* Fixed GC77: Allowed specifying a symmetric cipher algorithm.
+* Fixed GC78: Fell back to utf-8 encoding when no other could be determined.
+* Fixed GC79: Default key length is now 2048 bits.
+* Fixed GC80: Removed the Name-Comment default in key generation.
+
+0.3.4
+-----
+
+Released: 2013-06-05
+
+* Fixed GC65: Fixed encoding exception when getting version.
+* Fixed GC66: Now accepts sets and frozensets where appropriate.
+* Fixed GC67: Hash algorithm now captured in sign result.
+* Fixed GC68: Added support for ``--secret-keyring``.
+* Fixed GC70: Added support for multiple keyrings.
+
+0.3.3
+-----
+
+Released: 2013-03-11
+
+* Fixed GC57: Handled control characters in ``list_keys()``.
+* Fixed GC61: Enabled fast random for testing.
+* Fixed GC62: Handled ``KEYEXPIRED`` status.
+* Fixed GC63: Handled ``NO_SGNR`` status.
+
+0.3.2
+-----
+
+Released: 2013-01-17
+
+* Fixed GC56: Disallowed blank values in key generation.
+* Fixed GC57: Handled colons and other characters in ``list_keys()``.
+* Fixed GC59/GC60: Handled ``INV_SGNR`` status during verification and removed
+  calls requiring interactive password input from doctests.
+
+0.3.1
+-----
+
+Released: 2012-09-01
+
+* Fixed GC45: Allowed additional arguments to gpg executable.
+* Fixed GC50: Used latin-1 encoding in tests when it's known to be required.
+* Fixed GC51: Test now returns non-zero exit status on test failure.
+* Fixed GC53: Now handles ``INV_SGNR`` and ``KEY_NOT_CREATED`` statuses.
+* Fixed GC55: Verification and decryption now return trust level of signer in
+  integer and text form.
+
+0.3.0
+-----
+
+Released: 2012-05-12
+
+* Fixed GC49: Reinstated Yann Leboulanger's change to support subkeys
+  (accidentally left out in 0.2.7).
+
+0.2.9
+-----
+
+Released: 2012-03-29
+
+* Fixed GC36: Now handles ``CARDCTRL`` and ``POLICY_URL`` messages.
+* Fixed GC40: Now handles ``DECRYPTION_INFO``, ``DECRYPTION_FAILED`` and
+  ``DECRYPTION_OKAY`` messages.
+* The ``random_binary_data file`` is no longer shipped, but constructed by the
+  test suite if needed.
+
+0.2.8
+-----
+
+Released: 2011-09-02
+
+* Fixed GC29: Now handles ``IMPORT_RES`` while verifying.
+* Fixed GC30: Fixed an encoding problem.
+* Fixed GC33: Quoted arguments for added safety.
+
+0.2.7
+-----
+
+Released: 2011-04-10
+
+* Fixed GC24: License is clarified as BSD.
+* Fixed GC25: Incorporated Daniel Folkinshteyn's changes.
+* Fixed GC26: Incorporated Yann Leboulanger's subkey change.
+* Fixed GC27: Incorporated hysterix's support for symmetric encryption.
+* Did some internal cleanups of Unicode handling.
+
+0.2.6
+-----
+
+Released: 2011-01-25
+
+* Fixed GC14: Should be able to accept passphrases from GPG-Agent.
+* Fixed GC19: Should be able to create a detached signature.
+* Fixed GC21/GC23: Better handling of less common responses from GPG.
+
+0.2.5
+-----
+
+Released: 2010-10-13
+
+* Fixed GC11/GC16: Detached signatures can now be created.
+* Fixed GC3: Detached signatures can be verified.
+* Fixed GC12: Better support for RSA and IDEA.
+* Fixed GC15/GC17: Better support for non-ASCII input.
+
+0.2.4
+-----
+
+Released: 2010-03-01
+
+* Fixed GC9: Now allows encryption without armor and the ability to encrypt
+  and decrypt directly to/from files.
+
+0.2.3
+-----
+
+Released: 2010-01-07
+
+* Fixed GC7: Made sending data to process threaded and added a test case.
+  With a test data file used by the test case, the archive size has gone up
+  to 5MB (the size of the test file).
+
+0.2.2
+-----
+
+Released: 2009-10-06
+
+* Fixed GC5/GC6: Added ``--batch`` when specifying ``--passphrase-fd`` and
+  changed the name of the distribution file to add the ``python-`` prefix.
+
+0.2.1
+-----
+
+Released: 2009-08-07
+
+* Fixed GC2: Added ``handle_status()`` method to the ``ListKeys`` class.
+
+0.2.0
+-----
+
+Released: 2009-07-16
+
+* Various changes made to support Python 3.0.
+
+0.1.0
+-----
+
+Released: 2009-07-04
+
+* Initial release.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.3.5/gnupg.py 
new/python-gnupg-0.3.7/gnupg.py
--- old/python-gnupg-0.3.5/gnupg.py     2013-08-30 19:10:36.000000000 +0200
+++ new/python-gnupg-0.3.7/gnupg.py     2014-12-07 19:46:19.000000000 +0100
@@ -27,15 +27,14 @@
 and so does not work on Windows). Renamed to gnupg.py to avoid confusion with
 the previous versions.
 
-Modifications Copyright (C) 2008-2013 Vinay Sajip. All rights reserved.
+Modifications Copyright (C) 2008-2014 Vinay Sajip. All rights reserved.
 
 A unittest harness (test_gnupg.py) has also been added.
 """
-import locale
 
-__version__ = "0.3.5"
+__version__ = "0.3.7"
 __author__ = "Vinay Sajip"
-__date__  = "$30-Aug-2013 18:10:36$"
+__date__  = "$07-Dec-2014 18:46:17$"
 
 try:
     from io import StringIO
@@ -103,16 +102,19 @@
             raise TypeError('Expected string type, got %s' % type(s))
         if not s:
             result = "''"
-        elif len(s) >= 2 and (s[0], s[-1]) == ("'", "'"):
-            result = '"%s"' % s.replace('"', r'\"')
         elif not UNSAFE.search(s):
             result = s
         else:
-            result = "'%s'" % s.replace("'", "'\"'\"'")
+            result = "'%s'" % s.replace("'", r"'\''")
         return result
 
     # end of sarge code
 
+# 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.
+def no_quote(s):
+    return s
 
 def _copy_data(instream, outstream):
     # Copy one stream to another
@@ -153,11 +155,19 @@
     passphrase = '%s\n' % passphrase
     passphrase = passphrase.encode(encoding)
     stream.write(passphrase)
-    logger.debug("Wrote passphrase: %r", passphrase)
+    logger.debug('Wrote passphrase')
 
 def _is_sequence(instance):
     return isinstance(instance, (list, tuple, set, frozenset))
 
+def _make_memory_stream(s):
+    try:
+        from io import BytesIO
+        rv = BytesIO(s)
+    except ImportError:
+        rv = StringIO(s)
+    return rv
+
 def _make_binary_stream(s, encoding):
     if _py3k:
         if isinstance(s, str):
@@ -165,12 +175,7 @@
     else:
         if type(s) is not str:
             s = s.encode(encoding)
-    try:
-        from io import BytesIO
-        rv = BytesIO(s)
-    except ImportError:
-        rv = StringIO(s)
-    return rv
+    return _make_memory_stream(s)
 
 class Verify(object):
     "Handle status messages for --verify"
@@ -216,7 +221,7 @@
                      "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"):
+                     "GOODMDC", "NO_SGNR", "NOTATION_NAME", "NOTATION_DATA"):
             pass
         elif key == "BADSIG":
             self.valid = False
@@ -271,6 +276,10 @@
             else:
                 self.key_status = 'signing key was revoked'
             self.status = self.key_status
+        elif key == "UNEXPECTED":
+            self.valid = False
+            self.key_id = value
+            self.status = 'unexpected data'
         else:
             raise ValueError("Unknown status message: %r" % key)
 
@@ -339,8 +348,8 @@
                 'problem': reason, 'text': self.problem_reason[reason]})
         elif key == "IMPORT_RES":
             import_res = value.split()
-            for i in range(len(self.counts)):
-                setattr(self, self.counts[i], int(import_res[i]))
+            for i, count in enumerate(self.counts):
+                setattr(self, count, int(import_res[i]))
         elif key == "KEYEXPIRED":
             self.results.append({'fingerprint': None,
                 'problem': '0', 'text': 'Key expired'})
@@ -374,7 +383,46 @@
     def handle_status(self, key, value):
         logger.debug('SendResult: %s: %s', key, value)
 
-class ListKeys(list):
+class SearchKeys(list):
+    ''' Handle status messages for --search-keys.
+
+        Handle pub and uid (relating the latter to the former).
+
+        Don't care about the rest
+    '''
+
+    UID_INDEX = 1
+    FIELDS = 'type keyid algo length date expires'.split()
+
+    def __init__(self, gpg):
+        self.gpg = gpg
+        self.curkey = None
+        self.fingerprints = []
+        self.uids = []
+
+    def get_fields(self, args):
+        result = {}
+        for i, var in enumerate(self.FIELDS):
+            result[var] = args[i]
+        result['uids'] = []
+        return result
+
+    def pub(self, args):
+        self.curkey = curkey = self.get_fields(args)
+        self.append(curkey)
+
+    def uid(self, args):
+        uid = args[self.UID_INDEX]
+        uid = ESCAPE_PATTERN.sub(lambda m: chr(int(m.group(1), 16)), uid)
+        for k, v in BASIC_ESCAPES.items():
+            uid = uid.replace(k, v)
+        self.curkey['uids'].append(uid)
+        self.uids.append(uid)
+
+    def handle_status(self, key, value):
+        pass
+
+class ListKeys(SearchKeys):
     ''' Handle status messages for --list-keys.
 
         Handle pub and uid (relating the latter to the former).
@@ -391,25 +439,17 @@
         grp = reserved for gpgsm
         rvk = revocation key
     '''
-    def __init__(self, gpg):
-        self.gpg = gpg
-        self.curkey = None
-        self.fingerprints = []
-        self.uids = []
+
+    UID_INDEX = 9
+    FIELDS = 'type trust length algo keyid date expires dummy ownertrust 
uid'.split()
 
     def key(self, args):
-        vars = ("""
-            type trust length algo keyid date expires dummy ownertrust uid
-        """).split()
-        self.curkey = {}
-        for i in range(len(vars)):
-            self.curkey[vars[i]] = args[i]
-        self.curkey['uids'] = []
-        if self.curkey['uid']:
-            self.curkey['uids'].append(self.curkey['uid'])
-        del self.curkey['uid']
-        self.curkey['subkeys'] = []
-        self.append(self.curkey)
+        self.curkey = curkey = self.get_fields(args)
+        if curkey['uid']:
+            curkey['uids'].append(curkey['uid'])
+        del curkey['uid']
+        curkey['subkeys'] = []
+        self.append(curkey)
 
     pub = sec = key
 
@@ -417,56 +457,34 @@
         self.curkey['fingerprint'] = args[9]
         self.fingerprints.append(args[9])
 
-    def uid(self, args):
-        uid = args[9]
-        uid = ESCAPE_PATTERN.sub(lambda m: chr(int(m.group(1), 16)), uid)
-        for k, v in BASIC_ESCAPES.items():
-            uid = uid.replace(k, v)
-        self.curkey['uids'].append(uid)
-        self.uids.append(uid)
-
     def sub(self, args):
         subkey = [args[4], args[11]]
         self.curkey['subkeys'].append(subkey)
 
-    def handle_status(self, key, value):
-        pass
 
-class SearchKeys(list):
-    ''' Handle status messages for --search-keys.
+class ScanKeys(ListKeys):
+    ''' Handle status messages for --with-fingerprint.'''
 
-        Handle pub and uid (relating the latter to the former).
+    def sub(self, args):
+        # --with-fingerprint --with-colons somehow outputs fewer colons,
+        # use the last value args[-1] instead of args[11]
+        subkey = [args[4], args[-1]]
+        self.curkey['subkeys'].append(subkey)
 
-        Don't care about the rest
-    '''
-    def __init__(self, gpg):
-        self.gpg = gpg
-        self.curkey = None
-        self.fingerprints = []
-        self.uids = []
+class TextHandler(object):
+    def _as_text(self):
+        return self.data.decode(self.gpg.encoding, self.gpg.decode_errors)
 
-    def pub(self, args):
-        vars = ("""
-            type keyid algo length date expires
-        """).split()
-        self.curkey = {}
-        for i in range(len(vars)):
-            self.curkey[vars[i]] = args[i]
-        self.curkey['uids'] = []
-        self.append(self.curkey)
+    if _py3k:
+        __str__ = _as_text
+    else:
+        __unicode__ = _as_text
 
-    def uid(self, args):
-        uid = args[1]
-        uid = ESCAPE_PATTERN.sub(lambda m: chr(int(m.group(1), 16)), uid)
-        for k, v in BASIC_ESCAPES.items():
-            uid = uid.replace(k, v)
-        self.curkey['uids'].append(uid)
-        self.uids.append(uid)
+        def __str__(self):
+            return self.data
 
-    def handle_status(self, key, value):
-        pass
 
-class Crypt(Verify):
+class Crypt(Verify, TextHandler):
     "Handle status messages for --encrypt and --decrypt"
     def __init__(self, gpg):
         Verify.__init__(self, gpg)
@@ -480,19 +498,16 @@
 
     __bool__ = __nonzero__
 
-    def __str__(self):
-        return self.data.decode(self.gpg.encoding, self.gpg.decode_errors)
-
     def handle_status(self, key, value):
         if key in ("ENC_TO", "USERID_HINT", "GOODMDC", "END_DECRYPTION",
-                   "BEGIN_SIGNING", "NO_SECKEY", "ERROR", "NODATA",
+                   "BEGIN_SIGNING", "NO_SECKEY", "ERROR", "NODATA", "PROGRESS",
                    "CARDCTRL", "BADMDC", "SC_OP_FAILURE", "SC_OP_SUCCESS"):
             # in the case of ERROR, this is because a more specific error
             # message will have come first
             pass
         elif key in ("NEED_PASSPHRASE", "BAD_PASSPHRASE", "GOOD_PASSPHRASE",
                      "MISSING_PASSPHRASE", "DECRYPTION_FAILED",
-                     "KEY_NOT_CREATED"):
+                     "KEY_NOT_CREATED", "NEED_PASSPHRASE_PIN"):
             self.status = key.replace("_", " ").lower()
         elif key == "NEED_PASSPHRASE_SYM":
             self.status = 'need symmetric passphrase'
@@ -569,7 +584,7 @@
     __bool__ = __nonzero__
 
 
-class Sign(object):
+class Sign(TextHandler):
     "Handle status messages for --sign"
     def __init__(self, gpg):
         self.gpg = gpg
@@ -582,15 +597,16 @@
 
     __bool__ = __nonzero__
 
-    def __str__(self):
-        return self.data.decode(self.gpg.encoding, self.gpg.decode_errors)
-
     def handle_status(self, key, value):
         if key in ("USERID_HINT", "NEED_PASSPHRASE", "BAD_PASSPHRASE",
                    "GOOD_PASSPHRASE", "BEGIN_SIGNING", "CARDCTRL", "INV_SGNR",
-                   "KEYEXPIRED", "SIGEXPIRED", "KEYREVOKED", "NO_SGNR",
-                   "MISSING_PASSPHRASE", "SC_OP_FAILURE", "SC_OP_SUCCESS"):
+                   "NO_SGNR", "MISSING_PASSPHRASE", "NEED_PASSPHRASE_PIN",
+                   "SC_OP_FAILURE", "SC_OP_SUCCESS"):
             pass
+        elif key in ("KEYEXPIRED", "SIGEXPIRED"):
+            self.status = 'key expired'
+        elif key == "KEYREVOKED":
+            self.status = 'key revoked'
         elif key == "SIG_CREATED":
             (self.type,
              algo, self.hash_algo, cls,
@@ -599,7 +615,8 @@
         else:
             raise ValueError("Unknown status message: %r" % key)
 
-VERSION_RE = re.compile(r'gpg \(GnuPG\) (\d+(\.\d+)*)'.encode('utf-8'), re.I)
+VERSION_RE = re.compile(r'gpg \(GnuPG\) (\d+(\.\d+)*)'.encode('ascii'), re.I)
+HEX_DIGITS_RE = re.compile(r'[0-9a-f]+$', re.I)
 
 class GPG(object):
 
@@ -612,6 +629,7 @@
         'import': ImportResult,
         'send': SendResult,
         'list': ListKeys,
+        'scan': ScanKeys,
         'search': SearchKeys,
         'sign': Sign,
         'verify': Verify,
@@ -652,13 +670,11 @@
         if isinstance(options, str):
             options = [options]
         self.options = options
-        self.encoding = locale.getpreferredencoding()
-        if self.encoding is None: # This happens on Jython!
-            self.encoding = sys.stdin.encoding
-        if self.encoding is None:
-            logger.warning('No encoding found via locale.getpreferredencoding '
-                           'or sys.stdin.encoding, defaulting to utf-8.')
-            self.encoding = 'utf-8'
+        # Changed in 0.3.7 to use Latin-1 encoding rather than
+        # locale.getpreferredencoding falling back to sys.stdin.encoding
+        # falling back to utf-8, because gpg itself uses latin-1 as the default
+        # encoding.
+        self.encoding = 'latin-1'
         if gnupghome and not os.path.isdir(self.gnupghome):
             os.makedirs(self.gnupghome,0x1C0)
         p = self._open_subprocess(["--version"])
@@ -671,7 +687,7 @@
         if not m:
             self.version = None
         else:
-            dot = '.'.encode('utf-8')
+            dot = '.'.encode('ascii')
             self.version = tuple([int(s) for s in m.groups()[0].split(dot)])
 
     def make_args(self, args, passphrase):
@@ -680,18 +696,18 @@
         will be appended. The ``passphrase`` argument needs to be True if
         a passphrase will be sent to GPG, else False.
         """
-        cmd = [self.gpgbinary, '--status-fd 2 --no-tty']
+        cmd = [self.gpgbinary, '--status-fd', '2', '--no-tty']
         if self.gnupghome:
-            cmd.append('--homedir %s' % shell_quote(self.gnupghome))
+            cmd.extend(['--homedir',  no_quote(self.gnupghome)])
         if self.keyring:
             cmd.append('--no-default-keyring')
             for fn in self.keyring:
-                cmd.append('--keyring %s' % shell_quote(fn))
+                cmd.extend(['--keyring', no_quote(fn)])
         if self.secret_keyring:
             for fn in self.secret_keyring:
-                cmd.append('--secret-keyring %s' % shell_quote(fn))
+                cmd.extend(['--secret-keyring', no_quote(fn)])
         if passphrase:
-            cmd.append('--batch --passphrase-fd 0')
+            cmd.extend(['--batch', '--passphrase-fd', '0'])
         if self.use_agent:
             cmd.append('--use-agent')
         if self.options:
@@ -702,11 +718,12 @@
     def _open_subprocess(self, args, passphrase=False):
         # Internal method: open a pipe to a GPG subprocess and return
         # the file objects for communicating with it.
-        cmd = ' '.join(self.make_args(args, passphrase))
+        cmd = self.make_args(args, passphrase)
         if self.verbose:
-            print(cmd)
+            pcmd = ' '.join(cmd)
+            print(pcmd)
         logger.debug("%s", cmd)
-        return Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
+        return Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE)
 
     def _read_response(self, stream, result):
         # Internal method: reads all the stderr output from GPG, taking notice
@@ -808,8 +825,15 @@
         f.close()
         return result
 
+    def set_output_without_confirmation(self, args, output):
+        "If writing to a file which exists, avoid a confirmation message."
+        if os.path.exists(output):
+            # We need to avoid an overwrite confirmation message
+            args.extend(['--batch', '--yes'])
+        args.extend(['--output', output])
+
     def sign_file(self, file, keyid=None, passphrase=None, clearsign=True,
-                  detach=False, binary=False):
+                  detach=False, binary=False, output=None):
         """sign file"""
         logger.debug("sign_file: %s", file)
         if binary:
@@ -823,7 +847,10 @@
         elif clearsign:
             args.append("--clearsign")
         if keyid:
-            args.append('--default-key %s' % shell_quote(keyid))
+            args.extend(['--default-key', no_quote(keyid)])
+        if output:  # write the output to a file with the specified name
+            self.set_output_without_confirmation(args, output)
+
         result = self.result_map['sign'](self)
         #We could use _handle_io here except for the fact that if the
         #passphrase is bad, gpg bails and you can't write the message.
@@ -875,8 +902,8 @@
             logger.debug('Wrote to temp file: %r', s)
             os.write(fd, s)
             os.close(fd)
-            args.append(shell_quote(fn))
-            args.append(shell_quote(data_filename))
+            args.append(no_quote(fn))
+            args.append(no_quote(data_filename))
             try:
                 p = self._open_subprocess(args)
                 self._collect_output(p, result, stdin=p.stdin)
@@ -884,6 +911,15 @@
                 os.unlink(fn)
         return result
 
+    def verify_data(self, sig_filename, data):
+        "Verify the signature in sig_filename against data in memory"
+        logger.debug('verify_data: %r, %r ...', sig_filename, data[:16])
+        result = self.result_map['verify'](self)
+        args = ['--verify', no_quote(sig_filename), '-']
+        stream = _make_memory_stream(data)
+        self._handle_io(args, stream, result, binary=True)
+        return result
+
     #
     # KEY MANAGEMENT
     #
@@ -956,8 +992,8 @@
         logger.debug('recv_keys: %r', keyids)
         data = _make_binary_stream("", self.encoding)
         #data = ""
-        args = ['--keyserver', shell_quote(keyserver), '--recv-keys']
-        args.extend([shell_quote(k) for k in keyids])
+        args = ['--keyserver', no_quote(keyserver), '--recv-keys']
+        args.extend([no_quote(k) for k in keyids])
         self._handle_io(args, data, result, binary=True)
         logger.debug('recv_keys result: %r', result.__dict__)
         data.close()
@@ -973,8 +1009,8 @@
         logger.debug('send_keys: %r', keyids)
         data = _make_binary_stream('', self.encoding)
         #data = ""
-        args = ['--keyserver', shell_quote(keyserver), '--send-keys']
-        args.extend([shell_quote(k) for k in keyids])
+        args = ['--keyserver', no_quote(keyserver), '--send-keys']
+        args.extend([no_quote(k) for k in keyids])
         self._handle_io(args, data, result, binary=True)
         logger.debug('send_keys result: %r', result.__dict__)
         data.close()
@@ -985,25 +1021,31 @@
         if secret:
             which='secret-key'
         if _is_sequence(fingerprints):
-            fingerprints = ' '.join([shell_quote(s) for s in fingerprints])
+            fingerprints = [no_quote(s) for s in fingerprints]
         else:
-            fingerprints = shell_quote(fingerprints)
-        args = ['--batch --delete-%s %s' % (which, fingerprints)]
+            fingerprints = [no_quote(fingerprints)]
+        args = ['--batch', '--delete-%s' % which]
+        args.extend(fingerprints)
         result = self.result_map['delete'](self)
         p = self._open_subprocess(args)
         self._collect_output(p, result, stdin=p.stdin)
         return result
 
-    def export_keys(self, keyids, secret=False):
+    def export_keys(self, keyids, secret=False, armor=True, minimal=False):
         "export the indicated keys. 'keyid' is anything gpg accepts"
         which=''
         if secret:
             which='-secret-key'
         if _is_sequence(keyids):
-            keyids = ' '.join([shell_quote(k) for k in keyids])
+            keyids = [no_quote(k) for k in keyids]
         else:
-            keyids = shell_quote(keyids)
-        args = ['--armor --export%s %s' % (which, keyids)]
+            keyids = [no_quote(keyids)]
+        args = ['--export%s' % which]
+        if armor:
+            args.append('--armor')
+        if minimal:
+            args.extend(['--export-options','export-minimal'])
+        args.extend(keyids)
         p = self._open_subprocess(args)
         # gpg --export produces no status-fd output; stdout will be
         # empty in case of failure
@@ -1013,6 +1055,27 @@
         logger.debug('export_keys result: %r', result.data)
         return result.data.decode(self.encoding, self.decode_errors)
 
+    def _get_list_output(self, p, kind):
+        # Get the response information
+        result = self.result_map[kind](self)
+        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()
+        for line in lines:
+            if self.verbose:
+                print(line)
+            logger.debug("line: %r", line.rstrip())
+            if not line:
+                break
+            L = line.strip().split(':')
+            if not L:
+                continue
+            keyword = L[0]
+            if keyword in valid_keywords:
+                getattr(result, keyword)(L)
+        return result
+
     def list_keys(self, secret=False):
         """ list the keys currently in the keyring
 
@@ -1033,32 +1096,23 @@
         which='keys'
         if secret:
             which='secret-keys'
-        args = "--list-%s --fixed-list-mode --fingerprint --with-colons" % 
(which,)
-        args = [args]
+        args = ["--list-%s" % which, "--fixed-list-mode", "--fingerprint",
+                "--with-colons"]
         p = self._open_subprocess(args)
+        return self._get_list_output(p, 'list')
 
-        # there might be some status thingumy here I should handle... (amk)
-        # ...nope, unless you care about expired sigs or keys (stevegt)
+    def scan_keys(self, filename):
+        """
+        List details of an ascii armored or binary key file
+        without first importing it to the local keyring.
 
-        # Get the response information
-        result = self.result_map['list'](self)
-        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()
-        for line in lines:
-            if self.verbose:
-                print(line)
-            logger.debug("line: %r", line.rstrip())
-            if not line:
-                break
-            L = line.strip().split(':')
-            if not L:
-                continue
-            keyword = L[0]
-            if keyword in valid_keywords:
-                getattr(result, keyword)(L)
-        return result
+        The function achieves this by running:
+        $ gpg --with-fingerprint --with-colons filename
+        """
+        args = ['--with-fingerprint', '--with-colons']
+        args.append(no_quote(filename))
+        p = self._open_subprocess(args)
+        return self._get_list_output(p, 'scan')
 
     def search_keys(self, query, keyserver='pgp.mit.edu'):
         """ search keyserver by query (using --search-keys option)
@@ -1068,16 +1122,18 @@
         >>> gpg = GPG(gnupghome='keys')
         >>> os.chmod('keys', 0x1C0)
         >>> result = gpg.search_keys('<vinay_sa...@hotmail.com>')
-        >>> assert result
+        >>> assert result, 'Failed using default keyserver'
         >>> keyserver = 'keyserver.ubuntu.com'
         >>> result = gpg.search_keys('<vinay_sa...@hotmail.com>', keyserver)
-        >>> assert result
+        >>> assert result, 'Failed using keyserver.ubuntu.com'
 
         """
-
+        query = query.strip()
+        if HEX_DIGITS_RE.match(query):
+            query = '0x' + query
         args = ['--fixed-list-mode', '--fingerprint', '--with-colons',
-                '--keyserver', shell_quote(keyserver), '--search-keys',
-                shell_quote(query)]
+                '--keyserver', no_quote(keyserver), '--search-keys',
+                no_quote(query)]
         p = self._open_subprocess(args)
 
         # Get the response information
@@ -1112,7 +1168,7 @@
         >>> assert not result
 
         """
-        args = ["--gen-key --batch"]
+        args = ["--gen-key", "--batch"]
         result = self.result_map['generate'](self)
         f = _make_binary_stream(input, self.encoding)
         self._handle_io(args, f, result, binary=True)
@@ -1179,24 +1235,26 @@
             # such as AES256
             args = ['--symmetric']
             if symmetric is not True:
-                args.extend(['--cipher-algo', shell_quote(symmetric)])
+                args.extend(['--cipher-algo', no_quote(symmetric)])
             # else use the default, currently CAST5
         else:
-            args = ['--encrypt']
+            if not recipients:
+                raise ValueError('No recipients specified with asymmetric '
+                                 'encryption')
             if not _is_sequence(recipients):
                 recipients = (recipients,)
             for recipient in recipients:
-                args.append('--recipient %s' % shell_quote(recipient))
+                args.extend(['--recipient', no_quote(recipient)])
         if armor:   # create ascii-armored output - False for binary output
             args.append('--armor')
         if output:  # write the output to a file with the specified name
-            if os.path.exists(output):
-                os.remove(output) # to avoid overwrite confirmation message
-            args.append('--output %s' % shell_quote(output))
-        if sign:
-            args.append('--sign --default-key %s' % shell_quote(sign))
+            self.set_output_without_confirmation(args, output)
+        if sign is True:
+            args.append('--sign')
+        elif sign:
+            args.extend(['--sign', '--default-key', no_quote(sign)])
         if always_trust:
-            args.append("--always-trust")
+            args.append('--always-trust')
         result = self.result_map['crypt'](self)
         self._handle_io(args, file, result, passphrase=passphrase, binary=True)
         logger.debug('encrypt result: %r', result.data)
@@ -1258,13 +1316,10 @@
                      output=None):
         args = ["--decrypt"]
         if output:  # write the output to a file with the specified name
-            if os.path.exists(output):
-                os.remove(output) # to avoid overwrite confirmation message
-            args.append('--output %s' % shell_quote(output))
+            self.set_output_without_confirmation(args, output)
         if always_trust:
             args.append("--always-trust")
         result = self.result_map['crypt'](self)
         self._handle_io(args, file, result, passphrase, binary=True)
         logger.debug('decrypt result: %r', result.data)
         return result
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-gnupg-0.3.5/setup.py 
new/python-gnupg-0.3.7/setup.py
--- old/python-gnupg-0.3.5/setup.py     2013-01-23 12:01:38.000000000 +0100
+++ new/python-gnupg-0.3.7/setup.py     2014-11-03 10:47:28.000000000 +0100
@@ -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-2013 by Vinay Sajip. All Rights Reserved. 
See LICENSE for license.""",
+    license="""Copyright (C) 2008-2014 by Vinay Sajip. All Rights Reserved. 
See LICENSE.txt for license.""",
     version=version,
     author="Vinay Sajip",
     author_email="vinay_sa...@red-dove.com",
@@ -16,7 +16,7 @@
     url="http://packages.python.org/python-gnupg/index.html";,
     py_modules=["gnupg"],
     platforms="No particular restrictions",
-    
download_url="http://python-gnupg.googlecode.com/files/python-gnupg-%s.tar.gz"; 
% version,
+    
download_url="https://pypi.python.org/packages/source/p/python-gnupg/python-gnupg-%s.tar.gz";
 % version,
     classifiers=[
         'Development Status :: 5 - Production/Stable',
         "Intended Audience :: Developers",
@@ -28,9 +28,9 @@
         "Programming Language :: Python :: 2.5",
         "Programming Language :: Python :: 2.6",
         "Programming Language :: Python :: 2.7",
-        "Programming Language :: Python :: 3.0",
-        "Programming Language :: Python :: 3.1",
         "Programming Language :: Python :: 3.2",
+        "Programming Language :: Python :: 3.3",
+        "Programming Language :: Python :: 3.4",
         "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.5/test_gnupg.py 
new/python-gnupg-0.3.7/test_gnupg.py
--- old/python-gnupg-0.3.5/test_gnupg.py        2013-08-30 19:10:57.000000000 
+0200
+++ new/python-gnupg-0.3.7/test_gnupg.py        2014-12-07 19:46:41.000000000 
+0100
@@ -2,13 +2,14 @@
 """
 A test harness for gnupg.py.
 
-Copyright (C) 2008-2013 Vinay Sajip. All rights reserved.
+Copyright (C) 2008-2014 Vinay Sajip. All rights reserved.
 """
 import doctest
 import logging
 import os.path
 import os
 import shutil
+import stat
 import sys
 import tempfile
 import unittest
@@ -16,7 +17,7 @@
 import gnupg
 
 __author__ = "Vinay Sajip"
-__date__  = "$30-Aug-2013 18:10:57$"
+__date__  = "$07-Dec-2014 18:46:40$"
 
 ALL_TESTS = True
 
@@ -98,9 +99,18 @@
                             "Not a directory: %s" % hd)
             shutil.rmtree(hd)
         self.homedir = hd
-        self.gpg = gnupg.GPG(gnupghome=hd, gpgbinary=GPGBINARY)
-        if self.gpg.version and self.gpg.version >= (2,):
-            self.gpg.options = ['--debug-quick-random']
+        self.gpg = gpg = gnupg.GPG(gnupghome=hd, gpgbinary=GPGBINARY)
+        v = gpg.version
+        if v:
+            if v >= (2,):
+                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):
+            data_file = open(test_fn, 'wb')
+            data_file.write(os.urandom(5120 * 1024))
+            data_file.close()
 
     def test_environment(self):
         "Test the environment by ensuring that setup worked"
@@ -129,18 +139,20 @@
             'Name-Comment': 'A test user',
             'Expire-Date': 0,
         }
-        if '--debug-quick-random' in (self.gpg.options or []):
+        options = self.gpg.options or []
+        if '--debug-quick-random' in options or '--quick-random' in options:
             # If using the fake RNG, a key isn't regarded as valid
             # unless its comment has the text (insecure!) in it.
             params['Name-Comment'] = 'A test user (insecure!)'
         params['Name-Real'] = '%s %s' % (first_name, last_name)
-        params['Name-Email'] = ("%s.%s@%s" % (first_name, last_name, 
domain)).lower()
+        params['Name-Email'] = ("%s.%s@%s" % (first_name, last_name,
+                                              domain)).lower()
         if passphrase is None:
             passphrase = ("%s%s" % (first_name[0], last_name)).lower()
         params['Passphrase'] = passphrase
         cmd = self.gpg.gen_key_input(**params)
         return self.gpg.gen_key(cmd)
-    
+
     def do_key_generation(self):
         "Test that key generation succeeds"
         result = self.generate_key("Barbara", "Brown", "beta.com")
@@ -210,7 +222,7 @@
         params['key_type'] = 'DSA'
         cmd = self.gpg.gen_key_input(**params)
         self.assertTrue('Key-Type: DSA\n' in cmd)
-        
+
     def test_list_keys_after_generation(self):
         "Test that after key generation, the generated key is available"
         self.test_list_keys_initial()
@@ -231,6 +243,20 @@
         private_keys_2 = gpg.list_keys(secret=True)
         self.assertEqual(private_keys_2, private_keys)
 
+    def test_scan_keys(self):
+        "Test that external key files can be scanned"
+        expected = set([
+            'Andrew Able (A test user) <andrew.a...@alpha.com>',
+            'Barbara Brown (A test user) <barbara.br...@beta.com>',
+            'Charlie Clark (A test user) <charlie.cl...@gamma.com>',
+        ])
+        for fn in ('test_pubring.gpg', 'test_secring.gpg'):
+            data = self.gpg.scan_keys(fn)
+            uids = set()
+            for d in data:
+                uids.add(d['uids'][0])
+            self.assertEqual(uids, expected)
+
     def test_encryption_and_decryption(self):
         "Test that encryption and decryption works"
         logger.debug("test_encryption_and_decryption begins")
@@ -266,11 +292,12 @@
         ddata = gpg.decrypt(edata, passphrase='bbrown')
         self.assertEqual(data, str(ddata))
         # Test symmetric encryption with non-default cipher
-        data = "chippy was here"
         edata = str(gpg.encrypt(data, None, passphrase='bbrown',
                     symmetric='AES256'))
         ddata = gpg.decrypt(edata, passphrase='bbrown')
         self.assertEqual(data, str(ddata))
+        # Test that you can't encrypt with no recipients
+        self.assertRaises(ValueError, self.gpg.encrypt, data, [])
 
     def test_import_and_export(self):
         "Test that key import and export works"
@@ -346,11 +373,7 @@
                          "Fingerprints must match")
         self.assertEqual(verified.trust_level, verified.TRUST_ULTIMATE)
         self.assertEqual(verified.trust_text, 'TRUST_ULTIMATE')
-        if not os.path.exists('random_binary_data'):
-            data_file = open('random_binary_data', 'wb')
-            data_file.write(os.urandom(5120 * 1024))
-            data_file.close()
-        data_file = open('random_binary_data', 'rb')
+        data_file = open(self.test_fn, 'rb')
         sig = self.gpg.sign_file(data_file, keyid=key.fingerprint,
                                  passphrase='aable')
         data_file.close()
@@ -367,7 +390,7 @@
             logger.debug("ver: %r", verified.fingerprint)
         self.assertEqual(key.fingerprint, verified.fingerprint,
                          "Fingerprints must match")
-        data_file = open('random_binary_data', 'rb')
+        data_file = open(self.test_fn, 'rb')
         sig = self.gpg.sign_file(data_file, keyid=key.fingerprint,
                                  passphrase='aable', detach=True)
         data_file.close()
@@ -375,7 +398,7 @@
         self.assertTrue(sig.hash_algo)
         try:
             file = gnupg._make_binary_stream(sig.data, self.gpg.encoding)
-            verified = self.gpg.verify_file(file, 'random_binary_data')
+            verified = self.gpg.verify_file(file, self.test_fn)
         except UnicodeDecodeError: #happens in Python 2.6
             from io import BytesIO
             verified = self.gpg.verify_file(BytesIO(sig.data))
@@ -384,8 +407,52 @@
             logger.debug("ver: %r", verified.fingerprint)
         self.assertEqual(key.fingerprint, verified.fingerprint,
                          "Fingerprints must match")
+        # Test in-memory verification
+        data_file = open(self.test_fn, 'rb')
+        data = data_file.read()
+        data_file.close()
+        fd, fn = tempfile.mkstemp()
+        os.write(fd, sig.data)
+        os.close(fd)
+        try:
+            verified = self.gpg.verify_data(fn, data)
+        finally:
+            os.unlink(fn)
+        if key.fingerprint != verified.fingerprint:
+            logger.debug("key: %r", key.fingerprint)
+            logger.debug("ver: %r", verified.fingerprint)
+        self.assertEqual(key.fingerprint, verified.fingerprint,
+                         "Fingerprints must match")
         logger.debug("test_signature_verification ends")
 
+    def test_signature_file(self):
+        "Test that signing and verification works via the GPG output"
+        logger.debug("test_signature_file begins")
+        key = self.generate_key("Andrew", "Able", "alpha.com")
+        data_file = open(self.test_fn, 'rb')
+        sig_file = self.test_fn + '.asc'
+        sig = self.gpg.sign_file(data_file, keyid=key.fingerprint,
+                                 passphrase='aable', detach=True,
+                                 output=sig_file)
+        data_file.close()
+        self.assertTrue(sig, "File signing should succeed")
+        self.assertTrue(sig.hash_algo)
+        self.assertTrue(os.path.exists(sig_file))
+        # Test in-memory verification
+        data_file = open(self.test_fn, 'rb')
+        data = data_file.read()
+        data_file.close()
+        try:
+            verified = self.gpg.verify_data(sig_file, data)
+        finally:
+            os.unlink(sig_file)
+        if key.fingerprint != verified.fingerprint:
+            logger.debug("key: %r", key.fingerprint)
+            logger.debug("ver: %r", verified.fingerprint)
+        self.assertEqual(key.fingerprint, verified.fingerprint,
+                         "Fingerprints must match")
+        logger.debug("test_signature_file ends")
+
     def test_deletion(self):
         "Test that key deletion works"
         logger.debug("test_deletion begins")
@@ -401,7 +468,7 @@
 
     def test_nogpg(self):
         "Test that absence of gpg is handled correctly"
-        self.assertRaises(ValueError, gnupg.GPG, gnupghome=self.homedir,
+        self.assertRaises(OSError, gnupg.GPG, gnupghome=self.homedir,
                           gpgbinary='frob')
 
     def test_make_args(self):
@@ -411,14 +478,16 @@
         self.assertTrue(len(args) > 4)
         self.assertEqual(args[-4:], ['--foo', '--bar', 'a', 'b'])
 
-    def test_file_encryption_and_decryption(self):
-        "Test that encryption/decryption to/from file works"
-        logger.debug("test_file_encryption_and_decryption begins")
-        encfno, encfname = tempfile.mkstemp()
-        decfno, decfname = tempfile.mkstemp()
-        # On Windows, if the handles aren't closed, the files can't be deleted
-        os.close(encfno)
-        os.close(decfno)
+    def do_file_encryption_and_decryption(self, encfname, decfname):
+        "Do the actual encryption.decryptin test using given filenames"
+        mode = None
+        if os.name == 'posix':
+            # pick a mode that won't be already in effect via umask
+            if os.path.exists(encfname) and os.path.exists(decfname):
+                mode = os.stat(encfname).st_mode | stat.S_IXUSR
+                os.chmod(encfname, mode)
+                # assume same for decfname
+                os.chmod(decfname, mode)
         logger.debug('Encrypting to: %r', encfname)
         logger.debug('Decrypting to: %r', decfname)
         try:
@@ -448,10 +517,36 @@
             self.assertEqual(data, ddata, "Round-trip must work")
         finally:
             for fn in (encfname, decfname):
+                if os.name == 'posix' and mode is not None:
+                    # Check that the file wasn't deleted, and that the
+                    # mode bits we set are still in effect
+                    self.assertEqual(os.stat(fn).st_mode, mode)
                 if os.path.exists(fn):
                     os.remove(fn)
+
+    def test_file_encryption_and_decryption(self):
+        "Test that encryption/decryption to/from file works"
+        logger.debug("test_file_encryption_and_decryption begins")
+        encfno, encfname = tempfile.mkstemp()
+        decfno, decfname = tempfile.mkstemp()
+        # On Windows, if the handles aren't closed, the files can't be deleted
+        os.close(encfno)
+        os.close(decfno)
+        self.do_file_encryption_and_decryption(encfname, decfname)
         logger.debug("test_file_encryption_and_decryption ends")
 
+    def test_filenames_with_spaces(self):       # See Issue #16
+        "Test that filenames with spaces are correctly handled"
+        logger.debug("test_filename_with_spaces begins")
+        d = tempfile.mkdtemp()
+        try:
+            encfname = os.path.join(d, 'encrypted file')
+            decfname = os.path.join(d, 'decrypted file')
+            self.do_file_encryption_and_decryption(encfname, decfname)
+        finally:
+            shutil.rmtree(d)
+        logger.debug("test_filename_with_spaces ends")
+
     def test_search_keys(self):
         "Test that searching for keys works"
         r = self.gpg.search_keys('<vinay_sa...@hotmail.com>')
@@ -461,29 +556,71 @@
         self.assertTrue(r)
         self.assertTrue('Vinay Sajip <vinay_sa...@hotmail.com>' in 
r[0]['uids'])
 
+    def test_quote_with_shell(self):
+        "Test shell quoting with a real shell"
+        if os.name != 'posix': return
+
+        from subprocess import PIPE, Popen
+
+        workdir = tempfile.mkdtemp()
+        try:
+            s = "'\\\"; touch %s/foo #'" % workdir
+            cmd = 'echo %s' % gnupg.shell_quote(s)
+            p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
+            p.communicate()
+            self.assertEqual(p.returncode, 0)
+            files = os.listdir(workdir)
+            self.assertEqual(files, [])
+            fn = "'ab?'"
+            cmd = 'touch %s/%s' % (workdir, gnupg.shell_quote(fn))
+            p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
+            p.communicate()
+            self.assertEqual(p.returncode, 0)
+            files = os.listdir(workdir)
+            self.assertEqual(files, ["'ab?'"])
+        finally:
+            shutil.rmtree(workdir)
+
+    def disabled_test_signing_with_uid(self):
+        "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")
+        uid = self.gpg.list_keys(True)[-1]['uids'][0]
+        try:
+            signfile = open(self.test_fn,'rb')
+            signed = self.gpg.sign_file(signfile, keyid=uid,
+                                        passphrase='aable',
+                                        detach=True)
+        finally:
+            signfile.close()
+        self.assertTrue(signed.data)
+        logger.debug("test_signing_with_uid ends")
 
 TEST_GROUPS = {
     'sign' : set(['test_signature_verification']),
     'crypt' : set(['test_encryption_and_decryption',
-                   'test_file_encryption_and_decryption']),
+                   'test_file_encryption_and_decryption',
+                   'test_filenames_with_spaces']),
     'key' : set(['test_deletion', 'test_import_and_export',
                  'test_list_keys_after_generation',
                  'test_key_generation_with_invalid_key_type',
                  'test_key_generation_with_escapes',
                  'test_key_generation_with_empty_value',
                  'test_key_generation_with_colons',
-                 'test_search_keys']),
+                 'test_search_keys', 'test_scan_keys']),
     'import' : set(['test_import_only']),
     'basic' : set(['test_environment', 'test_list_keys_initial',
-                   'test_nogpg', 'test_make_args']),
+                   'test_nogpg', 'test_make_args',
+                   'test_quote_with_shell']),
+    'test': set(['test_search_keys']),
 }
 
 def suite(args=None):
     if args is None:
         args = sys.argv[1:]
-    if not args:
+    if not args or args == ['--no-doctests']:
         result = unittest.TestLoader().loadTestsFromTestCase(GPGTestCase)
-        want_doctests = True
+        want_doctests = not args
     else:
         tests = set()
         want_doctests = False
@@ -493,7 +630,7 @@
             elif arg == "doc":
                 want_doctests = True
             else:
-                print("Ignoring unknown test group %r" % arg)        
+                print("Ignoring unknown test group %r" % arg)
         result = unittest.TestSuite(list(map(GPGTestCase, tests)))
     if want_doctests:
         result.addTest(doctest.DocTestSuite(gnupg))


Reply via email to