diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b1b0b9e0..a639f411 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,7 +8,7 @@ on:
 
 env:
   TAGS: "-tags=ci"
-  COVERAGE: "-coverpkg=github.com/go-python/gopy/..."
+  COVERAGE: "-coverpkg=github.com/rudderlabs/gopy/..."
   # Init() in main_test will make sure all backends are available if
   # GOPY_TRAVIS_CI is set
   GOPY_TRAVIS_CI: 1
diff --git a/SUPPORT_MATRIX.md b/SUPPORT_MATRIX.md
index 5e473e01..bd5e1ec5 100644
--- a/SUPPORT_MATRIX.md
+++ b/SUPPORT_MATRIX.md
@@ -17,6 +17,7 @@ _examples/hi | no | yes
 _examples/iface | no | yes
 _examples/lot | yes | yes
 _examples/maps | yes | yes
+_examples/multireturn | no | yes
 _examples/named | yes | yes
 _examples/osfile | yes | yes
 _examples/pkgconflict | yes | yes
@@ -29,4 +30,5 @@ _examples/sliceptr | yes | yes
 _examples/slices | yes | yes
 _examples/structs | yes | yes
 _examples/unicode | no | yes
+_examples/variadic | no | yes
 _examples/vars | yes | yes
diff --git a/_examples/cpkg/run.go b/_examples/cpkg/run.go
index 85bd647d..5fc61ac2 100644
--- a/_examples/cpkg/run.go
+++ b/_examples/cpkg/run.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build ignore
 // +build ignore
 
 package main
