<gh_stars>0
from .support import HPyTest


class TestCPythonCompatibility(HPyTest):

    # One note about the should_check_refcount() in the tests below: on
    # CPython, handles are actually implemented as INCREF/DECREF, so we can
    # check e.g. after an HPy_Dup the refcnt is += 1. However, on PyPy they
    # are implemented in a completely different way which is unrelated to the
    # refcnt (this is the whole point of HPy, after all :)). So in many of the
    # following ttests, checking the actual result of the function doesn't
    # really make sens on PyPy. We still run the functions to ensure they do
    # not crash, though.

    def test_frompyobject(self):
        mod = self.make_module("""
            #include <Python.h>
            HPy_DEF_METH_NOARGS(f)
            static HPy f_impl(HPyContext ctx, HPy self)
            {
                PyObject *o = PyList_New(0);
                Py_ssize_t initial_refcount = o->ob_refcnt;
                HPy h = HPy_FromPyObject(ctx, o);
                Py_ssize_t final_refcount = o->ob_refcnt;

                PyList_Append(o, PyLong_FromLong(1234));
                PyList_Append(o, PyLong_FromSsize_t(final_refcount -
                                                    initial_refcount));
                Py_DECREF(o);
                return h;
            }
            @EXPORT f HPy_METH_NOARGS
            @INIT
        """)
        x = mod.f()
        assert x[0] == 1234
        assert len(x) == 2
        if self.should_check_refcount():
            assert x == [1234, +1]

    def test_aspyobject(self):
        mod = self.make_module("""
            #include <Python.h>
            HPy_DEF_METH_O(f)
            static HPy f_impl(HPyContext ctx, HPy self, HPy arg)
            {
                PyObject *o = HPy_AsPyObject(ctx, arg);
                long val = PyLong_AsLong(o);
                Py_DecRef(o);
                return HPyLong_FromLong(ctx, val*2);
            }
            @EXPORT f HPy_METH_O
            @INIT
        """)
        assert mod.f(21) == 42

    def test_aspyobject_custom_class(self):
        mod = self.make_module("""
            #include <Python.h>
            HPy_DEF_METH_O(f)
            static HPy f_impl(HPyContext ctx, HPy self, HPy arg)
            {
                PyObject *o = HPy_AsPyObject(ctx, arg);
                PyObject *o_res = PyObject_CallMethod(o, "foo", "");
                HPy h_res = HPy_FromPyObject(ctx, o_res);
                Py_DecRef(o);
                Py_DecRef(o_res);
                return h_res;
            }
            @EXPORT f HPy_METH_O
            @INIT
        """)
        class MyClass:
            def foo(self):
                return 1234
        obj = MyClass()
        assert mod.f(obj) == 1234

    def test_hpy_close(self):
        mod = self.make_module("""
            #include <Python.h>
            HPy_DEF_METH_NOARGS(f)
            static HPy f_impl(HPyContext ctx, HPy self)
            {
                PyObject *o = PyList_New(0);

                HPy h = HPy_FromPyObject(ctx, o);
                Py_ssize_t initial_refcount = o->ob_refcnt;
                HPy_Close(ctx, h);
                Py_ssize_t final_refcount = o->ob_refcnt;

                Py_DECREF(o);
                return HPyLong_FromLong(ctx, (long)(final_refcount -
                                                    initial_refcount));
            }
            @EXPORT f HPy_METH_NOARGS
            @INIT
        """)
        x = mod.f()
        if self.should_check_refcount():
            assert x == -1

    def test_hpy_dup(self):
        mod = self.make_module("""
            #include <Python.h>
            HPy_DEF_METH_NOARGS(f)
            static HPy f_impl(HPyContext ctx, HPy self)
            {
                PyObject *o = PyList_New(0);

                HPy h = HPy_FromPyObject(ctx, o);
                Py_ssize_t initial_refcount = o->ob_refcnt;
                HPy h2 = HPy_Dup(ctx, h);
                Py_ssize_t final_refcount = o->ob_refcnt;

                HPy_Close(ctx, h);
                HPy_Close(ctx, h2);
                Py_DECREF(o);
                return HPyLong_FromLong(ctx, (long)(final_refcount -
                                                    initial_refcount));
            }
            @EXPORT f HPy_METH_NOARGS
            @INIT
        """)
        x = mod.f()
        if self.should_check_refcount():
            assert x == +1

    def test_many_handles(self):
        mod = self.make_module("""
            #include <Python.h>
            #define NUM_HANDLES  10000

            HPy_DEF_METH_NOARGS(f)
            static HPy f_impl(HPyContext ctx, HPy self)
            {
                PyObject *o = PyList_New(0);

                Py_ssize_t result = -42;
                HPy handles[NUM_HANDLES];
                int i;
                Py_ssize_t initial_refcount = o->ob_refcnt;
                for (i = 0; i < NUM_HANDLES; i++)
                    handles[i] = HPy_FromPyObject(ctx, o);
                for (i = 0; i < NUM_HANDLES; i++)
                    if (HPy_IsNull(handles[i]))
                        goto error;
                for (i = 0; i < NUM_HANDLES; i++)
                    HPy_Close(ctx, handles[i]);
                Py_ssize_t final_refcount = o->ob_refcnt;
                result = final_refcount - initial_refcount;

             error:
                return HPyLong_FromLong(ctx, (long)result);
            }
            @EXPORT f HPy_METH_NOARGS
            @INIT
        """)
        assert mod.f() == 0

    def test_meth_cpy_noargs(self):
        mod = self.make_module("""
            #include <Python.h>

            static PyObject *f(PyObject *self, PyObject *args)
            {
                return PyLong_FromLong(1234);
            }
            @EXPORT f METH_NOARGS
            @INIT
        """)
        assert mod.f() == 1234

    def test_meth_cpy_o(self):
        mod = self.make_module("""
            #include <Python.h>

            static PyObject *f(PyObject *self, PyObject *arg)
            {
                long x = PyLong_AsLong(arg);
                return PyLong_FromLong(x * 2);
            }
            @EXPORT f METH_O
            @INIT
        """)
        assert mod.f(45) == 90

    def test_meth_cpy_varargs(self):
        mod = self.make_module("""
            #include <Python.h>

            static PyObject *f(PyObject *self, PyObject *args)
            {
                long a, b, c;
                if (!PyArg_ParseTuple(args, "lll", &a, &b, &c))
                    return NULL;
                return PyLong_FromLong(100*a + 10*b + c);
            }
            @EXPORT f METH_VARARGS
            @INIT
        """)
        assert mod.f(4, 5, 6) == 456

    def test_meth_cpy_keywords(self):
        mod = self.make_module("""
            #include <Python.h>

            static PyObject *f(PyObject *self, PyObject *args, PyObject *kwargs)
            {
                static char *kwlist[] = { "a", "b", "c", NULL };
                long a, b, c;
                if (!PyArg_ParseTupleAndKeywords(args, kwargs, "lll", kwlist, &a, &b, &c))
                    return NULL;
                return PyLong_FromLong(100*a + 10*b + c);
            }
            @EXPORT f METH_VARARGS | METH_KEYWORDS
            @INIT
        """)
        assert mod.f(c=6, b=5, a=4) == 456

