Add basic PyPy support. See #90

Use PyObject_GetAttrString() for accessing exception args as that works
on PyPy too.

Set tp_hash=PyObject_HashNotImplemented for some types, as PyPy doesn't
make them unhashable if tp_richcompare is set:
    https://bitbucket.org/pypy/pypy/issues/2740

Skip some tests due to:
    https://bitbucket.org/pypy/pypy/issues/2741
    https://bitbucket.org/pypy/pypy/issues/2742

Skip test_image_surface_create_for_data_array(), as it hangs.
And test_fspaths(). Needs investigating..
This commit is contained in:
Christoph Reiter 2018-01-30 10:20:38 +01:00
parent 54c268fe65
commit 947f0055cf
11 changed files with 65 additions and 13 deletions

View File

@ -25,6 +25,11 @@ matrix:
language: python
python: "3.6"
env: PYCAIRO_WARN=1 CFLAGS="-Werror -coverage"
- os: linux
dist: trusty
language: python
python: "pypy"
env: PYCAIRO_WARN=1 CFLAGS="-Werror -coverage"
- os: osx
osx_image: xcode7.3
language: generic
@ -50,12 +55,12 @@ install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then source ../venv/bin/activate; fi
- python -m pip install --upgrade setuptools
- python -m pip install --upgrade pytest flake8 sphinx sphinx_rtd_theme coverage codecov hypothesis
- if [ "$TRAVIS_PYTHON_VERSION" != "3.3" ]; then python -m pip install --upgrade pygame; fi
- if [[ "$TRAVIS_PYTHON_VERSION" != "3.3" ]] && [[ "$TRAVIS_PYTHON_VERSION" != "pypy" ]]; then python -m pip install --upgrade pygame; fi
script:
- python -m coverage run --branch setup.py test
- python -m codecov
- python -m flake8 .
- python setup.py sdist
- python -m pip install "$(eval 'echo dist/*')"
- if [ "$TRAVIS_PYTHON_VERSION" != "3.3" ]; then python -m sphinx -W -a -E -b html -n docs docs/_build; fi
- if [[ "$TRAVIS_PYTHON_VERSION" != "pypy" ]]; then python -m pip install "$(eval 'echo dist/*')"; fi
- if [[ "$TRAVIS_PYTHON_VERSION" != "3.3" ]]; then python -m sphinx -W -a -E -b html -n docs docs/_build; fi

View File

