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
Changes from 1 commit
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
93 changes: 93 additions & 0 deletions test/test_call.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from .support import HPyTest


class TestCall(HPyTest):
def test_hpy_call(self):
import pytest
mod = self.make_module("""
HPyDef_METH(call, "call", call_impl, HPyFunc_VARARGS)
static HPy call_impl(HPyContext ctx, HPy self,
HPy *args, HPy_ssize_t nargs)
{

HPy f, f_args;
HPy f_kw = HPy_NULL;
if (!HPyArg_Parse(ctx, NULL, args, nargs, "OO|O", &f, &f_args,
&f_kw))
return HPy_NULL;
return HPy_Call(ctx, f, f_args, f_kw);
}
@EXPORT(call)
@INIT
""")

def f(a, b):
return a + b

assert mod.call(f, (1, 2)) == 3
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe we should also test what happens if you pass something which is not a tuple and/or a dict?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea. I'll add some.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The answer is that C Python segfaults in some cases and works fine in others (e.g. a list of args). I've added tests and changed the HPy implementation to check the types of handles rather than segfault.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder if doing these extra check costs any performance. Do you feel like adding microbenchmarks?
If they are costly, we could do the check only in debug mode.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm guessing not a whole lot, but I'll add the microbenchmarks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Microbenchmarks added.

assert mod.call(f, (), {"a": 2, "b": 3}) == 5
assert mod.call(str, (2,)) == "2"
assert mod.call(str, (2,), {}) == "2"
with pytest.raises(TypeError):
mod.call(f, (1,))
with pytest.raises(TypeError):
mod.call(f, (), {"b": 2})
with pytest.raises(TypeError):
mod.call(f, (), {})
with pytest.raises(TypeError):
mod.call("not callable", (2,))
with pytest.raises(TypeError):
mod.call("not callable", (2,), {})

def test_hpy_callobject(self):
import pytest
mod = self.make_module("""
HPyDef_METH(call, "call", call_impl, HPyFunc_VARARGS)
static HPy call_impl(HPyContext ctx, HPy self,
HPy *args, HPy_ssize_t nargs)
{

HPy f;
HPy f_args = HPy_NULL;
if (!HPyArg_Parse(ctx, NULL, args, nargs, "O|O", &f, &f_args))
return HPy_NULL;
return HPy_CallObject(ctx, f, f_args);
}
@EXPORT(call)
@INIT
""")

def f(a, b):
return a + b

def g():
return "this is g"

assert mod.call(f, (1, 2)) == 3
assert mod.call(g) == "this is g"
assert mod.call(str, (2,)) == "2"
with pytest.raises(TypeError):
mod.call(f, (1,))
with pytest.raises(TypeError):
mod.call("not callable", (2,))

def test_hpycallable_check(self):
mod = self.make_module("""
HPyDef_METH(f, "f", f_impl, HPyFunc_O)
static HPy f_impl(HPyContext ctx, HPy self, HPy arg)
{
if (HPyCallable_Check(ctx, arg))
return HPy_Dup(ctx, ctx->h_True);
return HPy_Dup(ctx, ctx->h_False);
}
@EXPORT(f)
@INIT
""")

def f():
return "this is f"

assert mod.f(f) is True
assert mod.f(str) is True
assert mod.f("a") is False
assert mod.f(3) is False