from .support import HPyTest


class TestCPythonCompatibility(HPyTest):

    # One note about the should_check_refcount() in the tests below: on
    # CPython, handles are actually implemented as INCREF/DECREF, so we can
    # check e.g. after an HPy_Dup the refcnt is += 1. However, on PyPy they
    # are implemented in a completely different way which is unrelated to the
    # refcnt (this is the whole point of HPy, after all :)). So in many of the
    # following ttests, checking the actual result of the function doesn't
    # really make sens on PyPy. We still run the functions to ensure they do
    # not crash, though.

    def test_frompyobject(self):
        mod = self.make_module("""
            #include <Python.h>
            HPy_DEF_METH_NOARGS(f)
            static HPy f_impl(HPyContext ctx, HPy self)
            {
                PyObject *o = PyList_New(0);
                Py_ssize_t initial_refcount = o->ob_refcnt;
                HPy h = HPy_FromPyObject(ctx, o);
                Py_ssize_t final_refcount = o->ob_refcnt;

                PyList_Append(o, PyLong_FromLong(1234));
                PyList_Append(o, PyLong_FromSsize_t(final_refcount -
                                                    initial_refcount));
                Py_DECREF(o);
                return h;
            }
            @EXPORT f HPy_METH_NOARGS
            @INIT
        """)
        x = mod.f()
        assert x[0] == 1234
        assert len(x) == 2
        if self.should_check_refcount():
            assert x == [1234, +1]

    def test_aspyobject(self):
        mod = self.make_module("""
            #include <Python.h>
            HPy_DEF_METH_O(f)
            static HPy f_impl(HPyContext ctx, HPy self, HPy arg)
            {
                PyObject *o = HPy_AsPyObject(ctx, arg);
                long val = PyLong_AsLong(o);
                Py_DecRef(o);
                return HPyLong_FromLong(ctx, val*2);
            }
            @EXPORT f HPy_METH_O
            @INIT
        """)
        assert mod.f(21) == 42

    def test_aspyobject_custom_class(self):
        mod = self.make_module("""
            #include <Python.h>
            HPy_DEF_METH_O(f)
            static HPy f_impl(HPyContext ctx, HPy self, HPy arg)
            {
                PyObject *o = HPy_AsPyObject(ctx, arg);
                PyObject *o_res = PyObject_CallMethod(o, "foo", "");
                HPy h_res = HPy_FromPyObject(ctx, o_res);
                Py_DecRef(o);
                Py_DecRef(o_res);
                return h_res;
            }
            @EXPORT f HPy_METH_O
            @INIT
        """)
        class MyClass:
            def foo(self):
                return 1234
        obj = MyClass()
        assert mod.f(obj) == 1234

    def test_hpy_close(self):
        mod = self.make_module("""
            #include <Python.h>
            HPy_DEF_METH_NOARGS(f)
            static HPy f_impl(HPyContext ctx, HPy self)
            {
                PyObject *o = PyList_New(0);

                HPy h = HPy_FromPyObject(ctx, o);
                Py_ssize_t initial_refcount = o->ob_refcnt;
                HPy_Close(ctx, h);
                Py_ssize_t final_refcount = o->ob_refcnt;

                Py_DECREF(o);
                return HPyLong_FromLong(ctx, (long)(final_refcount -
                                                    initial_refcount));
            }
            @EXPORT f HPy_METH_NOARGS
            @INIT
        """)
        x = mod.f()
        if self.should_check_refcount():
            assert x == -1

    def test_hpy_dup(self):
        mod = self.make_module("""
            #include <Python.h>
            HPy_DEF_METH_NOARGS(f)
            static HPy f_impl(HPyContext ctx, HPy self)
            {
                PyObject *o = PyList_New(0);

                HPy h = HPy_FromPyObject(ctx, o);
                Py_ssize_t initial_refcount = o->ob_refcnt;
                HPy h2 = HPy_Dup(ctx, h);
                Py_ssize_t final_refcount = o->ob_refcnt;

                HPy_Close(ctx, h);
                HPy_Close(ctx, h2);
                Py_DECREF(o);
                return HPyLong_FromLong(ctx, (long)(final_refcount -
                                                    initial_refcount));
            }
            @EXPORT f HPy_METH_NOARGS
            @INIT
        """)
        x = mod.f()
        if self.should_check_refcount():
            assert x == +1

    def test_many_handles(self):
        mod = self.make_module("""
            #include <Python.h>
            #define NUM_HANDLES  10000

            HPy_DEF_METH_NOARGS(f)
            static HPy f_impl(HPyContext ctx, HPy self)
            {
                PyObject *o = PyList_New(0);

                Py_ssize_t result = -42;
                HPy handles[NUM_HANDLES];
                int i;
                Py_ssize_t initial_refcount = o->ob_refcnt;
                for (i = 0; i < NUM_HANDLES; i++)
                    handles[i] = HPy_FromPyObject(ctx, o);
                for (i = 0; i < NUM_HANDLES; i++)
                    if (HPy_IsNull(handles[i]))
                        goto error;
                for (i = 0; i < NUM_HANDLES; i++)
                    HPy_Close(ctx, handles[i]);
                Py_ssize_t final_refcount = o->ob_refcnt;
                result = final_refcount - initial_refcount;

             error:
                return HPyLong_FromLong(ctx, (long)result);
            }
            @EXPORT f HPy_METH_NOARGS
            @INIT
        """)
        assert mod.f() == 0

    def test_meth_cpy_noargs(self):
        mod = self.make_module("""
            #include <Python.h>

            static PyObject *f(PyObject *self, PyObject *args)
            {
                return PyLong_FromLong(1234);
            }
            @EXPORT f METH_NOARGS
            @INIT
        """)
        assert mod.f() == 1234

    def test_meth_cpy_o(self):
        mod = self.make_module("""
            #include <Python.h>

            static PyObject *f(PyObject *self, PyObject *arg)
            {
                long x = PyLong_AsLong(arg);
                return PyLong_FromLong(x * 2);
            }
            @EXPORT f METH_O
            @INIT
        """)
        assert mod.f(45) == 90

    def test_meth_cpy_varargs(self):
        mod = self.make_module("""
            #include <Python.h>

            static PyObject *f(PyObject *self, PyObject *args)
            {
                long a, b, c;
                if (!PyArg_ParseTuple(args, "lll", &a, &b, &c))
                    return NULL;
                return PyLong_FromLong(100*a + 10*b + c);
            }
            @EXPORT f METH_VARARGS
            @INIT
        """)
        assert mod.f(4, 5, 6) == 456

    def test_meth_cpy_keywords(self):
        mod = self.make_module("""
            #include <Python.h>

            static PyObject *f(PyObject *self, PyObject *args, PyObject *kwargs)
            {
                static char *kwlist[] = { "a", "b", "c", NULL };
                long a, b, c;
                if (!PyArg_ParseTupleAndKeywords(args, kwargs, "lll", kwlist, &a, &b, &c))
                    return NULL;
                return PyLong_FromLong(100*a + 10*b + c);
            }
            @EXPORT f METH_VARARGS | METH_KEYWORDS
            @INIT
        """)
        assert mod.f(c=6, b=5, a=4) == 456
