From d4b082eab025d4b4401e4b2054f802cba8202cda Mon Sep 17 00:00:00 2001 From: Li Jie Date: Sun, 10 Nov 2024 14:53:45 +0800 Subject: [PATCH] auto generate function signature --- README.md | 8 +- convert.go | 2 + convert_test.go | 55 +++++ demo/gradio/gradio.go | 8 +- extension.go | 156 +++++++++++++- extension_test.go | 459 +++++++++++++++++++++++++++++++++--------- function.go | 4 + object.go | 10 + wrap.c | 87 +++++++- wrap.h | 48 ++++- 10 files changed, 725 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index dfd694b..0a8f4de 100644 --- a/README.md +++ b/README.md @@ -258,7 +258,7 @@ demo.launch() var gr Module -func UpdateExamples(country string) Object { +func updateExamples(country string) Object { println("country:", country) if country == "USA" { return gr.Call("Dataset", KwArgs{ @@ -280,10 +280,6 @@ func main() { Initialize() defer Finalize() gr = ImportModule("gradio") - fn := CreateFunc("update_examples", UpdateExamples, - "(country, /)\n--\n\nUpdate examples based on country") - // Would be (in the future): - // fn := FuncOf(UpdateExamples) demo := With(gr.Call("Blocks"), func(v Object) { dropdown := gr.Call("Dropdown", KwArgs{ "label": "Country", @@ -293,7 +289,7 @@ func main() { textbox := gr.Call("Textbox") examples := gr.Call("Examples", [][]string{{"Chicago"}, {"Little Rock"}, {"San Francisco"}}, textbox) dataset := examples.Attr("dataset") - dropdown.Call("change", fn, dropdown, dataset) + dropdown.Call("change", updateExamples, dropdown, dataset) }) demo.Call("launch") } diff --git a/convert.go b/convert.go index 52e4b68..6d98f95 100644 --- a/convert.go +++ b/convert.go @@ -76,6 +76,8 @@ func From(from any) Object { return fromMap(vv).Object case reflect.Struct: return fromStruct(vv) + case reflect.Func: + return FuncOf(vv.Interface()).Object } panic(fmt.Errorf("unsupported type for Python: %T\n", v)) } diff --git a/convert_test.go b/convert_test.go index 434365c..f94b3ae 100644 --- a/convert_test.go +++ b/convert_test.go @@ -197,6 +197,61 @@ func TestFromSpecialCases(t *testing.T) { t.Errorf("Object was not independent, got %d after modifying original", got) } }() + + func() { + // Test From with functions + add := func(a, b int) int { return a + b } + obj := From(add) + + // Verify it's a function type + if !obj.IsFunc() { + t.Error("From(func) did not create Function object") + } + + fn := obj.AsFunc() + + // Test function call + result := fn.Call(5, 3) + + if !result.IsLong() { + t.Error("Function call result is not a Long") + } + if got := result.AsLong().Int64(); got != 8 { + t.Errorf("Function call = %d, want 8", got) + } + }() + + func() { + // Test From with function that returns multiple values + divMod := func(a, b int) (int, int) { + return a / b, a % b + } + obj := From(divMod) + if !obj.IsFunc() { + t.Error("From(func) did not create Function object") + } + + fn := obj.AsFunc() + + result := fn.Call(7, 3) + + // Result should be a tuple with two values + if !result.IsTuple() { + t.Error("Multiple return value function did not return a Tuple") + } + + tuple := result.AsTuple() + if tuple.Len() != 2 { + t.Errorf("Expected tuple of length 2, got %d", tuple.Len()) + } + + quotient := tuple.Get(0).AsLong().Int64() + remainder := tuple.Get(1).AsLong().Int64() + + if quotient != 2 || remainder != 1 { + t.Errorf("Got (%d, %d), want (2, 1)", quotient, remainder) + } + }() } func TestToValueWithCustomType(t *testing.T) { diff --git a/demo/gradio/gradio.go b/demo/gradio/gradio.go index 64ce671..14658cf 100644 --- a/demo/gradio/gradio.go +++ b/demo/gradio/gradio.go @@ -27,7 +27,7 @@ demo.launch() var gr Module -func UpdateExamples(country string) Object { +func updateExamples(country string) Object { println("country:", country) if country == "USA" { return gr.Call("Dataset", KwArgs{ @@ -49,10 +49,6 @@ func main() { Initialize() defer Finalize() gr = ImportModule("gradio") - fn := CreateFunc("update_examples", UpdateExamples, - "(country, /)\n--\n\nUpdate examples based on country") - // Would be (in the future): - // fn := FuncOf(UpdateExamples) demo := With(gr.Call("Blocks"), func(v Object) { dropdown := gr.Call("Dropdown", KwArgs{ "label": "Country", @@ -62,7 +58,7 @@ func main() { textbox := gr.Call("Textbox") examples := gr.Call("Examples", [][]string{{"Chicago"}, {"Little Rock"}, {"San Francisco"}}, textbox) dataset := examples.Attr("dataset") - dropdown.Call("change", fn, dropdown, dataset) + dropdown.Call("change", updateExamples, dropdown, dataset) }) demo.Call("launch") } diff --git a/extension.go b/extension.go index a0d68ff..788346c 100644 --- a/extension.go +++ b/extension.go @@ -25,6 +25,10 @@ import ( "unsafe" ) +func FuncOf(fn any) Func { + return CreateFunc("", fn, "") +} + func CreateFunc(name string, fn any, doc string) Func { m := MainModule() return m.AddMethod(name, fn, doc) @@ -583,7 +587,24 @@ func (m Module) AddMethod(name string, fn any, doc string) Func { } } name = goNameToPythonName(name) - doc = name + doc + + hasRecv := false + if t.NumIn() > 0 { + firstParam := t.In(0) + if firstParam.Kind() == reflect.Ptr || firstParam.Kind() == reflect.Interface { + hasRecv = true + } + } + + kwargsType := reflect.TypeOf(KwArgs{}) + hasKwArgs := false + if t.NumIn() > 0 && t.In(t.NumIn()-1) == kwargsType { + hasKwArgs = true + } + + sig := genSig(fn, hasRecv) + fullDoc := name + sig + "\n--\n\n" + doc + cDoc := C.CString(fullDoc) maps := getGlobalData() meta, ok := maps.typeMetas[m.obj] @@ -596,14 +617,16 @@ func (m Module) AddMethod(name string, fn any, doc string) Func { methodId := uint(len(meta.methods)) - methodPtr := C.wrapperMethods[methodId] cName := C.CString(name) - cDoc := C.CString(doc) def := (*C.PyMethodDef)(C.malloc(C.size_t(unsafe.Sizeof(C.PyMethodDef{})))) def.ml_name = cName - def.ml_meth = C.PyCFunction(methodPtr) + def.ml_meth = C.PyCFunction(C.wrapperMethods[methodId]) def.ml_flags = C.METH_VARARGS + if hasKwArgs { + def.ml_flags |= C.METH_KEYWORDS + def.ml_meth = C.PyCFunction(C.wrapperMethodsWithKwargs[methodId]) + } def.ml_doc = cDoc methodMeta := &slotMeta{ @@ -611,8 +634,8 @@ func (m Module) AddMethod(name string, fn any, doc string) Func { methodName: name, fn: fn, typ: t, - doc: doc, - hasRecv: false, + doc: fullDoc, + hasRecv: hasRecv, def: def, } meta.methods[methodId] = methodMeta @@ -665,3 +688,124 @@ func FetchError() error { return fmt.Errorf("python error: %s", C.GoString(cstr)) } + +func genSig(fn any, hasRecv bool) string { + t := reflect.TypeOf(fn) + if t.Kind() != reflect.Func { + panic("genSig: fn must be a function") + } + + var args []string + startIdx := 0 + if hasRecv { + startIdx = 1 // skip receiver + } + + kwargsType := reflect.TypeOf(KwArgs{}) + hasKwArgs := false + lastParamIdx := t.NumIn() - 1 + if lastParamIdx >= startIdx && t.In(lastParamIdx) == kwargsType { + hasKwArgs = true + lastParamIdx-- // don't include KwArgs in regular parameters + } + + for i := startIdx; i <= lastParamIdx; i++ { + paramName := fmt.Sprintf("arg%d", i-startIdx) + args = append(args, paramName) + } + + // add "/" separator only if there are parameters + if len(args) > 0 { + args = append(args, "/") + } + + // add "**kwargs" if there are keyword arguments + if hasKwArgs { + args = append(args, "**kwargs") + } + + return fmt.Sprintf("(%s)", strings.Join(args, ", ")) +} + +//export wrapperMethodWithKwargs +func wrapperMethodWithKwargs(self, args, kwargs *C.PyObject, methodId C.int) *C.PyObject { + key := self + if C.isModule(self) == 0 { + key = (*C.PyObject)(unsafe.Pointer(self.ob_type)) + } + + maps := getGlobalData() + typeMeta, ok := maps.typeMetas[key] + check(ok, fmt.Sprintf("type %v not registered", FromPy(key))) + + methodMeta := typeMeta.methods[uint(methodId)] + methodType := methodMeta.typ + hasReceiver := methodMeta.hasRecv + + expectedArgs := methodType.NumIn() + if hasReceiver { + expectedArgs-- // skip receiver + } + expectedArgs-- // skip KwArgs + + argc := C.PyTuple_Size(args) + if int(argc) != expectedArgs { + SetTypeError(fmt.Errorf("method %s expects %d arguments, got %d", methodMeta.name, expectedArgs, argc)) + return nil + } + + goArgs := make([]reflect.Value, methodType.NumIn()) + argIndex := 0 + + if hasReceiver { + wrapper := (*wrapperType)(unsafe.Pointer(self)) + receiverType := methodType.In(0) + var recv reflect.Value + + if receiverType.Kind() == reflect.Ptr { + recv = reflect.ValueOf(wrapper.goObj) + } else { + recv = reflect.ValueOf(wrapper.goObj).Elem() + } + + goArgs[0] = recv + argIndex = 1 + } + + for i := 0; i < int(argc); i++ { + arg := C.PySequence_GetItem(args, C.Py_ssize_t(i)) + argType := methodType.In(i + argIndex) + argPy := FromPy(arg) + goValue := reflect.New(argType).Elem() + if !ToValue(argPy, goValue) { + SetTypeError(fmt.Errorf("failed to convert argument %v to %v", argPy, argType)) + return nil + } + goArgs[i+argIndex] = goValue + } + + kwargsValue := make(KwArgs) + if kwargs != nil { + dict := newDict(kwargs) + dict.Items()(func(key, value Object) bool { + kwargsValue[key.String()] = value + return true + }) + } + goArgs[len(goArgs)-1] = reflect.ValueOf(kwargsValue) + + results := reflect.ValueOf(methodMeta.fn).Call(goArgs) + + if len(results) == 0 { + return None().cpyObj() + } + if len(results) == 1 { + return From(results[0].Interface()).cpyObj() + } + + tuple := MakeTupleWithLen(len(results)) + for i := range results { + tuple.Set(i, From(results[i].Interface())) + } + return tuple.cpyObj() +} diff --git a/extension_test.go b/extension_test.go index ea4ad8b..edb4bf5 100644 --- a/extension_test.go +++ b/extension_test.go @@ -1,6 +1,8 @@ package gp import ( + "fmt" + "strings" "testing" ) @@ -160,54 +162,111 @@ except TypeError: func TestCreateFunc(t *testing.T) { setupTest(t) - // Test simple function - simpleFunc := func(x int) int { - return x * 2 - } - f1 := CreateFunc("simple_func", simpleFunc, "Doubles the input value") - if f1.Nil() { - t.Fatal("Failed to create simple function") - } - - // Test function with multiple arguments and return values - multiFunc := func(x int, y string) (int, string) { - return x * 2, y + y - } - f2 := CreateFunc("multi_func", multiFunc, "Returns doubled number and duplicated string") - if f2.Nil() { - t.Fatal("Failed to create function with multiple returns") - } - - // Test the functions using Python code - code := ` -# Test simple function + tests := []struct { + name string + fn any + doc string + testCode string + }{ + { + name: "simple", + fn: func(x int) int { + return x * 2 + }, + doc: "Doubles the input value", + testCode: ` result = simple_func(21) assert result == 42, f"Expected 42, got {result}" - -# Test multiple return values -num, text = multi_func(5, "hello") +assert str(inspect.signature(simple_func)) == "(arg0, /)" +`, + }, + { + name: "multi_args", + fn: func(x int, y string) (int, string) { + return x * 2, y + y + }, + doc: "Returns doubled number and duplicated string", + testCode: ` +num, text = multi_args_func(5, "hello") assert num == 10, f"Expected 10, got {num}" assert text == "hellohello", f"Expected 'hellohello', got {text}" +assert str(inspect.signature(multi_args_func)) == "(arg0, arg1, /)" +`, + }, + { + name: "with_kwargs", + fn: func(name string, kwargs KwArgs) Object { + return None() + }, + doc: "Function with kwargs", + testCode: ` +result = with_kwargs_func("test", extra="value") +assert result is None +assert str(inspect.signature(with_kwargs_func)) == "(arg0, /, **kwargs)" +`, + }, + { + name: "no_args", + fn: func() string { + return "hello" + }, + doc: "Function with no arguments", + testCode: ` +result = no_args_func() +assert result == "hello" +assert str(inspect.signature(no_args_func)) == "()" +`, + }, + { + name: "only_kwargs", + fn: func(kwargs KwArgs) Object { + return None() + }, + doc: "Function with only kwargs", + testCode: ` +result = only_kwargs_func(x=1, y=2) +assert result is None +assert str(inspect.signature(only_kwargs_func)) == "(**kwargs)" +`, + }, + } -# Test error handling - wrong argument type -try: - simple_func("not a number") - assert False, "Should fail with wrong argument type" -except TypeError: - pass + code := ` +import inspect -# Test error handling - wrong number of arguments -try: - simple_func(1, 2) - assert False, "Should fail with wrong number of arguments" -except TypeError: - pass ` - + for _, tt := range tests { + funcName := tt.name + "_func" + f := CreateFunc(funcName, tt.fn, tt.doc) + if f.Nil() { + t.Fatalf("Failed to create function for test case: %s", tt.name) + } + code += tt.testCode + "\n" + } + t.Log(code) err := RunString(code) if err != nil { t.Fatalf("Test failed: %v", err) } + + // Test failure cases + func() { + defer func() { + if r := recover(); r == nil { + t.Error("CreateFunc should panic with non-function argument") + } + }() + CreateFunc("invalid", 42, "should panic") + }() + + func() { + defer func() { + if r := recover(); r == nil { + t.Error("CreateFunc should panic with nil function") + } + }() + CreateFunc("nil_func", nil, "should panic") + }() } func TestCreateFuncInvalid(t *testing.T) { @@ -225,75 +284,122 @@ func explicitFunc(x int) int { return x + 1 } -func namedFunc(x string) string { - return "Hello " + x -} - func TestModuleAddMethod(t *testing.T) { setupTest(t) m := MainModule() - // Test with explicit name - f1 := m.AddMethod("", explicitFunc, " - adds one to input") - if f1.Nil() { - t.Fatal("Failed to create function with explicit name") - } - - // Test with empty name (should use function name) - f2 := m.AddMethod("", namedFunc, " - adds greeting") - if f2.Nil() { - t.Fatal("Failed to create function with derived name") - } - - // Test with anonymous function (should generate name) - f3 := m.AddMethod("", func(x, y int) int { - return x * y - }, " - multiplies two numbers") - if f3.Nil() { - t.Fatal("Failed to create anonymous function") - } - - code := ` -# Test explicit named function + tests := []struct { + name string + fn any + doc string + testCode string + }{ + { + name: "explicit", + fn: explicitFunc, + doc: "adds one to input", + testCode: ` result = explicit_func(41) assert result == 42, f"Expected 42, got {result}" +assert str(inspect.signature(explicit_func)) == "(arg0, /)" +`, + }, + { + name: "with_kwargs", + fn: func(x int, kwargs KwArgs) int { + return x + }, + doc: "function with kwargs", + testCode: ` +result = with_kwargs_func(42, extra="value") +assert result == 42 +assert str(inspect.signature(with_kwargs_func)) == "(arg0, /, **kwargs)" +`, + }, + { + name: "multi_args", + fn: func(x, y int) int { + return x * y + }, + doc: "multiplies two numbers", + testCode: ` +result = multi_args_func(6, 7) +assert result == 42 +assert str(inspect.signature(multi_args_func)) == "(arg0, arg1, /)" +`, + }, + { + name: "no_args", + fn: func() string { + return "hello" + }, + doc: "returns hello", + testCode: ` +result = no_args_func() +assert result == "hello" +assert str(inspect.signature(no_args_func)) == "()" +`, + }, + { + name: "only_kwargs", + fn: func(kwargs KwArgs) Object { + return None() + }, + doc: "function with only kwargs", + testCode: ` +result = only_kwargs_func(x=1, y=2) +assert result is None +assert str(inspect.signature(only_kwargs_func)) == "(**kwargs)" +`, + }, + } -# Test function with derived name -result = named_func("World") -assert result == "Hello World", f"Expected 'Hello World', got {result}" - -# Test documentation -import sys -if sys.version_info >= (3, 2): - assert explicit_func.__doc__.strip() == "explicit_func - adds one to input" - assert named_func.__doc__.strip() == "named_func - adds greeting" - -# Test error cases -try: - explicit_func("wrong type") - assert False, "Should fail with wrong argument type" -except TypeError: - pass + code := ` +import inspect -try: - explicit_func(1, 2) - assert False, "Should fail with wrong number of arguments" -except TypeError: - pass ` + for _, tt := range tests { + funcName := tt.name + "_func" + f := m.AddMethod(funcName, tt.fn, tt.doc) + if f.Nil() { + t.Fatalf("Failed to create function for test case: %s", tt.name) + } + code += tt.testCode + "\n" + } + t.Log(code) err := RunString(code) if err != nil { t.Fatalf("Test failed: %v", err) } - // Test invalid function type - defer func() { - if r := recover(); r == nil { - t.Error("AddMethod should panic with non-function argument") - } + func() { + defer func() { + if r := recover(); r == nil { + t.Error("AddMethod should panic with non-function argument") + } + }() + m.AddMethod("invalid", 42, "should panic") + }() + + func() { + defer func() { + if r := recover(); r == nil { + t.Error("AddMethod should panic with nil function") + } + }() + m.AddMethod("nil_func", nil, "should panic") + }() + + func() { + defer func() { + if r := recover(); r == nil { + t.Error("AddMethod should panic with empty module") + } + }() + var emptyModule Module + emptyModule.AddMethod("empty", func() {}, "should panic") }() - m.AddMethod("invalid", "not a function", "") } func TestAddTypeWithPtrReceiverInit(t *testing.T) { @@ -837,3 +943,176 @@ assert obj.field.string_val == "test" t.Fatal(err) } } + +func TestGenSig(t *testing.T) { + type CustomStruct struct { + Value int + } + + tests := []struct { + name string + fn any + hasRecv bool + expectedSig string + }{ + { + name: "function with return", + fn: func(x int) string { return "" }, + hasRecv: false, + expectedSig: "(arg0, /)", + }, + { + name: "multiple arguments", + fn: func(x string, y int) (int, string) { return 0, "" }, + hasRecv: false, + expectedSig: "(arg0, arg1, /)", + }, + { + name: "with kwargs", + fn: func(name string, kwargs KwArgs) Object { return None() }, + hasRecv: false, + expectedSig: "(arg0, /, **kwargs)", + }, + { + name: "method with receiver", + fn: func(r *CustomStruct, x int) float64 { return 0 }, + hasRecv: true, + expectedSig: "(arg0, /)", + }, + { + name: "no arguments", + fn: func() {}, + hasRecv: false, + expectedSig: "()", + }, + { + name: "only kwargs", + fn: func(kwargs KwArgs) {}, + hasRecv: false, + expectedSig: "(**kwargs)", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := genSig(tt.fn, tt.hasRecv) + if got != tt.expectedSig { + t.Errorf("genSig() = %q, want %q", got, tt.expectedSig) + } + }) + } +} + +func TestCreateFuncNaming(t *testing.T) { + setupTest(t) + + tests := []struct { + name string + givenName string + fn any + doc string + expectedName string + }{ + { + name: "explicit name", + givenName: "my_func", + fn: func(x int) int { return x }, + doc: "test function", + expectedName: "my_func", + }, + { + name: "empty name uses function name", + givenName: "", + fn: explicitFunc, + doc: "test function", + expectedName: "explicit_func", + }, + { + name: "camelCase to snake_case", + givenName: "camelCaseName", + fn: func() {}, + doc: "test function", + expectedName: "camel_case_name", + }, + { + name: "package path stripped", + givenName: "github.com/user/pkg.MyFunc", + fn: func() {}, + doc: "test function", + expectedName: "my_func", + }, + } + + for _, tt := range tests { + func() { + f := CreateFunc(tt.givenName, tt.fn, tt.doc) + if f.Nil() { + t.Fatal("Failed to create function") + } + + code := fmt.Sprintf(` +assert "%s" in globals(), "Function %s not found in globals" +assert callable(globals()["%s"]), "Function %s is not callable" +`, tt.expectedName, tt.expectedName, tt.expectedName, tt.expectedName) + + err := RunString(code) + if err != nil { + t.Fatalf("Test failed: %v", err) + } + }() + } +} + +func TestAddMethodNaming(t *testing.T) { + setupTest(t) + m := MainModule() + + tests := []struct { + name string + givenName string + fn any + doc string + expectedName string + }{ + { + name: "explicit name", + givenName: "my_method", + fn: func(x int) int { return x }, + doc: "test method", + expectedName: "my_method", + }, + { + name: "empty name uses function name", + givenName: "", + fn: explicitFunc, + doc: "test method", + expectedName: "explicit_func", + }, + { + name: "anonymous function gets generated name", + givenName: "", + fn: func() {}, + doc: "test method", + expectedName: "func", + }, + { + name: "camelCase to snake_case", + givenName: "myMethodName", + fn: func() {}, + doc: "test method", + expectedName: "my_method_name", + }, + } + + for _, tt := range tests { + func() { + f := m.AddMethod(tt.givenName, tt.fn, tt.doc) + if f.Nil() { + t.Fatal("Failed to create method") + } + if !strings.HasPrefix(f.Name(), tt.expectedName) { + t.Errorf("Expected method name to start with %s, got %s", tt.expectedName, f.Name()) + } + }() + } +} diff --git a/function.go b/function.go index 6cb6cb8..69f9a14 100644 --- a/function.go +++ b/function.go @@ -23,6 +23,10 @@ func (f Func) Ensure() { f.pyObject.Ensure() } +func (f Func) Name() string { + return f.AttrString("__name__").String() +} + func (f Func) call(args Tuple, kwargs Dict) Object { return newObject(C.PyObject_Call(f.obj, args.obj, kwargs.obj)) } diff --git a/object.go b/object.go index e07f75e..fa031e0 100644 --- a/object.go +++ b/object.go @@ -29,6 +29,10 @@ func (o *pyObject) Nil() bool { return o == nil } +func (o *pyObject) RefCount() int { + return int(C.Py_REFCNT(o.obj)) +} + func (o *pyObject) Ensure() { if o == nil { C.PyErr_Print() @@ -163,6 +167,12 @@ func (o Object) IsDict() bool { return C.Py_IS_TYPE(o.obj, &C.PyDict_Type) != 0 } +func (o Object) IsFunc() bool { + return C.Py_IS_TYPE(o.obj, &C.PyFunction_Type) != 0 || + C.Py_IS_TYPE(o.obj, &C.PyMethod_Type) != 0 || + C.Py_IS_TYPE(o.obj, &C.PyCFunction_Type) != 0 +} + func (o Object) AsFloat() Float { return cast[Float](o) } diff --git a/wrap.c b/wrap.c index f852c06..96241c0 100644 --- a/wrap.c +++ b/wrap.c @@ -3,7 +3,12 @@ #define WRAP_METHOD(ida, idb) \ PyObject *wrapperMethod##ida##idb(PyObject *self, PyObject *args) { \ - return wrapperMethod(self, args, 0x##ida * 16 + 0x##idb); \ + return wrapperMethod(self, args, 0x##ida * 16 + 0x##idb); \ +} + +#define WRAP_METHOD_WITH_KWARGS(ida, idb) \ +PyObject *wrapperMethodWithKwargs##ida##idb(PyObject *self, PyObject *args, PyObject *kwargs) { \ + return wrapperMethodWithKwargs(self, args, kwargs, 0x##ida * 16 + 0x##idb); \ } #define WRAP_METHODS(ida) \ @@ -24,6 +29,24 @@ PyObject *wrapperMethod##ida##idb(PyObject *self, PyObject *args) { \ WRAP_METHOD(ida, e) \ WRAP_METHOD(ida, f) +#define WRAP_METHODS_WITH_KWARGS(ida) \ + WRAP_METHOD_WITH_KWARGS(ida, 0) \ + WRAP_METHOD_WITH_KWARGS(ida, 1) \ + WRAP_METHOD_WITH_KWARGS(ida, 2) \ + WRAP_METHOD_WITH_KWARGS(ida, 3) \ + WRAP_METHOD_WITH_KWARGS(ida, 4) \ + WRAP_METHOD_WITH_KWARGS(ida, 5) \ + WRAP_METHOD_WITH_KWARGS(ida, 6) \ + WRAP_METHOD_WITH_KWARGS(ida, 7) \ + WRAP_METHOD_WITH_KWARGS(ida, 8) \ + WRAP_METHOD_WITH_KWARGS(ida, 9) \ + WRAP_METHOD_WITH_KWARGS(ida, a) \ + WRAP_METHOD_WITH_KWARGS(ida, b) \ + WRAP_METHOD_WITH_KWARGS(ida, c) \ + WRAP_METHOD_WITH_KWARGS(ida, d) \ + WRAP_METHOD_WITH_KWARGS(ida, e) \ + WRAP_METHOD_WITH_KWARGS(ida, f) + #define WRAP_METHOD_ALL() \ WRAP_METHODS(0) \ WRAP_METHODS(1) \ @@ -42,9 +65,29 @@ PyObject *wrapperMethod##ida##idb(PyObject *self, PyObject *args) { \ WRAP_METHODS(e) \ WRAP_METHODS(f) +#define WRAP_METHOD_ALL_WITH_KWARGS() \ + WRAP_METHODS_WITH_KWARGS(0) \ + WRAP_METHODS_WITH_KWARGS(1) \ + WRAP_METHODS_WITH_KWARGS(2) \ + WRAP_METHODS_WITH_KWARGS(3) \ + WRAP_METHODS_WITH_KWARGS(4) \ + WRAP_METHODS_WITH_KWARGS(5) \ + WRAP_METHODS_WITH_KWARGS(6) \ + WRAP_METHODS_WITH_KWARGS(7) \ + WRAP_METHODS_WITH_KWARGS(8) \ + WRAP_METHODS_WITH_KWARGS(9) \ + WRAP_METHODS_WITH_KWARGS(a) \ + WRAP_METHODS_WITH_KWARGS(b) \ + WRAP_METHODS_WITH_KWARGS(c) \ + WRAP_METHODS_WITH_KWARGS(d) \ + WRAP_METHODS_WITH_KWARGS(e) \ + WRAP_METHODS_WITH_KWARGS(f) + WRAP_METHOD_ALL() +WRAP_METHOD_ALL_WITH_KWARGS() #define WARP_METHOD_NAME(ida, idb) wrapperMethod##ida##idb, +#define WARP_METHOD_NAME_WITH_KWARGS(ida, idb) wrapperMethodWithKwargs##ida##idb, #define WARP_METHOD_NAMES(ida) \ WARP_METHOD_NAME(ida, 0) \ @@ -64,6 +107,25 @@ WRAP_METHOD_ALL() WARP_METHOD_NAME(ida, e) \ WARP_METHOD_NAME(ida, f) + +#define WARP_METHOD_NAMES_WITH_KWARGS(ida) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, 0) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, 1) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, 2) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, 3) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, 4) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, 5) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, 6) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, 7) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, 8) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, 9) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, a) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, b) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, c) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, d) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, e) \ + WARP_METHOD_NAME_WITH_KWARGS(ida, f) + #define WARP_METHOD_NAMES_ALL() \ WARP_METHOD_NAMES(0) \ WARP_METHOD_NAMES(1) \ @@ -82,10 +144,33 @@ WRAP_METHOD_ALL() WARP_METHOD_NAMES(e) \ WARP_METHOD_NAMES(f) + +#define WARP_METHOD_NAMES_ALL_WITH_KWARGS() \ + WARP_METHOD_NAMES_WITH_KWARGS(0) \ + WARP_METHOD_NAMES_WITH_KWARGS(1) \ + WARP_METHOD_NAMES_WITH_KWARGS(2) \ + WARP_METHOD_NAMES_WITH_KWARGS(3) \ + WARP_METHOD_NAMES_WITH_KWARGS(4) \ + WARP_METHOD_NAMES_WITH_KWARGS(5) \ + WARP_METHOD_NAMES_WITH_KWARGS(6) \ + WARP_METHOD_NAMES_WITH_KWARGS(7) \ + WARP_METHOD_NAMES_WITH_KWARGS(8) \ + WARP_METHOD_NAMES_WITH_KWARGS(9) \ + WARP_METHOD_NAMES_WITH_KWARGS(a) \ + WARP_METHOD_NAMES_WITH_KWARGS(b) \ + WARP_METHOD_NAMES_WITH_KWARGS(c) \ + WARP_METHOD_NAMES_WITH_KWARGS(d) \ + WARP_METHOD_NAMES_WITH_KWARGS(e) \ + WARP_METHOD_NAMES_WITH_KWARGS(f) + PyObject* (*wrapperMethods[256])(PyObject *self, PyObject *args) = { WARP_METHOD_NAMES_ALL() }; +PyObject* (*wrapperMethodsWithKwargs[256])(PyObject *self, PyObject *args, PyObject *kwargs) = { + WARP_METHOD_NAMES_ALL_WITH_KWARGS() +}; + #define GETTER_METHOD(ida, idb) \ PyObject *getterMethod##ida##idb(PyObject *self, void *closure) { \ return getterMethod(self, closure, 0x##ida * 16 + 0x##idb); \ diff --git a/wrap.h b/wrap.h index 6958ab0..e70e1a9 100644 --- a/wrap.h +++ b/wrap.h @@ -6,6 +6,9 @@ extern PyObject *wrapperMethod(PyObject *self, PyObject *args, int methodId); extern PyObject *(*wrapperMethods[256])(PyObject *self, PyObject *args); +extern PyObject *wrapperMethodWithKwargs(PyObject *self, PyObject *args, PyObject *kwargs, int methodId); +extern PyObject *(*wrapperMethodsWithKwargs[256])(PyObject *self, PyObject *args, PyObject *kwargs); + extern PyObject *getterMethod(PyObject *self, void *closure, int methodId); extern int setterMethod(PyObject *self, PyObject *value, void *closure, int methodId); @@ -18,8 +21,11 @@ extern setter setterMethods[256]; #define DECLARE_SETTER_METHOD(ida, idb) \ extern int setterMethod##ida##idb(PyObject* self, PyObject* value, void* closure); -#define DECLARE_WRAP_METHOD(ida, idb) \ - extern PyObject* wrapperMethod##ida##idb(PyObject* self, PyObject* args); +#define DECLARE_WRAP_METHOD(ida, idb) \ + extern PyObject *wrapperMethod##ida##idb(PyObject *self, PyObject *args); + +#define DECLARE_WRAP_METHOD_WITH_KWARGS(ida, idb) \ + extern PyObject *wrapperMethodWithKwargs##ida##idb(PyObject *self, PyObject *args, PyObject *kwargs); #define DECLARE_WRAP_METHODS(ida) \ DECLARE_WRAP_METHOD(ida, 0) \ @@ -39,6 +45,24 @@ extern setter setterMethods[256]; DECLARE_WRAP_METHOD(ida, e) \ DECLARE_WRAP_METHOD(ida, f) +#define DECLARE_WRAP_METHODS_WITH_KWARGS(ida) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, 0) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, 1) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, 2) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, 3) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, 4) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, 5) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, 6) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, 7) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, 8) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, 9) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, a) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, b) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, c) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, d) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, e) \ + DECLARE_WRAP_METHOD_WITH_KWARGS(ida, f) + #define DECLARE_WRAPPER_ALL_METHODS() \ DECLARE_WRAP_METHODS(0) \ DECLARE_WRAP_METHODS(1) \ @@ -57,8 +81,26 @@ extern setter setterMethods[256]; DECLARE_WRAP_METHODS(e) \ DECLARE_WRAP_METHODS(f) -DECLARE_WRAPPER_ALL_METHODS() +#define DECLARE_WRAPPER_ALL_METHODS_WITH_KWARGS() \ + DECLARE_WRAP_METHODS_WITH_KWARGS(0) \ + DECLARE_WRAP_METHODS_WITH_KWARGS(1) \ + DECLARE_WRAP_METHODS_WITH_KWARGS(2) \ + DECLARE_WRAP_METHODS_WITH_KWARGS(3) \ + DECLARE_WRAP_METHODS_WITH_KWARGS(4) \ + DECLARE_WRAP_METHODS_WITH_KWARGS(5) \ + DECLARE_WRAP_METHODS_WITH_KWARGS(6) \ + DECLARE_WRAP_METHODS_WITH_KWARGS(7) \ + DECLARE_WRAP_METHODS_WITH_KWARGS(8) \ + DECLARE_WRAP_METHODS_WITH_KWARGS(9) \ + DECLARE_WRAP_METHODS_WITH_KWARGS(a) \ + DECLARE_WRAP_METHODS_WITH_KWARGS(b) \ + DECLARE_WRAP_METHODS_WITH_KWARGS(c) \ + DECLARE_WRAP_METHODS_WITH_KWARGS(d) \ + DECLARE_WRAP_METHODS_WITH_KWARGS(e) \ + DECLARE_WRAP_METHODS_WITH_KWARGS(f) +DECLARE_WRAPPER_ALL_METHODS() +DECLARE_WRAPPER_ALL_METHODS_WITH_KWARGS() #define DECLARE_GETTER_METHODS(ida) \ DECLARE_GETTER_METHOD(ida, 0) \