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

Extend HPy call API #251

Merged
merged 16 commits into from
Apr 13, 2023
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
2 changes: 2 additions & 0 deletions docs/api-reference/function-index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ HPy Core API Function Index
* :c:func:`HPy_And`
* :c:func:`HPy_AsPyObject`
* :c:func:`HPy_Bytes`
* :c:func:`HPy_Call`
* :c:func:`HPy_CallMethod`
* :c:func:`HPy_CallTupleDict`
* :c:func:`HPy_Close`
* :c:func:`HPy_Compile_s`
Expand Down
5 changes: 5 additions & 0 deletions docs/api-reference/hpy-call.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
HPy Call API
============

.. autocmodule:: autogen/public_api.h
:members: HPy_Call,HPy_CallMethod,HPy_CallTupleDict
1 change: 1 addition & 0 deletions docs/api-reference/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ between the modes.
hpy-ctx
hpy-object
hpy-type
hpy-call
hpy-field
hpy-global
hpy-gil
Expand Down
3 changes: 3 additions & 0 deletions docs/porting-guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ with the code for the :term:`CPython ABI` mode, so it is guaranteed to be correc
`PyNumber_Xor <https://docs.python.org/3/c-api/number.html#c.PyNumber_Xor>`_ :c:func:`HPy_Xor`
`PyObject_ASCII <https://docs.python.org/3/c-api/object.html#c.PyObject_ASCII>`_ :c:func:`HPy_ASCII`
`PyObject_Bytes <https://docs.python.org/3/c-api/object.html#c.PyObject_Bytes>`_ :c:func:`HPy_Bytes`
`PyObject_Call <https://docs.python.org/3/c-api/call.html#c.PyObject_Call>`_ :c:func:`HPy_CallTupleDict`
`PyObject_DelItem <https://docs.python.org/3/c-api/object.html#c.PyObject_DelItem>`_ :c:func:`HPy_DelItem`
`PyObject_GetAttr <https://docs.python.org/3/c-api/object.html#c.PyObject_GetAttr>`_ :c:func:`HPy_GetAttr`
`PyObject_GetAttrString <https://docs.python.org/3/c-api/object.html#c.PyObject_GetAttrString>`_ :c:func:`HPy_GetAttr_s`
Expand All @@ -258,6 +259,8 @@ with the code for the :term:`CPython ABI` mode, so it is guaranteed to be correc
`PyObject_Str <https://docs.python.org/3/c-api/object.html#c.PyObject_Str>`_ :c:func:`HPy_Str`
`PyObject_Type <https://docs.python.org/3/c-api/object.html#c.PyObject_Type>`_ :c:func:`HPy_Type`
`PyObject_TypeCheck <https://docs.python.org/3/c-api/object.html#c.PyObject_TypeCheck>`_ :c:func:`HPy_TypeCheck`
`PyObject_Vectorcall <https://docs.python.org/3/c-api/call.html#c.PyObject_Vectorcall>`_ :c:func:`HPy_Call`
`PyObject_VectorcallMethod <https://docs.python.org/3/c-api/call.html#c.PyObject_VectorcallMethod>`_ :c:func:`HPy_CallMethod`
`PySequence_Contains <https://docs.python.org/3/c-api/sequence.html#c.PySequence_Contains>`_ :c:func:`HPy_Contains`
`PySlice_AdjustIndices <https://docs.python.org/3/c-api/slice.html#c.PySlice_AdjustIndices>`_ :c:func:`HPySlice_AdjustIndices`
`PySlice_Unpack <https://docs.python.org/3/c-api/slice.html#c.PySlice_Unpack>`_ :c:func:`HPySlice_Unpack`
Expand Down
4 changes: 4 additions & 0 deletions hpy/debug/src/autogen_debug_ctx_init.h

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

72 changes: 72 additions & 0 deletions hpy/debug/src/debug_ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -610,3 +610,75 @@ DHPy debug_ctx_Unicode_Substring(HPyContext *dctx, DHPy str, HPy_ssize_t start,
ctx_info->is_valid = true;
return DHPy_open(dctx, universal_result);
}