# One note about the should_check_refcount() in the tests below: on # CPython, handles are actually implemented as INCREF/DECREF, so we can # check e.g. after an HPy_Dup the refcnt is += 1. However, on PyPy they # are implemented in a completely different way which is unrelated to the # refcnt (this is the whole point of HPy, after all :)). So in many of the # following ttests, checking the actual result of the function doesn't # really make sens on PyPy. We still run the functions to ensure they do # not crash, though. #include <Python.h>             HPy_DEF_METH_NOARGS(f)             static HPy f_impl(HPyContext ctx, HPy self)             {                 PyObject *o = PyList_New(0);                 Py_ssize_t initial_refcount = o->ob_refcnt;                 HPy h = HPy_FromPyObject(ctx, o);                 Py_ssize_t final_refcount = o->ob_refcnt;                  PyList_Append(o, PyLong_FromLong(1234));                 PyList_Append(o, PyLong_FromSsize_t(final_refcount -                                                     initial_refcount));                 Py_DECREF(o);                 return h;             }             @EXPORT f HPy_METH_NOARGS             @INIT #include <Python.h>             HPy_DEF_METH_O(f)             static HPy f_impl(HPyContext ctx, HPy self, HPy arg)             {                 PyObject *o = HPy_AsPyObject(ctx, arg);                 long val = PyLong_AsLong(o);                 Py_DecRef(o);                 return HPyLong_FromLong(ctx, val*2);             }             @EXPORT f HPy_METH_O             @INIT #include <Python.h>             HPy_DEF_METH_O(f)             static HPy f_impl(HPyContext ctx, HPy self, HPy arg)             {                 PyObject *o = HPy_AsPyObject(ctx, arg);                 PyObject *o_res = PyObject_CallMethod(o, "foo", "");                 HPy h_res = HPy_FromPyObject(ctx, o_res);                 Py_DecRef(o);                 Py_DecRef(o_res);                 return h_res;             }             @EXPORT f HPy_METH_O             @INIT #include <Python.h>             HPy_DEF_METH_NOARGS(f)             static HPy f_impl(HPyContext ctx, HPy self)             {                 PyObject *o = PyList_New(0);                  HPy h = HPy_FromPyObject(ctx, o);                 Py_ssize_t initial_refcount = o->ob_refcnt;                 HPy_Close(ctx, h);                 Py_ssize_t final_refcount = o->ob_refcnt;                  Py_DECREF(o);                 return HPyLong_FromLong(ctx, (long)(final_refcount -                                                     initial_refcount));             }             @EXPORT f HPy_METH_NOARGS             @INIT #include <Python.h>             HPy_DEF_METH_NOARGS(f)             static HPy f_impl(HPyContext ctx, HPy self)             {                 PyObject *o = PyList_New(0);                  HPy h = HPy_FromPyObject(ctx, o);                 Py_ssize_t initial_refcount = o->ob_refcnt;                 HPy h2 = HPy_Dup(ctx, h);                 Py_ssize_t final_refcount = o->ob_refcnt;                  HPy_Close(ctx, h);                 HPy_Close(ctx, h2);                 Py_DECREF(o);                 return HPyLong_FromLong(ctx, (long)(final_refcount -                                                     initial_refcount));             }             @EXPORT f HPy_METH_NOARGS             @INIT #include <Python.h>             #define NUM_HANDLES  10000              HPy_DEF_METH_NOARGS(f)             static HPy f_impl(HPyContext ctx, HPy self)             {                 PyObject *o = PyList_New(0);                  Py_ssize_t result = -42;                 HPy handles[NUM_HANDLES];                 int i;                 Py_ssize_t initial_refcount = o->ob_refcnt;                 for (i = 0; i < NUM_HANDLES; i++)                     handles[i] = HPy_FromPyObject(ctx, o);                 for (i = 0; i < NUM_HANDLES; i++)                     if (HPy_IsNull(handles[i]))                         goto error;                 for (i = 0; i < NUM_HANDLES; i++)                     HPy_Close(ctx, handles[i]);                 Py_ssize_t final_refcount = o->ob_refcnt;                 result = final_refcount - initial_refcount;               error:                 return HPyLong_FromLong(ctx, (long)result);             }             @EXPORT f HPy_METH_NOARGS             @INIT #include <Python.h>              static PyObject *f(PyObject *self, PyObject *args)             {                 return PyLong_FromLong(1234);             }             @EXPORT f METH_NOARGS             @INIT #include <Python.h>              static PyObject *f(PyObject *self, PyObject *arg)             {                 long x = PyLong_AsLong(arg);                 return PyLong_FromLong(x * 2);             }             @EXPORT f METH_O             @INIT #include <Python.h>              static PyObject *f(PyObject *self, PyObject *args)             {                 long a, b, c;                 if (!PyArg_ParseTuple(args, "lll", &a, &b, &c))                     return NULL;                 return PyLong_FromLong(100*a + 10*b + c);             }             @EXPORT f METH_VARARGS             @INIT #include <Python.h>              static PyObject *f(PyObject *self, PyObject *args, PyObject *kwargs)             {                 static char *kwlist[] = { "a", "b", "c", NULL };                 long a, b, c;                 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "lll", kwlist, &a, &b, &c))                     return NULL;                 return PyLong_FromLong(100*a + 10*b + c);             }             @EXPORT f METH_VARARGS | METH_KEYWORDS             @INIT