aboutsummaryrefslogtreecommitdiffstats
path: root/lang/python
diff options
context:
space:
mode:
authorJustus Winter <[email protected]>2016-09-16 12:56:29 +0000
committerJustus Winter <[email protected]>2016-09-16 12:56:29 +0000
commita458e7fe2006d92bd5a838e2747fb66bbac4b1b8 (patch)
tree7460cc8f3ba21fec51a0a706f17747887ef37d96 /lang/python
parentcore: Fix typos. (diff)
downloadgpgme-a458e7fe2006d92bd5a838e2747fb66bbac4b1b8.tar.gz
gpgme-a458e7fe2006d92bd5a838e2747fb66bbac4b1b8.zip
python: Adapt to 'gpgme_op_interact'.
* lang/python/examples/inter-edit.py: Update example. * lang/python/gpgme.i (gpgme_edit_cb_t): Turn into 'gpgme_interact_cb_t'. * lang/python/helpers.c (_pyme_edit_cb): Turn into '_pyme_interact_cb_t'. * lang/python/private.h (_pyme_edit_cb): Likewise. * lang/python/pyme/constants/__init__.py: Replace numeric status codes with the keywords. * lang/python/pyme/constants/status.py: Likewise. * lang/python/pyme/core.py (Context.interact): New method. (Context.op_edit): Deprecate, update docstring, implement using Context.interact. * lang/python/tests/t-edit.py: Test both interfaces. Signed-off-by: Justus Winter <[email protected]>
Diffstat (limited to '')
-rw-r--r--lang/python/examples/inter-edit.py15
-rw-r--r--lang/python/gpgme.i10
-rw-r--r--lang/python/helpers.c23
-rw-r--r--lang/python/private.h5
-rw-r--r--lang/python/pyme/constants/__init__.py105
-rw-r--r--lang/python/pyme/constants/status.py106
-rw-r--r--lang/python/pyme/core.py59
-rwxr-xr-xlang/python/tests/t-edit.py11
8 files changed, 296 insertions, 38 deletions
diff --git a/lang/python/examples/inter-edit.py b/lang/python/examples/inter-edit.py
index 459df114..39d6f176 100644
--- a/lang/python/examples/inter-edit.py
+++ b/lang/python/examples/inter-edit.py
@@ -23,13 +23,6 @@ del absolute_import, print_function, unicode_literals
import sys
import pyme
-import pyme.constants.status
-
-# Get names for the status codes
-status2str = {}
-for name in dir(pyme.constants.status):
- if not name.startswith('__') and name != "util":
- status2str[getattr(pyme.constants.status, name)] = name
if len(sys.argv) != 2:
sys.exit("Usage: %s <Gpg key pattern>\n" % sys.argv[0])
@@ -46,11 +39,11 @@ with pyme.Context() as c:
key = keys[0]
print("Editing key {} ({}):".format(key.uids[0].uid, key.subkeys[0].fpr))
- def edit_fnc(status, args):
+ def edit_fnc(keyword, args):
print("Status: {} ({}), args: {} > ".format(
- status2str[status], status, args), end='', flush=True)
+ keyword, status, args), end='', flush=True)
- if not 'GET' in status2str[status]:
+ if not 'GET' in keyword:
# no prompt
print()
return None
@@ -60,4 +53,4 @@ with pyme.Context() as c:
except EOFError:
return "quit"
- c.op_edit(key, edit_fnc, None, sys.stdout)
+ c.interact(key, edit_fnc, sink=sys.stdout)
diff --git a/lang/python/gpgme.i b/lang/python/gpgme.i
index 458ae7f1..84addae2 100644
--- a/lang/python/gpgme.i
+++ b/lang/python/gpgme.i
@@ -476,15 +476,15 @@
-// Include mapper for edit callbacks
-%typemap(in) (gpgme_edit_cb_t fnc, void *fnc_value) {
+/* Include mapper for interact callbacks. */
+%typemap(in) (gpgme_interact_cb_t fnc, void *fnc_value) {
if (! PyTuple_Check($input))
- return PyErr_Format(PyExc_TypeError, "edit callback must be a tuple");
+ return PyErr_Format(PyExc_TypeError, "interact callback must be a tuple");
if (PyTuple_Size($input) != 2 && PyTuple_Size($input) != 3)
return PyErr_Format(PyExc_TypeError,
- "edit callback must be a tuple of size 2 or 3");
+ "interact callback must be a tuple of size 2 or 3");
- $1 = (gpgme_edit_cb_t) _pyme_edit_cb;
+ $1 = (gpgme_interact_cb_t) _pyme_interact_cb;
$2 = $input;
}
diff --git a/lang/python/helpers.c b/lang/python/helpers.c
index bc8aed40..bb2128c9 100644
--- a/lang/python/helpers.c
+++ b/lang/python/helpers.c
@@ -656,11 +656,16 @@ pyme_set_status_cb(PyObject *self, PyObject *cb) {
Py_INCREF(Py_None);
return Py_None;
}
+
-/* Edit callbacks. */
-gpgme_error_t _pyme_edit_cb(void *opaque, gpgme_status_code_t status,
- const char *args, int fd) {
+
+/* Interact callbacks. */
+gpgme_error_t
+_pyme_interact_cb(void *opaque, const char *keyword,
+ const char *args, int fd)
+{
PyObject *func = NULL, *dataarg = NULL, *pyargs = NULL, *retval = NULL;
+ PyObject *py_keyword;
PyObject *pyopaque = (PyObject *) opaque;
gpgme_error_t err_status = 0;
PyObject *self = NULL;
@@ -678,7 +683,15 @@ gpgme_error_t _pyme_edit_cb(void *opaque, gpgme_status_code_t status,
pyargs = PyTuple_New(2);
}
- PyTuple_SetItem(pyargs, 0, PyLong_FromLong((long) status));
+ if (keyword)
+ py_keyword = PyUnicode_FromString(keyword);
+ else
+ {
+ Py_INCREF(Py_None);
+ py_keyword = Py_None;
+ }
+
+ PyTuple_SetItem(pyargs, 0, py_keyword);
PyTuple_SetItem(pyargs, 1, PyUnicode_FromString(args));
if (dataarg) {
Py_INCREF(dataarg); /* Because GetItem doesn't give a ref but SetItem taketh away */
@@ -726,7 +739,9 @@ gpgme_error_t _pyme_edit_cb(void *opaque, gpgme_status_code_t status,
Py_XDECREF(retval);
return err_status;
}
+
+
/* Data callbacks. */
/* Read up to SIZE bytes into buffer BUFFER from the data object with
diff --git a/lang/python/private.h b/lang/python/private.h
index cb4d2f80..3a903c18 100644
--- a/lang/python/private.h
+++ b/lang/python/private.h
@@ -34,9 +34,8 @@ PyObject *_pyme_obj2gpgme_data_t(PyObject *input, int argnum,
PyObject *_pyme_wrap_result(PyObject *fragile, const char *classname);
-gpgme_error_t _pyme_edit_cb(void *opaque, gpgme_status_code_t status,
- const char *args, int fd);
-
+gpgme_error_t _pyme_interact_cb(void *opaque, const char *keyword,
+ const char *args, int fd);
gpgme_error_t _pyme_assuan_data_cb (void *hook,
const void *data, size_t datalen);
gpgme_error_t _pyme_assuan_inquire_cb (void *hook,
diff --git a/lang/python/pyme/constants/__init__.py b/lang/python/pyme/constants/__init__.py
index 96465de5..96d89e47 100644
--- a/lang/python/pyme/constants/__init__.py
+++ b/lang/python/pyme/constants/__init__.py
@@ -7,3 +7,108 @@ util.process_constants('GPGME_', globals())
__all__ = ['data', 'event', 'import', 'keylist', 'md', 'pk',
'protocol', 'sig', 'sigsum', 'status', 'validity']
+
+# GPGME 1.7 replaced gpgme_op_edit with gpgme_op_interact. We
+# implement pyme.Context.op_edit using gpgme_op_interact, so the
+# callbacks will be called with string keywords instead of numeric
+# status messages. Code that is using these constants will continue
+# to work.
+
+STATUS_ABORT = "ABORT"
+STATUS_ALREADY_SIGNED = "ALREADY_SIGNED"
+STATUS_ATTRIBUTE = "ATTRIBUTE"
+STATUS_BACKUP_KEY_CREATED = "BACKUP_KEY_CREATED"
+STATUS_BAD_PASSPHRASE = "BAD_PASSPHRASE"
+STATUS_BADARMOR = "BADARMOR"
+STATUS_BADMDC = "BADMDC"
+STATUS_BADSIG = "BADSIG"
+STATUS_BEGIN_DECRYPTION = "BEGIN_DECRYPTION"
+STATUS_BEGIN_ENCRYPTION = "BEGIN_ENCRYPTION"
+STATUS_BEGIN_SIGNING = "BEGIN_SIGNING"
+STATUS_BEGIN_STREAM = "BEGIN_STREAM"
+STATUS_CARDCTRL = "CARDCTRL"
+STATUS_DECRYPTION_FAILED = "DECRYPTION_FAILED"
+STATUS_DECRYPTION_INFO = "DECRYPTION_INFO"
+STATUS_DECRYPTION_OKAY = "DECRYPTION_OKAY"
+STATUS_DELETE_PROBLEM = "DELETE_PROBLEM"
+STATUS_ENC_TO = "ENC_TO"
+STATUS_END_DECRYPTION = "END_DECRYPTION"
+STATUS_END_ENCRYPTION = "END_ENCRYPTION"
+STATUS_END_STREAM = "END_STREAM"
+STATUS_ENTER = "ENTER"
+STATUS_ERRMDC = "ERRMDC"
+STATUS_ERROR = "ERROR"
+STATUS_ERRSIG = "ERRSIG"
+STATUS_EXPKEYSIG = "EXPKEYSIG"
+STATUS_EXPSIG = "EXPSIG"
+STATUS_FAILURE = "FAILURE"
+STATUS_FILE_DONE = "FILE_DONE"
+STATUS_FILE_ERROR = "FILE_ERROR"
+STATUS_FILE_START = "FILE_START"
+STATUS_GET_BOOL = "GET_BOOL"
+STATUS_GET_HIDDEN = "GET_HIDDEN"
+STATUS_GET_LINE = "GET_LINE"
+STATUS_GOOD_PASSPHRASE = "GOOD_PASSPHRASE"
+STATUS_GOODMDC = "GOODMDC"
+STATUS_GOODSIG = "GOODSIG"
+STATUS_GOT_IT = "GOT_IT"
+STATUS_IMPORT_OK = "IMPORT_OK"
+STATUS_IMPORT_PROBLEM = "IMPORT_PROBLEM"
+STATUS_IMPORT_RES = "IMPORT_RES"
+STATUS_IMPORTED = "IMPORTED"
+STATUS_INQUIRE_MAXLEN = "INQUIRE_MAXLEN"
+STATUS_INV_RECP = "INV_RECP"
+STATUS_INV_SGNR = "INV_SGNR"
+STATUS_KEY_CONSIDERED = "KEY_CONSIDERED"
+STATUS_KEY_CREATED = "KEY_CREATED"
+STATUS_KEY_NOT_CREATED = "KEY_NOT_CREATED"
+STATUS_KEYEXPIRED = "KEYEXPIRED"
+STATUS_KEYREVOKED = "KEYREVOKED"
+STATUS_LEAVE = "LEAVE"
+STATUS_MISSING_PASSPHRASE = "MISSING_PASSPHRASE"
+STATUS_MOUNTPOINT = "MOUNTPOINT"
+STATUS_NEED_PASSPHRASE = "NEED_PASSPHRASE"
+STATUS_NEED_PASSPHRASE_PIN = "NEED_PASSPHRASE_PIN"
+STATUS_NEED_PASSPHRASE_SYM = "NEED_PASSPHRASE_SYM"
+STATUS_NEWSIG = "NEWSIG"
+STATUS_NO_PUBKEY = "NO_PUBKEY"
+STATUS_NO_RECP = "NO_RECP"
+STATUS_NO_SECKEY = "NO_SECKEY"
+STATUS_NO_SGNR = "NO_SGNR"
+STATUS_NODATA = "NODATA"
+STATUS_NOTATION_DATA = "NOTATION_DATA"
+STATUS_NOTATION_FLAGS = "NOTATION_FLAGS"
+STATUS_NOTATION_NAME = "NOTATION_NAME"
+STATUS_PINENTRY_LAUNCHED = "PINENTRY_LAUNCHED"
+STATUS_PKA_TRUST_BAD = "PKA_TRUST_BAD"
+STATUS_PKA_TRUST_GOOD = "PKA_TRUST_GOOD"
+STATUS_PLAINTEXT = "PLAINTEXT"
+STATUS_PLAINTEXT_LENGTH = "PLAINTEXT_LENGTH"
+STATUS_POLICY_URL = "POLICY_URL"
+STATUS_PROGRESS = "PROGRESS"
+STATUS_REVKEYSIG = "REVKEYSIG"
+STATUS_RSA_OR_IDEA = "RSA_OR_IDEA"
+STATUS_SC_OP_FAILURE = "SC_OP_FAILURE"
+STATUS_SC_OP_SUCCESS = "SC_OP_SUCCESS"
+STATUS_SESSION_KEY = "SESSION_KEY"
+STATUS_SHM_GET = "SHM_GET"
+STATUS_SHM_GET_BOOL = "SHM_GET_BOOL"
+STATUS_SHM_GET_HIDDEN = "SHM_GET_HIDDEN"
+STATUS_SHM_INFO = "SHM_INFO"
+STATUS_SIG_CREATED = "SIG_CREATED"
+STATUS_SIG_ID = "SIG_ID"
+STATUS_SIG_SUBPACKET = "SIG_SUBPACKET"
+STATUS_SIGEXPIRED = "SIGEXPIRED"
+STATUS_SUCCESS = "SUCCESS"
+STATUS_TOFU_STATS = "TOFU_STATS"
+STATUS_TOFU_STATS_LONG = "TOFU_STATS_LONG"
+STATUS_TOFU_USER = "TOFU_USER"
+STATUS_TRUNCATED = "TRUNCATED"
+STATUS_TRUST_FULLY = "TRUST_FULLY"
+STATUS_TRUST_MARGINAL = "TRUST_MARGINAL"
+STATUS_TRUST_NEVER = "TRUST_NEVER"
+STATUS_TRUST_ULTIMATE = "TRUST_ULTIMATE"
+STATUS_TRUST_UNDEFINED = "TRUST_UNDEFINED"
+STATUS_UNEXPECTED = "UNEXPECTED"
+STATUS_USERID_HINT = "USERID_HINT"
+STATUS_VALIDSIG = "VALIDSIG"
diff --git a/lang/python/pyme/constants/status.py b/lang/python/pyme/constants/status.py
index ee522591..a04d9aae 100644
--- a/lang/python/pyme/constants/status.py
+++ b/lang/python/pyme/constants/status.py
@@ -18,5 +18,107 @@
from __future__ import absolute_import, print_function, unicode_literals
del absolute_import, print_function, unicode_literals
-from pyme import util
-util.process_constants('GPGME_STATUS_', globals())
+# GPGME 1.7 replaced gpgme_op_edit with gpgme_op_interact. We
+# implement pyme.Context.op_edit using gpgme_op_interact, so the
+# callbacks will be called with string keywords instead of numeric
+# status messages. Code that is using these constants will continue
+# to work.
+
+ABORT = "ABORT"
+ALREADY_SIGNED = "ALREADY_SIGNED"
+ATTRIBUTE = "ATTRIBUTE"
+BACKUP_KEY_CREATED = "BACKUP_KEY_CREATED"
+BAD_PASSPHRASE = "BAD_PASSPHRASE"
+BADARMOR = "BADARMOR"
+BADMDC = "BADMDC"
+BADSIG = "BADSIG"
+BEGIN_DECRYPTION = "BEGIN_DECRYPTION"
+BEGIN_ENCRYPTION = "BEGIN_ENCRYPTION"
+BEGIN_SIGNING = "BEGIN_SIGNING"
+BEGIN_STREAM = "BEGIN_STREAM"
+CARDCTRL = "CARDCTRL"
+DECRYPTION_FAILED = "DECRYPTION_FAILED"
+DECRYPTION_INFO = "DECRYPTION_INFO"
+DECRYPTION_OKAY = "DECRYPTION_OKAY"
+DELETE_PROBLEM = "DELETE_PROBLEM"
+ENC_TO = "ENC_TO"
+END_DECRYPTION = "END_DECRYPTION"
+END_ENCRYPTION = "END_ENCRYPTION"
+END_STREAM = "END_STREAM"
+ENTER = "ENTER"
+ERRMDC = "ERRMDC"
+ERROR = "ERROR"
+ERRSIG = "ERRSIG"
+EXPKEYSIG = "EXPKEYSIG"
+EXPSIG = "EXPSIG"
+FAILURE = "FAILURE"
+FILE_DONE = "FILE_DONE"
+FILE_ERROR = "FILE_ERROR"
+FILE_START = "FILE_START"
+GET_BOOL = "GET_BOOL"
+GET_HIDDEN = "GET_HIDDEN"
+GET_LINE = "GET_LINE"
+GOOD_PASSPHRASE = "GOOD_PASSPHRASE"
+GOODMDC = "GOODMDC"
+GOODSIG = "GOODSIG"
+GOT_IT = "GOT_IT"
+IMPORT_OK = "IMPORT_OK"
+IMPORT_PROBLEM = "IMPORT_PROBLEM"
+IMPORT_RES = "IMPORT_RES"
+IMPORTED = "IMPORTED"
+INQUIRE_MAXLEN = "INQUIRE_MAXLEN"
+INV_RECP = "INV_RECP"
+INV_SGNR = "INV_SGNR"
+KEY_CONSIDERED = "KEY_CONSIDERED"
+KEY_CREATED = "KEY_CREATED"
+KEY_NOT_CREATED = "KEY_NOT_CREATED"
+KEYEXPIRED = "KEYEXPIRED"
+KEYREVOKED = "KEYREVOKED"
+LEAVE = "LEAVE"
+MISSING_PASSPHRASE = "MISSING_PASSPHRASE"
+MOUNTPOINT = "MOUNTPOINT"
+NEED_PASSPHRASE = "NEED_PASSPHRASE"
+NEED_PASSPHRASE_PIN = "NEED_PASSPHRASE_PIN"
+NEED_PASSPHRASE_SYM = "NEED_PASSPHRASE_SYM"
+NEWSIG = "NEWSIG"
+NO_PUBKEY = "NO_PUBKEY"
+NO_RECP = "NO_RECP"
+NO_SECKEY = "NO_SECKEY"
+NO_SGNR = "NO_SGNR"
+NODATA = "NODATA"
+NOTATION_DATA = "NOTATION_DATA"
+NOTATION_FLAGS = "NOTATION_FLAGS"
+NOTATION_NAME = "NOTATION_NAME"
+PINENTRY_LAUNCHED = "PINENTRY_LAUNCHED"
+PKA_TRUST_BAD = "PKA_TRUST_BAD"
+PKA_TRUST_GOOD = "PKA_TRUST_GOOD"
+PLAINTEXT = "PLAINTEXT"
+PLAINTEXT_LENGTH = "PLAINTEXT_LENGTH"
+POLICY_URL = "POLICY_URL"
+PROGRESS = "PROGRESS"
+REVKEYSIG = "REVKEYSIG"
+RSA_OR_IDEA = "RSA_OR_IDEA"
+SC_OP_FAILURE = "SC_OP_FAILURE"
+SC_OP_SUCCESS = "SC_OP_SUCCESS"
+SESSION_KEY = "SESSION_KEY"
+SHM_GET = "SHM_GET"
+SHM_GET_BOOL = "SHM_GET_BOOL"
+SHM_GET_HIDDEN = "SHM_GET_HIDDEN"
+SHM_INFO = "SHM_INFO"
+SIG_CREATED = "SIG_CREATED"
+SIG_ID = "SIG_ID"
+SIG_SUBPACKET = "SIG_SUBPACKET"
+SIGEXPIRED = "SIGEXPIRED"
+SUCCESS = "SUCCESS"
+TOFU_STATS = "TOFU_STATS"
+TOFU_STATS_LONG = "TOFU_STATS_LONG"
+TOFU_USER = "TOFU_USER"
+TRUNCATED = "TRUNCATED"
+TRUST_FULLY = "TRUST_FULLY"
+TRUST_MARGINAL = "TRUST_MARGINAL"
+TRUST_NEVER = "TRUST_NEVER"
+TRUST_ULTIMATE = "TRUST_ULTIMATE"
+TRUST_UNDEFINED = "TRUST_UNDEFINED"
+UNEXPECTED = "UNEXPECTED"
+USERID_HINT = "USERID_HINT"
+VALIDSIG = "VALIDSIG"
diff --git a/lang/python/pyme/core.py b/lang/python/pyme/core.py
index 55e86872..88a086b1 100644
--- a/lang/python/pyme/core.py
+++ b/lang/python/pyme/core.py
@@ -29,6 +29,7 @@ del absolute_import, print_function, unicode_literals
import re
import os
+import warnings
import weakref
from . import gpgme
from .errors import errorcheck, GPGMEError
@@ -536,6 +537,39 @@ class Context(GpgmeWrapper):
return GPGMEError(status) if status != 0 else None
+ def interact(self, key, func, sink=None, flags=0, fnc_value=None):
+ """Interact with the engine
+
+ This method can be used to edit keys and cards interactively.
+ KEY is the key to edit, FUNC is called repeatedly with two
+ unicode arguments, 'keyword' and 'args'. See the GPGME manual
+ for details.
+
+ Keyword arguments:
+ sink -- if given, additional output is written here
+ flags -- use constants.INTERACT_CARD to edit a card
+
+ Raises:
+ GPGMEError -- as signaled by the underlying library
+
+ """
+ if key == None:
+ raise ValueError("First argument cannot be None")
+
+ if sink == None:
+ sink = Data()
+
+ if fnc_value:
+ opaquedata = (weakref.ref(self), func, fnc_value)
+ else:
+ opaquedata = (weakref.ref(self), func)
+
+ result = gpgme.gpgme_op_interact(self.wrapped, key, flags,
+ opaquedata, sink)
+ if self._callback_excinfo:
+ gpgme.pyme_raise_callback_exception(self)
+ errorcheck(result)
+
@property
def signers(self):
"""Keys used for signing"""
@@ -793,18 +827,21 @@ class Context(GpgmeWrapper):
errorcheck(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")
- if fnc_value:
- opaquedata = (weakref.ref(self), func, fnc_value)
- else:
- opaquedata = (weakref.ref(self), func)
+ """Start key editing using supplied callback function
+
+ Note: This interface is deprecated and will be removed with
+ GPGME 1.8. Please use .interact instead. Furthermore, we
+ implement this using gpgme_op_interact, so callbacks will get
+ called with string keywords instead of numeric status
+ messages. Code that is using constants.STATUS_X or
+ constants.status.X will continue to work, whereas code using
+ magic numbers will break as a result.
+
+ """
+ warnings.warn("Call to deprecated method op_edit.",
+ category=DeprecationWarning)
+ return self.interact(key, func, sink=out, fnc_value=fnc_value)
- result = gpgme.gpgme_op_edit(self.wrapped, key, opaquedata, out)
- if self._callback_excinfo:
- gpgme.pyme_raise_callback_exception(self)
- errorcheck(result)
class Data(GpgmeWrapper):
"""Data buffer
diff --git a/lang/python/tests/t-edit.py b/lang/python/tests/t-edit.py
index 9ba187d5..18bcb94e 100755
--- a/lang/python/tests/t-edit.py
+++ b/lang/python/tests/t-edit.py
@@ -33,7 +33,7 @@ class KeyEditor(object):
self.done = False
self.verbose = int(os.environ.get('verbose', 0)) > 1
- def edit_fnc(self, status, args, out):
+ def edit_fnc(self, status, args, out=None):
if args == "keyedit.prompt":
result = self.steps[self.step]
self.step += 1
@@ -57,8 +57,15 @@ c = core.Context()
c.set_pinentry_mode(constants.PINENTRY_MODE_LOOPBACK)
c.set_passphrase_cb(lambda *args: "abc")
c.set_armor(True)
-sink = core.Data()
+# The deprecated interface.
+editor = KeyEditor()
+c.interact(c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False),
+ editor.edit_fnc)
+assert editor.done
+
+# The deprecated interface.
+sink = core.Data()
editor = KeyEditor()
c.op_edit(c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False),
editor.edit_fnc, sink, sink)