DHPy debug_ctx_Call(HPyContext *dctx, DHPy dh_callable, const DHPy *dh_args, size_t nargs, DHPy dh_kwnames)
{
HPyDebugCtxInfo *ctx_info;
HPyContext *uctx;

ctx_info = get_ctx_info(dctx);
if (!ctx_info->is_valid) {
report_invalid_debug_context();
}

UHPy uh_callable = DHPy_unwrap(dctx, dh_callable);
UHPy uh_kwnames = DHPy_unwrap(dctx, dh_kwnames);
uctx = ctx_info->info->uctx;
HPy_ssize_t nkw;
if (!HPy_IsNull(uh_kwnames)) {
if (!HPyTuple_Check(uctx, uh_kwnames)) {
HPy_FatalError(uctx, "HPy_Call arg 'kwnames' must be a tuple object or HPy_NULL");
}
nkw = HPy_Length(uctx, uh_kwnames);
if (nkw < 0) {
return HPy_NULL;
}
} else {
nkw = 0;
}
const size_t n_all_args = nargs + nkw;
UHPy *uh_args = (UHPy *)alloca(n_all_args * sizeof(UHPy));
for(size_t i=0; i < n_all_args; i++) {
uh_args[i] = DHPy_unwrap(dctx, dh_args[i]);
}
ctx_info->is_valid = false;
DHPy dh_result = DHPy_open(dctx, HPy_Call(uctx, uh_callable, uh_args, nargs, uh_kwnames));
ctx_info->is_valid = true;
return dh_result;
}

DHPy debug_ctx_CallMethod(HPyContext *dctx, DHPy dh_name, const DHPy *dh_args, size_t nargs, DHPy dh_kwnames)
{
HPyDebugCtxInfo *ctx_info;
HPyContext *uctx;

ctx_info = get_ctx_info(dctx);
if (!ctx_info->is_valid) {
report_invalid_debug_context();
}

UHPy uh_name = DHPy_unwrap(dctx, dh_name);
UHPy uh_kwnames = DHPy_unwrap(dctx, dh_kwnames);
uctx = ctx_info->info->uctx;
HPy_ssize_t nkw;
if (!HPy_IsNull(uh_kwnames)) {
if (!HPyTuple_Check(uctx, uh_kwnames)) {
HPy_FatalError(uctx, "HPy_CallMethod arg 'kwnames' must be a tuple object or HPy_NULL");
}
nkw = HPy_Length(uctx, uh_kwnames);
if (nkw < 0) {
return HPy_NULL;
}
} else {
nkw = 0;
}
const size_t n_all_args = nargs + nkw;
UHPy *uh_args = (UHPy *)alloca(n_all_args * sizeof(UHPy));
for(size_t i=0; i < n_all_args; i++) {
uh_args[i] = DHPy_unwrap(dctx, dh_args[i]);
}
ctx_info->is_valid = false;
DHPy dh_result = DHPy_open(dctx, HPy_CallMethod(uctx, uh_name, uh_args, nargs, uh_kwnames));
ctx_info->is_valid = true;
return dh_result;
}
24 changes: 24 additions & 0 deletions hpy/devel/include/hpy/cpython/misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,30 @@ HPyAPI_FUNC HPy HPy_CallTupleDict(HPyContext *ctx, HPy callable, HPy args, HPy k
return ctx_CallTupleDict(ctx, callable, args, kw);
}

#if PY_VERSION_HEX < 0x03090000
# define PyObject_Vectorcall _PyObject_Vectorcall
#endif

HPyAPI_FUNC HPy HPy_Call(HPyContext *ctx, HPy callable, const HPy *args, size_t nargs, HPy kwnames)
{
assert(sizeof(HPy) == sizeof(PyObject *));
return _py2h(PyObject_Vectorcall(_h2py(callable), (PyObject *const *)args, nargs, _h2py(kwnames)));
}
fangerer marked this conversation as resolved.
Show resolved Hide resolved

#if PY_VERSION_HEX < 0x03090000
# undef PyObject_Vectorcall
#endif

HPyAPI_FUNC HPy HPy_CallMethod(HPyContext *ctx, HPy name, const HPy *args, size_t nargs, HPy kwnames)
{
#if PY_VERSION_HEX >= 0x03090000
assert(sizeof(HPy) == sizeof(PyObject *));
return _py2h(PyObject_VectorcallMethod(_h2py(name), (PyObject *const *)args, nargs, _h2py(kwnames)));
#else
return ctx_CallMethod(ctx, name, args, nargs, kwnames);
#endif
}

