@@ -59,6 +59,30 @@ def add_checked_function(mod, name, retval, params, failure_expression="", *a, *
5959add_checked_string_function = add_checked_function
6060
6161
62+ # How wrapper converts an argument of each C type: as pybindgen's generated
63+ # code would, for the same exception on a wrongly-typed argument under every
64+ # backend (see the gopy_arg_* helpers in cxx_args.inc). %(o)s is the
65+ # argument's PyObject*, %(n)d its 1-based position. Identical in
66+ # pybind11_build.py and nanobind_build.py.
67+ ARG_CONV = {
68+ "int64_t" : "gopy_arg_L(%(o)s)" ,
69+ "long long" : "gopy_arg_L(%(o)s)" ,
70+ "uint64_t" : "gopy_arg_K(%(o)s, %(n)d)" ,
71+ "int" : "gopy_arg_i(%(o)s)" ,
72+ "int32_t" : "gopy_arg_i(%(o)s)" ,
73+ "uint32_t" : "gopy_arg_I(%(o)s)" ,
74+ "unsigned int" : "gopy_arg_I(%(o)s)" ,
75+ "int16_t" : "(int16_t)gopy_arg_i_max(%(o)s, 0x7fff)" ,
76+ "uint16_t" : "(uint16_t)gopy_arg_i_max(%(o)s, 0xffff)" ,
77+ "int8_t" : "(int8_t)gopy_arg_i_max(%(o)s, 0x7f)" ,
78+ "uint8_t" : "(uint8_t)gopy_arg_i_max(%(o)s, 0xff)" ,
79+ "double" : "gopy_arg_d(%(o)s)" ,
80+ "float" : "(float)gopy_arg_d(%(o)s)" ,
81+ "bool" : "gopy_arg_bool(%(o)s)" ,
82+ "char*" : "const_cast<char*>(gopy_arg_str(%(o)s, %(n)d))" ,
83+ }
84+
85+
6286def wrapper (name , ret , params , callback_kinds ):
6387 """Returns the m.def(...) call binding name, or "" if its signature
6488 isn't supported yet (a raw PyObject*): the .cpp simply never binds it, so
@@ -73,23 +97,25 @@ def wrapper(name, ret, params, callback_kinds):
7397 args = []
7498 setup = []
7599 call_args = []
76- for ctype , pname in params :
77- if ctype == "char*" :
78- args .append ("const std::string& " + pname )
79- call_args .append ("const_cast<char*>(%s.c_str())" % pname )
80- elif ctype == "bool" :
81- args .append ("bool " + pname )
82- call_args .append ("(char)(%s ? 1 : 0)" % pname )
100+ for i , (ctype , pname ) in enumerate (params ):
101+ if ctype in ARG_CONV :
102+ # converted up front, in order, while the GIL is still held
103+ args .append ("nb::handle " + pname )
104+ setup .append (
105+ "auto _a_%s = %s;" % (pname , ARG_CONV [ctype ] % {"o" : pname + ".ptr()" , "n" : i + 1 })
106+ )
107+ call_args .append ("_a_" + pname )
83108 elif ctype in ("complex64" , "complex128" ):
84109 cxxfloat = "float" if ctype == "complex64" else "double"
85110 args .append ("std::complex<%s> %s" % (cxxfloat , pname ))
86111 call_args .append ("%s.real(), %s.imag()" % (pname , pname ))
87112 elif ctype .startswith ("callback:" ):
88113 i = callback_kinds .setdefault (ctype , len (callback_kinds ))
89- args .append ("nb::callable " + pname )
114+ args .append ("nb::handle " + pname )
90115 setup .append (
91- "int64_t _h_%s = gopy_cb_register(%s);\n "
92- " GopyCBGuard _g_%s{_h_%s};" % (pname , pname , pname , pname )
116+ "gopy_arg_callable(%s.ptr(), %d);\n "
117+ " int64_t _h_%s = gopy_cb_register(nb::borrow<nb::callable>(%s));\n "
118+ " GopyCBGuard _g_%s{_h_%s};" % (pname , i + 1 , pname , pname , pname , pname )
93119 )
94120 call_args .append ("_h_%s" % pname )
95121 else :
@@ -134,11 +160,20 @@ def wrapper(name, ret, params, callback_kinds):
134160 cpptype = ret
135161 if setup :
136162 body = "\n " .join (setup ) + "\n " + body
137- return ' m.def("%s", [](%s) -> %s {\n %s\n });' % (
163+ # Unlike pybind11, nanobind refuses None for an nb::handle parameter
164+ # unless told otherwise, before the gopy_arg_* helper that should report
165+ # it (as pybindgen would) ever runs; telling it so takes an nb::arg for
166+ # every parameter, not just those.
167+ annotations = "" .join (
168+ ', nb::arg("%s")%s' % (a .rsplit (" " , 1 )[1 ], ".none()" if a .startswith ("nb::handle " ) else "" )
169+ for a in args
170+ )
171+ return ' m.def("%s", [](%s) -> %s {\n %s\n }%s);' % (
138172 name ,
139173 ", " .join (args ),
140174 cpptype ,
141175 body ,
176+ annotations ,
142177 )
143178
144179
@@ -205,9 +240,11 @@ def cxxparam(t):
205240
206241MODULE_TEMPLATE = '''// python bindings for package @NAME@ using nanobind.
207242// File is generated by gopy version @VERSION@. Do not edit.
243+ #include <climits>
208244#include <complex>
209245#include <cstdint>
210246#include <cstdlib>
247+ #include <cstring>
211248#include <mutex>
212249#include <string>
213250#include <unordered_map>
@@ -222,6 +259,13 @@ def cxxparam(t):
222259#include "@HEADER@"
223260}
224261
262+ // Hands the python error already set back to python, from C++.
263+ [[noreturn]] static void gopy_raise() {
264+ throw nb::python_error();
265+ }
266+
267+ @ARG_HELPERS@
268+
225269// Raises the exception, if any, that the last Go call on this thread
226270// recorded (see gopySetError/GopyTakeError, shared with the cffi backend).
227271static inline void _check() {
0 commit comments