python: Handle slight differences between Python 2 and 3.

* lang/python/helpers.c (pyDataWriteCb): Handle Python integers being
returned on Python 2.
(pyDataSeekCb): Likewise.
* lang/python/pyme/core.py (Data.__init__): Fix testing for string
argument.
(Data.new_from_filepart): Likewise.
* lang/python/pyme/util.py (is_a_string): New function.
* lang/python/tests/t-encrypt-large.py (read_cb): Force evaluation of
generator.
* lang/python/tests/t-idiomatic.py: Partly skip test on Python 2.
* lang/python/tests/t-verify.py (check_result): Here, the difference
between 2 and 3 really matters.  We cannot change the char *
conversion in Python 2 without breaking all existing applications, and
using bytestrings in Python 3 would be very inconvenient.

Signed-off-by: Justus Winter <justus@g10code.com>
This commit is contained in:
Justus Winter 2016-09-13 10:44:14 +02:00
parent 4abff7d750
commit 70a3be27a5
6 changed files with 61 additions and 32 deletions

View File

@ -833,17 +833,21 @@ static ssize_t pyDataWriteCb(void *hook, const void *buffer, size_t size)
goto leave;
}
if (! PyLong_Check(retval)) {
#if PY_MAJOR_VERSION < 3
if (PyInt_Check(retval))
result = PyInt_AsSsize_t(retval);
else
#endif
if (PyLong_Check(retval))
result = PyLong_AsSsize_t(retval);
else {
PyErr_Format(PyExc_TypeError,
"expected int from read callback, got %s",
"expected int from write callback, got %s",
retval->ob_type->tp_name);
_pyme_stash_callback_exception(self);
result = -1;
goto leave;
}
result = PyLong_AsSsize_t(retval);
leave:
Py_XDECREF(retval);
return result;
@ -894,20 +898,24 @@ static off_t pyDataSeekCb(void *hook, off_t offset, int whence)
goto leave;
}
if (! PyLong_Check(retval)) {
PyErr_Format(PyExc_TypeError,
"expected int from read callback, got %s",
retval->ob_type->tp_name);
_pyme_stash_callback_exception(self);
result = -1;
goto leave;
}
#if PY_MAJOR_VERSION < 3
if (PyInt_Check(retval))
result = PyInt_AsLong(retval);
else
#endif
if (PyLong_Check(retval))
#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64
result = PyLong_AsLongLong(retval);
#else
result = PyLong_AsLong(retval);
#endif
else {
PyErr_Format(PyExc_TypeError,
"expected int from seek callback, got %s",
retval->ob_type->tp_name);
_pyme_stash_callback_exception(self);
result = -1;
}
leave:
Py_XDECREF(retval);

View File

@ -884,7 +884,7 @@ class Data(GpgmeWrapper):
elif file != None and offset != None and length != None:
self.new_from_filepart(file, offset, length)
elif file != None:
if type(file) == type("x"):
if util.is_a_string(file):
self.new_from_file(file, copy)
else:
self.new_from_fd(file)
@ -961,7 +961,7 @@ class Data(GpgmeWrapper):
filename = None
fp = None
if type(file) == type("x"):
if util.is_a_string(file):
filename = file
else:
fp = gpgme.fdopen(file.fileno(), file.mode)

View File

@ -16,6 +16,8 @@
# 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 sys
def process_constants(prefix, scope):
"""Called by the constant modules to load up the constants from the C
library starting with PREFIX. Matching constants will be inserted
@ -36,3 +38,13 @@ def percent_escape(s):
'%{0:2x}'.format(ord(c))
if c == '+' or c == '"' or c == '%' or ord(c) <= 0x20 else c
for c in s)
# Python2/3 compatibility
if sys.version_info[0] == 3:
# Python3
def is_a_string(x):
return isinstance(x, str)
else:
# Python2
def is_a_string(x):
return isinstance(x, basestring)

View File

@ -37,7 +37,7 @@ def read_cb(amount):
ntoread -= chunk
assert ntoread >= 0
assert chunk >= 0
return bytes(random.randrange(256) for i in range(chunk))
return bytes(bytearray(random.randrange(256) for i in range(chunk)))
nwritten = 0
def write_cb(data):

View File

@ -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 sys
import io
import os
import tempfile
@ -60,6 +61,10 @@ with tempfile.TemporaryFile() as source, \
sign_and_verify(source, signed, sink)
if sys.version_info[0] == 3:
# Python2's io.BytesIO does not implement the buffer interface,
# hence we cannot use it as sink.
# XXX: Python's io.BytesIo.truncate does not work as advertised.
# http://bugs.python.org/issue27261
bio = io.BytesIO()

View File

@ -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 sys
import os
import pyme
from pyme import core, constants, errors
@ -67,8 +68,11 @@ def check_result(result, summary, validity, fpr, status, notation):
if notation:
expected_notations = {
"bar": b"\xc3\xb6\xc3\xa4\xc3\xbc\xc3\x9f".decode() +
" das waren Umlaute und jetzt ein prozent%-Zeichen",
"bar": (b"\xc3\xb6\xc3\xa4\xc3\xbc\xc3\x9f" +
b" das waren Umlaute und jetzt ein prozent%-Zeichen"
if sys.version_info[0] < 3 else
b"\xc3\xb6\xc3\xa4\xc3\xbc\xc3\x9f".decode() +
" das waren Umlaute und jetzt ein prozent%-Zeichen"),
"foobar.1": "this is a notation data with 2 lines",
None: "http://www.gu.org/policy/",
}