@@ -9,7 +10,7 @@ package main
 import (
 	"fmt"
 
-	"github.com/go-python/gopy/_examples/cpkg"
+	"github.com/rudderlabs/gopy/_examples/cpkg"
 )
 
 func main() {
diff --git a/_examples/funcs/funcs.go b/_examples/funcs/funcs.go
index baac0111..0c0fc269 100644
--- a/_examples/funcs/funcs.go
+++ b/_examples/funcs/funcs.go
@@ -7,7 +7,7 @@ package funcs
 import (
 	"fmt"
 
-	"github.com/go-python/gopy/_examples/cpkg"
+	"github.com/rudderlabs/gopy/_examples/cpkg"
 )
 
 type FunStruct struct {
diff --git a/_examples/gopyerrors/gopyerrors.go b/_examples/gopyerrors/gopyerrors.go
deleted file mode 100644
index fbec63d5..00000000
--- a/_examples/gopyerrors/gopyerrors.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2020 The go-python Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// package gopyerrors contains functions that generate error
-// messages from gopy itself.
-package gopyerrors
-
-func NotErrorMany() (int, int) {
-	return 0, 0
-}
-
-func TooMany() (int, int, string) {
-	return 0, 1, "Hi"
-}
-
-func OK() (int, error) {
-	return 0, nil
-}
-
-type Struct struct{}
-
-func (s *Struct) NotErrorMany() (int, string) {
-	return 0, "Hi"
-}
-
-func (s *Struct) TooMany() (int, int, string) {
-	return 0, 1, "Hi"
-}
diff --git a/_examples/gopyerrors/test.py b/_examples/gopyerrors/test.py
deleted file mode 100644
index eb6e169a..00000000
--- a/_examples/gopyerrors/test.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2020 go-python Authors.  All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# py2/py3 compat
-from __future__ import print_function
-
-import gopyerrors
-
-# This is empty, its only purpose is to have a test that catches
-# errors generated by the gopy itself.
-
-print("OK")
diff --git a/_examples/hi/hi.go b/_examples/hi/hi.go
index 0d62e1d0..d968928d 100644
--- a/_examples/hi/hi.go
+++ b/_examples/hi/hi.go
@@ -8,8 +8,8 @@ package hi
 import (
 	"fmt"
 
-	"github.com/go-python/gopy/_examples/cpkg"
-	"github.com/go-python/gopy/_examples/structs"
+	"github.com/rudderlabs/gopy/_examples/cpkg"
+	"github.com/rudderlabs/gopy/_examples/structs"
 )
 
 const (
diff --git a/_examples/iface/iface.go b/_examples/iface/iface.go
index 56d98633..6c8d3765 100644
--- a/_examples/iface/iface.go
+++ b/_examples/iface/iface.go
@@ -6,7 +6,7 @@
 package iface
 
 import (
-	"github.com/go-python/gopy/_examples/cpkg"
+	"github.com/rudderlabs/gopy/_examples/cpkg"
 )
 
 // Iface has a single F() method
diff --git a/_examples/multireturn/multireturn.go b/_examples/multireturn/multireturn.go
new file mode 100644
index 00000000..55a6e433
--- /dev/null
+++ b/_examples/multireturn/multireturn.go
@@ -0,0 +1,111 @@
+package multireturn
+
+import (
+	"fmt"
+)
+
+/////////////// No Return //////////////
+func NoReturnFunc() {
+}
+
+/////////////// Single WithoutError Return //////////////
+func SingleWithoutErrorFunc() int {
+	return 100
+}
+
+/////////////// Single Str WithoutError Return //////////////
+func SingleStrWithoutErrorFunc(vargs ...int) string {
+	return "150"
+}
+
+/////////////// Single WithError Return //////////////
+func SingleWithErrorFunc(throwError bool) error {
+	if throwError {
+		return fmt.Errorf("Error")
+	} else {
+		return nil
+	}
+}
+
+/////////////// Double WithoutError Return //////////////
+func DoubleWithoutErrorFunc1() (int, int) {
+	return 200, 300
+}
+func DoubleWithoutErrorFunc2() (string, string) {
+	return "200", "300"
+}
+
+/////////////// Double WithError Return //////////////
+func DoubleWithErrorFunc(throwError bool) (string, error) {
+	if throwError {
+		return "400", fmt.Errorf("Error")
+	} else {
+		return "500", nil
+	}
+}
+
+/////////////// Triple Returns Without Error //////////////
+func TripleWithoutErrorFunc1(vargs ...int) (int, int, int) {
+	return 600, 700, 800
+}
+func TripleWithoutErrorFunc2(vargs ...int) (int, string, int) {
+	return 600, "700", 800
+}
+
+/////////////// Triple Returns With Error //////////////
+func TripleWithErrorFunc(throwError bool) (int, int, error) {
+	if throwError {
+		return 900, 1000, fmt.Errorf("Error")
+	} else {
+		return 1100, 1200, nil
+	}
+}
+
+/////////////// Triple Struct Returns With Error //////////////
+type IntStrUct struct {
+	P int
+}
+
+func NewIntStrUct(n int) IntStrUct {
+	return IntStrUct{
+		P: n,
+	}
+}
+
+func TripleWithStructWithErrorFunc(throwError bool) (*IntStrUct, IntStrUct, error) {
+	s1300 := IntStrUct{P: 1300}
+	s1400 := IntStrUct{P: 1400}
+	s1500 := IntStrUct{P: 1500}
+	s1600 := IntStrUct{P: 1600}
+	if throwError {
+		return &s1300, s1400, fmt.Errorf("Error")
+	} else {
+		return &s1500, s1600, nil
+	}
+}
+
+/////////////// Triple Interface Returns Without Error //////////////
+type IntInterFace interface {
+	Number() int
+}
+
+func (is *IntStrUct) Number() int {
+	return is.P
+}
+
+func TripleWithInterfaceWithoutErrorFunc() (IntInterFace, IntStrUct, *IntStrUct) {
+	i1700 := IntStrUct{P: 1700}
+	s1800 := IntStrUct{P: 1800}
+	s1900 := IntStrUct{P: 1900}
+
+	return &i1700, s1800, &s1900
+}
+
+//// Function returning function /////
+type FunctionType func(input int) int
+
+func FunctionReturningFunction() FunctionType {
+	return func(input int) int {
+		return input
+	}
+}
diff --git a/_examples/multireturn/test.py b/_examples/multireturn/test.py
new file mode 100644
index 00000000..e885a868
--- /dev/null
+++ b/_examples/multireturn/test.py
@@ -0,0 +1,77 @@
+# Copyright 2018 The go-python Authors.  All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+import multireturn, go
+
+############### No Return ##############
+noResult = multireturn.NoReturnFunc()
+print("No Return %r" % noResult)
+
+############### Single WithoutError Return ##############
+oneResult = multireturn.SingleWithoutErrorFunc()
+print("Single WithoutError Return %r" % oneResult)
+
+############### Single Str WithoutError Return ##############
+oneStrResult = multireturn.SingleStrWithoutErrorFunc()
+print("Single Str WithoutError Return %r" % oneStrResult)
+
+############### Single WithError Return ##############
+errorFalseResult1 = multireturn.SingleWithErrorFunc(False)
+print("Single WithError(False) Return %r" % errorFalseResult1)
+
+try:
+	errorTrueResult1 = multireturn.SingleWithErrorFunc(True)
+	print("Failed to throw an exception")
+except RuntimeError as ex:
+	print("Single WithError(True). Exception: %r" % ex)
+
+############### Double WithoutError Return ##############
+twoResults = multireturn.DoubleWithoutErrorFunc1()
+print("Double WithoutError(Without String) Return (%r, %r)" % twoResults)
+
+twoResults = multireturn.DoubleWithoutErrorFunc2()
+print("Double WithoutError(With String) Return (%r, %r)" % twoResults)
+
+############### Double WithError Return ##############
+try:
+	value400 = multireturn.DoubleWithErrorFunc(True)
+	print("Failed to throw an exception. Return (%r, %r)." % value400)
+except RuntimeError as ex:
+	print("Double WithError(True). Exception: %r" % ex)
+
+value500 = multireturn.DoubleWithErrorFunc(False)
+print("Double WithError(False) Return %r" % value500)
+
+############### Triple Without Error Return ##############
+threeResults = multireturn.TripleWithoutErrorFunc1()
+print("Triple WithoutError(Without String) Return (%r, %r, %r)" % threeResults)
+
+threeResults = multireturn.TripleWithoutErrorFunc2()
+print("Triple WithoutError(With String) Return (%r, %r, %r)" % threeResults)
+
+############### Triple With Error Return ##############
+try:
+	(value900, value1000) = multireturn.TripleWithErrorFunc(True)
+	print("Triple WithError(True) Return (%r, %r, %r)" % (value900, value1000))
+except RuntimeError as ex:
+	print("Triple WithError(True) Exception: %r" % ex)
+
+(value1100, value1200) = multireturn.TripleWithErrorFunc(False)
+print("Triple WithError(False) Return (%r, %r)" % (value1100, value1200))
+
+############### Triple Struct Return With Error ##############
+try:
+	(ptr1300, struct1400) = multireturn.TripleWithStructWithErrorFunc(True)
+	print("Triple WithError(True) Return (%r, %r)" % (ptr1300.P, struct1400.P))
+except RuntimeError as ex:
+	print("Triple WithError(True) Exception: %r" % ex)
+
+(value1500, value1600) = multireturn.TripleWithStructWithErrorFunc(False)
+print("Triple WithError(False) Return (%r, %r)" % (value1500.P, value1600.P))
+
+############### Triple Interface Return Without Error ##############
+(interface1700, struct1800, ptr1900) = multireturn.TripleWithInterfaceWithoutErrorFunc()
+print("Triple WithoutError() Return (%r, %r, %r)" % (interface1700.Number(), struct1800.P, ptr1900.P))
+
+############## Function Returning Functions ignored ##############
+assert("FunctionReturningFunction" not in dir(multireturn))
diff --git a/_examples/variadic/variadic.go b/_examples/variadic/variadic.go
index dce6447d..1766d6b7 100644
--- a/_examples/variadic/variadic.go
+++ b/_examples/variadic/variadic.go
@@ -1,7 +1,7 @@
 package variadic
 
 /////////////// Non Variadic //////////////
-func NonVariFunc(arg1 int, arg2 []int, arg3 int) int{
+func NonVariFunc(arg1 int, arg2 []int, arg3 int) int {
 	total := arg1
 	for _, num := range arg2 {
 		total += num
@@ -12,7 +12,7 @@ func NonVariFunc(arg1 int, arg2 []int, arg3 int) int{
 }
 
 /////////////// Variadic Over Int //////////////
-func VariFunc(vargs ...int) int{
+func VariFunc(vargs ...int) int {
 	total := 0
 	for _, num := range vargs {
 		total += num
@@ -26,15 +26,15 @@ type IntStrUct struct {
 }
 
 func NewIntStrUct(n int) IntStrUct {
-	return IntStrUct {
-		p:n,
+	return IntStrUct{
+		p: n,
 	}
-} 
+}
 
-func VariStructFunc(vargs ...IntStrUct) int{
+func VariStructFunc(vargs ...IntStrUct) int {
 	total := 0
 	for _, inst := range vargs {
-		total += inst.p 
+		total += inst.p
 	}
 	return total
 }
@@ -48,7 +48,7 @@ func (is *IntStrUct) Number() int {
 	return is.p
 }
 
-func VariInterFaceFunc(vargs ...IntInterFace) int{
+func VariInterFaceFunc(vargs ...IntInterFace) int {
 	total := 0
 	for _, inst := range vargs {
 		total += inst.Number()
diff --git a/_examples/wrapper/pywrapper/wrapper_code.go b/_examples/wrapper/pywrapper/wrapper_code.go
index 1ded41b0..2b928ef3 100644
--- a/_examples/wrapper/pywrapper/wrapper_code.go
+++ b/_examples/wrapper/pywrapper/wrapper_code.go
@@ -7,7 +7,7 @@ package pywrapper
 import (
 	"fmt"
 
-	"github.com/go-python/gopy/_examples/wrapper"
+	"github.com/rudderlabs/gopy/_examples/wrapper"
 )
 
 type WrapperStruct struct {
diff --git a/bind/bind.go b/bind/bind.go
index a3b7a853..32b8690b 100644
--- a/bind/bind.go
+++ b/bind/bind.go
@@ -16,18 +16,34 @@ import (
 type BindCfg struct {
 	// output directory for bindings
 	OutputDir string
+
 	// name of output package (otherwise name of first package is used)
 	Name string
+
 	// code string to run in the go main() function in the cgo library
 	Main string
+
 	// the full command args as a string, without path to exe
 	Cmd string
+
 	// path to python interpreter
 	VM string
+
 	// package prefix used when generating python import statements
 	PkgPrefix string
+
 	// rename Go exported symbols to python PEP snake_case
 	RenameCase bool
+
+	// If set, python exceptions are not thrown.
+	NoPyExceptions bool
+
+	// Path to Go module, which is to be used to translate Go errors to Python exceptions.
+	ModPathGoErr2PyEx string
+
+	// If set, when a Go function returns a (value, err), python returns (value, ) tuple.
+	// By default, we return just value.
+	UsePyTuple4VE bool
 }
 
 // ErrorList is a list of errors
diff --git a/bind/gen.go b/bind/gen.go
index d8c83930..c151748d 100644
--- a/bind/gen.go
+++ b/bind/gen.go
@@ -38,7 +38,8 @@ var WindowsOS = false
 
 // for all preambles: 1 = name of package (outname), 2 = cmdstr
 
-// 3 = libcfg, 4 = GoHandle, 5 = CGoHandle, 6 = all imports, 7 = mainstr, 8 = exe pre C, 9 = exe pre go
+// 3 = libcfg, 4 = GoHandle, 5 = CGoHandle, 6 = all imports, 7 = goerr2pyex Package path, 8 = mainstr,
+// 9 = exe pre C, 10 = exe pre go
 const (
 	goPreamble = `/*
 cgo stubs for package %[1]s.
@@ -85,17 +86,32 @@ static inline void gopy_err_handle() {
 		PyErr_Print();
 	}
 }
-%[8]s
+static PyObject* Py_BuildValue1(char *format, void* arg0)
+{
+	PyObject *retval = Py_BuildValue(format, arg0);
+	free(format);
+	return retval;
+}
+static PyObject* Py_BuildValue2(char *format, long long arg0)
+{
+	PyObject *retval = Py_BuildValue(format, arg0);
+	free(format);
+	return retval;
+}
+
+%[9]s
 */
 import "C"
 import (
-	"github.com/go-python/gopy/gopyh" // handler
+	"github.com/rudderlabs/gopy/gopyh" // handler
+	"%[7]s" // Error Translator
+
 	%[6]s
 )
 
 // main doesn't do anything in lib / pkg mode, but is essential for exe mode
 func main() {
-	%[7]s
+	%[8]s
 }
 
 // initialization functions -- can be called from python after library is loaded
@@ -104,7 +120,7 @@ func main() {
 
 //export GoPyInit
 func GoPyInit() {
-	%[7]s
+	%[8]s
 }
 
 // type for the handle -- int64 for speed (can switch to string)
@@ -164,7 +180,7 @@ func complex128PyToGo(o *C.PyObject) complex128 {
 	return complex(float64(v.real), float64(v.imag))
 }
 
-%[9]s
+%[10]s
 `
 
 	goExePreambleC = `
@@ -229,19 +245,22 @@ class CheckedFunction(Function):
         failure_cleanup = self._failure_cleanup or None
         self.before_call.write_error_check(check, failure_cleanup)
 
-def add_checked_function(mod, name, retval, params, failure_expression='', *a, **kw):
-    fn = CheckedFunction(name, retval, params, *a, **kw)
-    fn.set_failure_expression(failure_expression)
-    mod._add_function_obj(fn)
-    return fn
-
-def add_checked_string_function(mod, name, retval, params, failure_expression='', *a, **kw):
-    fn = CheckedFunction(name, retval, params, *a, **kw)
-    fn.set_failure_cleanup('if (retval != NULL) free(retval);')
-    fn.after_call.add_cleanup_code('free(retval);')
-    fn.set_failure_expression(failure_expression)
-    mod._add_function_obj(fn)
-    return fn
+def add_checked_function_generator(pyTupleBuilt, retvals_to_free):
+    if not pyTupleBuilt:
+        if retvals_to_free:
+            assert(len(retvals_to_free) == 1)
+            assert(retvals_to_free[0] == 0)
+    def rv_format(format_str, rv):
+        return format_str.format("PyTuple_GetItem(retval, {0})".format(rv))
+    def add_checked_function(mod, name, retval, params, failure_expression='', *a, **kw):
+        fn = CheckedFunction(name, retval, params, *a, **kw)
+        #TODO: Figure out how to free allocated variables. Stop leaking memory.
+        #fn.set_failure_cleanup('\n'.join([rv_format('if ({0} != NULL) free({0});', rv) for rv in retvals_to_free]))
+        #fn.after_call.add_cleanup_code('\n'.join([rv_format('free({0});', rv) for rv in retvals_to_free]))
+        fn.set_failure_expression(failure_expression)
+        mod._add_function_obj(fn)
+        return fn
+    return add_checked_function
 
 mod = Module('_%[1]s')
 mod.add_include('"%[1]s_go.h"')
@@ -372,6 +391,10 @@ build:
 	# generated %[1]s.py python wrapper imports this c-code package
 	%[9]s
 	$(GCC) %[1]s.c %[6]s %[1]s_go$(LIBEXT) -o _%[1]s$(LIBEXT) $(CFLAGS) $(LDFLAGS) -fPIC --shared -w
+
+halfbuild:
+	$(GOBUILD) -buildmode=c-shared -o %[1]s_go$(LIBEXT) %[1]s.go
+	$(GCC) %[1]s.c %[6]s %[1]s_go$(LIBEXT) -o _%[1]s$(LIBEXT) $(CFLAGS) $(LDFLAGS) -fPIC --shared -w
 	
 `
 
@@ -430,6 +453,9 @@ var NoWarn = false
 // NoMake turns off generation of Makefiles
 var NoMake = false
 
+// NoPyExceptions turns off generation of Python Exceptions
+var NoPyExceptions = false
+
 // GenPyBind generates a .go file, build.py file to enable pybindgen to create python bindings,
 // and wrapper .py file(s) that are loaded as the interface to the package with shadow
 // python-side classes
@@ -603,7 +629,7 @@ func (g *pyGen) genGoPreamble() {
 		exeprego = goExePreambleGo
 	}
 	g.gofile.Printf(goPreamble, g.cfg.Name, g.cfg.Cmd, libcfg, GoHandle, CGoHandle,
-		pkgimport, g.cfg.Main, exeprec, exeprego)
+		pkgimport, g.cfg.ModPathGoErr2PyEx, g.cfg.Main, exeprec, exeprego)
 	g.gofile.Printf("\n// --- generated code for package: %[1]s below: ---\n\n", g.cfg.Name)
 }
 
diff --git a/bind/gen_func.go b/bind/gen_func.go
index 991165f9..43f267fd 100644
--- a/bind/gen_func.go
+++ b/bind/gen_func.go
@@ -7,9 +7,22 @@ package bind
 import (
 	"fmt"
 	"go/types"
+	"log"
+	"strconv"
 	"strings"
 )
 
+func buildPyTuple(fsym *Func) bool {
+	npyres := len(fsym.sig.Results())
+	if fsym.haserr {
+		if !NoPyExceptions {
+			npyres -= 1
+		}
+	}
+
+	return (npyres > 1)
+}
+
 func (g *pyGen) recurse(gotype types.Type, prefix, name string) {
 	switch t := gotype.(type) {
 	case *types.Basic:
@@ -58,13 +71,11 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool {
 	res := sig.Results()
 	nargs := 0
 	nres := len(res)
-
-	// note: this is enforced in creation of Func, in newFuncFrom
-	if nres > 2 {
-		return false
-	}
-	if nres == 2 && !fsym.err {
-		return false
+	npyres := nres
+	if fsym.haserr {
+		if !NoPyExceptions {
+			npyres -= 1
+		}
 	}
 
 	var (
@@ -100,7 +111,7 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool {
 			}
 		}
 
-		if i!=nargs-1 || !fsym.isVariadic {
+		if i != nargs-1 || !fsym.isVariadic {
 			wpArgs = append(wpArgs, anm)
 		}
 	}
@@ -121,18 +132,24 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool {
 	// a function that adds function calls with exception checking.
 	// But given specific return types, we may want to add more
 	// behavior to the wrapped function code gen.
-	addFuncName := "add_checked_function"
-	if len(res) > 0 {
-		ret := res[0]
-		switch t := ret.GoType().(type) {
-		case *types.Basic:
-			// string return types need special memory leak patches
-			// to free the allocated char*
-			if t.Kind() == types.String {
-				addFuncName = "add_checked_string_function"
+	retvalsToFree := make([]string, 0, npyres)
+	if npyres > 0 {
+		for i := 0; i < npyres; i++ {
+			switch t := res[i].GoType().(type) {
+			case *types.Basic:
+				// string return types need special memory leak patches
+				// to free the allocated char*
+				if t.Kind() == types.String {
+					retvalsToFree = append(retvalsToFree, strconv.Itoa(i))
+				}
 			}
 		}
 	}
+	pyTupleBuilt := "True"
+	if !buildPyTuple(fsym) {
+		pyTupleBuilt = "False"
+	}
+	addFuncName := "add_checked_function_generator(" + pyTupleBuilt + ", [" + strings.Join(retvalsToFree, ", ") + "])"
 
 	switch {
 	case isMethod:
@@ -154,36 +171,47 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool {
 	}
 
 	goRet := ""
-	nres = len(res)
-	if nres > 0 {
-		ret := res[0]
-		sret := current.symtype(ret.GoType())
-		if sret == nil {
-			panic(fmt.Errorf(
-				"gopy: could not find symbol for %q",
-				ret.Name(),
-			))
+	if npyres == 0 {
+		g.pybuild.Printf("None")
+	} else if buildPyTuple(fsym) {
+		// We are returning PyTuple*. Setup pybindgen accordingly.
+		g.pybuild.Printf("retval('PyObject*', caller_owns_return=True)")
+
+		// On Go side, return *C.PyObject.
+		goRet = "unsafe.Pointer"
+	} else {
+		ownership := ""
+		pyrets := make([]string, npyres, npyres)
+		gorets := make([]string, npyres, npyres)
+		for i := 0; i < npyres; i++ {
+			sret := current.symtype(res[i].GoType())
+			if sret == nil {
+				panic(fmt.Errorf(
+					"gopy: could not find symbol for %q",
+					res[i].Name(),
+				))
+			}
+			gorets[i] = sret.cgoname
+			pyrets[i] = "'" + sret.cpyname + "'"
+			if sret.cpyname == "PyObject*" {
+				ownership = ", caller_owns_return=True"
+			}
 		}
 
-		if sret.cpyname == "PyObject*" {
-			g.pybuild.Printf("retval('%s', caller_owns_return=True)", sret.cpyname)
-		} else {
-			g.pybuild.Printf("retval('%s')", sret.cpyname)
+		g.pybuild.Printf("retval(%s%s)", strings.Join(pyrets, ", "), ownership)
+
+		goRet = strings.Join(gorets, ", ")
+		if npyres > 1 {
+			goRet = "(" + goRet + ")"
 		}
-		goRet = fmt.Sprintf("%s", sret.cgoname)
-	} else {
-		g.pybuild.Printf("None")
 	}
 
 	if len(goArgs) > 0 {
-		gstr := strings.Join(goArgs, ", ")
-		g.gofile.Printf("%v) %v", gstr, goRet)
+		g.gofile.Printf("%v) %v", strings.Join(goArgs, ", "), goRet)
 
-		pstr := strings.Join(pyArgs, ", ")
-		g.pybuild.Printf(", [%v])\n", pstr)
+		g.pybuild.Printf(", [%v])\n", strings.Join(pyArgs, ", "))
 
-		wstr := strings.Join(wpArgs, ", ")
-		g.pywrap.Printf("%v)", wstr)
+		g.pywrap.Printf("%v)", strings.Join(wpArgs, ", "))
 
 	} else {
 		g.gofile.Printf(") %v", goRet)
@@ -216,6 +244,74 @@ func isIfaceHandle(gdoc string) (bool, string) {
 	return false, gdoc
 }
 
+func isPointer(pyfmt string) bool {
+	if pyfmt == "s" {
+		return true
+	}
+	return false
+}
+
+func (g *pyGen) generateReturn(buildPyTuple bool, npyres int, results []*Var, retvals *[]string) {
+	g.gofile.Printf("\n")
+	valueCalls := make([]string, npyres, npyres)
+	for i := 0; i < npyres; i++ {
+		result := results[i]
+		sret := current.symtype(result.GoType())
+		if sret == nil {
+			panic(fmt.Errorf(
+				"gopy: could not find symbol for %q",
+				result.Name(),
+			))
+		}
+		formatStr := sret.pyfmt
+		if sret.pyfmt == "" {
+			formatStr = "?"
+		}
+
+		retval := ""
+		if retvals != nil {
+			retval = (*retvals)[i]
+		} else if result.sym.zval != "" {
+			retval = result.sym.zval
+		} else {
+			fmt.Printf("gopy: programmer error: empty zval zero value in symbol: %v\n", result.sym)
+		}
+
+		if result.sym.go2py != "" {
+			retval = result.sym.go2py + "(" + retval + ")" + result.sym.go2pyParenEx
+		}
+
+		if buildPyTuple {
+			buildValueFunc := "C.Py_BuildValue1"
+			typeCast := "unsafe.Pointer"
+			if !isPointer(formatStr) {
+				buildValueFunc = "C.Py_BuildValue2"
+				typeCast = "C.longlong"
+				formatStr = "L"
+			}
+			valueCalls[i] = fmt.Sprintf("%s(C.CString(\"%s\"), %s(%s))",
+				buildValueFunc,
+				formatStr,
+				typeCast,
+				retval)
+		} else {
+			valueCalls[i] = retval
+		}
+	}
+
+	if npyres == 0 {
+		g.gofile.Printf("return\n")
+	} else if buildPyTuple {
+		g.gofile.Printf("retTuple := C.PyTuple_New(%d);\n", npyres)
+		for i := 0; i < npyres; i++ {
+			g.gofile.Printf("C.PyTuple_SetItem(retTuple, %d, %s);\n", i, valueCalls[i])
+		}
+		g.gofile.Printf("return unsafe.Pointer(retTuple);")
+	} else {
+		g.gofile.Printf("return %s\n", strings.Join(valueCalls, ", "))
+	}
+}
+
 func (g *pyGen) genFuncBody(sym *symbol, fsym *Func) {
 	isMethod := (sym != nil)
 	isIface := false
@@ -230,22 +326,23 @@ func (g *pyGen) genFuncBody(sym *symbol, fsym *Func) {
 
 	pkgname := g.cfg.Name
 
-	_, gdoc, _ := extractPythonName(fsym.GoName(), fsym.Doc())
-	ifchandle, gdoc := isIfaceHandle(gdoc)
-
 	sig := fsym.Signature()
 	res := sig.Results()
 	args := sig.Params()
 	nres := len(res)
-
-	rvIsErr := false // set to true if the main return is an error
-	if nres == 1 {
-		ret := res[0]
-		if isErrorType(ret.GoType()) {
-			rvIsErr = true
+	npyres := nres
+	rvHasErr := false // set to true if the main return is an error
+	if fsym.haserr {
+		if NoPyExceptions {
+			rvHasErr = true
+		} else {
+			npyres -= 1
 		}
 	}
 
+	_, gdoc, _ := extractPythonName(fsym.GoName(), fsym.Doc())
+	ifchandle, gdoc := isIfaceHandle(gdoc)
+
 	g.pywrap.Printf(":\n")
 	g.pywrap.Indent()
 	g.pywrap.Printf(`"""%s"""`, gdoc)
@@ -260,28 +357,17 @@ func (g *pyGen) genFuncBody(sym *symbol, fsym *Func) {
 			}
 		}
 	}
+
 	if isMethod {
 		g.gofile.Printf(
 			`vifc, __err := gopyh.VarFromHandleTry((gopyh.CGoHandle)(_handle), "%s")
 if __err != nil {
 `, symNm)
 		g.gofile.Indent()
-		if nres > 0 {
-			ret := res[0]
-			if ret.sym.zval == "" {
-				fmt.Printf("gopy: programmer error: empty zval zero value in symbol: %v\n", ret.sym)
-			}
-			if ret.sym.go2py != "" {
-				g.gofile.Printf("return %s(%s)%s\n", ret.sym.go2py, ret.sym.zval, ret.sym.go2pyParenEx)
-			} else {
-				g.gofile.Printf("return %s\n", ret.sym.zval)
-			}
-		} else {
-			g.gofile.Printf("return\n")
-		}
+		g.generateReturn(buildPyTuple(fsym), npyres, res, nil)
 		g.gofile.Outdent()
 		g.gofile.Printf("}\n")
-	} else if rvIsErr {
+	} else if rvHasErr {
 		g.gofile.Printf("var __err error\n")
 	}
 
@@ -303,19 +389,19 @@ if __err != nil {
 		default:
 			na = anm
 		}
-		if i == len(args) - 1 && fsym.isVariadic {
+		if i == len(args)-1 && fsym.isVariadic {
 			na = na + "..."
 		}
 		callArgs = append(callArgs, na)
 		switch {
 		case arg.sym.goname == "interface{}":
 			if ifchandle {
-				wrapArgs = append(wrapArgs, fmt.Sprintf("%s.handle", anm))
+				wrapArgs = append(wrapArgs, fmt.Sprintf("(-1 if %s == None else %s.handle)", anm, anm))
 			} else {
 				wrapArgs = append(wrapArgs, anm)
 			}
 		case arg.sym.hasHandle():
-			wrapArgs = append(wrapArgs, fmt.Sprintf("%s.handle", anm))
+			wrapArgs = append(wrapArgs, fmt.Sprintf("(-1 if %s == None else %s.handle)", anm, anm))
 		default:
 			wrapArgs = append(wrapArgs, anm)
 		}
@@ -335,120 +421,131 @@ if __err != nil {
 	if isMethod {
 		mnm = sym.id + "_" + fsym.GoName()
 	}
-	rvHasHandle := false
-	if nres > 0 {
-		ret := res[0]
-		if !rvIsErr && ret.sym.hasHandle() {
-			rvHasHandle = true
-			cvnm := ret.sym.pyPkgId(g.pkg.pkg)
-			g.pywrap.Printf("return %s(handle=_%s.%s(", cvnm, pkgname, mnm)
-		} else {
-			g.pywrap.Printf("return _%s.%s(", pkgname, mnm)
-		}
-	} else {
-		g.pywrap.Printf("_%s.%s(", pkgname, mnm)
-	}
 
-	hasRetCvt := false
-	hasAddrOfTmp := false
-	if nres > 0 {
-		ret := res[0]
-		switch {
-		case rvIsErr:
-			g.gofile.Printf("__err = ")
-		case nres == 2:
-			g.gofile.Printf("cret, __err := ")
-		case ret.sym.hasHandle() && !ret.sym.isPtrOrIface():
-			hasAddrOfTmp = true
-			g.gofile.Printf("cret := ")
-		case ret.sym.go2py != "":
-			hasRetCvt = true
-			g.gofile.Printf("return %s(", ret.sym.go2py)
-		default:
-			g.gofile.Printf("return ")
-		}
-	}
+	log.Println(mnm)
+
 	if nres == 0 {
 		wrapArgs = append(wrapArgs, "goRun")
 	}
-	g.pywrap.Printf("%s)", strings.Join(wrapArgs, ", "))
-	if rvHasHandle {
-		g.pywrap.Printf(")")
+
+	pyFuncCall := fmt.Sprintf("_%s.%s(%s)", pkgname, mnm, strings.Join(wrapArgs, ", "))
+	if npyres > 0 {
+		retvars := make([]string, nres, nres)
+		for i := 0; i < npyres; i++ {
+			retvars[i] = "ret" + strconv.Itoa(i)
+			if res[i].sym.hasHandle() {
+				retvars[i] = "_" + retvars[i]
+			}
+		}
+
+		if fsym.haserr {
+			// Only used if npyres == nres.
+			retvars[nres-1] = "__err"
+		}
+
+		// May drop the error var, if it is not supposed to be returned.
+		retvars = retvars[0:npyres]
+
+		// Call upstream method and collect returns.
+		g.pywrap.Printf(fmt.Sprintf("%s = %s\n", strings.Join(retvars, ", "), pyFuncCall))
+
+		// ReMap handle returns from pyFuncCall.
+		for i := 0; i < npyres; i++ {
+			if res[i].sym.hasHandle() {
+				cvnm := res[i].sym.pyPkgId(g.pkg.pkg)
+				g.pywrap.Printf("ret%d = %s(handle=_ret%d)\n", i, cvnm, i)
+				retvars[i] = "ret" + strconv.Itoa(i)
+			}
+		}
+
+		g.pywrap.Printf("return %s", strings.Join(retvars, ", "))
+	} else {
+		g.pywrap.Printf(pyFuncCall)
 	}
 
-	funCall := ""
+	g.pywrap.Printf("\n")
+	g.pywrap.Outdent()
+	g.pywrap.Printf("\n")
+
+	goFuncCall := ""
 	if isMethod {
 		if sym.isStruct() {
-			funCall = fmt.Sprintf("gopyh.Embed(vifc, reflect.TypeOf(%s{})).(%s).%s(%s)", nonPtrName(symNm), symNm, fsym.GoName(), strings.Join(callArgs, ", "))
+			goFuncCall = fmt.Sprintf("gopyh.Embed(vifc, reflect.TypeOf(%s{})).(%s).%s(%s)",
+				nonPtrName(symNm),
+				symNm,
+				fsym.GoName(),
+				strings.Join(callArgs, ", "))
 		} else {
-			funCall = fmt.Sprintf("vifc.(%s).%s(%s)", symNm, fsym.GoName(), strings.Join(callArgs, ", "))
+			goFuncCall = fmt.Sprintf("vifc.(%s).%s(%s)",
+				symNm,
+				fsym.GoName(),
+				strings.Join(callArgs, ", "))
 		}
 	} else {
-		funCall = fmt.Sprintf("%s(%s)", fsym.GoFmt(), strings.Join(callArgs, ", "))
-	}
-	if hasRetCvt {
-		ret := res[0]
-		funCall += fmt.Sprintf(")%s", ret.sym.go2pyParenEx)
+		goFuncCall = fmt.Sprintf("%s(%s)",
+			fsym.GoFmt(),
+			strings.Join(callArgs, ", "))
 	}
 
-	if nres == 0 {
+	if nres > 0 {
+		retvals := make([]string, nres, nres)
+		for i := 0; i < npyres; i++ {
+			retvals[i] = "cret" + strconv.Itoa(i)
+		}
+		if fsym.haserr {
+			retvals[nres-1] = "__err_ret"
+		}
+
+		// Call upstream method and collect returns.
+		g.gofile.Printf(fmt.Sprintf("%s := %s\n", strings.Join(retvals, ", "), goFuncCall))
+
+		// ReMap handle returns from pyFuncCall.
+		for i := 0; i < npyres; i++ {
+			if res[i].sym.hasHandle() && !res[i].sym.isPtrOrIface() {
+				retvals[i] = "&" + retvals[i]
+			}
+		}
+
+		if fsym.haserr {
+			g.gofile.Printf("\n")
+
+			if rvHasErr {
+				retvals[npyres-1] = "estr" // NOTE: leaked string
+				g.gofile.Printf("var estr C.CString\n")
+				g.gofile.Printf("if __err_ret != nil {\n")
+				g.gofile.Indent()
+				g.gofile.Printf("estr = C.CString(__err_ret.Error())// NOTE: leaked string\n") // NOTE: leaked string
+				g.gofile.Outdent()
+				g.gofile.Printf("} else {\n")
+				g.gofile.Indent()
+				g.gofile.Printf("estr = C.CString(\"\")// NOTE: leaked string\n") // NOTE: leaked string
+				g.gofile.Outdent()
+				g.gofile.Printf("}\n")
+			} else {
+				g.gofile.Printf("if __err_ret != nil {\n")
+				g.gofile.Indent()
+				g.gofile.Printf("estr := C.CString(__err_ret.Error())\n") // NOTE: freed string
+				g.gofile.Printf("C.PyErr_SetString(C.PyExc_RuntimeError, estr)\n")
+				g.gofile.Printf("C.free(unsafe.Pointer(estr))\n") // python should have converted, safe
+				g.gofile.Outdent()
+				g.gofile.Printf("}\n")
+			}
+		}
+
+		g.generateReturn(buildPyTuple(fsym), npyres, res, &retvals)
+	} else {
 		g.gofile.Printf("if boolPyToGo(goRun) {\n")
 		g.gofile.Indent()
-		g.gofile.Printf("go %s\n", funCall)
+		g.gofile.Printf("go %s\n", goFuncCall)
 		g.gofile.Outdent()
 		g.gofile.Printf("} else {\n")
 		g.gofile.Indent()
-		g.gofile.Printf("%s\n", funCall)
+		g.gofile.Printf("%s\n", goFuncCall)
 		g.gofile.Outdent()
 		g.gofile.Printf("}")
-	} else {
-		g.gofile.Printf("%s\n", funCall)
 	}
 
-	if rvIsErr || nres == 2 {
-		g.gofile.Printf("\n")
-		g.gofile.Printf("if __err != nil {\n")
-		g.gofile.Indent()
-		g.gofile.Printf("estr := C.CString(__err.Error())\n")
-		g.gofile.Printf("C.PyErr_SetString(C.PyExc_RuntimeError, estr)\n")
-		if rvIsErr {
-			g.gofile.Printf("return estr\n") // NOTE: leaked string
-		} else {
-			g.gofile.Printf("C.free(unsafe.Pointer(estr))\n") // python should have converted, safe
-			ret := res[0]
-			if ret.sym.zval == "" {
-				fmt.Printf("gopy: programmer error: empty zval zero value in symbol: %v\n", ret.sym)
-			}
-			if ret.sym.go2py != "" {
-				g.gofile.Printf("return %s(%s)%s\n", ret.sym.go2py, ret.sym.zval, ret.sym.go2pyParenEx)
-			} else {
-				g.gofile.Printf("return %s\n", ret.sym.zval)
-			}
-		}
-		g.gofile.Outdent()
-		g.gofile.Printf("}\n")
-		if rvIsErr {
-			g.gofile.Printf("return C.CString(\"\")") // NOTE: leaked string
-		} else {
-			ret := res[0]
-			if ret.sym.go2py != "" {
-				if ret.sym.hasHandle() && !ret.sym.isPtrOrIface() {
-					g.gofile.Printf("return %s(&cret)%s", ret.sym.go2py, ret.sym.go2pyParenEx)
-				} else {
-					g.gofile.Printf("return %s(cret)%s", ret.sym.go2py, ret.sym.go2pyParenEx)
-				}
-			} else {
-				g.gofile.Printf("return cret")
-			}
-		}
-	} else if hasAddrOfTmp {
-		ret := res[0]
-		g.gofile.Printf("\nreturn %s(&cret)%s", ret.sym.go2py, ret.sym.go2pyParenEx)
-	}
 	g.gofile.Printf("\n")
 	g.gofile.Outdent()
 	g.gofile.Printf("}\n")
-
-	g.pywrap.Printf("\n")
-	g.pywrap.Outdent()
 }
diff --git a/bind/package.go b/bind/package.go
index 8a87df57..044e2e25 100644
--- a/bind/package.go
+++ b/bind/package.go
@@ -163,7 +163,7 @@ func (p *Package) getDoc(parent string, o types.Object) string {
 
 	case *types.Func:
 		sig := o.Type().(*types.Signature)
-		_, _, _, err := isPyCompatFunc(sig)
+		_, _, err := isPyCompatFunc(sig)
 		if err != nil {
 			return ""
 		}
@@ -241,11 +241,26 @@ func (p *Package) getDoc(parent string, o types.Object) string {
 		}
 
 		params := parseFn(sig.Params())
-		results := parseFn(sig.Results())
+
+		res := sig.Results()
+		results := parseFn(res)
+
+		if len(results) > 0 {
+			lastResult := res.At(len(results) - 1)
+			if isErrorType(lastResult.Type()) {
+				if !NoPyExceptions {
+					results = results[0 : len(results)-1]
+				}
+			}
+		}
 
 		paramString := strings.Join(params, ", ")
 		resultString := strings.Join(results, ", ")
 
+		if len(results) > 1 {
+			resultString = "(" + resultString + ")"
+		}
+
 		//FIXME(sbinet): add receiver for methods?
 		docSig := fmt.Sprintf("%s(%s) %s", o.Name(), paramString, resultString)
 
@@ -407,12 +422,12 @@ func (p *Package) process() error {
 				continue
 			}
 			ret := fct.Return()
-			if ret == nil {
+			if len(ret) == 0 || len(ret) > 1 {
 				continue
 			}
-			retptr, retIsPtr := ret.(*types.Pointer)
+			retptr, retIsPtr := ret[0].(*types.Pointer)
 
-			if ret == styp || (retIsPtr && retptr.Elem() == styp) {
+			if ret[0] == styp || (retIsPtr && retptr.Elem() == styp) {
 				delete(funcs, name)
 				fct.doc = p.getDoc(sname, scope.Lookup(name))
 				fct.ctor = true
diff --git a/bind/symbols.go b/bind/symbols.go
index a9249d26..20be4c6e 100644
--- a/bind/symbols.go
+++ b/bind/symbols.go
@@ -144,48 +144,50 @@ func isPyCompatField(f *types.Var) (*symbol, error) {
 	return ftyp, isPyCompatVar(ftyp)
 }
 
+func getPyReturnType(sig *types.Signature) (ret []types.Type, err error) {
+	results := sig.Results()
+
+	if results.Len() == 0 {
+		return make([]types.Type, 0, 0), nil
+	} else {
+		outCount := results.Len()
+		if !NoPyExceptions && isErrorType(results.At(outCount-1).Type()) {
+			outCount -= 1
+		}
+
+		retval := make([]types.Type, outCount, outCount)
+		for i := 0; i < outCount; i++ {
+			retval[i] = results.At(i).Type()
+		}
+
+		return retval, nil
+	}
+}
+
 // isPyCompatFunc checks if function signature is a python-compatible function.
 // Returns nil if function is compatible, err message if not.
 // Also returns the return type of the function
 // haserr is true if 2nd arg is an error type, which is only
 // supported form of multi-return-value functions
 // hasfun is true if one of the args is a function signature
-func isPyCompatFunc(sig *types.Signature) (ret types.Type, haserr, hasfun bool, err error) {
-	res := sig.Results()
-
-	switch res.Len() {
-	case 2:
-		if !isErrorType(res.At(1).Type()) {
-			err = fmt.Errorf("gopy: second result value must be of type error: %s", sig.String())
-			return
-		}
-		haserr = true
-		ret = res.At(0).Type()
-	case 1:
-		if isErrorType(res.At(0).Type()) {
-			haserr = true
-			ret = nil
+func isPyCompatFunc(sig *types.Signature) (haserr, hasfun bool, err error) {
+	results := sig.Results()
+
+	for i := 0; i < results.Len(); i++ {
+		result := results.At(i)
+		if i < results.Len()-1 {
+			if isErrorType(result.Type()) {
+				err = fmt.Errorf("gopy: Only last result value can be of type error: %s", sig.String())
+				return
+			}
 		} else {
-			ret = res.At(0).Type()
-		}
-	case 0:
-		ret = nil
-	default:
-		err = fmt.Errorf("gopy: too many results to return: %s", sig.String())
-		return
-	}
-
-	if ret != nil {
-		if err = isPyCompatType(ret); err != nil {
-			return
-		}
-		if _, isSig := ret.Underlying().(*types.Signature); isSig {
-			err = fmt.Errorf("gopy: return type is signature")
-			return
-		}
-		if ret.Underlying().String() == "interface{}" {
-			err = fmt.Errorf("gopy: return type is interface{}")
-			return
+			if isErrorType(result.Type()) {
+				haserr = true
+			}
+			if isFuncType(result.Type()) {
+				err = fmt.Errorf("gopy: Result values of type function are not supported: %s", sig.String())
+				return
+			}
 		}
 	}
 
@@ -554,7 +556,7 @@ func (sym *symtab) addSymbol(obj types.Object) error {
 
 	case *types.Func:
 		sig := obj.Type().Underlying().(*types.Signature)
-		_, _, _, err := isPyCompatFunc(sig)
+		_, _, err := isPyCompatFunc(sig)
 		if err == nil {
 			sym.syms[fn] = &symbol{
 				gopkg:   pkg,
@@ -1137,7 +1139,7 @@ func (sym *symtab) addSignatureType(pkg *types.Package, obj types.Object, t type
 
 func (sym *symtab) addMethod(pkg *types.Package, obj types.Object, t types.Type, kind symkind, id, n string) error {
 	sig := t.Underlying().(*types.Signature)
-	_, _, _, err := isPyCompatFunc(sig)
+	_, _, err := isPyCompatFunc(sig)
 	if err != nil {
 		if !NoWarn {
 			fmt.Printf("ignoring python incompatible method: %v.%v: %v: %v\n", pkg.Name(), obj.String(), t.String(), err)
diff --git a/bind/types.go b/bind/types.go
index 1de871e3..58a4e2ec 100644
--- a/bind/types.go
+++ b/bind/types.go
@@ -369,19 +369,24 @@ type Func struct {
 
 	id         string
 	doc        string
-	ret        types.Type // return type, if any
-	err        bool       // true if original go func has comma-error
-	ctor       bool       // true if this is a newXXX function
-	hasfun     bool       // true if this function has a function argument
-	isVariadic bool       // True, if this is a variadic function.
+	ret        []types.Type // return type, if any
+	haserr     bool         // true if original go func has comma-error
+	ctor       bool         // true if this is a newXXX function
+	hasfun     bool         // true if this function has a function argument
+	isVariadic bool         // True, if this is a variadic function.
 }
 
 func newFuncFrom(p *Package, parent string, obj types.Object, sig *types.Signature) (*Func, error) {
-	ret, haserr, hasfun, err := isPyCompatFunc(sig)
+	haserr, hasfun, err := isPyCompatFunc(sig)
 	if err != nil {
 		return nil, err
 	}
 
+	ret, err2 := getPyReturnType(sig)
+	if err2 != nil {
+		return nil, err2
+	}
+
 	id := obj.Pkg().Name() + "_" + obj.Name()
 	if parent != "" {
 		id = obj.Pkg().Name() + "_" + parent + "_" + obj.Name()
@@ -401,7 +406,7 @@ func newFuncFrom(p *Package, parent string, obj types.Object, sig *types.Signatu
 		id:         id,
 		doc:        p.getDoc(parent, obj),
 		ret:        ret,
-		err:        haserr,
+		haserr:     haserr,
 		hasfun:     hasfun,
 		isVariadic: sig.Variadic(),
 	}, nil
@@ -452,7 +457,7 @@ func (f *Func) Signature() *Signature {
 	return f.sig
 }
 
-func (f *Func) Return() types.Type {
+func (f *Func) Return() []types.Type {
 	return f.ret
 }
 
diff --git a/bind/utils.go b/bind/utils.go
index a78fe4f2..ab2f158b 100644
--- a/bind/utils.go
+++ b/bind/utils.go
@@ -24,6 +24,11 @@ func isErrorType(typ types.Type) bool {
 	return typ == types.Universe.Lookup("error").Type()
 }
 
+func isFuncType(typ types.Type) bool {
+	_, ok := typ.Underlying().(*types.Signature)
+	return ok
+}
+
 func isStringer(obj types.Object) bool {
 	switch obj := obj.(type) {
 	case *types.Func:
diff --git a/cmd_build.go b/cmd_build.go
index 43d3c393..da03d2c7 100644
--- a/cmd_build.go
+++ b/cmd_build.go
@@ -16,7 +16,7 @@ import (
 	"github.com/gonuts/commander"
 	"github.com/gonuts/flag"
 
-	"github.com/go-python/gopy/bind"
+	"github.com/rudderlabs/gopy/bind"
 )
 
 func gopyMakeCmdBuild() *commander.Command {
@@ -29,21 +29,18 @@ build generates and compiles (C)Python language bindings for Go package(s).
 
 ex:
  $ gopy build [options] <go-package-name> [other-go-package...]
- $ gopy build github.com/go-python/gopy/_examples/hi
+ $ gopy build github.com/rudderlabs/gopy/_examples/hi
 `,
 		Flag: *flag.NewFlagSet("gopy-build", flag.ExitOnError),
 	}
 
-	cmd.Flag.String("vm", "python", "path to python interpreter")
-	cmd.Flag.String("output", "", "output directory for bindings")
-	cmd.Flag.String("name", "", "name of output package (otherwise name of first package is used)")
-	cmd.Flag.String("main", "", "code string to run in the go main() function in the cgo library")
+	AddCommonCmdFlags(&cmd.Flag)
+
+	// Build Specific flags.
 	cmd.Flag.String("package-prefix", ".", "custom package prefix used when generating import "+
 		"statements for generated package")
-	cmd.Flag.Bool("rename", false, "rename Go symbols to python PEP snake_case")
 	cmd.Flag.Bool("symbols", true, "include symbols in output")
-	cmd.Flag.Bool("no-warn", false, "suppress warning messages, which may be expected")
-	cmd.Flag.Bool("no-make", false, "do not generate a Makefile, e.g., when called from Makefile")
+
 	return cmd
 }
 
@@ -54,19 +51,12 @@ func gopyRunCmdBuild(cmdr *commander.Command, args []string) error {
 		return err
 	}
 
-	cfg := NewBuildCfg()
-	cfg.OutputDir = cmdr.Flag.Lookup("output").Value.Get().(string)
-	cfg.Name = cmdr.Flag.Lookup("name").Value.Get().(string)
-	cfg.Main = cmdr.Flag.Lookup("main").Value.Get().(string)
-	cfg.VM = cmdr.Flag.Lookup("vm").Value.Get().(string)
-	cfg.PkgPrefix = cmdr.Flag.Lookup("package-prefix").Value.Get().(string)
-	cfg.RenameCase = cmdr.Flag.Lookup("rename").Value.Get().(bool)
+	cfg := NewBuildCfg(&cmdr.Flag)
 	cfg.Symbols = cmdr.Flag.Lookup("symbols").Value.Get().(bool)
-	cfg.NoWarn = cmdr.Flag.Lookup("no-warn").Value.Get().(bool)
-	cfg.NoMake = cmdr.Flag.Lookup("no-make").Value.Get().(bool)
 
 	bind.NoWarn = cfg.NoWarn
 	bind.NoMake = cfg.NoMake
+	bind.NoPyExceptions = cfg.NoPyExceptions
 
 	for _, path := range args {
 		bpkg, err := loadPackage(path, true) // build first
@@ -116,8 +106,6 @@ func runBuild(mode bind.BuildMode, cfg *BuildCfg) error {
 		return err
 	}
 
-	pycfg, err := bind.GetPythonConfig(cfg.VM)
-
 	if mode == bind.ModeExe {
 		of, err := os.Create(buildname + ".h") // overwrite existing
 		fmt.Fprintf(of, "typedef uint8_t bool;\n")
@@ -155,38 +143,13 @@ func runBuild(mode bind.BuildMode, cfg *BuildCfg) error {
 		}
 
 	} else {
-		buildLib := buildname + libExt
-		extext := libExt
-		if runtime.GOOS == "windows" {
-			extext = ".pyd"
-		}
-		if pycfg.ExtSuffix != "" {
-			extext = pycfg.ExtSuffix
-		}
-		modlib := "_" + cfg.Name + extext
-
-		// build the go shared library upfront to generate the header
-		// needed by our generated cpython code
-		args := []string{"build", "-mod=mod", "-buildmode=c-shared"}
-		if !cfg.Symbols {
-			// These flags will omit the various symbol tables, thereby
-			// reducing the final size of the binary. From https://golang.org/cmd/link/
-			// -s Omit the symbol table and debug information
-			// -w Omit the DWARF symbol table
-			args = append(args, "-ldflags=-s -w")
-		}
-		args = append(args, "-o", buildLib, ".")
-		fmt.Printf("go %v\n", strings.Join(args, " "))
-		cmd = exec.Command("go", args...)
-		cmdout, err = cmd.CombinedOutput()
+
+		// build extension with go + c
+		args, env, err := getBuildArgsAndEnv(cfg)
 		if err != nil {
-			fmt.Printf("cmd had error: %v  output:\n%v\n", err, string(cmdout))
+			fmt.Printf("error building environment: %v\n", err)
 			return err
 		}
-		// update the output name to the one with the ABI extension
-		args[len(args)-2] = modlib
-		// we don't need this initial lib because we are going to relink
-		os.Remove(buildLib)
 
 		// generate c code
 		fmt.Printf("%v build.py\n", cfg.VM)
@@ -207,48 +170,6 @@ func runBuild(mode bind.BuildMode, cfg *BuildCfg) error {
 			}
 		}
 
-		cflags := strings.Fields(strings.TrimSpace(pycfg.CFlags))
-		cflags = append(cflags, "-fPIC", "-Ofast")
-		if include, exists := os.LookupEnv("GOPY_INCLUDE"); exists {
-			cflags = append(cflags, "-I"+filepath.ToSlash(include))
-		}
-
-		ldflags := strings.Fields(strings.TrimSpace(pycfg.LdFlags))
-		if !cfg.Symbols {
-			ldflags = append(ldflags, "-s")
-		}
-		if lib, exists := os.LookupEnv("GOPY_LIBDIR"); exists {
-			ldflags = append(ldflags, "-L"+filepath.ToSlash(lib))
-		}
-		if libname, exists := os.LookupEnv("GOPY_PYLIB"); exists {
-			ldflags = append(ldflags, "-l"+filepath.ToSlash(libname))
-		}
-
-		removeEmpty := func(src []string) []string {
-			o := make([]string, 0, len(src))
-			for _, v := range src {
-				if v == "" {
-					continue
-				}
-				o = append(o, v)
-			}
-			return o
-		}
-
-		cflags = removeEmpty(cflags)
-		ldflags = removeEmpty(ldflags)
-
-		cflagsEnv := fmt.Sprintf("CGO_CFLAGS=%s", strings.Join(cflags, " "))
-		ldflagsEnv := fmt.Sprintf("CGO_LDFLAGS=%s", strings.Join(ldflags, " "))
-
-		env := os.Environ()
-		env = append(env, cflagsEnv)
-		env = append(env, ldflagsEnv)
-
-		fmt.Println(cflagsEnv)
-		fmt.Println(ldflagsEnv)
-
-		// build extension with go + c
 		fmt.Printf("go %v\n", strings.Join(args, " "))
 		cmd = exec.Command("go", args...)
 		cmd.Env = env
@@ -261,3 +182,89 @@ func runBuild(mode bind.BuildMode, cfg *BuildCfg) error {
 
 	return err
 }
+
+func getBuildArgsAndEnv(cfg *BuildCfg) (args []string, env []string, err error) {
+	buildname := cfg.Name + "_go"
+	buildLib := buildname + libExt
+
+	var pycfg bind.PyConfig
+	pycfg, err = bind.GetPythonConfig(cfg.VM)
+	if err != nil {
+		fmt.Printf("error creating pycfg: %r\n", err)
+		return args, env, err
+	}
+
+	extext := libExt
+	if runtime.GOOS == "windows" {
+		extext = ".pyd"
+	}
+	if pycfg.ExtSuffix != "" {
+		extext = pycfg.ExtSuffix
+	}
+	modlib := "_" + cfg.Name + extext
+
+	// build the go shared library upfront to generate the header
+	// needed by our generated cpython code
+	args = []string{"build", "-mod=mod", "-buildmode=c-shared"}
+	if !cfg.Symbols {
+		// These flags will omit the various symbol tables, thereby
+		// reducing the final size of the binary. From https://golang.org/cmd/link/
+		// -s Omit the symbol table and debug information
+		// -w Omit the DWARF symbol table
+		args = append(args, "-ldflags=-s -w")
+	}
+	args = append(args, "-o", buildLib, ".")
+	fmt.Printf("go %v\n", strings.Join(args, " "))
+	cmd := exec.Command("go", args...)
+	cmdout, err := cmd.CombinedOutput()
+	if err != nil {
+		fmt.Printf("cmd had error: %v  output:\n%v\n", err, string(cmdout))
+		return args, env, err
+	}
+	// update the output name to the one with the ABI extension
+	args[len(args)-2] = modlib
+	// we don't need this initial lib because we are going to relink
+	os.Remove(buildLib)
+
+	cflags := strings.Fields(strings.TrimSpace(pycfg.CFlags))
+	cflags = append(cflags, "-fPIC", "-Ofast")
+	if include, exists := os.LookupEnv("GOPY_INCLUDE"); exists {
+		cflags = append(cflags, "-I"+filepath.ToSlash(include))
+	}
+
+	ldflags := strings.Fields(strings.TrimSpace(pycfg.LdFlags))
+	if !cfg.Symbols {
+		ldflags = append(ldflags, "-s")
+	}
+	if lib, exists := os.LookupEnv("GOPY_LIBDIR"); exists {
+		ldflags = append(ldflags, "-L"+filepath.ToSlash(lib))
+	}
+	if libname, exists := os.LookupEnv("GOPY_PYLIB"); exists {
+		ldflags = append(ldflags, "-l"+filepath.ToSlash(libname))
+	}
+
+	removeEmpty := func(src []string) []string {
+		o := make([]string, 0, len(src))
+		for _, v := range src {
+			if v == "" {
+				continue
+			}
+			o = append(o, v)
+		}
+		return o
+	}
+
+	cflags = removeEmpty(cflags)
+	ldflags = removeEmpty(ldflags)
+
+	cflagsEnv := fmt.Sprintf("CGO_CFLAGS=%s", strings.Join(cflags, " "))
+	ldflagsEnv := fmt.Sprintf("CGO_LDFLAGS=%s", strings.Join(ldflags, " "))
+
+	env = os.Environ()
+	env = append(env, cflagsEnv)
+	env = append(env, ldflagsEnv)
+
+	fmt.Println(cflagsEnv)
+	fmt.Println(ldflagsEnv)
+	return args, env, err
+}
diff --git a/cmd_exe.go b/cmd_exe.go
index 22b43665..1f0dd6d2 100644
--- a/cmd_exe.go
+++ b/cmd_exe.go
@@ -11,9 +11,9 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/go-python/gopy/bind"
 	"github.com/gonuts/commander"
 	"github.com/gonuts/flag"
+	"github.com/rudderlabs/gopy/bind"
 )
 
 // python packaging links:
@@ -35,19 +35,16 @@ When including multiple packages, list in order of increasing dependency, and us
 
 ex:
  $ gopy exe [options] <go-package-name> [other-go-package...]
- $ gopy exe github.com/go-python/gopy/_examples/hi
+ $ gopy exe github.com/rudderlabs/gopy/_examples/hi
 `,
 		Flag: *flag.NewFlagSet("gopy-exe", flag.ExitOnError),
 	}
 
-	cmd.Flag.String("vm", "python", "path to python interpreter")
-	cmd.Flag.String("output", "", "output directory for root of package")
-	cmd.Flag.String("name", "", "name of output package (otherwise name of first package is used)")
-	cmd.Flag.String("main", "", "code string to run in the go main() function in the cgo library "+
-		"-- defaults to GoPyMainRun() but typically should be overriden")
+	AddCommonCmdFlags(&cmd.Flag)
+
+	// Exe specific flags.
 	// cmd.Flag.String("package-prefix", ".", "custom package prefix used when generating import "+
 	// 	"statements for generated package")
-	cmd.Flag.Bool("rename", false, "rename Go symbols to python PEP snake_case")
 	cmd.Flag.Bool("symbols", true, "include symbols in output")
 	cmd.Flag.String("exclude", "", "comma-separated list of package names to exclude")
 	cmd.Flag.String("user", "", "username on https://www.pypa.io/en/latest/ for package name suffix")
@@ -55,9 +52,7 @@ ex:
 	cmd.Flag.String("author", "gopy", "author name")
 	cmd.Flag.String("email", "gopy@example.com", "author email")
 	cmd.Flag.String("desc", "", "short description of project (long comes from README.md)")
-	cmd.Flag.String("url", "https://github.com/go-python/gopy", "home page for project")
-	cmd.Flag.Bool("no-warn", false, "suppress warning messages, which may be expected")
-	cmd.Flag.Bool("no-make", false, "do not generate a Makefile, e.g., when called from Makefile")
+	cmd.Flag.String("url", "https://github.com/rudderlabs/gopy", "home page for project")
 
 	return cmd
 }
@@ -69,17 +64,8 @@ func gopyRunCmdExe(cmdr *commander.Command, args []string) error {
 		return err
 	}
 
-	cfg := NewBuildCfg()
-	cfg.OutputDir = cmdr.Flag.Lookup("output").Value.Get().(string)
-	cfg.Name = cmdr.Flag.Lookup("name").Value.Get().(string)
-	cfg.Main = cmdr.Flag.Lookup("main").Value.Get().(string)
-	cfg.VM = cmdr.Flag.Lookup("vm").Value.Get().(string)
-	// cfg.PkgPrefix = cmdr.Flag.Lookup("package-prefix").Value.Get().(string)
-	cfg.PkgPrefix = "" // doesn't make sense for exe
-	cfg.RenameCase = cmdr.Flag.Lookup("rename").Value.Get().(bool)
+	cfg := NewBuildCfg(&cmdr.Flag)
 	cfg.Symbols = cmdr.Flag.Lookup("symbols").Value.Get().(bool)
-	cfg.NoWarn = cmdr.Flag.Lookup("no-warn").Value.Get().(bool)
-	cfg.NoMake = cmdr.Flag.Lookup("no-make").Value.Get().(bool)
 
 	var (
 		exclude = cmdr.Flag.Lookup("exclude").Value.Get().(string)
@@ -93,6 +79,7 @@ func gopyRunCmdExe(cmdr *commander.Command, args []string) error {
 
 	bind.NoWarn = cfg.NoWarn
 	bind.NoMake = cfg.NoMake
+	bind.NoPyExceptions = cfg.NoPyExceptions
 
 	if cfg.Name == "" {
 		path := args[0]
diff --git a/cmd_gen.go b/cmd_gen.go
index 97a62384..660cc0cd 100644
--- a/cmd_gen.go
+++ b/cmd_gen.go
@@ -8,9 +8,9 @@ import (
 	"fmt"
 	"log"
 
-	"github.com/go-python/gopy/bind"
 	"github.com/gonuts/commander"
 	"github.com/gonuts/flag"
+	"github.com/rudderlabs/gopy/bind"
 )
 
 func gopyMakeCmdGen() *commander.Command {
@@ -23,20 +23,17 @@ gen generates (C)Python language bindings for Go package(s).
 
 ex:
  $ gopy gen [options] <go-package-name> [other-go-package...]
- $ gopy gen github.com/go-python/gopy/_examples/hi
+ $ gopy gen github.com/rudderlabs/gopy/_examples/hi
 `,
 		Flag: *flag.NewFlagSet("gopy-gen", flag.ExitOnError),
 	}
 
-	cmd.Flag.String("vm", "python", "path to python interpreter")
-	cmd.Flag.String("output", "", "output directory for bindings")
-	cmd.Flag.String("name", "", "name of output package (otherwise name of first package is used)")
-	cmd.Flag.String("main", "", "code string to run in the go main() function in the cgo library")
+	AddCommonCmdFlags(&cmd.Flag)
+
+	// Gen specific flags.
 	cmd.Flag.String("package-prefix", ".", "custom package prefix used when generating import "+
 		"statements for generated package")
-	cmd.Flag.Bool("rename", false, "rename Go symbols to python PEP snake_case")
-	cmd.Flag.Bool("no-warn", false, "suppress warning messages, which may be expected")
-	cmd.Flag.Bool("no-make", false, "do not generate a Makefile, e.g., when called from Makefile")
+
 	return cmd
 }
 
@@ -49,19 +46,7 @@ func gopyRunCmdGen(cmdr *commander.Command, args []string) error {
 		return err
 	}
 
-	cfg := NewBuildCfg()
-	cfg.OutputDir = cmdr.Flag.Lookup("output").Value.Get().(string)
-	cfg.VM = cmdr.Flag.Lookup("vm").Value.Get().(string)
-	cfg.Name = cmdr.Flag.Lookup("name").Value.Get().(string)
-	cfg.Main = cmdr.Flag.Lookup("main").Value.Get().(string)
-	cfg.PkgPrefix = cmdr.Flag.Lookup("package-prefix").Value.Get().(string)
-	cfg.RenameCase = cmdr.Flag.Lookup("rename").Value.Get().(bool)
-	cfg.NoWarn = cmdr.Flag.Lookup("no-warn").Value.Get().(bool)
-	cfg.NoMake = cmdr.Flag.Lookup("no-make").Value.Get().(bool)
-
-	if cfg.VM == "" {
-		cfg.VM = "python"
-	}
+	cfg := NewBuildCfg(&cmdr.Flag)
 
 	bind.NoWarn = cfg.NoWarn
 	bind.NoMake = cfg.NoMake
diff --git a/cmd_pkg.go b/cmd_pkg.go
index ce0f437f..ba913305 100644
--- a/cmd_pkg.go
+++ b/cmd_pkg.go
@@ -11,9 +11,9 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/go-python/gopy/bind"
 	"github.com/gonuts/commander"
 	"github.com/gonuts/flag"
+	"github.com/rudderlabs/gopy/bind"
 )
 
 // python packaging links:
@@ -33,18 +33,16 @@ When including multiple packages, list in order of increasing dependency, and us
 
 ex:
  $ gopy pkg [options] <go-package-name> [other-go-package...]
- $ gopy pkg github.com/go-python/gopy/_examples/hi
+ $ gopy pkg github.com/rudderlabs/gopy/_examples/hi
 `,
 		Flag: *flag.NewFlagSet("gopy-pkg", flag.ExitOnError),
 	}
 
-	cmd.Flag.String("vm", "python", "path to python interpreter")
-	cmd.Flag.String("output", "", "output directory for root of package")
-	cmd.Flag.String("name", "", "name of output package (otherwise name of first package is used)")
-	cmd.Flag.String("main", "", "code string to run in the go GoPyInit() function in the cgo library")
+	AddCommonCmdFlags(&cmd.Flag)
+
+	// Pkg specific flags.
 	cmd.Flag.String("package-prefix", ".", "custom package prefix used when generating import "+
 		"statements for generated package")
-	cmd.Flag.Bool("rename", false, "rename Go symbols to python PEP snake_case")
 	cmd.Flag.Bool("symbols", true, "include symbols in output")
 	cmd.Flag.String("exclude", "", "comma-separated list of package names to exclude")
 	cmd.Flag.String("user", "", "username on https://www.pypa.io/en/latest/ for package name suffix")
@@ -52,9 +50,7 @@ ex:
 	cmd.Flag.String("author", "gopy", "author name")
 	cmd.Flag.String("email", "gopy@example.com", "author email")
 	cmd.Flag.String("desc", "", "short description of project (long comes from README.md)")
-	cmd.Flag.String("url", "https://github.com/go-python/gopy", "home page for project")
-	cmd.Flag.Bool("no-warn", false, "suppress warning messages, which may be expected")
-	cmd.Flag.Bool("no-make", false, "do not generate a Makefile, e.g., when called from Makefile")
+	cmd.Flag.String("url", "https://github.com/rudderlabs/gopy", "home page for project")
 
 	return cmd
 }
@@ -66,16 +62,8 @@ func gopyRunCmdPkg(cmdr *commander.Command, args []string) error {
 		return err
 	}
 
-	cfg := NewBuildCfg()
-	cfg.OutputDir = cmdr.Flag.Lookup("output").Value.Get().(string)
-	cfg.Name = cmdr.Flag.Lookup("name").Value.Get().(string)
-	cfg.Main = cmdr.Flag.Lookup("main").Value.Get().(string)
-	cfg.VM = cmdr.Flag.Lookup("vm").Value.Get().(string)
-	cfg.PkgPrefix = cmdr.Flag.Lookup("package-prefix").Value.Get().(string)
-	cfg.RenameCase = cmdr.Flag.Lookup("rename").Value.Get().(bool)
+	cfg := NewBuildCfg(&cmdr.Flag)
 	cfg.Symbols = cmdr.Flag.Lookup("symbols").Value.Get().(bool)
-	cfg.NoWarn = cmdr.Flag.Lookup("no-warn").Value.Get().(bool)
-	cfg.NoMake = cmdr.Flag.Lookup("no-make").Value.Get().(bool)
 
 	var (
 		exclude = cmdr.Flag.Lookup("exclude").Value.Get().(string)
@@ -89,6 +77,7 @@ func gopyRunCmdPkg(cmdr *commander.Command, args []string) error {
 
 	bind.NoWarn = cfg.NoWarn
 	bind.NoMake = cfg.NoMake
+	bind.NoPyExceptions = cfg.NoPyExceptions
 
 	if cfg.Name == "" {
 		path := args[0]
diff --git a/gen.go b/gen.go
index 78e6235b..88cd9ca9 100644
--- a/gen.go
+++ b/gen.go
@@ -19,7 +19,7 @@ import (
 	"github.com/pkg/errors"
 	"golang.org/x/tools/go/packages"
 
-	"github.com/go-python/gopy/bind"
+	"github.com/rudderlabs/gopy/bind"
 )
 
 // argStr returns the full command args as a string, without path to exe
diff --git a/go.mod b/go.mod
index 72ad4872..ebbff956 100644
--- a/go.mod
+++ b/go.mod
@@ -1,4 +1,4 @@
-module github.com/go-python/gopy
+module github.com/rudderlabs/gopy
 
 go 1.15
 
diff --git a/goerr2pyex/error_translator.go b/goerr2pyex/error_translator.go
new file mode 100644
index 00000000..11744089
--- /dev/null
+++ b/goerr2pyex/error_translator.go
@@ -0,0 +1,18 @@
+package goerr2pyex
+
+//#include <Python.h>
+import "C"
+import (
+	"unsafe"
+)
+
+func DefaultGoErrorToPyException(err error) bool {
+	if err != nil {
+		estr := C.CString(err.Error())
+		C.PyErr_SetString(C.PyExc_RuntimeError, estr)
+		C.free(unsafe.Pointer(estr))
+		return true
+	} else {
+		return false
+	}
+}
diff --git a/goerr2pyex/test.sh b/goerr2pyex/test.sh
new file mode 100644
index 00000000..d9e6c438
--- /dev/null
+++ b/goerr2pyex/test.sh
@@ -0,0 +1,9 @@
+# The script to test build.
+#env CGO_ENABLED=1 CGO_CFLAGS="-I/opt/homebrew/opt/python@3.9/Frameworks/Python.framework/Versions/3.9/include/python3.9 -Wno-error -Wno-implicit-function-declaration -Wno-int-conversion" CGO_LDFLAGS="-L/opt/homebrew/opt/python@3.9/Frameworks/Python.framework/Versions/3.9/lib -lpython3.9 -ldl -framework CoreFoundation" go build -mod=mod -buildmode=c-shared -o ./error_translator.so error_translator.go
+#env CGO_ENABLED=1 CGO_CFLAGS="-I/opt/homebrew/opt/python@3.9/Frameworks/Python.framework/Versions/3.9/include/python3.9 -Wno-error -Wno-implicit-function-declaration -Wno-int-conversion" CGO_LDFLAGS="-L/opt/homebrew/opt/python@3.9/Frameworks/Python.framework/Versions/3.9/lib -lpython3.9 -ldl -framework CoreFoundation" go build -mod=mod -o ./error_translator.so error_translator.go
+#env CGO_ENABLED=1 CGO_CFLAGS="-I/opt/homebrew/opt/python@3.9/Frameworks/Python.framework/Versions/3.9/include/python3.9 -Wno-error -Wno-implicit-function-declaration -Wno-int-conversion" CGO_LDFLAGS="-L/opt/homebrew/opt/python@3.9/Frameworks/Python.framework/Versions/3.9/lib -lpython3.9 -ldl -framework CoreFoundation" go build -o ./error_translator.so error_translator.go
+#env CGO_ENABLED=1 CGO_CFLAGS="-I/opt/homebrew/opt/python@3.9/Frameworks/Python.framework/Versions/3.9/include/python3.9 -Wno-error -Wno-implicit-function-declaration -Wno-int-conversion" CGO_LDFLAGS="-L/opt/homebrew/opt/python@3.9/Frameworks/Python.framework/Versions/3.9/lib -lpython3.9 -ldl -framework CoreFoundation" go build error_translator.go
+
+
+env CGO_ENABLED=1 CGO_CFLAGS="-I/opt/homebrew/opt/python@3.9/Frameworks/Python.framework/Versions/3.9/include/python3.9 -Wno-error -Wno-implicit-function-declaration -Wno-int-conversion" CGO_LDFLAGS="-L/opt/homebrew/opt/python@3.9/Frameworks/Python.framework/Versions/3.9/lib -lpython3.9 -ldl -framework CoreFoundation" go build
+
diff --git a/main.go b/main.go
index a8d7dfdb..6c722bd2 100644
--- a/main.go
+++ b/main.go
@@ -15,7 +15,7 @@ import (
 	"github.com/gonuts/flag"
 	"github.com/pkg/errors"
 
-	"github.com/go-python/gopy/bind"
+	"github.com/rudderlabs/gopy/bind"
 )
 
 // BuildCfg contains command options and binding generation options
@@ -24,19 +24,58 @@ type BuildCfg struct {
 
 	// include symbols in output
 	Symbols bool
+
 	// suppress warning messages, which may be expected
 	NoWarn bool
+
 	// do not generate a Makefile, e.g., when called from Makefile
 	NoMake bool
 }
 
 // NewBuildCfg returns a newly constructed build config
-func NewBuildCfg() *BuildCfg {
+func NewBuildCfg(flagSet *flag.FlagSet) *BuildCfg {
 	var cfg BuildCfg
 	cfg.Cmd = argStr()
+
+	cfg.OutputDir = flagSet.Lookup("output").Value.Get().(string)
+	cfg.Name = flagSet.Lookup("name").Value.Get().(string)
+	cfg.Main = flagSet.Lookup("main").Value.Get().(string)
+	cfg.VM = flagSet.Lookup("vm").Value.Get().(string)
+	cfg.RenameCase = flagSet.Lookup("rename").Value.Get().(bool)
+	cfg.NoWarn = flagSet.Lookup("no-warn").Value.Get().(bool)
+	cfg.NoMake = flagSet.Lookup("no-make").Value.Get().(bool)
+	cfg.PkgPrefix = flagSet.Lookup("package-prefix").Value.Get().(string)
+	cfg.NoPyExceptions = flagSet.Lookup("no-exceptions").Value.Get().(bool)
+	cfg.ModPathGoErr2PyEx = flagSet.Lookup("gomod-2pyex").Value.Get().(string)
+	cfg.UsePyTuple4VE = flagSet.Lookup("use-pytuple-4ve").Value.Get().(bool)
+
+	if cfg.ModPathGoErr2PyEx == "" {
+		cfg.ModPathGoErr2PyEx = "github.com/rudderlabs/gopy/goerr2pyex/"
+	}
+
+	if cfg.VM == "" {
+		cfg.VM = "python"
+	}
+
 	return &cfg
 }
 
+func AddCommonCmdFlags(flagSet *flag.FlagSet) {
+	flagSet.String("vm", "python", "path to python interpreter")
+	flagSet.String("output", "", "output directory for root of package")
+	flagSet.String("name", "", "name of output package (otherwise name of first package is used)")
+	flagSet.String("main", "", "code string to run in the go main()/GoPyInit() function(s) in the cgo library "+
+		"-- defaults to GoPyMainRun() but typically should be overriden")
+
+	flagSet.Bool("rename", false, "rename Go symbols to python PEP snake_case")
+	flagSet.Bool("no-warn", false, "suppress warning messages, which may be expected")
+	flagSet.Bool("no-make", false, "do not generate a Makefile, e.g., when called from Makefile")
+
+	flagSet.Bool("no-exceptions", false, "Don't throw python exceptions when the last return is in error.")
+	flagSet.Bool("use-pytuple-4ve", false, "When Go returns (value, err) with err=nil, return (value, ) tuple to python.")
+	flagSet.String("gomod-2pyex", "", "Go module to use to translate Go errors to Python exceptions.")
+}
+
 func run(args []string) error {
 	app := &commander.Command{
 		UsageLine: "gopy",
diff --git a/main_darwin.go b/main_darwin.go
index 8697476a..5edc9205 100644
--- a/main_darwin.go
+++ b/main_darwin.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build darwin
 // +build darwin
 
 package main
diff --git a/main_test.go b/main_test.go
index 12a01930..0a3a1a5e 100644
--- a/main_test.go
+++ b/main_test.go
@@ -17,7 +17,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/go-python/gopy/bind"
+	"github.com/rudderlabs/gopy/bind"
 )
 
 var (
@@ -49,6 +49,7 @@ var (
 		"_examples/cstrings":    []string{"py2", "py3"},
 		"_examples/pkgconflict": []string{"py2", "py3"},
 		"_examples/variadic":    []string{"py3"},
+		"_examples/multireturn": []string{"py3"},
 	}
 
 	testEnvironment = os.Environ()
@@ -60,11 +61,20 @@ func init() {
 }
 
 func TestGovet(t *testing.T) {
+	buildCmd := gopyMakeCmdBuild()
+	buildCfg := NewBuildCfg(&buildCmd.Flag)
+	buildCfg.VM = testBackends["py3"]
+	buildArgs, buildEnv, err := getBuildArgsAndEnv(buildCfg)
+	if err != nil {
+		t.Fatalf("error building env:%v.\n Args:%v.\n Env:%v\n", err, buildArgs, buildEnv)
+	}
+
 	cmd := exec.Command("go", "vet", "./...")
 	buf := new(bytes.Buffer)
 	cmd.Stdout = buf
 	cmd.Stderr = buf
-	err := cmd.Run()
+	cmd.Env = buildEnv
+	err = cmd.Run()
 	if err != nil {
 		t.Fatalf("error running %s:\n%s\n%v", "go vet", string(buf.Bytes()), err)
 	}
@@ -99,34 +109,6 @@ func TestGofmt(t *testing.T) {
 	}
 }
 
-func TestGoPyErrors(t *testing.T) {
-	pyvm := testBackends["py3"]
-	workdir, err := ioutil.TempDir("", "gopy-")
-	if err != nil {
-		t.Fatalf("could not create workdir: %v\n", err)
-	}
-	t.Logf("pyvm: %s making work dir: %s\n", pyvm, workdir)
-	defer os.RemoveAll(workdir)
-
-	curPkgPath := reflect.TypeOf(pkg{}).PkgPath()
-	fpath := filepath.Join(curPkgPath, "_examples/gopyerrors")
-	cmd := exec.Command("go", "run", ".", "gen", "-vm="+pyvm, "-output="+workdir, fpath)
-	t.Logf("running: %v\n", cmd.Args)
-	out, err := cmd.CombinedOutput()
-	if err != nil {
-		t.Fatalf("could not run %v: %+v\n", strings.Join(cmd.Args, " "), err)
-	}
-	contains := `--- Processing package: github.com/go-python/gopy/_examples/gopyerrors ---
-ignoring python incompatible function: .func github.com/go-python/gopy/_examples/gopyerrors.NotErrorMany() (int, int): func() (int, int): gopy: second result value must be of type error: func() (int, int)
-ignoring python incompatible method: gopyerrors.func (*github.com/go-python/gopy/_examples/gopyerrors.Struct).NotErrorMany() (int, string): func() (int, string): gopy: second result value must be of type error: func() (int, string)
-ignoring python incompatible method: gopyerrors.func (*github.com/go-python/gopy/_examples/gopyerrors.Struct).TooMany() (int, int, string): func() (int, int, string): gopy: too many results to return: func() (int, int, string)
-ignoring python incompatible function: .func github.com/go-python/gopy/_examples/gopyerrors.TooMany() (int, int, string): func() (int, int, string): gopy: too many results to return: func() (int, int, string)
-`
-	if got, want := string(out), contains; !strings.Contains(got, want) {
-		t.Fatalf("%v does not contain\n%v\n", got, want)
-	}
-}
-
 func TestHi(t *testing.T) {
 	// t.Parallel()
 	path := "_examples/hi"
@@ -829,6 +811,34 @@ Type OK
 	})
 }
 
+func TestBindMultiReturn(t *testing.T) {
+	// t.Parallel()
+	path := "_examples/multireturn"
+	testPkg(t, pkg{
+		path:   path,
+		lang:   features[path],
+		cmd:    "build",
+		extras: nil,
+		want: []byte(`No Return None
+Single WithoutError Return 100
+Single Str WithoutError Return '150'
+Single WithError(False) Return None
+Single WithError(True). Exception: RuntimeError('Error')
+Double WithoutError(Without String) Return (200, 300)
+Double WithoutError(With String) Return ('200', '300')
+Double WithError(True). Exception: RuntimeError('Error')
+Double WithError(False) Return '500'
+Triple WithoutError(Without String) Return (600, 700, 800)
+Triple WithoutError(With String) Return (600, '700', 800)
+Triple WithError(True) Exception: RuntimeError('Error')
+Triple WithError(False) Return (1100, 1200)
+Triple WithError(True) Exception: RuntimeError('Error')
+Triple WithError(False) Return (1500, 1600)
+Triple WithoutError() Return (1700, 1800, 1900)
+`),
+	})
+}
+
 // Generate / verify SUPPORT_MATRIX.md from features map.
 func TestCheckSupportMatrix(t *testing.T) {
 	var buf bytes.Buffer
@@ -954,8 +964,8 @@ func writeGoMod(t *testing.T, pkgDir, tstDir string) {
 	template := `
 module dummy
 
-require github.com/go-python/gopy v0.0.0
-replace github.com/go-python/gopy => %s
+require github.com/rudderlabs/gopy v0.0.0
+replace github.com/rudderlabs/gopy => %s
 `
 	contents := fmt.Sprintf(template, pkgDir)
 	if err := ioutil.WriteFile(filepath.Join(tstDir, "go.mod"), []byte(contents), 0666); err != nil {
diff --git a/main_unix.go b/main_unix.go
index fcd18713..1acf66f9 100644
--- a/main_unix.go
+++ b/main_unix.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build (linux && !android) || dragonfly || openbsd
 // +build linux,!android dragonfly openbsd
 
 package main
diff --git a/main_unix_test.go b/main_unix_test.go
index 8d0733a6..0d709f74 100644
--- a/main_unix_test.go
+++ b/main_unix_test.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !windows
 // +build !windows
 
 package main
diff --git a/main_windows.go b/main_windows.go
index 9800b099..4eaf4222 100644
--- a/main_windows.go
+++ b/main_windows.go
@@ -7,7 +7,7 @@
 
 package main
 
-import "github.com/go-python/gopy/bind"
+import "github.com/rudderlabs/gopy/bind"
 
 const (
 	libExt       = ".pyd"
diff --git a/pkgsetup.go b/pkgsetup.go
index dbc4ad11..54b74189 100644
--- a/pkgsetup.go
+++ b/pkgsetup.go
@@ -9,7 +9,7 @@ import (
 	"os"
 	"path/filepath"
 
-	"github.com/go-python/gopy/bind"
+	"github.com/rudderlabs/gopy/bind"
 )
 
 // 1 = pkg name, 2 = -user, 3 = version 4 = author, 5 = email, 6 = desc, 7 = url