Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HPy_CallTupleDict, HPyCallable_Check and HPyTuple_Check. #147

Merged
merged 16 commits into from
Jan 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions docs/porting-guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,24 @@ For lists of (say) integers::
HPy h_list = HPyListBuilder_i_Build(ctx, builder);

And similar for building tuples or bytes


PyObject_Call and PyObject_CallObject
-------------------------------------

Both ``PyObject_Call`` and ``PyObject_CallObject`` are replaced by
``HPy_CallTupleDict(callable, args, kwargs)`` in which either or both of
``args`` and ``kwargs`` may be null handles.

``PyObject_Call(callable, args, kwargs)`` becomes:

HPy result = HPy_CallTupleDict(ctx, callable, args, kwargs);

``PyObject_CallObject(callable, args)`` becomes:

HPy result = HPy_CallTupleDict(ctx, callable, args, HPy_NULL);

If ``args`` is not a handle to a tuple or ``kwargs`` is not a handle to a
dictionary, ``HPy_CallTupleDict`` will return ``HPy_NULL`` and raise a
``TypeError``. This is different to ``PyObject_Call`` and
``PyObject_CallObject`` which may segfault instead.
10 changes: 10 additions & 0 deletions hpy/devel/include/common/autogen_impl.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions hpy/devel/include/common/runtime/ctx_call.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef HPY_COMMON_RUNTIME_CALL_H
#define HPY_COMMON_RUNTIME_CALL_H

#include <Python.h>
#include "hpy.h"

_HPy_HIDDEN HPy
ctx_CallTupleDict(HPyContext ctx, HPy callable, HPy args, HPy kw);

#endif /* HPY_COMMON_RUNTIME_CALL_H */
7 changes: 7 additions & 0 deletions hpy/devel/include/cpython/hpy.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ HPy_AsPyObject(HPyContext ctx, HPy h)
#include "../common/hpydef.h"
#include "../common/hpytype.h"
#include "../common/hpymodule.h"
#include "../common/runtime/ctx_call.h"
#include "../common/runtime/ctx_module.h"
#include "../common/runtime/ctx_type.h"
#include "../common/runtime/ctx_listbuilder.h"
Expand Down Expand Up @@ -315,6 +316,12 @@ _HPy_Cast(HPyContext ctx, HPy h)
return ctx_Cast(ctx, h);
}

HPyAPI_FUNC(HPy)
HPy_CallTupleDict(HPyContext ctx, HPy callable, HPy args, HPy kw)
{
return ctx_CallTupleDict(ctx, callable, args, kw);
}

