python: Wrap file-like objects on demand.

* lang/python/gpgme.i (gpgme_data_t): Use new function to create
wrapper objects if necessary, and deallocate them after the function
call.
* lang/python/helpers.c (object_to_gpgme_data_t): New function.
* lang/python/helpers.h (object_to_gpgme_data_t): New prototype.
* lang/python/tests/Makefile.am (pytests): Add new test.
* lang/python/tests/t-idiomatic.py: New file.

Signed-off-by: Justus Winter <justus@g10code.com>
This commit is contained in:
Justus Winter 2016-06-06 13:11:15 +02:00
parent 26c3accc95
commit 8196edf9ca
5 changed files with 127 additions and 3 deletions

View File

@ -113,13 +113,17 @@
}
// Special handling for references to our objects.
%typemap(in) gpgme_data_t DATAIN {
%typemap(in) gpgme_data_t DATAIN (PyObject *wrapper) {
/* 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;
if ($input == Py_None)
$1 = NULL;
else {
PyObject *pypointer = NULL;
if((pypointer=object_to_gpgme_t($input, "$1_ltype", $argnum)) == NULL)
if((pypointer=object_to_gpgme_data_t($input, $argnum, &wrapper)) == NULL)
return NULL;
/* input = $input, 1 = $1, 1_descriptor = $1_descriptor */
@ -135,6 +139,11 @@
}
}
%typemap(freearg) gpgme_data_t DATAIN {
/* Free the temporary wrapper, if any. */
Py_XDECREF(wrapper$argnum);
}
%apply gpgme_data_t DATAIN {gpgme_data_t plain, gpgme_data_t cipher,
gpgme_data_t sig, gpgme_data_t signed_text,
gpgme_data_t plaintext, gpgme_data_t keydata,

View File

@ -180,6 +180,71 @@ object_to_gpgme_t(PyObject *input, const char *objtype, int argnum)
return pypointer;
}
/* Convert object to a pointer to gpgme type, version for data
objects. Constructs a wrapper Python on the fly e.g. for file-like
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)
{
static PyObject *Data = NULL;
PyObject *data = input;
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;
}
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. */
Py_DECREF(fd);
args = PyTuple_New(0);
kw = PyDict_New();
if (args == NULL || kw == NULL)
{
fail:
Py_XDECREF(args);
Py_XDECREF(kw);
return NULL;
}
if (PyDict_SetItemString(kw, "file", input) < 0)
goto fail;
*wrapper = PyObject_Call(Data, args, kw);
if (*wrapper == NULL)
goto fail;
Py_DECREF(args);
Py_DECREF(kw);
data = *wrapper;
}
else
PyErr_Clear();
result = object_to_gpgme_t(data, "gpgme_data_t", argnum);
return result;
}
/* Callback support. */

View File

@ -29,6 +29,8 @@ void pygpgme_exception_init(void);
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);
void pygpgme_clear_generic_cb(PyObject **cb);
PyObject *pygpgme_raise_callback_exception(PyObject *self);

View File

@ -46,7 +46,8 @@ py_tests = t-wrapper.py \
t-edit.py \
t-wait.py \
t-encrypt-large.py \
t-file-name.py
t-file-name.py \
t-idiomatic.py
TESTS = $(top_srcdir)/tests/gpg/initial.test \
$(py_tests) \

View File

@ -0,0 +1,47 @@
#!/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 <http://www.gnu.org/licenses/>.
import os
import tempfile
from pyme import core, constants, errors
import support
support.init_gpgme(constants.PROTOCOL_OpenPGP)
c = core.Context()
# 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)
c.op_sign(source, signed, constants.SIG_MODE_NORMAL)
signed.seek(0, os.SEEK_SET)
c.op_verify(signed, None, sink)
result = c.op_verify_result()
assert len(result.signatures) == 1, "Unexpected number of signatures"
sig = result.signatures[0]
assert sig.summary == 0
assert errors.GPGMEError(sig.status).getcode() == errors.NO_ERROR
sink.seek(0, os.SEEK_SET)
assert sink.read() == b"Hallo Leute\n"