aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lang/python/Makefile.am2
-rw-r--r--lang/python/gpgme.i98
-rw-r--r--lang/python/helpers.c100
-rw-r--r--lang/python/helpers.h7
-rwxr-xr-xlang/python/tests/t-idiomatic.py35
5 files changed, 179 insertions, 63 deletions
diff --git a/lang/python/Makefile.am b/lang/python/Makefile.am
index 18005bf9..e156d46e 100644
--- a/lang/python/Makefile.am
+++ b/lang/python/Makefile.am
@@ -45,7 +45,7 @@ gpgme_wrap.c pyme/pygpgme.py: gpgme.i errors.i gpgme.h copystamp
$<
all-local: gpgme_wrap.c pyme/pygpgme.py copystamp
- CFLAGS="$(CFLAGS)" \
+ CFLAGS="$(CFLAGS) -I$(top_srcdir)" \
$(PYTHON) setup.py build --verbose
clean-local:
diff --git a/lang/python/gpgme.i b/lang/python/gpgme.i
index 98f30d55..1e4c9ff6 100644
--- a/lang/python/gpgme.i
+++ b/lang/python/gpgme.i
@@ -114,17 +114,19 @@
}
// Special handling for references to our objects.
-%typemap(in) gpgme_data_t DATAIN (PyObject *wrapper) {
+%typemap(in) gpgme_data_t DATAIN (gpgme_data_t wrapper = NULL,
+ PyObject *bytesio = NULL, Py_buffer view) {
/* If we create a temporary wrapper object, we will store it in
wrapperN, where N is $argnum. Here in this fragment, SWIG will
automatically append $argnum. */
- wrapper = NULL;
+ memset(&view, 0, sizeof view);
if ($input == Py_None)
$1 = NULL;
else {
- PyObject *pypointer = NULL;
-
- if((pypointer=object_to_gpgme_data_t($input, $argnum, &wrapper)) == NULL)
+ PyObject *pypointer;
+ pypointer = object_to_gpgme_data_t($input, $argnum, &wrapper,
+ &bytesio, &view);
+ if (pypointer == NULL)
return NULL;
/* input = $input, 1 = $1, 1_descriptor = $1_descriptor */
@@ -141,8 +143,79 @@
}
%typemap(freearg) gpgme_data_t DATAIN {
+ /* See whether we need to update the Python buffer. */
+ if (resultobj && wrapper$argnum && view$argnum.buf
+ && wrapper$argnum->data.mem.buffer != NULL)
+ {
+ /* The buffer is dirty. */
+ if (view$argnum.readonly)
+ {
+ Py_XDECREF(resultobj);
+ resultobj = NULL;
+ PyErr_SetString(PyExc_ValueError, "cannot update read-only buffer");
+ }
+
+ /* See if we need to truncate the buffer. */
+ if (resultobj && view$argnum.len != wrapper$argnum->data.mem.length)
+ {
+ if (bytesio$argnum == NULL)
+ {
+ Py_XDECREF(resultobj);
+ resultobj = NULL;
+ PyErr_SetString(PyExc_ValueError, "cannot resize buffer");
+ }
+ else
+ {
+ PyObject *retval;
+ PyBuffer_Release(&view$argnum);
+ retval = PyObject_CallMethod(bytesio$argnum, "truncate", "l",
+ (long)
+ wrapper$argnum->data.mem.length);
+ if (retval == NULL)
+ {
+ Py_XDECREF(resultobj);
+ resultobj = NULL;
+ }
+ else
+ {
+ Py_DECREF(retval);
+
+ retval = PyObject_CallMethod(bytesio$argnum, "getbuffer", NULL);
+ if (retval == NULL
+ || PyObject_GetBuffer(retval, &view$argnum,
+ PyBUF_SIMPLE|PyBUF_WRITABLE) < 0)
+ {
+ Py_XDECREF(resultobj);
+ resultobj = NULL;
+ }
+
+ Py_XDECREF(retval);
+
+ if (resultobj && view$argnum.len
+ != wrapper$argnum->data.mem.length)
+ {
+ Py_XDECREF(resultobj);
+ resultobj = NULL;
+ PyErr_Format(PyExc_ValueError,
+ "Expected buffer of length %zu, got %zi",
+ wrapper$argnum->data.mem.length,
+ view$argnum.len);
+ }
+ }
+ }
+ }
+
+ if (resultobj)
+ memcpy(view$argnum.buf, wrapper$argnum->data.mem.buffer,
+ wrapper$argnum->data.mem.length);
+ }
+
/* Free the temporary wrapper, if any. */
- Py_XDECREF(wrapper$argnum);
+ if (wrapper$argnum)
+ gpgme_data_release(wrapper$argnum);
+ Py_XDECREF (bytesio$argnum);
+ if (wrapper$argnum && view$argnum.buf)
+ PyBuffer_Release(&view$argnum);
}
%apply gpgme_data_t DATAIN {gpgme_data_t plain, gpgme_data_t cipher,
@@ -240,7 +313,10 @@
version for SWIG. We do, however, want to hide certain fields on
some structs, which we provide prior to including the version for
SWIG. */
- %{ #include <gpgme.h> %}
+%{
+#include <gpgme.h>
+#include "src/data.h" /* For struct gpgme_data. */
+%}
/* This is for notations, where we want to hide the length fields, and
the unused bit field block. */
@@ -291,5 +367,13 @@ FILE *fdopen(int fildes, const char *mode);
%{
#include "helpers.h"
+
+/* SWIG support for helpers.c */
+PyObject *
+pygpgme_wrap_gpgme_data_t(gpgme_data_t data)
+{
+ return SWIG_NewPointerObj(data, SWIGTYPE_p_gpgme_data, 0);
+}
%}
+
%include "helpers.h"
diff --git a/lang/python/helpers.c b/lang/python/helpers.c
index 810423d0..ad33d07b 100644
--- a/lang/python/helpers.c
+++ b/lang/python/helpers.c
@@ -206,64 +206,72 @@ object_to_gpgme_t(PyObject *input, const char *objtype, int argnum)
objects with a fileno method, returning it in WRAPPER. This object
must be de-referenced when no longer needed. */
PyObject *
-object_to_gpgme_data_t(PyObject *input, int argnum, PyObject **wrapper)
+object_to_gpgme_data_t(PyObject *input, int argnum, gpgme_data_t *wrapper,
+ PyObject **bytesio, Py_buffer *view)
{
- static PyObject *Data = NULL;
- PyObject *data = input;
+ gpgme_error_t err;
+ PyObject *data;
PyObject *fd;
- PyObject *result;
- *wrapper = NULL;
-
- if (Data == NULL) {
- PyObject *core;
- PyObject *from_list = PyList_New(0);
- core = PyImport_ImportModuleLevel("core", PyEval_GetGlobals(),
- PyEval_GetLocals(), from_list, 1);
- Py_XDECREF(from_list);
- if (core) {
- Data = PyDict_GetItemString(PyModule_GetDict(core), "Data");
- Py_XINCREF(Data);
- }
- else
- return NULL;
- }
+ /* See if it is a file-like object with file number. */
fd = PyObject_CallMethod(input, "fileno", NULL);
if (fd) {
- /* File-like object with file number. */
- PyObject *args = NULL;
- PyObject *kw = NULL;
-
- /* We don't need the fd, as we have no constructor accepting file
- descriptors directly. */
+ err = gpgme_data_new_from_fd(wrapper, (int) PyLong_AsLong(fd));
Py_DECREF(fd);
+ if (err)
+ return pygpgme_raise_exception (err);
+
+ return pygpgme_wrap_gpgme_data_t(*wrapper);
+ }
+ else
+ PyErr_Clear();
- args = PyTuple_New(0);
- kw = PyDict_New();
- if (args == NULL || kw == NULL)
- {
- fail:
- Py_XDECREF(args);
- Py_XDECREF(kw);
+ /* No? Maybe it implements the buffer protocol. */
+ data = PyObject_CallMethod(input, "getbuffer", NULL);
+ if (data)
+ {
+ /* Save a reference to input, which seems to be a BytesIO
+ object. */
+ Py_INCREF(input);
+ *bytesio = input;
+ }
+ else
+ {
+ PyErr_Clear();
+
+ /* No, but maybe the user supplied a buffer object? */
+ data = input;
+ }
+
+ /* Do we have a buffer object? */
+ if (PyObject_CheckBuffer(data))
+ {
+ if (PyObject_GetBuffer(data, view, PyBUF_SIMPLE) < 0)
return NULL;
- }
- if (PyDict_SetItemString(kw, "file", input) < 0)
- goto fail;
+ if (data != input)
+ Py_DECREF(data);
- *wrapper = PyObject_Call(Data, args, kw);
- if (*wrapper == NULL)
- goto fail;
+ assert (view->ndim == 1);
+ assert (view->shape == NULL);
+ assert (view->strides == NULL);
+ assert (view->suboffsets == NULL);
- Py_DECREF(args);
- Py_DECREF(kw);
- data = *wrapper;
- }
- else
- PyErr_Clear();
+ err = gpgme_data_new_from_mem(wrapper, view->buf, (size_t) view->len, 0);
+ if (err)
+ return pygpgme_raise_exception (err);
- result = object_to_gpgme_t(data, "gpgme_data_t", argnum);
- return result;
+ return pygpgme_wrap_gpgme_data_t(*wrapper);
+ }
+
+ /* As last resort we assume it is a wrapped data object. */
+ if (PyObject_HasAttrString(data, "_getctype"))
+ return object_to_gpgme_t(data, "gpgme_data_t", argnum);
+
+ return PyErr_Format(PyExc_TypeError,
+ "arg %d: expected pyme.Data, file, or an object "
+ "implementing the buffer protocol, got %s",
+ argnum, data->ob_type->tp_name);
}
diff --git a/lang/python/helpers.h b/lang/python/helpers.h
index 24502631..37362aee 100644
--- a/lang/python/helpers.h
+++ b/lang/python/helpers.h
@@ -1,4 +1,5 @@
/*
+# Copyright (C) 2016 g10 Code GmbH
# Copyright (C) 2004 Igor Belyi <[email protected]>
# Copyright (C) 2002 John Goerzen <[email protected]>
#
@@ -30,7 +31,8 @@ gpgme_error_t pygpgme_exception2code(void);
PyObject *object_to_gpgme_t(PyObject *input, const char *objtype, int argnum);
PyObject *object_to_gpgme_data_t(PyObject *input, int argnum,
- PyObject **wrapper);
+ gpgme_data_t *wrapper,
+ PyObject **bytesio, Py_buffer *view);
void pygpgme_clear_generic_cb(PyObject **cb);
PyObject *pygpgme_raise_callback_exception(PyObject *self);
@@ -47,3 +49,6 @@ gpgme_error_t pyEditCb(void *opaque, gpgme_status_code_t status,
gpgme_error_t pygpgme_data_new_from_cbs(gpgme_data_t *r_data,
PyObject *pycbs,
PyObject **freelater);
+
+/* SWIG support for helpers.c */
+PyObject *pygpgme_wrap_gpgme_data_t(gpgme_data_t data);
diff --git a/lang/python/tests/t-idiomatic.py b/lang/python/tests/t-idiomatic.py
index 37cfb64a..b2526902 100755
--- a/lang/python/tests/t-idiomatic.py
+++ b/lang/python/tests/t-idiomatic.py
@@ -17,6 +17,7 @@
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+import io
import os
import tempfile
from pyme import core, constants, errors
@@ -33,14 +34,7 @@ with core.Context() as c, core.Data() as d:
assert leak_c.wrapped == None
assert leak_d.wrapped == None
-# Demonstrate automatic wrapping of file-like objects with 'fileno'
-# method.
-with tempfile.TemporaryFile() as source, \
- tempfile.TemporaryFile() as signed, \
- tempfile.TemporaryFile() as sink:
- source.write(b"Hallo Leute\n")
- source.seek(0, os.SEEK_SET)
-
+def sign_and_verify(source, signed, sink):
with core.Context() as c:
c.op_sign(source, signed, constants.SIG_MODE_NORMAL)
signed.seek(0, os.SEEK_SET)
@@ -54,3 +48,28 @@ with tempfile.TemporaryFile() as source, \
sink.seek(0, os.SEEK_SET)
assert sink.read() == b"Hallo Leute\n"
+
+# Demonstrate automatic wrapping of file-like objects with 'fileno'
+# method.
+with tempfile.TemporaryFile() as source, \
+ tempfile.TemporaryFile() as signed, \
+ tempfile.TemporaryFile() as sink:
+ source.write(b"Hallo Leute\n")
+ source.seek(0, os.SEEK_SET)
+
+ sign_and_verify(source, signed, sink)
+
+# XXX: Python's io.BytesIo.truncate does not work as advertised.
+# http://bugs.python.org/issue27261
+bio = io.BytesIO()
+bio.truncate(1)
+if len(bio.getvalue()) != 1:
+ # This version of Python is affected, preallocate buffer.
+ preallocate = 128*b'\x00'
+else:
+ preallocate = b''
+
+# Demonstrate automatic wrapping of objects implementing the buffer
+# interface, and the use of data objects with the 'with' statement.
+with io.BytesIO(preallocate) as signed, core.Data() as sink:
+ sign_and_verify(b"Hallo Leute\n", signed, sink)