HPyAPI_FUNC(HPyListBuilder)
HPyListBuilder_New(HPyContext ctx, HPy_ssize_t initial_size)
{
Expand Down
5 changes: 4 additions & 1 deletion hpy/devel/include/universal/autogen_ctx.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions hpy/devel/include/universal/autogen_trampolines.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 38 additions & 0 deletions hpy/devel/src/runtime/ctx_call.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include <Python.h>
#include "hpy.h"

#ifdef HPY_UNIVERSAL_ABI
// for _h2py and _py2h
# include "handles.h"
#endif

_HPy_HIDDEN HPy
ctx_CallTupleDict(HPyContext ctx, HPy callable, HPy args, HPy kw)
{
PyObject *obj;
if (!HPy_IsNull(args) && !HPyTuple_Check(ctx, args)) {
HPyErr_SetString(ctx, ctx->h_TypeError,
"HPy_CallTupleDict requires args to be a tuple or null handle");
return HPy_NULL;
}
if (!HPy_IsNull(kw) && !HPyDict_Check(ctx, kw)) {
HPyErr_SetString(ctx, ctx->h_TypeError,
"HPy_CallTupleDict requires kw to be a dict or null handle");
return HPy_NULL;
}
if (HPy_IsNull(kw)) {
obj = PyObject_CallObject(_h2py(callable), _h2py(args));
}
else if (!HPy_IsNull(args)){
obj = PyObject_Call(_h2py(callable), _h2py(args), _h2py(kw));
}
else {
// args is null, but kw is not, so we need to create an empty args tuple
// for CPython's PyObject_Call
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, nice catch. I didn't know that CPython doesn't have a way to pass only kwargs. I don't know if it will be actually useful but it doesn't cost anything to add support for it, so it's probably a good idea to be more complete, +1

HPy items[] = {};
HPy empty_tuple = HPyTuple_FromArray(ctx, items, 0);
obj = PyObject_Call(_h2py(callable), _h2py(empty_tuple), _h2py(kw));
HPy_Close(ctx, empty_tuple);
}
return _py2h(obj);
}
2 changes: 1 addition & 1 deletion hpy/tools/autogen/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def _visit_hpyslot_slot(self, node):
'HPy_SetItem_i': None,
'HPy_SetItem_s': None,
'HPy_Length': 'PyObject_Length',
'HPy_Call': 'PyObject_Call',
'HPy_CallTupleDict': None,
'_HPy_Cast': None,
'HPy_FromPyObject': None,
'HPy_AsPyObject': None,
Expand Down
8 changes: 5 additions & 3 deletions hpy/tools/autogen/public_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,11 @@ HPy HPy_InPlaceAnd(HPyContext ctx, HPy h1, HPy h2);
HPy HPy_InPlaceXor(HPyContext ctx, HPy h1, HPy h2);
HPy HPy_InPlaceOr(HPyContext ctx, HPy h1, HPy h2);

int HPyCallable_Check(HPyContext ctx, HPy h);
HPy HPy_CallTupleDict(HPyContext ctx, HPy callable, HPy args, HPy kw);

/* pyerrors.h */
void HPy_FatalError(HPyContext ctx, const char *message);
void HPyErr_SetString(HPyContext ctx, HPy h_type, const char *message);
void HPyErr_SetObject(HPyContext ctx, HPy h_type, HPy h_value);
/* note: HPyErr_Occurred() returns a flag 0-or-1, instead of a 'PyObject *' */
Expand Down Expand Up @@ -232,10 +236,8 @@ int HPyList_Append(HPyContext ctx, HPy h_list, HPy h_item);
int HPyDict_Check(HPyContext ctx, HPy h);
HPy HPyDict_New(HPyContext ctx);

/* pyerrors.h */
antocuni marked this conversation as resolved.
Show resolved Hide resolved
void HPy_FatalError(HPyContext ctx, const char *message);

/* tupleobject.h */
int HPyTuple_Check(HPyContext ctx, HPy h);
HPy HPyTuple_FromArray(HPyContext ctx, HPy items[], HPy_ssize_t n);
// note: HPyTuple_Pack is implemented as a macro in common/macros.h

Expand Down
5 changes: 4 additions & 1 deletion hpy/universal/src/autogen_ctx_def.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions hpy/universal/src/ctx.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "hpy.h"
#include "handles.h"

#include "common/runtime/ctx_call.h"
#include "common/runtime/ctx_type.h"
#include "common/runtime/ctx_module.h"
#include "common/runtime/ctx_listbuilder.h"
Expand Down
32 changes: 32 additions & 0 deletions microbench/src/cpy_simple.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,36 @@ static PyObject* varargs(PyObject* self, PyObject* args)
Py_RETURN_NONE;
}

static PyObject* call_with_tuple(PyObject* self, PyObject* args)
{
PyObject* f;
PyObject* f_args;
f = PyTuple_GetItem(args, 0);
if (f == NULL)
Py_RETURN_NONE;
f_args = PyTuple_GetItem(args, 1);
if (f_args == NULL)
Py_RETURN_NONE;
return PyObject_CallObject(f, f_args);
}

static PyObject* call_with_tuple_and_dict(PyObject* self, PyObject* args)
{
PyObject* f;
PyObject* f_args;
PyObject* f_kw;
f = PyTuple_GetItem(args, 0);
if (f == NULL)
Py_RETURN_NONE;
f_args = PyTuple_GetItem(args, 1);
if (f_args == NULL)
Py_RETURN_NONE;
f_kw = PyTuple_GetItem(args, 2);
if (f_kw == NULL)
Py_RETURN_NONE;
return PyObject_Call(f, f_args, f_kw);
}