antocuni marked this conversation as resolved.
Show resolved Hide resolved
HPyAPI_FUNC void _HPy_Dump(HPyContext *ctx, HPy h)
{
ctx_Dump(ctx, h);
Expand Down
75 changes: 75 additions & 0 deletions hpy/devel/include/hpy/inline_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -459,4 +459,79 @@ HPySlice_AdjustIndices(HPyContext *_HPy_UNUSED_ARG(ctx), HPy_ssize_t length,
return 0;
}

/**
* Call a method of a Python object.
*
* This is a convenience function for calling a method. It uses
* :c:func:`HPy_GetAttr_s` and :c:func:`HPy_CallTupleDict` to perform the method
* call.
*
* :param ctx:
* The execution context.
* :param utf8_name:
* The name (UTF-8 encoded C string) of the method. Must not be ``NULL``.
* :param receiver:
* A handle to the receiver of the call (i.e. the ``self``). Must not be
* ``HPy_NULL``.
* :param args:
* A handle to a tuple containing the positional arguments (must not be
* ``HPy_NULL`` but can, of course, be empty).
* :param kw:
* A handle to a Python dictionary containing the keyword arguments (may be
* ``HPy_NULL``).
*
* :returns:
* The result of the call on success, or ``HPy_NULL`` in case of an error.
*/
HPyAPI_INLINE_HELPER HPy
HPy_CallMethodTupleDict_s(HPyContext *ctx, const char *utf8_name, HPy receiver, HPy args, HPy kw)
{
HPy method = HPy_GetAttr_s(ctx, receiver, utf8_name);
if (HPy_IsNull(method)) {
return HPy_NULL;
}

HPy result = HPy_CallTupleDict(ctx, method, args, kw);
HPy_Close(ctx, method);
return result;
}

/**
* Call a method of a Python object.
*
* This is a convenience function for calling a method. It uses
* :c:func:`HPy_GetAttr` and :c:func:`HPy_CallTupleDict` to perform the method
* call.
*
* :param ctx:
* The execution context.
* :param name:
* A handle to the name (a Unicode object) of the method. Must not be
* ``HPy_NULL``.
* :param receiver:
* A handle to the receiver of the call (i.e. the ``self``). Must not be
* ``HPy_NULL``.
* :param args:
* A handle to a tuple containing the positional arguments (must not be
* ``HPy_NULL`` but can, of course, be empty).
* :param kw:
* A handle to a Python dictionary containing the keyword arguments (may be
* ``HPy_NULL``).
*
* :returns:
* The result of the call on success, or ``HPy_NULL`` in case of an error.
*/
HPyAPI_INLINE_HELPER HPy
HPy_CallMethodTupleDict(HPyContext *ctx, HPy name, HPy receiver, HPy args, HPy kw)
{
HPy method = HPy_GetAttr(ctx, receiver, name);
if (HPy_IsNull(method)) {
return HPy_NULL;
}

HPy result = HPy_CallTupleDict(ctx, method, args, kw);
HPy_Close(ctx, method);
return result;
}

#endif //HPY_INLINE_HELPERS_H
2 changes: 2 additions & 0 deletions hpy/devel/include/hpy/runtime/ctx_funcs.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ _HPy_HIDDEN HPy ctx_Bytes_FromStringAndSize(HPyContext *ctx, const char *v,

// ctx_call.c
_HPy_HIDDEN HPy ctx_CallTupleDict(HPyContext *ctx, HPy callable, HPy args, HPy kw);
_HPy_HIDDEN HPy ctx_Call(HPyContext *ctx, HPy callable, const HPy *args, size_t nargs, HPy kwnames);
_HPy_HIDDEN HPy ctx_CallMethod(HPyContext *ctx, HPy name, const HPy *args, size_t nargs, HPy kwnames);

// ctx_err.c
_HPy_HIDDEN int ctx_Err_Occurred(HPyContext *ctx);
Expand Down
2 changes: 2 additions & 0 deletions hpy/devel/include/hpy/universal/autogen_ctx.h

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

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

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

81 changes: 81 additions & 0 deletions hpy/devel/src/runtime/ctx_call.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#include <Python.h>
#include "hpy.h"
#if defined(_MSC_VER)
# include <malloc.h> /* for alloca() */
#endif

#ifndef HPY_ABI_CPYTHON
// for _h2py and _py2h
Expand Down Expand Up @@ -36,3 +39,81 @@ ctx_CallTupleDict(HPyContext *ctx, HPy callable, HPy args, HPy kw)
}
return _py2h(obj);
}

