Skip to content
This repository was archived by the owner on Mar 23, 2023. It is now read-only.

Commit f87ed68

Browse files
committed
WIP - Allow passing Python functions to Go for invocation.
1 parent d52d231 commit f87ed68

File tree

2 files changed

+83
-5
lines changed

2 files changed

+83
-5
lines changed

runtime/function.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ func functionGet(_ *Frame, desc, instance *Object, owner *Type) (*Object, *BaseE
134134
return NewMethod(toFunctionUnsafe(desc), instance, owner).ToObject(), nil
135135
}
136136

137+
func functionNative(f *Frame, o *Object) (reflect.Value, *BaseException) {
138+
return reflect.ValueOf(o.Call), nil
139+
}
140+
137141
func functionRepr(_ *Frame, o *Object) (*Object, *BaseException) {
138142
fun := toFunctionUnsafe(o)
139143
return NewStr(fmt.Sprintf("<%s %s at %p>", fun.typ.Name(), fun.Name(), fun)).ToObject(), nil
@@ -143,6 +147,7 @@ func initFunctionType(map[string]*Object) {
143147
FunctionType.flags &= ^(typeFlagInstantiable | typeFlagBasetype)
144148
FunctionType.slots.Call = &callSlot{functionCall}
145149
FunctionType.slots.Get = &getSlot{functionGet}
150+
FunctionType.slots.Native = &nativeSlot{functionNative}
146151
FunctionType.slots.Repr = &unaryOpSlot{functionRepr}
147152
}
148153

runtime/native.go

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -479,22 +479,95 @@ func maybeConvertValue(f *Frame, o *Object, expectedRType reflect.Type) (reflect
479479
if raised != nil {
480480
return reflect.Value{}, raised
481481
}
482-
rtype := val.Type()
483482
for {
483+
rtype := val.Type()
484484
if rtype == expectedRType {
485485
return val, nil
486486
}
487487
if rtype.ConvertibleTo(expectedRType) {
488488
return val.Convert(expectedRType), nil
489489
}
490-
if rtype.Kind() == reflect.Ptr {
490+
switch rtype.Kind() {
491+
case reflect.Ptr:
491492
val = val.Elem()
492-
rtype = val.Type()
493493
continue
494+
495+
case reflect.Func:
496+
if fn, ok := val.Interface().(func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException)); ok {
497+
val = nativeToPyFuncBridge(Func(fn), expectedRType)
498+
continue
499+
}
494500
}
495-
break
501+
return val, f.RaiseType(TypeErrorType, fmt.Sprintf("cannot convert %s to %s", rtype, expectedRType))
502+
}
503+
}
504+
505+
func nativeToPyFuncBridge(fn Func, target reflect.Type) reflect.Value {
506+
firstInIsFrame := target.NumIn() > 0 && target.In(0) == reflect.TypeOf((*Frame)(nil))
507+
508+
outs := make([]reflect.Type, target.NumOut())
509+
for i := range outs {
510+
outs[i] = target.Out(i)
496511
}
497-
return reflect.Value{}, f.RaiseType(TypeErrorType, fmt.Sprintf("cannot convert %s to %s", rtype, expectedRType))
512+
513+
return reflect.MakeFunc(target, func(args []reflect.Value) []reflect.Value {
514+
var frame *Frame
515+
if firstInIsFrame {
516+
frame, args = args[0].Interface().(*Frame), args[1:]
517+
} else {
518+
frame = NewRootFrame()
519+
}
520+
521+
pyArgs := frame.MakeArgs(len(args))
522+
for i, arg := range args {
523+
var raised *BaseException
524+
pyArgs[i], raised = WrapNative(frame, arg)
525+
if raised != nil {
526+
panic(fmt.Sprintf("WrapNative(%v)=%v", arg, raised)) // TODO: Figure this out...
527+
}
528+
}
529+
530+
ret, raised := fn(frame, pyArgs, nil)
531+
if raised != nil {
532+
panic(fmt.Sprintf("python error %v crossing into Go", raised)) // TODO: Figure this out...
533+
}
534+
535+
switch len(outs) {
536+
case 0:
537+
if ret != nil && ret != None {
538+
panic(fmt.Sprintf("unexpected return of %v when None expected", ret)) // TODO: Figure this out...
539+
}
540+
return nil
541+
542+
case 1:
543+
v, raised := maybeConvertValue(frame, ret, outs[0])
544+
if raised != nil {
545+
panic(fmt.Sprintf("maybeConvertValue(%v)=%v", ret, raised)) // TODO: Figure this out...
546+
}
547+
return []reflect.Value{v}
548+
}
549+
550+
// TODO: Support other iterables?
551+
if !ret.isInstance(TupleType) {
552+
panic(fmt.Sprintf("return value %v is not a tuple", ret)) // TODO
553+
}
554+
555+
retTuple := toTupleUnsafe(ret)
556+
if retTuple.Len() != len(outs) {
557+
panic(fmt.Sprintf("return value has wrong length %v, want %v", retTuple.Len(), len(outs))) // TODO
558+
}
559+
560+
converted := make([]reflect.Value, len(outs))
561+
for i, out := range outs {
562+
var raised *BaseException
563+
converted[i], raised = maybeConvertValue(frame, retTuple.GetItem(i), out)
564+
if raised != nil {
565+
panic(fmt.Sprintf("maybeConvertValue(%v)=%v", retTuple.GetItem(i), raised)) // TODO: Figure this out...
566+
}
567+
}
568+
569+
return converted
570+
})
498571
}
499572

500573
func nativeFuncTypeName(rtype reflect.Type) string {

0 commit comments

Comments
 (0)