static PyObject* allocate_int(PyObject* self, PyObject* args)
{
return PyLong_FromLong(2048);
Expand All @@ -41,6 +71,8 @@ static PyMethodDef SimpleMethods[] = {
{"noargs", (PyCFunction)noargs, METH_NOARGS, ""},
{"onearg", (PyCFunction)onearg, METH_O, ""},
{"varargs", (PyCFunction)varargs, METH_VARARGS, ""},
{"call_with_tuple", (PyCFunction)call_with_tuple, METH_VARARGS, ""},
{"call_with_tuple_and_dict", (PyCFunction)call_with_tuple_and_dict, METH_VARARGS, ""},
{"allocate_int", (PyCFunction)allocate_int, METH_NOARGS, ""},
{"allocate_tuple", (PyCFunction)allocate_tuple, METH_NOARGS, ""},
{NULL, NULL, 0, NULL}
Expand Down
29 changes: 29 additions & 0 deletions microbench/src/hpy_simple.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,33 @@ static HPy varargs_impl(HPyContext ctx, HPy self, HPy *args, HPy_ssize_t nargs)
return HPy_Dup(ctx, ctx->h_None);
}

HPyDef_METH(call_with_tuple, "call_with_tuple", call_with_tuple_impl, HPyFunc_VARARGS)
static HPy call_with_tuple_impl(HPyContext ctx, HPy self, HPy *args, HPy_ssize_t nargs)
{
HPy f, f_args;
if (nargs != 2) {
HPyErr_SetString(ctx, ctx->h_TypeError, "call_with_tuple requires two arguments");
return HPy_NULL;
}
f = args[0];
f_args = args[1];
return HPy_CallTupleDict(ctx, f, f_args, HPy_NULL);
}

HPyDef_METH(call_with_tuple_and_dict, "call_with_tuple_and_dict", call_with_tuple_and_dict_impl, HPyFunc_VARARGS)
static HPy call_with_tuple_and_dict_impl(HPyContext ctx, HPy self, HPy *args, HPy_ssize_t nargs)
{
HPy f, f_args, f_kw;
if (nargs != 3) {
HPyErr_SetString(ctx, ctx->h_TypeError, "call_with_tuple_and_dict requires three arguments");
return HPy_NULL;
}
f = args[0];
f_args = args[1];
f_kw = args[2];
return HPy_CallTupleDict(ctx, f, f_args, f_kw);
}

HPyDef_METH(allocate_int, "allocate_int", allocate_int_impl, HPyFunc_NOARGS)
static HPy allocate_int_impl(HPyContext ctx, HPy self)
{
Expand Down Expand Up @@ -81,6 +108,8 @@ static HPyDef *module_defines[] = {
&noargs,
&onearg,
&varargs,
&call_with_tuple,
&call_with_tuple_and_dict,
&allocate_int,
&allocate_tuple,
NULL
Expand Down
16 changes: 16 additions & 0 deletions microbench/test_microbench.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,22 @@ def test_varargs(self, simple, timer, N):
for i in range(N):
simple.varargs(None, None)

def test_call_with_tuple(self, simple, timer, N):
def f(a, b):
return a + b

with timer:
for i in range(N):
simple.call_with_tuple(f, (1, 2))

def test_call_with_tuple_and_dict(self, simple, timer, N):
def f(a, b):
return a + b

with timer:
for i in range(N):
simple.call_with_tuple_and_dict(f, (1,), {"b": 2})

def test_allocate_int(self, simple, timer, N):
with timer:
for i in range(N):
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ def get_scm_config():
'hpy/universal/src/ctx.c',
'hpy/universal/src/ctx_meth.c',
'hpy/universal/src/ctx_misc.c',
'hpy/devel/src/runtime/argparse.c',
'hpy/devel/src/runtime/ctx_call.c',
'hpy/devel/src/runtime/ctx_module.c',
'hpy/devel/src/runtime/ctx_type.c',
'hpy/devel/src/runtime/argparse.c',
'hpy/devel/src/runtime/ctx_tracker.c',
'hpy/devel/src/runtime/ctx_listbuilder.c',
'hpy/devel/src/runtime/ctx_tuple.c',
Expand Down
Loading