From f5d0847027cadf11d56b91b8f018ce5041aec6df Mon Sep 17 00:00:00 2001 From: Antonio Cuni Date: Wed, 27 Jan 2021 16:17:01 +0100 Subject: [PATCH 1/4] add HPy_Type --- hpy/devel/include/common/autogen_impl.h | 5 +++++ hpy/devel/include/universal/autogen_ctx.h | 1 + hpy/devel/include/universal/autogen_trampolines.h | 4 ++++ hpy/tools/autogen/parse.py | 1 + hpy/tools/autogen/public_api.h | 2 ++ hpy/universal/src/autogen_ctx_def.h | 1 + test/test_object.py | 14 ++++++++++++++ 7 files changed, 28 insertions(+) diff --git a/hpy/devel/include/common/autogen_impl.h b/hpy/devel/include/common/autogen_impl.h index 4b9a1b940..9bd370ac3 100644 --- a/hpy/devel/include/common/autogen_impl.h +++ b/hpy/devel/include/common/autogen_impl.h @@ -340,6 +340,11 @@ HPyAPI_STORAGE int _HPy_IMPL_NAME_NOPREFIX(SetItem)(HPyContext ctx, HPy obj, HPy return PyObject_SetItem(_h2py(obj), _h2py(key), _h2py(value)); } +HPyAPI_STORAGE HPy _HPy_IMPL_NAME_NOPREFIX(Type)(HPyContext ctx, HPy obj) +{ + return _py2h(PyObject_Type(_h2py(obj))); +} + HPyAPI_STORAGE HPy _HPy_IMPL_NAME_NOPREFIX(Repr)(HPyContext ctx, HPy obj) { return _py2h(PyObject_Repr(_h2py(obj))); diff --git a/hpy/devel/include/universal/autogen_ctx.h b/hpy/devel/include/universal/autogen_ctx.h index 546823a07..6a8be8bac 100644 --- a/hpy/devel/include/universal/autogen_ctx.h +++ b/hpy/devel/include/universal/autogen_ctx.h @@ -163,6 +163,7 @@ struct _HPyContext_s { int (*ctx_SetItem)(HPyContext ctx, HPy obj, HPy key, HPy value); int (*ctx_SetItem_i)(HPyContext ctx, HPy obj, HPy_ssize_t idx, HPy value); int (*ctx_SetItem_s)(HPyContext ctx, HPy obj, const char *key, HPy value); + HPy (*ctx_Type)(HPyContext ctx, HPy obj); void *(*ctx_Cast)(HPyContext ctx, HPy h); HPy (*ctx_New)(HPyContext ctx, HPy h_type, void **data); HPy (*ctx_Repr)(HPyContext ctx, HPy obj); diff --git a/hpy/devel/include/universal/autogen_trampolines.h b/hpy/devel/include/universal/autogen_trampolines.h index 748e1c78e..de6f935b3 100644 --- a/hpy/devel/include/universal/autogen_trampolines.h +++ b/hpy/devel/include/universal/autogen_trampolines.h @@ -318,6 +318,10 @@ static inline int HPy_SetItem_s(HPyContext ctx, HPy obj, const char *key, HPy va return ctx->ctx_SetItem_s ( ctx, obj, key, value ); } +static inline HPy HPy_Type(HPyContext ctx, HPy obj) { + return ctx->ctx_Type ( ctx, obj ); +} + static inline void *_HPy_Cast(HPyContext ctx, HPy h) { return ctx->ctx_Cast ( ctx, h ); } diff --git a/hpy/tools/autogen/parse.py b/hpy/tools/autogen/parse.py index 75d7ca6e0..91ee84cd9 100644 --- a/hpy/tools/autogen/parse.py +++ b/hpy/tools/autogen/parse.py @@ -219,6 +219,7 @@ def _visit_hpyslot_slot(self, node): 'HPyTracker_ForgetAll': None, 'HPyTracker_Close': None, '_HPy_Dump': None, + 'HPy_Type': 'PyObject_Type', } diff --git a/hpy/tools/autogen/public_api.h b/hpy/tools/autogen/public_api.h index 82d01289f..b85c0b8fa 100644 --- a/hpy/tools/autogen/public_api.h +++ b/hpy/tools/autogen/public_api.h @@ -200,6 +200,8 @@ int HPy_SetItem(HPyContext ctx, HPy obj, HPy key, HPy value); int HPy_SetItem_i(HPyContext ctx, HPy obj, HPy_ssize_t idx, HPy value); int HPy_SetItem_s(HPyContext ctx, HPy obj, const char *key, HPy value); +HPy HPy_Type(HPyContext ctx, HPy obj); + void* _HPy_Cast(HPyContext ctx, HPy h); HPy _HPy_New(HPyContext ctx, HPy h_type, void **data); diff --git a/hpy/universal/src/autogen_ctx_def.h b/hpy/universal/src/autogen_ctx_def.h index 8cfc90b9b..a9254555b 100644 --- a/hpy/universal/src/autogen_ctx_def.h +++ b/hpy/universal/src/autogen_ctx_def.h @@ -163,6 +163,7 @@ struct _HPyContext_s global_ctx = { .ctx_SetItem = &ctx_SetItem, .ctx_SetItem_i = &ctx_SetItem_i, .ctx_SetItem_s = &ctx_SetItem_s, + .ctx_Type = &ctx_Type, .ctx_Cast = &ctx_Cast, .ctx_New = &ctx_New, .ctx_Repr = &ctx_Repr, diff --git a/test/test_object.py b/test/test_object.py index 72dad35e5..9814a5b8a 100644 --- a/test/test_object.py +++ b/test/test_object.py @@ -477,3 +477,17 @@ def test_dump(self): @INIT """) mod.f('hello') + + def test_type(self): + mod = self.make_module(""" + HPyDef_METH(f, "f", f_impl, HPyFunc_O) + static HPy f_impl(HPyContext ctx, HPy self, HPy arg) + { + return HPy_Type(ctx, arg); + } + @EXPORT(f) + @INIT + """) + assert mod.f('hello') is str + assert mod.f(42) is int + From 22a84c4654793121193579bd46d1ae90c368c7fc Mon Sep 17 00:00:00 2001 From: Antonio Cuni Date: Wed, 27 Jan 2021 16:47:33 +0100 Subject: [PATCH 2/4] introduce HPyBool_FromLong --- hpy/devel/include/common/autogen_impl.h | 5 +++++ hpy/devel/include/universal/autogen_ctx.h | 1 + .../include/universal/autogen_trampolines.h | 4 ++++ hpy/tools/autogen/public_api.h | 3 +++ hpy/universal/src/autogen_ctx_def.h | 1 + test/test_number.py | 16 ++++++++++++++++ 6 files changed, 30 insertions(+) diff --git a/hpy/devel/include/common/autogen_impl.h b/hpy/devel/include/common/autogen_impl.h index 9bd370ac3..cb77cc99d 100644 --- a/hpy/devel/include/common/autogen_impl.h +++ b/hpy/devel/include/common/autogen_impl.h @@ -90,6 +90,11 @@ HPyAPI_STORAGE double _HPy_IMPL_NAME(Float_AsDouble)(HPyContext ctx, HPy h) return PyFloat_AsDouble(_h2py(h)); } +HPyAPI_STORAGE HPy _HPy_IMPL_NAME(Bool_FromLong)(HPyContext ctx, long v) +{ + return _py2h(PyBool_FromLong(v)); +} + HPyAPI_STORAGE HPy_ssize_t _HPy_IMPL_NAME_NOPREFIX(Length)(HPyContext ctx, HPy h) { return PyObject_Length(_h2py(h)); diff --git a/hpy/devel/include/universal/autogen_ctx.h b/hpy/devel/include/universal/autogen_ctx.h index 6a8be8bac..d11eb2a52 100644 --- a/hpy/devel/include/universal/autogen_ctx.h +++ b/hpy/devel/include/universal/autogen_ctx.h @@ -104,6 +104,7 @@ struct _HPyContext_s { HPy_ssize_t (*ctx_Long_AsSsize_t)(HPyContext ctx, HPy h); HPy (*ctx_Float_FromDouble)(HPyContext ctx, double v); double (*ctx_Float_AsDouble)(HPyContext ctx, HPy h); + HPy (*ctx_Bool_FromLong)(HPyContext ctx, long v); HPy_ssize_t (*ctx_Length)(HPyContext ctx, HPy h); int (*ctx_Number_Check)(HPyContext ctx, HPy h); HPy (*ctx_Add)(HPyContext ctx, HPy h1, HPy h2); diff --git a/hpy/devel/include/universal/autogen_trampolines.h b/hpy/devel/include/universal/autogen_trampolines.h index de6f935b3..c6755885f 100644 --- a/hpy/devel/include/universal/autogen_trampolines.h +++ b/hpy/devel/include/universal/autogen_trampolines.h @@ -86,6 +86,10 @@ static inline double HPyFloat_AsDouble(HPyContext ctx, HPy h) { return ctx->ctx_Float_AsDouble ( ctx, h ); } +static inline HPy HPyBool_FromLong(HPyContext ctx, long v) { + return ctx->ctx_Bool_FromLong ( ctx, v ); +} + static inline HPy_ssize_t HPy_Length(HPyContext ctx, HPy h) { return ctx->ctx_Length ( ctx, h ); } diff --git a/hpy/tools/autogen/public_api.h b/hpy/tools/autogen/public_api.h index b85c0b8fa..6431cd217 100644 --- a/hpy/tools/autogen/public_api.h +++ b/hpy/tools/autogen/public_api.h @@ -125,6 +125,9 @@ HPy_ssize_t HPyLong_AsSsize_t(HPyContext ctx, HPy h); HPy HPyFloat_FromDouble(HPyContext ctx, double v); double HPyFloat_AsDouble(HPyContext ctx, HPy h); +HPy HPyBool_FromLong(HPyContext ctx, long v); + + /* abstract.h */ HPy_ssize_t HPy_Length(HPyContext ctx, HPy h); diff --git a/hpy/universal/src/autogen_ctx_def.h b/hpy/universal/src/autogen_ctx_def.h index a9254555b..355c8b02d 100644 --- a/hpy/universal/src/autogen_ctx_def.h +++ b/hpy/universal/src/autogen_ctx_def.h @@ -104,6 +104,7 @@ struct _HPyContext_s global_ctx = { .ctx_Long_AsSsize_t = &ctx_Long_AsSsize_t, .ctx_Float_FromDouble = &ctx_Float_FromDouble, .ctx_Float_AsDouble = &ctx_Float_AsDouble, + .ctx_Bool_FromLong = &ctx_Bool_FromLong, .ctx_Length = &ctx_Length, .ctx_Number_Check = &ctx_Number_Check, .ctx_Add = &ctx_Add, diff --git a/test/test_number.py b/test/test_number.py index 78d622e7a..b4b183330 100644 --- a/test/test_number.py +++ b/test/test_number.py @@ -3,6 +3,22 @@ class TestNumber(HPyTest): + def test_bool_from_long(self): + mod = self.make_module(""" + HPyDef_METH(f, "f", f_impl, HPyFunc_O) + static HPy f_impl(HPyContext ctx, HPy self, HPy arg) + { + long x = HPyLong_AsLong(ctx, arg); + if (HPyErr_Occurred(ctx)) + return HPy_NULL; + return HPyBool_FromLong(ctx, x); + } + @EXPORT(f) + @INIT + """) + assert mod.f(0) is False + assert mod.f(42) is True + def test_unary(self): import pytest import operator From 48831a30dfbb8b22225d615955d365e5832855fc Mon Sep 17 00:00:00 2001 From: Antonio Cuni Date: Thu, 28 Jan 2021 12:20:12 +0100 Subject: [PATCH 3/4] tentatively introduce HPy_TypeCheck. However, note that the precise semantics is still under discussion, see issue #160 --- hpy/devel/include/common/runtime/ctx_object.h | 1 + hpy/devel/include/cpython/hpy.h | 6 +++++ hpy/devel/include/universal/autogen_ctx.h | 1 + .../include/universal/autogen_trampolines.h | 4 ++++ hpy/devel/src/runtime/ctx_object.c | 23 +++++++++++++++++++ hpy/tools/autogen/parse.py | 1 + hpy/tools/autogen/public_api.h | 2 ++ hpy/universal/src/autogen_ctx_def.h | 1 + test/test_object.py | 19 +++++++++++++++ 9 files changed, 58 insertions(+) diff --git a/hpy/devel/include/common/runtime/ctx_object.h b/hpy/devel/include/common/runtime/ctx_object.h index e22dfbf0a..30a6919ef 100644 --- a/hpy/devel/include/common/runtime/ctx_object.h +++ b/hpy/devel/include/common/runtime/ctx_object.h @@ -5,5 +5,6 @@ #include "hpy.h" _HPy_HIDDEN void ctx_Dump(HPyContext ctx, HPy h); +_HPy_HIDDEN int ctx_TypeCheck(HPyContext ctx, HPy h_obj, HPy h_type); #endif /* HPY_COMMON_RUNTIME_CTX_OBJECT_H */ diff --git a/hpy/devel/include/cpython/hpy.h b/hpy/devel/include/cpython/hpy.h index c6308d6a1..4269f8aee 100644 --- a/hpy/devel/include/cpython/hpy.h +++ b/hpy/devel/include/cpython/hpy.h @@ -329,6 +329,12 @@ _HPy_Dump(HPyContext ctx, HPy h) return ctx_Dump(ctx, h); } +HPyAPI_FUNC(int) +HPy_TypeCheck(HPyContext ctx, HPy h_obj, HPy h_type) +{ + return ctx_TypeCheck(ctx, h_obj, h_type); +} + HPyAPI_FUNC(HPyListBuilder) HPyListBuilder_New(HPyContext ctx, HPy_ssize_t initial_size) { diff --git a/hpy/devel/include/universal/autogen_ctx.h b/hpy/devel/include/universal/autogen_ctx.h index d11eb2a52..922abc28b 100644 --- a/hpy/devel/include/universal/autogen_ctx.h +++ b/hpy/devel/include/universal/autogen_ctx.h @@ -165,6 +165,7 @@ struct _HPyContext_s { int (*ctx_SetItem_i)(HPyContext ctx, HPy obj, HPy_ssize_t idx, HPy value); int (*ctx_SetItem_s)(HPyContext ctx, HPy obj, const char *key, HPy value); HPy (*ctx_Type)(HPyContext ctx, HPy obj); + int (*ctx_TypeCheck)(HPyContext ctx, HPy obj, HPy type); void *(*ctx_Cast)(HPyContext ctx, HPy h); HPy (*ctx_New)(HPyContext ctx, HPy h_type, void **data); HPy (*ctx_Repr)(HPyContext ctx, HPy obj); diff --git a/hpy/devel/include/universal/autogen_trampolines.h b/hpy/devel/include/universal/autogen_trampolines.h index c6755885f..9a974d2ec 100644 --- a/hpy/devel/include/universal/autogen_trampolines.h +++ b/hpy/devel/include/universal/autogen_trampolines.h @@ -326,6 +326,10 @@ static inline HPy HPy_Type(HPyContext ctx, HPy obj) { return ctx->ctx_Type ( ctx, obj ); } +static inline int HPy_TypeCheck(HPyContext ctx, HPy obj, HPy type) { + return ctx->ctx_TypeCheck ( ctx, obj, type ); +} + static inline void *_HPy_Cast(HPyContext ctx, HPy h) { return ctx->ctx_Cast ( ctx, h ); } diff --git a/hpy/devel/src/runtime/ctx_object.c b/hpy/devel/src/runtime/ctx_object.c index 8184cb97e..c74e5470d 100644 --- a/hpy/devel/src/runtime/ctx_object.c +++ b/hpy/devel/src/runtime/ctx_object.c @@ -14,3 +14,26 @@ ctx_Dump(HPyContext ctx, HPy h) // about the handle itself in the future. _PyObject_Dump(_h2py(h)); } + +/* NOTE: contrarily to CPython, the HPy have to check that h_type is a + type. On CPython it's not necessarily because it passes a PyTypeObject*, + but here we can only receive an HPy. + + However, we can't/don't want to raise an exception if you pass a non-type, + because the CPython version (PyObject_TypeCheck) always succeed and it + would be too easy to forget to check the return value. We just raise a + fatal error instead. + + Hopefully the slowdown is not too much. If it proves to be too much, we + could say that the function is allowed to crash if you pass a non-type, and + do the check only in debug mode. +*/ +_HPy_HIDDEN int +ctx_TypeCheck(HPyContext ctx, HPy h_obj, HPy h_type) +{ + PyObject *type= _h2py(h_type); + if (!PyType_Check(type)) { + Py_FatalError("HPy_TypeCheck arg 2 must be a type"); + } + return PyObject_TypeCheck(_h2py(h_obj), (PyTypeObject*)type); +} diff --git a/hpy/tools/autogen/parse.py b/hpy/tools/autogen/parse.py index 91ee84cd9..ec8b4a56a 100644 --- a/hpy/tools/autogen/parse.py +++ b/hpy/tools/autogen/parse.py @@ -220,6 +220,7 @@ def _visit_hpyslot_slot(self, node): 'HPyTracker_Close': None, '_HPy_Dump': None, 'HPy_Type': 'PyObject_Type', + 'HPy_TypeCheck': None, } diff --git a/hpy/tools/autogen/public_api.h b/hpy/tools/autogen/public_api.h index 6431cd217..3acbb1f26 100644 --- a/hpy/tools/autogen/public_api.h +++ b/hpy/tools/autogen/public_api.h @@ -204,6 +204,8 @@ int HPy_SetItem_i(HPyContext ctx, HPy obj, HPy_ssize_t idx, HPy value); int HPy_SetItem_s(HPyContext ctx, HPy obj, const char *key, HPy value); HPy HPy_Type(HPyContext ctx, HPy obj); +// WARNING: HPy_TypeCheck could be tweaked/removed in the future, see issue #160 +int HPy_TypeCheck(HPyContext ctx, HPy obj, HPy type); void* _HPy_Cast(HPyContext ctx, HPy h); HPy _HPy_New(HPyContext ctx, HPy h_type, void **data); diff --git a/hpy/universal/src/autogen_ctx_def.h b/hpy/universal/src/autogen_ctx_def.h index 355c8b02d..9eee68a00 100644 --- a/hpy/universal/src/autogen_ctx_def.h +++ b/hpy/universal/src/autogen_ctx_def.h @@ -165,6 +165,7 @@ struct _HPyContext_s global_ctx = { .ctx_SetItem_i = &ctx_SetItem_i, .ctx_SetItem_s = &ctx_SetItem_s, .ctx_Type = &ctx_Type, + .ctx_TypeCheck = &ctx_TypeCheck, .ctx_Cast = &ctx_Cast, .ctx_New = &ctx_New, .ctx_Repr = &ctx_Repr, diff --git a/test/test_object.py b/test/test_object.py index 9814a5b8a..dc22d2abe 100644 --- a/test/test_object.py +++ b/test/test_object.py @@ -491,3 +491,22 @@ def test_type(self): assert mod.f('hello') is str assert mod.f(42) is int + def test_typecheck(self): + mod = self.make_module(""" + HPyDef_METH(f, "f", f_impl, HPyFunc_VARARGS) + static HPy f_impl(HPyContext ctx, HPy self, HPy *args, HPy_ssize_t nargs) + { + HPy a, b; + if (!HPyArg_Parse(ctx, NULL, args, nargs, "OO", &a, &b)) + return HPy_NULL; + int res = HPy_TypeCheck(ctx, a, b); + return HPyBool_FromLong(ctx, res); + } + @EXPORT(f) + @INIT + """) + class MyStr(str): + pass + assert mod.f('hello', str) + assert not mod.f('hello', int) + assert mod.f(MyStr('hello'), str) From 85dcd788576ce1cb97616447cfb8a682d37099b3 Mon Sep 17 00:00:00 2001 From: Antonio Cuni Date: Thu, 28 Jan 2021 14:37:31 +0100 Subject: [PATCH 4/4] add an assert to make infer happy --- hpy/devel/src/runtime/ctx_object.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hpy/devel/src/runtime/ctx_object.c b/hpy/devel/src/runtime/ctx_object.c index c74e5470d..9216cb4e9 100644 --- a/hpy/devel/src/runtime/ctx_object.c +++ b/hpy/devel/src/runtime/ctx_object.c @@ -32,6 +32,7 @@ _HPy_HIDDEN int ctx_TypeCheck(HPyContext ctx, HPy h_obj, HPy h_type) { PyObject *type= _h2py(h_type); + assert(type != NULL); if (!PyType_Check(type)) { Py_FatalError("HPy_TypeCheck arg 2 must be a type"); }