From f4ba16b31ea282d0787a40be3f37b951584143a1 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Tue, 10 May 2016 13:19:26 +0200 Subject: python: Rename bindings. -- Signed-off-by: Justus Winter --- lang/python/pyme/core.py | 463 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 463 insertions(+) create mode 100644 lang/python/pyme/core.py (limited to 'lang/python/pyme/core.py') diff --git a/lang/python/pyme/core.py b/lang/python/pyme/core.py new file mode 100644 index 00000000..b03cedb0 --- /dev/null +++ b/lang/python/pyme/core.py @@ -0,0 +1,463 @@ +# $Id$ +# Copyright (C) 2004,2008 Igor Belyi +# Copyright (C) 2002 John Goerzen +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# import generators for portability with python2.2 + + +from . import pygpgme +from .errors import errorcheck +from . import errors +from .util import GpgmeWrapper + +class Context(GpgmeWrapper): + """From the GPGME C documentation: + + * All cryptographic operations in GPGME are performed within a + * context, which contains the internal state of the operation as well as + * configuration parameters. By using several contexts you can run + * several cryptographic operations in parallel, with different + * configuration. + + Thus, this is the place that you will usually start.""" + + def _getctype(self): + return 'gpgme_ctx_t' + + def _getnameprepend(self): + return 'gpgme_' + + def _errorcheck(self, name): + """This function should list all functions returning gpgme_error_t""" + if (name.startswith('gpgme_op_') and \ + not name.endswith('_result')) or \ + name == 'gpgme_signers_add' or \ + name == 'gpgme_set_locale' or \ + name == 'gpgme_set_keylist_mode' or \ + name == 'gpgme_set_protocol': + return 1 + return 0 + + def __init__(self, wrapped=None): + if wrapped: + self.wrapped = wrapped + self.own = False + else: + tmp = pygpgme.new_gpgme_ctx_t_p() + errorcheck(pygpgme.gpgme_new(tmp)) + self.wrapped = pygpgme.gpgme_ctx_t_p_value(tmp) + pygpgme.delete_gpgme_ctx_t_p(tmp) + self.own = True + self.last_passcb = None + self.last_progresscb = None + + def __del__(self): + self._free_passcb() + self._free_progresscb() + if self.own: + pygpgme.gpgme_release(self.wrapped) + + def _free_passcb(self): + if self.last_passcb != None: + pygpgme.pygpgme_clear_generic_cb(self.last_passcb) + pygpgme.delete_PyObject_p_p(self.last_passcb) + self.last_passcb = None + + def _free_progresscb(self): + if self.last_progresscb != None: + pygpgme.pygpgme_clear_generic_cb(self.last_progresscb) + pygpgme.delete_PyObject_p_p(self.last_progresscb) + self.last_progresscb = None + + def op_keylist_all(self, *args, **kwargs): + self.op_keylist_start(*args, **kwargs) + key = self.op_keylist_next() + while key: + yield key + key = self.op_keylist_next() + + def op_keylist_next(self): + """Returns the next key in the list created + by a call to op_keylist_start(). The object returned + is of type Key.""" + ptr = pygpgme.new_gpgme_key_t_p() + try: + errorcheck(pygpgme.gpgme_op_keylist_next(self.wrapped, ptr)) + key = pygpgme.gpgme_key_t_p_value(ptr) + except errors.GPGMEError as excp: + key = None + if excp.getcode() != errors.EOF: + raise excp + pygpgme.delete_gpgme_key_t_p(ptr) + if key: + key.__del__ = lambda self: pygpgme.gpgme_key_unref(self) + return key + + def get_key(self, fpr, secret): + """Return the key corresponding to the fingerprint 'fpr'""" + ptr = pygpgme.new_gpgme_key_t_p() + errorcheck(pygpgme.gpgme_get_key(self.wrapped, fpr, ptr, secret)) + key = pygpgme.gpgme_key_t_p_value(ptr) + pygpgme.delete_gpgme_key_t_p(ptr) + if key: + key.__del__ = lambda self: pygpgme.gpgme_key_unref(self) + return key + + def op_trustlist_all(self, *args, **kwargs): + self.op_trustlist_start(*args, **kwargs) + trust = self.ctx.op_trustlist_next() + while trust: + yield trust + trust = self.ctx.op_trustlist_next() + + def op_trustlist_next(self): + """Returns the next trust item in the list created + by a call to op_trustlist_start(). The object returned + is of type TrustItem.""" + ptr = pygpgme.new_gpgme_trust_item_t_p() + try: + errorcheck(pygpgme.gpgme_op_trustlist_next(self.wrapped, ptr)) + trust = pygpgme.gpgme_trust_item_t_p_value(ptr) + except errors.GPGMEError as excp: + trust = None + if excp.getcode() != errors.EOF: + raise + pygpgme.delete_gpgme_trust_item_t_p(ptr) + return trust + + def set_passphrase_cb(self, func, hook=None): + """Sets the passphrase callback to the function specified by func. + + When the system needs a passphrase, it will call func with three args: + hint, a string describing the key it needs the passphrase for; + desc, a string describing the passphrase it needs; + prev_bad, a boolean equal True if this is a call made after + unsuccessful previous attempt. + + If hook has a value other than None it will be passed into the func + as a forth argument. + + Please see the GPGME manual for more information. + """ + self._free_passcb() + if func == None: + hookdata = None + else: + self.last_passcb = pygpgme.new_PyObject_p_p() + if hook == None: + hookdata = func + else: + hookdata = (func, hook) + pygpgme.pygpgme_set_passphrase_cb(self.wrapped, hookdata, self.last_passcb) + + def set_progress_cb(self, func, hook=None): + """Sets the progress meter callback to the function specified by + + This function will be called to provide an interactive update of + the system's progress. + + Please see the GPGME manual for more information.""" + self._free_progresscb() + if func == None: + hookdata = None + else: + self.last_progresscb = pygpgme.new_PyObject_p_p() + if hook == None: + hookdata = func + else: + hookdata = (func, hook) + pygpgme.pygpgme_set_progress_cb(self.wrapped, hookdata, self.last_progresscb) + + def get_engine_info(self): + """Returns this context specific engine info""" + return pygpgme.gpgme_ctx_get_engine_info(self.wrapped) + + def set_engine_info(self, proto, file_name, home_dir=None): + """Changes the configuration of the crypto engine implementing the + protocol 'proto' for the context. 'file_name' is the file name of + the executable program implementing this protocol. 'home_dir' is the + directory name of the configuration directory (engine's default is + used if omitted).""" + errorcheck(pygpgme.gpgme_ctx_set_engine_info(self.wrapped, proto, file_name, home_dir)) + + def wait(self, hang): + """Wait for asynchronous call to finish. Wait forever if hang is True + + Return: + On an async call completion its return status. + On timeout - None. + + Please read the GPGME manual for more information.""" + ptr = pygpgme.new_gpgme_error_t_p() + context = pygpgme.gpgme_wait(self.wrapped, ptr, hang) + status = pygpgme.gpgme_error_t_p_value(ptr) + pygpgme.delete_gpgme_error_t_p(ptr) + + if context == None: + errorcheck(status) + return None + else: + return status + + def op_edit(self, key, func, fnc_value, out): + """Start key editing using supplied callback function""" + if key == None: + raise ValueError("op_edit: First argument cannot be None") + opaquedata = (func, fnc_value) + errorcheck(pygpgme.gpgme_op_edit(self.wrapped, key, opaquedata, out)) + +class Data(GpgmeWrapper): + """From the GPGME C manual: + +* A lot of data has to be exchanged between the user and the crypto +* engine, like plaintext messages, ciphertext, signatures and information +* about the keys. The technical details about exchanging the data +* information are completely abstracted by GPGME. The user provides and +* receives the data via `gpgme_data_t' objects, regardless of the +* communication protocol between GPGME and the crypto engine in use. + + This Data class is the implementation of the GpgmeData objects. + + Please see the information about __init__ for instantiation.""" + + def _getctype(self): + return 'gpgme_data_t' + + def _getnameprepend(self): + return 'gpgme_data_' + + def _errorcheck(self, name): + """This function should list all functions returning gpgme_error_t""" + if name == 'gpgme_data_release_and_get_mem' or \ + name == 'gpgme_data_get_encoding' or \ + name == 'gpgme_data_seek': + return 0 + return 1 + + def __init__(self, string = None, file = None, offset = None, + length = None, cbs = None): + """Initialize a new gpgme_data_t object. + + If no args are specified, make it an empty object. + + If string alone is specified, initialize it with the data + contained there. + + If file, offset, and length are all specified, file must + be either a filename or a file-like object, and the object + will be initialized by reading the specified chunk from the file. + + If cbs is specified, it MUST be a tuple of the form: + + ((read_cb, write_cb, seek_cb, release_cb), hook) + + where func is a callback function taking two arguments (count, + hook) and returning a string of read data, or None on EOF. + This will supply the read() method for the system. + + If file is specified without any other arguments, then + it must be a filename, and the object will be initialized from + that file. + + Any other use will result in undefined or erroneous behavior.""" + self.wrapped = None + self.last_readcb = None + + if cbs != None: + self.new_from_cbs(*cbs) + elif string != None: + self.new_from_mem(string) + elif file != None and offset != None and length != None: + self.new_from_filepart(file, offset, length) + elif file != None: + if type(file) == type("x"): + self.new_from_file(file) + else: + self.new_from_fd(file) + else: + self.new() + + def __del__(self): + if self.wrapped != None: + pygpgme.gpgme_data_release(self.wrapped) + self._free_readcb() + + def _free_readcb(self): + if self.last_readcb != None: + pygpgme.pygpgme_clear_generic_cb(self.last_readcb) + pygpgme.delete_PyObject_p_p(self.last_readcb) + self.last_readcb = None + + def new(self): + tmp = pygpgme.new_gpgme_data_t_p() + errorcheck(pygpgme.gpgme_data_new(tmp)) + self.wrapped = pygpgme.gpgme_data_t_p_value(tmp) + pygpgme.delete_gpgme_data_t_p(tmp) + + def new_from_mem(self, string, copy = 1): + tmp = pygpgme.new_gpgme_data_t_p() + errorcheck(pygpgme.gpgme_data_new_from_mem(tmp,string,len(string),copy)) + self.wrapped = pygpgme.gpgme_data_t_p_value(tmp) + pygpgme.delete_gpgme_data_t_p(tmp) + + def new_from_file(self, filename, copy = 1): + tmp = pygpgme.new_gpgme_data_t_p() + errorcheck(pygpgme.gpgme_data_new_from_file(tmp, filename, copy)) + self.wrapped = pygpgme.gpgme_data_t_p_value(tmp) + pygpgme.delete_gpgme_data_t_p(tmp) + + def new_from_cbs(self, funcs, hook): + """Argument funcs must be a 4 element tuple with callbacks: + (read_cb, write_cb, seek_cb, release_cb)""" + tmp = pygpgme.new_gpgme_data_t_p() + self._free_readcb() + self.last_readcb = pygpgme.new_PyObject_p_p() + hookdata = (funcs, hook) + pygpgme.pygpgme_data_new_from_cbs(tmp, hookdata, self.last_readcb) + self.wrapped = pygpgme.gpgme_data_t_p_value(tmp) + pygpgme.delete_gpgme_data_t_p(tmp) + + def new_from_filepart(self, file, offset, length): + """This wraps the GPGME gpgme_data_new_from_filepart() function. + The argument "file" may be: + + 1. a string specifying a file name, or + 3. a a file-like object. supporting the fileno() call and the mode + attribute.""" + + tmp = pygpgme.new_gpgme_data_t_p() + filename = None + fp = None + + if type(file) == type("x"): + filename = file + else: + fp = pygpgme.fdopen(file.fileno(), file.mode) + if fp == None: + raise ValueError("Failed to open file from %s arg %s" % \ + (str(type(file)), str(file))) + + errorcheck(pygpgme.gpgme_data_new_from_filepart(tmp, filename, fp, + offset, length)) + self.wrapped = pygpgme.gpgme_data_t_p_value(tmp) + pygpgme.delete_gpgme_data_t_p(tmp) + + def new_from_fd(self, file): + """This wraps the GPGME gpgme_data_new_from_fd() function. + The argument "file" may be a file-like object, supporting the fileno() + call and the mode attribute.""" + + tmp = pygpgme.new_gpgme_data_t_p() + fp = pygpgme.fdopen(file.fileno(), file.mode) + if fp == None: + raise ValueError("Failed to open file from %s arg %s" % \ + (str(type(file)), str(file))) + errorcheck(gpgme_data_new_from_fd(tmp, fp)) + self.wrapped = pygpgme.gpgme_data_t_p_value(tmp) + pygpgme.delete_gpgme_data_t_p(tmp) + + def new_from_stream(self, file): + """This wrap around gpgme_data_new_from_stream is an alias for + new_from_fd() method since in python there's not difference + between file stream and file descriptor""" + self.new_from_fd(file) + + def write(self, buffer): + errorcheck(pygpgme.gpgme_data_write(self.wrapped, buffer, len(buffer))) + + def read(self, size = -1): + """Read at most size bytes, returned as a string. + + If the size argument is negative or omitted, read until EOF is reached. + + Returns the data read, or the empty string if there was no data + to read before EOF was reached.""" + + if size == 0: + return '' + + if size > 0: + return pygpgme.gpgme_data_read(self.wrapped, size) + else: + retval = '' + while 1: + result = pygpgme.gpgme_data_read(self.wrapped, 10240) + if len(result) == 0: + break + retval += result + return retval + +def pubkey_algo_name(algo): + return pygpgme.gpgme_pubkey_algo_name(algo) + +def hash_algo_name(algo): + return pygpgme.gpgme_hash_algo_name(algo) + +def get_protocol_name(proto): + return pygpgme.gpgme_get_protocol_name(proto) + +def check_version(version=None): + return pygpgme.gpgme_check_version(version) + +def engine_check_version (proto): + try: + errorcheck(pygpgme.gpgme_engine_check_version(proto)) + return True + except errors.GPGMEError: + return False + +def get_engine_info(): + ptr = pygpgme.new_gpgme_engine_info_t_p() + try: + errorcheck(pygpgme.gpgme_get_engine_info(ptr)) + info = pygpgme.gpgme_engine_info_t_p_value(ptr) + except errors.GPGMEError: + info = None + pygpgme.delete_gpgme_engine_info_t_p(ptr) + return info + +def set_engine_info(proto, file_name, home_dir=None): + """Changes the default configuration of the crypto engine implementing + the protocol 'proto'. 'file_name' is the file name of + the executable program implementing this protocol. 'home_dir' is the + directory name of the configuration directory (engine's default is + used if omitted).""" + errorcheck(pygpgme.gpgme_set_engine_info(proto, file_name, home_dir)) + +def set_locale(category, value): + """Sets the default locale used by contexts""" + errorcheck(pygpgme.gpgme_set_locale(None, category, value)) + +def wait(hang): + """Wait for asynchronous call on any Context to finish. + Wait forever if hang is True. + + For finished anynch calls it returns a tuple (status, context): + status - status return by asnynchronous call. + context - context which caused this call to return. + + Please read the GPGME manual of more information.""" + ptr = pygpgme.new_gpgme_error_t_p() + context = pygpgme.gpgme_wait(None, ptr, hang) + status = pygpgme.gpgme_error_t_p_value(ptr) + pygpgme.delete_gpgme_error_t_p(ptr) + if context == None: + errorcheck(status) + else: + context = Context(context) + return (status, context) + -- cgit From aade53a12b9716997684b872bc2ac87229f73fb3 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Tue, 10 May 2016 13:30:30 +0200 Subject: python: Delete trailing whitespace. -- Signed-off-by: Justus Winter --- lang/python/README.rst | 2 +- lang/python/docs/old-commits.log | 84 +++++++++++++++++----------------- lang/python/examples/encrypt-to-all.py | 2 +- lang/python/examples/signverify.py | 2 +- lang/python/examples/t-edit.py | 2 +- lang/python/examples/testCMSgetkey.py | 4 +- lang/python/examples/verifydetails.py | 2 - lang/python/gpgme.i | 4 +- lang/python/helpers.c | 6 +-- lang/python/pyme/callbacks.py | 2 +- lang/python/pyme/core.py | 26 +++++------ lang/python/pyme/errors.py | 4 +- lang/python/pyme/util.py | 4 +- lang/python/setup.py | 8 ++-- 14 files changed, 74 insertions(+), 78 deletions(-) (limited to 'lang/python/pyme/core.py') diff --git a/lang/python/README.rst b/lang/python/README.rst index 4cad03a0..57df1f29 100644 --- a/lang/python/README.rst +++ b/lang/python/README.rst @@ -22,7 +22,7 @@ Authors * Martin Albrecht, `PyME 0.9+ `_, 2014 to present. * Ben McGinnes, `PyME Python 3 Port `_, 2015 to present. - + ------------ Mailing List ------------ diff --git a/lang/python/docs/old-commits.log b/lang/python/docs/old-commits.log index 4d2bfe09..0db59a08 100644 --- a/lang/python/docs/old-commits.log +++ b/lang/python/docs/old-commits.log @@ -4,7 +4,7 @@ Author: Ben McGinnes Date: Wed May 6 03:04:19 2015 +1000 Merge pull request #4 from Hasimir/master - + history commit 2036f1a0a670a0561993e195c458059220b36114 @@ -19,7 +19,7 @@ Author: Ben McGinnes Date: Wed May 6 02:52:23 2015 +1000 Added a short history - + * A (very) brief summary of the project's history since 2002. * Deals with why the commit log in the GPGME repo does not include the history of PyME. @@ -36,7 +36,7 @@ Author: Ben McGinnes Date: Wed May 6 02:21:34 2015 +1000 Merge pull request #3 from Hasimir/master - + Version release preparation commit 7c37a27a6845c58222d4d947c2efbe38e955b612 @@ -51,7 +51,7 @@ Author: Ben McGinnes Date: Wed May 6 02:09:44 2015 +1000 TODO update - + * Removed reference to GitHub, replaced with impending new home at gnupg.org. docs/TODO.rst | 4 ++-- @@ -62,7 +62,7 @@ Author: Ben McGinnes Date: Wed May 6 02:00:44 2015 +1000 Version bump - + * Bumped version number to 0.9.1 to keep it somewhat in line with the existing PyME project, even though there will be some divergence at some point (or even re-merging, depending on how many of the Python 3 @@ -80,7 +80,7 @@ Author: Ben McGinnes Date: Wed May 6 01:48:01 2015 +1000 README preparation. - + * Changes in preparation for impending move of code to the GnuPG git server as a part of GPGME. @@ -92,7 +92,7 @@ Author: Ben McGinnes Date: Wed May 6 01:43:53 2015 +1000 TODO moved to docs - + * As it says. TODO.rst | 25 ------------------------- @@ -114,7 +114,7 @@ Author: Ben McGinnes Date: Sun May 3 14:59:44 2015 +1000 Merge pull request #2 from Hasimir/master - + Minor editing. commit 44837f6e50fc539c86aef1f75a6a3538b02029ea @@ -122,7 +122,7 @@ Author: Ben McGinnes Date: Sun May 3 14:56:55 2015 +1000 Minor editing. - + * Fixed another URL. * Changed Py3 version's version number to v0.9.1-beta0. @@ -135,7 +135,7 @@ Author: Ben McGinnes Date: Sun May 3 14:26:11 2015 +1000 Merge pull request #1 from Hasimir/master - + Links commit 48eb1856cb0739cc9f0b9084da9d965e1fc7fddd @@ -143,7 +143,7 @@ Author: Ben McGinnes Date: Sun May 3 14:22:30 2015 +1000 Links - + * Fixed URLs for authors. * Updated my entry to point to github location. ** I strongly suspect the result of this work will be concurrent @@ -157,9 +157,9 @@ Author: Ben McGinnes Date: Sun May 3 11:29:00 2015 +1000 Explicit over Implicit ... - + ... isn't just for code. - + * Removed the 2to3 working directory and its contents. * Made the README.rst file a little more clear that this branch is for Python 3 (set Python 3.2 as a fairly arbitrary requirement for the @@ -176,7 +176,7 @@ Author: Ben McGinnes Date: Sun May 3 11:19:41 2015 +1000 Added authors. - + * In alphabetical order. * Mine will need updating once Martin and I have decided what to do regarding the two main branches. @@ -189,7 +189,7 @@ Author: Ben McGinnes Date: Sun May 3 10:23:00 2015 +1000 Docs and other things. - + * Now able to import pyme.core without error, indicates port process is successful. * Code is *not* compatible with the Python 2 version. @@ -227,7 +227,7 @@ Author: Ben McGinnes Date: Sun May 3 10:08:22 2015 +1000 Bytes vs. Unicode - + * Trying PyBytes instead of PyUnicode. gpgme.i | 14 +++++++------- @@ -239,7 +239,7 @@ Author: Ben McGinnes Date: Sun May 3 09:22:58 2015 +1000 String to Unicode - + * Replaced all instances of PyString with PyUnicode (and hoping there's no byte data in there). @@ -252,7 +252,7 @@ Author: Ben McGinnes Date: Sun May 3 09:17:06 2015 +1000 PyInt_AsLong - + * Replaced all instances of PyInt with PyLong, as per C API docs. gpgme.i | 4 ++-- @@ -264,7 +264,7 @@ Author: Ben McGinnes Date: Sun May 3 05:59:36 2015 +1000 Import correction - + * Once pygpgme.py is generated and moved, it will be in the right directory for the explicit "from . import pygpgme" to be correct. @@ -303,10 +303,10 @@ Author: Ben McGinnes Date: Sun May 3 04:53:21 2015 +1000 Pet Peeve - + def pet_peeve(self): peeve = print("people who don't press return after a colon!") - + FFS! pyme/core.py | 5 +++-- @@ -371,7 +371,7 @@ Author: Ben McGinnes Date: Sun May 3 03:51:48 2015 +1000 Linking swig to py3 - + * Changed the swig invocations to run with the -python -py3 flags explicitly. Makefile | 4 ++-- @@ -383,7 +383,7 @@ Author: Ben McGinnes Date: Sat May 2 11:12:00 2015 +1000 String fun - + * streamlined confdata details, including decoding strom binary to string. setup.py | 4 +--- @@ -394,7 +394,7 @@ Author: Ben McGinnes Date: Sat May 2 10:46:59 2015 +1000 Open File - + * Removed deprecated file() and replaced with open(). examples/PyGtkGpgKeys.py | 2 +- @@ -407,7 +407,7 @@ Author: Ben McGinnes Date: Sat May 2 10:36:15 2015 +1000 print() fix - + * Makefile includes a python print, changed from statement to function. Makefile | 2 +- @@ -418,7 +418,7 @@ Author: Ben McGinnes Date: Sat May 2 10:28:42 2015 +1000 Updated Makefile - + * set make to use python3 instead. * This will mean a successful port may need to be maintained seperately from the original python2 code instead of merged, but ought to be able @@ -434,7 +434,7 @@ Author: Ben McGinnes Date: Sat May 2 10:15:20 2015 +1000 Env and a little license issue - + * Updated all the /usr/bin/env paths to point to python3. * Also fixed the hard coded /usr/bin/python paths. * Updated part of setup.py which gave the impression this package was @@ -463,7 +463,7 @@ Author: Ben McGinnes Date: Sat May 2 08:50:54 2015 +1000 Removed extraneous files. - + * The two .bak files. pyme/errors.py.bak | 46 --------------------- @@ -475,7 +475,7 @@ Author: Ben McGinnes Date: Sat May 2 08:19:37 2015 +1000 Added TODO.org - + * TODO list in Emacs org-mode. * Will eventually be removed along with this entire directory when the porting process is complete. @@ -488,7 +488,7 @@ Author: Ben McGinnes Date: Sat May 2 07:58:40 2015 +1000 2to3 conversion of remaining files - + * Ran the extended version against all the unmodified python files. * Only pyme/errors.py required additional work. @@ -502,7 +502,7 @@ Author: Ben McGinnes Date: Sat May 2 07:50:39 2015 +1000 2to3 conversion of setup.py - + * Ran extended 2to3 command to produce python 3 code for setup.py. * Effectively testing for what to run against the other originally unmodified py2 files. @@ -517,7 +517,7 @@ Author: Ben McGinnes Date: Fri May 1 21:50:07 2015 +1000 Removing 2to3 generated .bak files. - + * Not really needed with a real VCS, but couldn't hurt to have them for a couple of revisions. ;) @@ -548,7 +548,7 @@ Author: Ben McGinnes Date: Fri May 1 21:45:50 2015 +1000 2to3 conversion log - + * The output of the command to convert the code from Python 2 to 3. * Note: this contains the list of files which were not modified and which will or may need to be modified. @@ -561,7 +561,7 @@ Author: Ben McGinnes Date: Fri May 1 21:36:58 2015 +1000 2to3 conversion of pyme master - + * Branch from commit 459f3eca659b4949e394c4a032d9ce2053e6c721 * Ran this: or x in `find . | egrep .py$` ; do 2to3 -w $x; done ; * Multiple files not modified, will record elsewhere (see next commit). @@ -614,7 +614,7 @@ Author: Martin Albrecht Date: Wed Jul 9 10:48:33 2014 +0100 Merged in jerrykan/pyme/fix_setup_26 (pull request #1) - + Provide support for using setup.py with Python v2.6 commit dae7f14a54e6c2bde0ad4da7308cc7fc0d0c0469 @@ -622,7 +622,7 @@ Author: John Kristensen Date: Wed Jul 9 15:54:39 2014 +1000 Provide support for using setup.py with Python v2.6 - + The setup.py script uses subprocess.check_output() which was introduced in Python v2.7. The equivalent functionality can be achieved without adding much extra code and provide support for Python v2.6. @@ -701,7 +701,7 @@ Author: Martin Albrecht Date: Sat May 10 15:41:02 2014 +0100 Merge branch 'master' of bitbucket.org:malb/pyme - + Conflicts: setup.py @@ -1018,10 +1018,10 @@ Author: Martin Albrecht Date: Mon Jan 6 17:44:20 2014 +0100 fixing op_export_keys() - + the conversion of gpgme_key_t [] was restricted to gpgme_key_t [] with the name recv, i.e. only the use-cases of encryption were covered. - + see: http://sourceforge.net/mailarchive/forum.php?forum_name=pyme-help&max_rows=25&style=nested&viewmonth=201309 pyme/gpgme.i | 6 +++--- @@ -1618,9 +1618,9 @@ Date: Fri Jun 10 03:01:22 2005 +0000 Update 'docs' rule in Makefile to build packages first to ensure that documentation is build for the current version of pyme and not for the installed one. - + Added 'callbacks' into the list of visible pyme modules (__all__ var.) - + Slightly updated INSTALL file. pyme/INSTALL | 11 ++++++++--- @@ -2100,7 +2100,7 @@ Author: belyi Date: Fri Mar 18 19:09:33 2005 +0000 Added package building for python2.4 - + Updated copyright notes to include myslef and avoid confusion who's the maintainer. In John's own words: "I'd prefer to just step out of the picture". Jonh's copyright notice left intact. diff --git a/lang/python/examples/encrypt-to-all.py b/lang/python/examples/encrypt-to-all.py index 8f9d2500..c890df4f 100755 --- a/lang/python/examples/encrypt-to-all.py +++ b/lang/python/examples/encrypt-to-all.py @@ -54,7 +54,7 @@ for key in c.op_keylist_all(None, 0): (keyid, can_encrypt and "enabled" or "disabled")) except UnicodeEncodeError as e: print(e) - + if valid: names.append(key) else: diff --git a/lang/python/examples/signverify.py b/lang/python/examples/signverify.py index f8804ca9..7194157b 100755 --- a/lang/python/examples/signverify.py +++ b/lang/python/examples/signverify.py @@ -43,7 +43,7 @@ if not c.signers_enum(0): passlist = { b"": b"Crypt0R0cks" } - + # callback will return password based on the e-mail listed in the hint. c.set_passphrase_cb(lambda x,y,z: passlist[x[x.rindex("<"):]]) diff --git a/lang/python/examples/t-edit.py b/lang/python/examples/t-edit.py index 6c533429..5553190c 100644 --- a/lang/python/examples/t-edit.py +++ b/lang/python/examples/t-edit.py @@ -32,7 +32,7 @@ class KeyEditor: out.seek(0,0) print(out.read(), end=' ') print("[-- Code: %d, %s --]" % (status, args)) - + if args == "keyedit.prompt": result = self.steps[self.step] self.step += 1 diff --git a/lang/python/examples/testCMSgetkey.py b/lang/python/examples/testCMSgetkey.py index 53fdef77..7c953012 100644 --- a/lang/python/examples/testCMSgetkey.py +++ b/lang/python/examples/testCMSgetkey.py @@ -22,7 +22,7 @@ from pyme.constants import protocol def printgetkeyresults(keyfpr): """Run gpgme_get_key().""" - # gpgme_check_version() necessary for initialisation according to + # gpgme_check_version() necessary for initialisation according to # gogme 1.1.6 and this is not done automatically in pyme-0.7.0 print("gpgme version:", core.check_version(None)) c = core.Context() @@ -45,5 +45,3 @@ def main(): if __name__ == "__main__": main() - - diff --git a/lang/python/examples/verifydetails.py b/lang/python/examples/verifydetails.py index 0d47ba54..a8d840e7 100755 --- a/lang/python/examples/verifydetails.py +++ b/lang/python/examples/verifydetails.py @@ -96,5 +96,3 @@ def main(): if __name__ == "__main__": main() - - diff --git a/lang/python/gpgme.i b/lang/python/gpgme.i index 0115caf3..87efce73 100644 --- a/lang/python/gpgme.i +++ b/lang/python/gpgme.i @@ -241,14 +241,14 @@ gpgme_error_t pyEditCb(void *opaque, gpgme_status_code_t status, func = pyopaque; pyargs = PyTuple_New(2); } - + PyTuple_SetItem(pyargs, 0, PyLong_FromLong((long) status)); PyTuple_SetItem(pyargs, 1, PyBytes_FromString(args)); if (dataarg) { Py_INCREF(dataarg); /* Because GetItem doesn't give a ref but SetItem taketh away */ PyTuple_SetItem(pyargs, 2, dataarg); } - + retval = PyObject_CallObject(func, pyargs); Py_DECREF(pyargs); if (PyErr_Occurred()) { diff --git a/lang/python/helpers.c b/lang/python/helpers.c index b53dbdd8..96c844fb 100644 --- a/lang/python/helpers.c +++ b/lang/python/helpers.c @@ -66,7 +66,7 @@ static gpgme_error_t pyPassphraseCb(void *hook, PyObject *args = NULL; PyObject *retval = NULL; PyObject *dataarg = NULL; - gpgme_error_t err_status = 0; + gpgme_error_t err_status = 0; pygpgme_exception_init(); @@ -119,7 +119,7 @@ static void pyProgressCb(void *hook, const char *what, int type, int current, int total) { PyObject *func = NULL, *dataarg = NULL, *args = NULL, *retval = NULL; PyObject *pyhook = (PyObject *) hook; - + if (PyTuple_Check(pyhook)) { func = PyTuple_GetItem(pyhook, 0); dataarg = PyTuple_GetItem(pyhook, 1); @@ -137,7 +137,7 @@ static void pyProgressCb(void *hook, const char *what, int type, int current, Py_INCREF(dataarg); /* Because GetItem doesn't give a ref but SetItem taketh away */ PyTuple_SetItem(args, 4, dataarg); } - + retval = PyObject_CallObject(func, args); Py_DECREF(args); Py_XDECREF(retval); diff --git a/lang/python/pyme/callbacks.py b/lang/python/pyme/callbacks.py index e3c810cd..3a507b99 100644 --- a/lang/python/pyme/callbacks.py +++ b/lang/python/pyme/callbacks.py @@ -33,7 +33,7 @@ def passphrase_stdin(hint, desc, prev_bad, hook=None): def progress_stdout(what, type, current, total, hook=None): print("PROGRESS UPDATE: what = %s, type = %d, current = %d, total = %d" %\ (what, type, current, total)) - + def readcb_fh(count, hook): """A callback for data. hook should be a Python file-like object.""" if count: diff --git a/lang/python/pyme/core.py b/lang/python/pyme/core.py index b03cedb0..09f0fa88 100644 --- a/lang/python/pyme/core.py +++ b/lang/python/pyme/core.py @@ -37,7 +37,7 @@ class Context(GpgmeWrapper): def _getctype(self): return 'gpgme_ctx_t' - + def _getnameprepend(self): return 'gpgme_' @@ -106,7 +106,7 @@ class Context(GpgmeWrapper): if key: key.__del__ = lambda self: pygpgme.gpgme_key_unref(self) return key - + def get_key(self, fpr, secret): """Return the key corresponding to the fingerprint 'fpr'""" ptr = pygpgme.new_gpgme_key_t_p() @@ -147,7 +147,7 @@ class Context(GpgmeWrapper): desc, a string describing the passphrase it needs; prev_bad, a boolean equal True if this is a call made after unsuccessful previous attempt. - + If hook has a value other than None it will be passed into the func as a forth argument. @@ -206,7 +206,7 @@ class Context(GpgmeWrapper): context = pygpgme.gpgme_wait(self.wrapped, ptr, hang) status = pygpgme.gpgme_error_t_p_value(ptr) pygpgme.delete_gpgme_error_t_p(ptr) - + if context == None: errorcheck(status) return None @@ -219,7 +219,7 @@ class Context(GpgmeWrapper): raise ValueError("op_edit: First argument cannot be None") opaquedata = (func, fnc_value) errorcheck(pygpgme.gpgme_op_edit(self.wrapped, key, opaquedata, out)) - + class Data(GpgmeWrapper): """From the GPGME C manual: @@ -236,7 +236,7 @@ class Data(GpgmeWrapper): def _getctype(self): return 'gpgme_data_t' - + def _getnameprepend(self): return 'gpgme_data_' @@ -247,7 +247,7 @@ class Data(GpgmeWrapper): name == 'gpgme_data_seek': return 0 return 1 - + def __init__(self, string = None, file = None, offset = None, length = None, cbs = None): """Initialize a new gpgme_data_t object. @@ -360,7 +360,7 @@ class Data(GpgmeWrapper): """This wraps the GPGME gpgme_data_new_from_fd() function. The argument "file" may be a file-like object, supporting the fileno() call and the mode attribute.""" - + tmp = pygpgme.new_gpgme_data_t_p() fp = pygpgme.fdopen(file.fileno(), file.mode) if fp == None: @@ -375,18 +375,18 @@ class Data(GpgmeWrapper): new_from_fd() method since in python there's not difference between file stream and file descriptor""" self.new_from_fd(file) - + def write(self, buffer): errorcheck(pygpgme.gpgme_data_write(self.wrapped, buffer, len(buffer))) def read(self, size = -1): """Read at most size bytes, returned as a string. - + If the size argument is negative or omitted, read until EOF is reached. Returns the data read, or the empty string if there was no data to read before EOF was reached.""" - + if size == 0: return '' @@ -445,11 +445,11 @@ def set_locale(category, value): def wait(hang): """Wait for asynchronous call on any Context to finish. Wait forever if hang is True. - + For finished anynch calls it returns a tuple (status, context): status - status return by asnynchronous call. context - context which caused this call to return. - + Please read the GPGME manual of more information.""" ptr = pygpgme.new_gpgme_error_t_p() context = pygpgme.gpgme_wait(None, ptr, hang) diff --git a/lang/python/pyme/errors.py b/lang/python/pyme/errors.py index 0ad22f2b..37d0fe67 100644 --- a/lang/python/pyme/errors.py +++ b/lang/python/pyme/errors.py @@ -22,7 +22,7 @@ class GPGMEError(Exception): def __init__(self, error = None, message = None): self.error = error self.message = message - + def getstring(self): message = "%s: %s" % (pygpgme.gpgme_strsource(self.error), pygpgme.gpgme_strerror(self.error)) @@ -35,7 +35,7 @@ class GPGMEError(Exception): def getsource(self): return pygpgme.gpgme_err_source(self.error) - + def __str__(self): return "%s (%d,%d)"%(self.getstring(), self.getsource(), self.getcode()) diff --git a/lang/python/pyme/util.py b/lang/python/pyme/util.py index c5f02a96..32e2f556 100644 --- a/lang/python/pyme/util.py +++ b/lang/python/pyme/util.py @@ -28,7 +28,7 @@ def process_constants(starttext, dict): continue name = identifier[index:] dict[name] = getattr(pygpgme, identifier) - + class GpgmeWrapper(object): """Base class all Pyme wrappers for GPGME functionality. Not to be instantiated directly.""" @@ -51,7 +51,7 @@ class GpgmeWrapper(object): def _getctype(self): raise NotImplementedException - + def __getattr__(self, name): """On-the-fly function generation.""" if name[0] == '_' or self._getnameprepend() == None: diff --git a/lang/python/setup.py b/lang/python/setup.py index b0ed3b2c..562c08f4 100755 --- a/lang/python/setup.py +++ b/lang/python/setup.py @@ -60,7 +60,7 @@ for item in getconfig('cflags'): # Adjust include and library locations in case of win32 uname_s = os.popen("uname -s").read() if uname_s.startswith("MINGW32"): - mnts = [x.split()[0:3:2] for x in os.popen("mount").read().split("\n") if x] + mnts = [x.split()[0:3:2] for x in os.popen("mount").read().split("\n") if x] tmplist = sorted([(len(x[1]), x[1], x[0]) for x in mnts]) tmplist.reverse() extra_dirs = [] @@ -89,9 +89,9 @@ except OSError as e: raise RuntimeError("Could not call swig, perhaps install swig.") else: raise - + subprocess.call(["make swig"], shell=True) - + swige = Extension("pyme._pygpgme", ["gpgme_wrap.c", "helpers.c"], include_dirs = include_dirs, define_macros = define_macros, @@ -106,7 +106,7 @@ setup(name = "pyme", url = version.homepage, ext_modules=[swige], packages = ['pyme', 'pyme.constants', 'pyme.constants.data', - 'pyme.constants.keylist', 'pyme.constants.sig'], + 'pyme.constants.keylist', 'pyme.constants.sig'], license = version.copyright + \ ", Licensed under the GPL version 2 and the LGPL version 2.1" ) -- cgit From d60deb8a127fb35c01acc729f33b014840af0e7b Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Thu, 12 May 2016 11:21:58 +0200 Subject: python: Fix type translation. * lang/python/gpgme.i: Adjust to Python3's string type being 'Unicode', not 'bytes'. Fix type checking. * lang/python/core.py (Data.write): Add docstring mentioning the expected type of parameter 'buffer'. (Data.read): Adjust read loop. Also, use a saner chunk size, and join all chunks at the end instead of adding them. * lang/python/examples/simple.py: Adjust example. Signed-off-by: Justus Winter --- lang/python/examples/simple.py | 6 ++--- lang/python/gpgme.i | 51 ++++++++++++++++++++++++++---------------- lang/python/pyme/core.py | 11 ++++----- 3 files changed, 41 insertions(+), 27 deletions(-) (limited to 'lang/python/pyme/core.py') diff --git a/lang/python/examples/simple.py b/lang/python/examples/simple.py index 739291ed..4ff6d285 100755 --- a/lang/python/examples/simple.py +++ b/lang/python/examples/simple.py @@ -25,7 +25,7 @@ core.check_version(None) # Set up our input and output buffers. -plain = core.Data(b'This is my message.') +plain = core.Data('This is my message.') cipher = core.Data() # Initialize our context. @@ -38,7 +38,7 @@ c.set_armor(1) sys.stdout.write("Enter name of your recipient: ") sys.stdout.flush() name = sys.stdin.readline().strip() -c.op_keylist_start(name.encode(), 0) +c.op_keylist_start(name, 0) r = c.op_keylist_next() if r == None: @@ -48,6 +48,6 @@ else: try: c.op_encrypt([r], 1, plain, cipher) cipher.seek(0,0) - print(cipher.read()) + sys.stdout.buffer.write(cipher.read()) except errors.GPGMEError as ex: print(ex.getstring()) diff --git a/lang/python/gpgme.i b/lang/python/gpgme.i index 87efce73..7c11943c 100644 --- a/lang/python/gpgme.i +++ b/lang/python/gpgme.i @@ -24,16 +24,18 @@ // Generate doc strings for all methods. %feature("autodoc", "0"); -// Allow use of None for strings. +/* Allow use of Unicode objects, bytes, and None for strings. */ %typemap(in) const char * { if ($input == Py_None) $1 = NULL; + else if (PyUnicode_Check($input)) + $1 = PyUnicode_AsUTF8($input); else if (PyBytes_Check($input)) $1 = PyBytes_AsString($input); else { PyErr_Format(PyExc_TypeError, - "arg %d: expected string or None, got %s", + "arg %d: expected str, bytes, or None, got %s", $argnum, $input->ob_type->tp_name); return NULL; } @@ -49,20 +51,27 @@ PyObject* object_to_gpgme_t(PyObject* input, const char* objtype, int argnum) { PyObject *pyname = NULL, *pypointer = NULL; pyname = PyObject_CallMethod(input, "_getctype", NULL); - if (pyname == NULL) { - PyErr_Format(PyExc_TypeError, - "arg %d: Expected an instance of type %s, but got %s", - argnum, objtype, - input == Py_None ? "None" : input->ob_type->tp_name); - return NULL; - } - if (strcmp(PyBytes_AsString(pyname), objtype) != 0) { - PyErr_Format(PyExc_TypeError, - "arg %d: Expected value of type %s, but got %s", - argnum, objtype, PyBytes_AsString(pyname)); - Py_DECREF(pyname); - return NULL; - } + if (pyname && PyUnicode_Check(pyname)) + { + if (strcmp(PyUnicode_AsUTF8(pyname), objtype) != 0) + { + PyErr_Format(PyExc_TypeError, + "arg %d: Expected value of type %s, but got %s", + argnum, objtype, PyUnicode_AsUTF8(pyname)); + Py_DECREF(pyname); + return NULL; + } + } + else + { + PyErr_Format(PyExc_TypeError, + "Protocol violation: Expected an instance of type str " + "from _getctype, but got %s", + pyname == NULL ? "NULL" + : (pyname == Py_None ? "None" : pyname->ob_type->tp_name)); + return NULL; + } + Py_DECREF(pyname); pypointer = PyObject_GetAttrString(input, "wrapped"); if (pypointer == NULL) { @@ -243,7 +252,7 @@ gpgme_error_t pyEditCb(void *opaque, gpgme_status_code_t status, } PyTuple_SetItem(pyargs, 0, PyLong_FromLong((long) status)); - PyTuple_SetItem(pyargs, 1, PyBytes_FromString(args)); + PyTuple_SetItem(pyargs, 1, PyUnicode_FromString(args)); if (dataarg) { Py_INCREF(dataarg); /* Because GetItem doesn't give a ref but SetItem taketh away */ PyTuple_SetItem(pyargs, 2, dataarg); @@ -254,8 +263,12 @@ gpgme_error_t pyEditCb(void *opaque, gpgme_status_code_t status, if (PyErr_Occurred()) { err_status = pygpgme_exception2code(); } else { - if (fd>=0 && retval) { - write(fd, PyBytes_AsString(retval), PyBytes_Size(retval)); + if (fd>=0 && retval && PyUnicode_Check(retval)) { + const char *buffer; + Py_ssize_t size; + + buffer = PyUnicode_AsUTF8AndSize(retval, &size); + write(fd, buffer, size); write(fd, "\n", 1); } } diff --git a/lang/python/pyme/core.py b/lang/python/pyme/core.py index 09f0fa88..fd4802ec 100644 --- a/lang/python/pyme/core.py +++ b/lang/python/pyme/core.py @@ -377,10 +377,11 @@ class Data(GpgmeWrapper): self.new_from_fd(file) def write(self, buffer): + """Write buffer given as bytes.""" errorcheck(pygpgme.gpgme_data_write(self.wrapped, buffer, len(buffer))) def read(self, size = -1): - """Read at most size bytes, returned as a string. + """Read at most size bytes, returned as bytes. If the size argument is negative or omitted, read until EOF is reached. @@ -393,13 +394,13 @@ class Data(GpgmeWrapper): if size > 0: return pygpgme.gpgme_data_read(self.wrapped, size) else: - retval = '' + chunks = [] while 1: - result = pygpgme.gpgme_data_read(self.wrapped, 10240) + result = pygpgme.gpgme_data_read(self.wrapped, 4096) if len(result) == 0: break - retval += result - return retval + chunks.append(result) + return b''.join(chunks) def pubkey_algo_name(algo): return pygpgme.gpgme_pubkey_algo_name(algo) -- cgit From ce5121ad53b0e17fbf9150b354c80da73f7fe190 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Thu, 12 May 2016 11:53:43 +0200 Subject: python: Handle interpreter shutdown. * lang/python/pyme/core.py: Avoid races at interpreter shutdown. This silences the most annoying occurrences, however this problem also affects the SWIG generated code, which might indicate that the real problem is somewhere else. If so, this change can be easily reverted. Signed-off-by: Justus Winter --- lang/python/pyme/core.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'lang/python/pyme/core.py') diff --git a/lang/python/pyme/core.py b/lang/python/pyme/core.py index fd4802ec..2a37ba35 100644 --- a/lang/python/pyme/core.py +++ b/lang/python/pyme/core.py @@ -66,21 +66,29 @@ class Context(GpgmeWrapper): self.last_progresscb = None def __del__(self): + if not pygpgme: + # At interpreter shutdown, pygpgme is set to NONE. + return + self._free_passcb() self._free_progresscb() - if self.own: + if self.own and pygpgme.gpgme_release: pygpgme.gpgme_release(self.wrapped) def _free_passcb(self): if self.last_passcb != None: - pygpgme.pygpgme_clear_generic_cb(self.last_passcb) - pygpgme.delete_PyObject_p_p(self.last_passcb) + if pygpgme.pygpgme_clear_generic_cb: + pygpgme.pygpgme_clear_generic_cb(self.last_passcb) + if pygpgme.delete_PyObject_p_p: + pygpgme.delete_PyObject_p_p(self.last_passcb) self.last_passcb = None def _free_progresscb(self): if self.last_progresscb != None: - pygpgme.pygpgme_clear_generic_cb(self.last_progresscb) - pygpgme.delete_PyObject_p_p(self.last_progresscb) + if pygpgme.pygpgme_clear_generic_cb: + pygpgme.pygpgme_clear_generic_cb(self.last_progresscb) + if pygpgme.delete_PyObject_p_p: + pygpgme.delete_PyObject_p_p(self.last_progresscb) self.last_progresscb = None def op_keylist_all(self, *args, **kwargs): @@ -292,14 +300,20 @@ class Data(GpgmeWrapper): self.new() def __del__(self): - if self.wrapped != None: + if not pygpgme: + # At interpreter shutdown, pygpgme is set to NONE. + return + + if self.wrapped != None and pygpgme.gpgme_data_release: pygpgme.gpgme_data_release(self.wrapped) self._free_readcb() def _free_readcb(self): if self.last_readcb != None: - pygpgme.pygpgme_clear_generic_cb(self.last_readcb) - pygpgme.delete_PyObject_p_p(self.last_readcb) + if pygpgme.pygpgme_clear_generic_cb: + pygpgme.pygpgme_clear_generic_cb(self.last_readcb) + if pygpgme.delete_PyObject_p_p: + pygpgme.delete_PyObject_p_p(self.last_readcb) self.last_readcb = None def new(self): -- cgit From e3d3d366bd1a1aea8a38ae5dcbf71ea3c784e920 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Thu, 12 May 2016 12:54:15 +0200 Subject: python: Fix function invocation. * lang/python/pyme/core.py (Data.new_from_fd): Fix function invocation. Signed-off-by: Justus Winter --- lang/python/pyme/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lang/python/pyme/core.py') diff --git a/lang/python/pyme/core.py b/lang/python/pyme/core.py index 2a37ba35..e822704a 100644 --- a/lang/python/pyme/core.py +++ b/lang/python/pyme/core.py @@ -380,7 +380,7 @@ class Data(GpgmeWrapper): if fp == None: raise ValueError("Failed to open file from %s arg %s" % \ (str(type(file)), str(file))) - errorcheck(gpgme_data_new_from_fd(tmp, fp)) + errorcheck(pygpgme.gpgme_data_new_from_fd(tmp, fp)) self.wrapped = pygpgme.gpgme_data_t_p_value(tmp) pygpgme.delete_gpgme_data_t_p(tmp) -- cgit From f7094d8358e933f3ce074eade7a40b2a7d291180 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Thu, 12 May 2016 17:44:54 +0200 Subject: python: Fix writing to data buffers. * lang/python/gpgme.i: Add typemap for buffers. * lang/python/pyme/core.py (Data.write): Fix function. * lang/python/tests/Makefile.am: Add new test. * lang/python/tests/t-data.py: New file. Signed-off-by: Justus Winter --- lang/python/gpgme.i | 17 +++++++++++++++ lang/python/pyme/core.py | 6 ++++-- lang/python/tests/Makefile.am | 2 +- lang/python/tests/t-data.py | 49 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 3 deletions(-) create mode 100755 lang/python/tests/t-data.py (limited to 'lang/python/pyme/core.py') diff --git a/lang/python/gpgme.i b/lang/python/gpgme.i index 7c11943c..c5b5bc97 100644 --- a/lang/python/gpgme.i +++ b/lang/python/gpgme.i @@ -173,6 +173,23 @@ PyObject* object_to_gpgme_t(PyObject* input, const char* objtype, int argnum) { free($1); } +/* For gpgme_data_write, but should be universal. */ +%typemap(in) (const void *buffer, size_t size) { + if ($input == Py_None) + $1 = NULL, $2 = 0; + else if (PyUnicode_Check($input)) + $1 = PyUnicode_AsUTF8AndSize($input, (size_t *) &$2); + else if (PyBytes_Check($input)) + PyBytes_AsStringAndSize($input, (char **) &$1, (size_t *) &$2); + else { + PyErr_Format(PyExc_TypeError, + "arg %d: expected str, bytes, or None, got %s", + $argnum, $input->ob_type->tp_name); + return NULL; + } +} +%typemap(freearg) (const void *buffer, size_t size) ""; + // Make types containing 'next' field to be lists %ignore next; %typemap(out) gpgme_sig_notation_t, gpgme_engine_info_t, gpgme_subkey_t, gpgme_key_sig_t, diff --git a/lang/python/pyme/core.py b/lang/python/pyme/core.py index e822704a..dafbd9b3 100644 --- a/lang/python/pyme/core.py +++ b/lang/python/pyme/core.py @@ -391,8 +391,10 @@ class Data(GpgmeWrapper): self.new_from_fd(file) def write(self, buffer): - """Write buffer given as bytes.""" - errorcheck(pygpgme.gpgme_data_write(self.wrapped, buffer, len(buffer))) + """Write buffer given as string or bytes. + + If a string is given, it is implicitly encoded using UTF-8.""" + return pygpgme.gpgme_data_write(self.wrapped, buffer) def read(self, size = -1): """Read at most size bytes, returned as bytes. diff --git a/lang/python/tests/Makefile.am b/lang/python/tests/Makefile.am index 56ff74ce..50c78e35 100644 --- a/lang/python/tests/Makefile.am +++ b/lang/python/tests/Makefile.am @@ -19,4 +19,4 @@ TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir) \ PYTHONPATH=`echo $(abs_builddir)/../build/lib.*` -TESTS = t-wrapper.py +TESTS = t-wrapper.py t-data.py diff --git a/lang/python/tests/t-data.py b/lang/python/tests/t-data.py new file mode 100755 index 00000000..af2eb986 --- /dev/null +++ b/lang/python/tests/t-data.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2016 g10 Code GmbH +# +# This file is part of GPGME. +# +# GPGME is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# GPGME is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +# Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, see . + +import os + +from pyme import core + +data = core.Data('Hello world!') +assert data.read() == b'Hello world!' +assert data.read() == b'' + +data.seek(0, os.SEEK_SET) +assert data.read() == b'Hello world!' +assert data.read() == b'' + +data = core.Data(b'Hello world!') +assert data.read() == b'Hello world!' + +data = core.Data() +data.write('Hello world!') +data.seek(0, os.SEEK_SET) +assert data.read() == b'Hello world!' + +data = core.Data() +data.write(b'Hello world!') +data.seek(0, os.SEEK_SET) +assert data.read() == b'Hello world!' + +binjunk = bytes(range(256)) +data = core.Data() +data.write(binjunk) +data.seek(0, os.SEEK_SET) +assert data.read() == binjunk -- cgit From c5d118b2a76e9528df780d11da9566ff7c22e4f5 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Thu, 12 May 2016 18:00:16 +0200 Subject: python: Raise exceptions on write errors. * lang/python/pyme/core.py (Data.write): Handle errors. * lang/python/pyme/errors.py (GPGMEError.fromSyserror): New function. Signed-off-by: Justus Winter --- lang/python/pyme/core.py | 5 ++++- lang/python/pyme/errors.py | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'lang/python/pyme/core.py') diff --git a/lang/python/pyme/core.py b/lang/python/pyme/core.py index dafbd9b3..1d6e3847 100644 --- a/lang/python/pyme/core.py +++ b/lang/python/pyme/core.py @@ -394,7 +394,10 @@ class Data(GpgmeWrapper): """Write buffer given as string or bytes. If a string is given, it is implicitly encoded using UTF-8.""" - return pygpgme.gpgme_data_write(self.wrapped, buffer) + written = pygpgme.gpgme_data_write(self.wrapped, buffer) + if written < 0: + raise GPGMEError.fromSyserror() + return written def read(self, size = -1): """Read at most size bytes, returned as bytes. diff --git a/lang/python/pyme/errors.py b/lang/python/pyme/errors.py index 37d0fe67..5c8f9223 100644 --- a/lang/python/pyme/errors.py +++ b/lang/python/pyme/errors.py @@ -23,6 +23,10 @@ class GPGMEError(Exception): self.error = error self.message = message + @classmethod + def fromSyserror(cls): + return cls(pygpgme.gpgme_err_code_from_syserror()) + def getstring(self): message = "%s: %s" % (pygpgme.gpgme_strsource(self.error), pygpgme.gpgme_strerror(self.error)) -- cgit From 64e5fe767f45e9ccb0fb3fe919171c222132a54c Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Tue, 17 May 2016 14:14:25 +0200 Subject: python: Import GPGMEError. * pyme/core.py: Import GPGMEError. Fixes c5d118b2. Signed-off-by: Justus Winter --- lang/python/pyme/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lang/python/pyme/core.py') diff --git a/lang/python/pyme/core.py b/lang/python/pyme/core.py index 1d6e3847..f3829d5f 100644 --- a/lang/python/pyme/core.py +++ b/lang/python/pyme/core.py @@ -20,7 +20,7 @@ from . import pygpgme -from .errors import errorcheck +from .errors import errorcheck, GPGMEError from . import errors from .util import GpgmeWrapper -- cgit