@ -100,20 +100,43 @@ typedef struct {
PyBaseExceptionObject base;
} PycairoErrorObject;
static PyObject *
error_get_args(PycairoErrorObject *self) {
PyObject *args;
args = PyObject_GetAttrString((PyObject *)self, "args");
if (args == NULL)
return NULL;
if (!PyTuple_Check(args)) {
PyErr_SetString(PyExc_TypeError, ".args not a tuple");
Py_DECREF(args);
return NULL;
}
return args;
}
static int
error_init(PycairoErrorObject *self, PyObject *args, PyObject *kwds)
{
PyObject *status_obj;
PyObject *status_obj, *error_args;
if (PycairoError_Type.tp_base->tp_init((PyObject *)self, args, kwds) < 0)
return -1;
if(PyTuple_GET_SIZE(self->base.args) >= 2) {
status_obj = PyTuple_GET_ITEM(self->base.args, 1);
error_args = error_get_args(self);
if (error_args == NULL)
return -1;
if(PyTuple_GET_SIZE(error_args) >= 2) {
status_obj = PyTuple_GET_ITEM(error_args, 1);
} else {
status_obj = Py_None;
}
Py_DECREF(error_args);
if (PyObject_SetAttrString ((PyObject *)self, "__status", status_obj) < 0)
return -1;
@ -148,12 +171,21 @@ static PyGetSetDef error_getset[] = {
static PyObject *
error_str(PycairoErrorObject *self)
{
PyObject *result, *error_args;
error_args = error_get_args(self);
if (error_args == NULL)
return NULL;
/* Default to printing just the message */
if (PyTuple_GET_SIZE(self->base.args) >= 1) {
return PyObject_Str(PyTuple_GET_ITEM(self->base.args, 0));
if (PyTuple_GET_SIZE(error_args) >= 1) {
result = PyObject_Str(PyTuple_GET_ITEM(error_args, 0));
} else {
return PycairoError_Type.tp_base->tp_str((PyObject*)self);
result = PycairoError_Type.tp_base->tp_str((PyObject*)self);
}
Py_DECREF(error_args);
return result;
}
static PyObject *

View File

@ -830,7 +830,7 @@ PyTypeObject PycairoFontOptions_Type = {
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
(hashfunc)PyObject_HashNotImplemented,/* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */

View File

@ -137,7 +137,7 @@ PyTypeObject PycairoRectangleInt_Type = {
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
(hashfunc)PyObject_HashNotImplemented,/* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
@ -556,7 +556,7 @@ PyTypeObject PycairoRegion_Type = {
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
(hashfunc)PyObject_HashNotImplemented,/* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */

View File

@ -260,6 +260,7 @@ def test_constants():
assert cairo.SVG_VERSION_1_2 == 1
@pytest.mark.skipif(not hasattr(sys, "getrefcount"), reason="PyPy")
def test_surface_get_set_mime_data_references():
surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1)
if sys.version_info[0] == 2:

View File

@ -1,6 +1,7 @@
import cairo
import pytest
import ctypes
import platform
@pytest.fixture
@ -49,6 +50,7 @@ def test_get_set_operator_limits(context):
assert context.get_operator() == val
@pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="PyPy")
def test_show_text_glyphs():
surface = cairo.PDFSurface(None, 300, 300)
context = cairo.Context(surface)
@ -306,6 +308,7 @@ def test_scale(context):
context.scale(object(), 0)
@pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="PyPy")
def test_select_font_face(context):
context.select_font_face("")
with pytest.raises(TypeError):
@ -458,6 +461,7 @@ def test_text_extents(context):
context.text_extents()
@pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="PyPy")
def test_text_path(context):
context.text_path("foo")
with pytest.raises(TypeError):

View File

@ -1,10 +1,12 @@
import pickle
import re
import platform
import pytest
import cairo
@pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="PyPy")
def test_type():
t = cairo.Antialias
assert int in t.__mro__

View File

@ -37,7 +37,7 @@ def test_error_check_status():
err.status = cairo.Status.DEVICE_FINISHED
assert err.status == cairo.Status.DEVICE_FINISHED
with pytest.raises(TypeError):
with pytest.raises((TypeError, AttributeError)):
del err.status
str(cairo.Error())

View File

@ -1,4 +1,5 @@
import sys
import platform
import cairo
import pytest
@ -166,6 +167,7 @@ def test_scaled_font_get_scale_matrix(scaled_font):
assert isinstance(scaled_font.get_scale_matrix(), cairo.Matrix)
@pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="PyPy")
def test_scaled_font_text_extents(scaled_font):
with pytest.raises(TypeError):
scaled_font.text_extents(object())
@ -180,6 +182,7 @@ def test_scaled_font_glyph_extents(scaled_font):
scaled_font.glyph_extents()
@pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="PyPy")
def test_toy_font_face():
with pytest.raises(TypeError):
cairo.ToyFontFace(object())

View File

@ -5,6 +5,7 @@ import os
import sys
import tempfile
import shutil
import platform
import pytest
import cairo
@ -39,6 +40,7 @@ def cairo_ver():
return tuple(map(int, cairo.cairo_version_string().split(".")))
@pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="PyPy")
@given(path=fspaths())
@settings(max_examples=500)
def test_fspaths(tempdir_path, path):

View File

@ -7,6 +7,7 @@ import array
import tempfile
import struct
import sysconfig
import platform
import cairo
import pytest
@ -110,6 +111,7 @@ def test_tee_surface():
tee.remove(s1)
@pytest.mark.skipif(not hasattr(sys, "getrefcount"), reason="PyPy")
def test_image_surface_get_data_refcount():
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10)
assert sys.getrefcount(surface) == 2
@ -387,6 +389,7 @@ def test_supports_mime_type():
surface.supports_mime_type(object())
@pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="PyPy")
def test_image_surface_create_for_data_array():
width, height = 255, 255
data = array.array('B', [0] * width * height * 4)