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:
parent
26c3accc95
commit
8196edf9ca
@ -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,
|
||||
|
@ -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. */
|
||||
|
@ -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);
|
||||
|
@ -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) \
|
||||
|
47
lang/python/tests/t-idiomatic.py
Executable file
47
lang/python/tests/t-idiomatic.py
Executable 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"
|
Loading…
Reference in New Issue
Block a user