#if PY_VERSION_HEX < 0x03090000
# define PyObject_Vectorcall _PyObject_Vectorcall
#endif

_HPy_HIDDEN HPy
ctx_Call(HPyContext *ctx, HPy h_callable, const HPy *h_args, size_t nargs, HPy h_kwnames)
{
PyObject *kwnames;
size_t n_all_args;

if (HPy_IsNull(h_kwnames)) {
kwnames = NULL;
n_all_args = nargs;
} else {
kwnames = _h2py(h_kwnames);
assert(kwnames != NULL);
assert(PyTuple_Check(kwnames));
fangerer marked this conversation as resolved.
Show resolved Hide resolved
n_all_args = nargs + PyTuple_GET_SIZE(kwnames);
assert(n_all_args >= nargs);
}

/* Since we already allocate a fresh args array, we make it one element
larger and set PY_VECTORCALL_ARGUMENTS_OFFSET to avoid further
allocations from CPython. */
PyObject **args = (PyObject **) alloca((n_all_args + 1) * sizeof(PyObject *));
for (size_t i = 0; i < n_all_args; i++) {
args[i+1] = _h2py(h_args[i]);
}

return _py2h(PyObject_Vectorcall(_h2py(h_callable), args+1,
nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames));
}

#if PY_VERSION_HEX < 0x03090000
# undef PyObject_Vectorcall
#endif

_HPy_HIDDEN HPy
ctx_CallMethod(HPyContext *ctx, HPy h_name, const HPy *h_args, size_t nargs,
HPy h_kwnames)
{
PyObject *result, *kwnames;
size_t n_all_args;

if (HPy_IsNull(h_kwnames)) {
kwnames = NULL;
n_all_args = nargs;
} else {
kwnames = _h2py(h_kwnames);
assert(kwnames != NULL);
assert(PyTuple_Check(kwnames));
n_all_args = nargs + PyTuple_GET_SIZE(kwnames);
assert(n_all_args >= nargs);
}

/* Since we already allocate a fresh args array, we make it one element
larger and set PY_VECTORCALL_ARGUMENTS_OFFSET to avoid further
allocations from CPython. */
PyObject **args = (PyObject **) alloca(
(n_all_args + 1) * sizeof(PyObject *));
for (size_t i = 0; i < n_all_args; i++) {
args[i+1] = _h2py(h_args[i]);
}
antocuni marked this conversation as resolved.
Show resolved Hide resolved

#if PY_VERSION_HEX < 0x03090000
PyObject *method = PyObject_GetAttr(args[1], _h2py(h_name));
if (method == NULL)
return HPy_NULL;
result = _PyObject_Vectorcall(method, &args[2],
(nargs-1) | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames);
Py_DECREF(method);
#else
result = PyObject_VectorcallMethod(_h2py(h_name), args+1,
nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, kwnames);
#endif
return _py2h(result);
}
2 changes: 2 additions & 0 deletions hpy/tools/autogen/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
'HPy_Contains': 'PySequence_Contains',
'HPy_Length': 'PyObject_Length',
'HPy_CallTupleDict': None,
'HPy_Call': None, # 'PyObject_Vectorcall', no auto arg conversion
'HPy_CallMethod': None, # 'PyObject_VectorcallMethod',no auto arg conversion
'HPy_FromPyObject': None,
'HPy_AsPyObject': None,
'_HPy_AsStruct_Object': None,
Expand Down
2 changes: 2 additions & 0 deletions hpy/tools/autogen/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ class autogen_debug_wrappers(AutoGenFile):
'HPyType_GetName',
'HPyType_IsSubtype',
'HPyUnicode_Substring',
'HPy_Call',
'HPy_CallMethod',
}

def generate(self):
Expand Down
Loading