Skip to content

Commit 27b8b5f

Browse files
authored
Merge pull request #13 from cpunion/tests
Tests
2 parents d2a750b + 7c230ec commit 27b8b5f

File tree

8 files changed

+135
-113
lines changed

8 files changed

+135
-113
lines changed

dict.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ func newDict(obj *PyObject) Dict {
1818
}
1919

2020
func DictFromPairs(pairs ...any) Dict {
21-
if len(pairs)%2 != 0 {
22-
panic("DictFromPairs requires an even number of arguments")
23-
}
21+
check(len(pairs)%2 == 0, "DictFromPairs requires an even number of arguments")
2422
dict := newDict(C.PyDict_New())
2523
for i := 0; i < len(pairs); i += 2 {
2624
key := From(pairs[i])
@@ -62,9 +60,7 @@ func (d Dict) SetString(key string, value Objecter) {
6260
ckey := AllocCStr(key)
6361
r := C.PyDict_SetItemString(d.obj, ckey, valueObj)
6462
C.free(unsafe.Pointer(ckey))
65-
if r != 0 {
66-
panic(fmt.Errorf("failed to set item string: %v", r))
67-
}
63+
check(r == 0, fmt.Sprintf("failed to set item string: %v", r))
6864
}
6965

7066
func (d Dict) GetString(key string) Object {
@@ -81,9 +77,7 @@ func (d Dict) Del(key Objecter) {
8177

8278
func (d Dict) ForEach(fn func(key, value Object)) {
8379
items := C.PyDict_Items(d.obj)
84-
if items == nil {
85-
panic(fmt.Errorf("failed to get items of dict"))
86-
}
80+
check(items != nil, "failed to get items of dict")
8781
defer C.Py_DecRef(items)
8882
iter := C.PyObject_GetIter(items)
8983
for {

function.go

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ func (f Func) Call(args ...any) Object {
7474
for i, arg := range args {
7575
obj := From(arg).Obj()
7676
C.Py_IncRef(obj)
77-
C.PyTuple_SetItem(argsTuple, C.Py_ssize_t(i), obj)
77+
r := C.PyTuple_SetItem(argsTuple, C.Py_ssize_t(i), obj)
78+
check(r == 0, fmt.Sprintf("failed to set item %d in tuple", i))
7879
}
7980
return newObject(C.PyObject_CallObject(f.obj, argsTuple))
8081
}
@@ -471,9 +472,7 @@ func getMembers(t reflect.Type, methods map[uint]*slotMeta) (members *C.PyMember
471472
func AddType[T any](m Module, init any, name string, doc string) Object {
472473
wrapper := wrapperType[T]{}
473474
ty := reflect.TypeOf(wrapper.v)
474-
if ty.Kind() != reflect.Struct {
475-
panic("AddType: t must be a struct")
476-
}
475+
check(ty.Kind() == reflect.Struct, "AddType: t must be a struct")
477476

478477
meta := &typeMeta{
479478
typ: ty,
@@ -517,27 +516,21 @@ func AddType[T any](m Module, init any, name string, doc string) Object {
517516
}
518517

519518
typeObj := C.PyType_FromSpec(spec)
520-
if typeObj == nil {
521-
panic(fmt.Sprintf("Failed to create type %s", name))
522-
}
519+
check(typeObj != nil, fmt.Sprintf("Failed to create type %s", name))
523520

524521
typeMetaMap[typeObj] = meta
525522
pyTypeMap[ty] = typeObj
526523

527-
if C.PyModule_AddObject(m.obj, C.CString(name), typeObj) < 0 {
528-
C.Py_DecRef(typeObj)
529-
panic(fmt.Sprintf("Failed to add type %s to module", name))
530-
}
524+
r := C.PyModule_AddObjectRef(m.obj, C.CString(name), typeObj)
525+
check(r == 0, fmt.Sprintf("Failed to add type %s to module", name))
531526

532527
return newObject(typeObj)
533528
}
534529

535530
func (m Module) AddMethod(name string, fn any, doc string) Func {
536531
v := reflect.ValueOf(fn)
537532
t := v.Type()
538-
if t.Kind() != reflect.Func {
539-
panic("AddFunction: fn must be a function")
540-
}
533+
check(t.Kind() == reflect.Func, "AddFunction: fn must be a function")
541534

542535
if name == "" {
543536
name = runtime.FuncForPC(v.Pointer()).Name()
@@ -583,14 +576,10 @@ func (m Module) AddMethod(name string, fn any, doc string) Func {
583576
}
584577

585578
pyFunc := C.PyCFunction_New(def, m.obj)
586-
if pyFunc == nil {
587-
panic(fmt.Sprintf("Failed to create function %s", name))
588-
}
579+
check(pyFunc != nil, fmt.Sprintf("Failed to create function %s", name))
589580

590-
if C.PyModule_AddObject(m.obj, cName, pyFunc) < 0 {
591-
C.Py_DecRef(pyFunc)
592-
panic(fmt.Sprintf("Failed to add function %s to module", name))
593-
}
581+
r := C.PyModule_AddObjectRef(m.obj, cName, pyFunc)
582+
check(r == 0, fmt.Sprintf("Failed to add function %s to module", name))
594583

595584
return newFunc(pyFunc)
596585
}

list.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package gp
44
#include <Python.h>
55
*/
66
import "C"
7+
import "fmt"
78

89
type List struct {
910
Object
@@ -31,13 +32,15 @@ func (l List) GetItem(index int) Object {
3132
func (l List) SetItem(index int, item Objecter) {
3233
itemObj := item.Obj()
3334
C.Py_IncRef(itemObj)
34-
C.PyList_SetItem(l.obj, C.Py_ssize_t(index), itemObj)
35+
r := C.PyList_SetItem(l.obj, C.Py_ssize_t(index), itemObj)
36+
check(r == 0, fmt.Sprintf("failed to set item %d in list", index))
3537
}
3638

3739
func (l List) Len() int {
3840
return int(C.PyList_Size(l.obj))
3941
}
4042

4143
func (l List) Append(obj Objecter) {
42-
C.PyList_Append(l.obj, obj.Obj())
44+
r := C.PyList_Append(l.obj, obj.Obj())
45+
check(r == 0, "failed to append item to list")
4346
}

object.go

Lines changed: 7 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ func (obj Object) AttrFunc(name string) Func {
117117

118118
func (obj Object) SetAttr(name string, value any) {
119119
cname := AllocCStr(name)
120-
C.PyObject_SetAttrString(obj.obj, cname, From(value).obj)
120+
r := C.PyObject_SetAttrString(obj.obj, cname, From(value).obj)
121+
C.PyErr_Print()
122+
check(r == 0, fmt.Sprintf("failed to set attribute %s", name))
121123
C.free(unsafe.Pointer(cname))
122124
}
123125

@@ -294,38 +296,21 @@ func ToValue(obj Object, v reflect.Value) bool {
294296
if !v.IsValid() || !v.CanSet() {
295297
return false
296298
}
299+
297300
switch v.Kind() {
298-
case reflect.Int8:
299-
fallthrough
300-
case reflect.Int16:
301-
fallthrough
302-
case reflect.Int32:
303-
fallthrough
304-
case reflect.Int64:
305-
fallthrough
306-
case reflect.Int:
301+
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
307302
if obj.IsLong() {
308303
v.SetInt(Cast[Long](obj).Int64())
309304
} else {
310305
return false
311306
}
312-
case reflect.Uint8:
313-
fallthrough
314-
case reflect.Uint16:
315-
fallthrough
316-
case reflect.Uint32:
317-
fallthrough
318-
case reflect.Uint64:
319-
fallthrough
320-
case reflect.Uint:
307+
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
321308
if obj.IsLong() {
322309
v.SetUint(Cast[Long](obj).Uint64())
323310
} else {
324311
return false
325312
}
326-
case reflect.Float32:
327-
fallthrough
328-
case reflect.Float64:
313+
case reflect.Float32, reflect.Float64:
329314
if obj.IsFloat() || obj.IsLong() {
330315
v.SetFloat(Cast[Float](obj).Float64())
331316
} else {
@@ -407,62 +392,6 @@ func ToValue(obj Object, v reflect.Value) bool {
407392
return true
408393
}
409394

410-
func To[T any](obj Object) (ret T) {
411-
switch any(ret).(type) {
412-
case int8:
413-
return any(int8(Cast[Long](obj).Int64())).(T)
414-
case int16:
415-
return any(int16(Cast[Long](obj).Int64())).(T)
416-
case int32:
417-
return any(int32(Cast[Long](obj).Int64())).(T)
418-
case int64:
419-
return any(Cast[Long](obj).Int64()).(T)
420-
case int:
421-
return any(int(Cast[Long](obj).Int64())).(T)
422-
case uint8:
423-
return any(uint8(Cast[Long](obj).Uint64())).(T)
424-
case uint16:
425-
return any(uint16(Cast[Long](obj).Uint64())).(T)
426-
case uint32:
427-
return any(uint32(Cast[Long](obj).Uint64())).(T)
428-
case uint64:
429-
return any(Cast[Long](obj).Uint64()).(T)
430-
case uint:
431-
return any(uint(Cast[Long](obj).Uint64())).(T)
432-
case float32:
433-
return any(float32(Cast[Float](obj).Float64())).(T)
434-
case float64:
435-
return any(Cast[Float](obj).Float64()).(T)
436-
case complex64:
437-
return any(complex64(Cast[Complex](obj).Complex128())).(T)
438-
case complex128:
439-
return any(Cast[Complex](obj).Complex128()).(T)
440-
case string:
441-
return any(Cast[Str](obj).String()).(T)
442-
case bool:
443-
return any(Cast[Bool](obj).Bool()).(T)
444-
case []byte:
445-
return any(Cast[Bytes](obj).Bytes()).(T)
446-
default:
447-
v := reflect.ValueOf(ret)
448-
switch v.Kind() {
449-
case reflect.Slice:
450-
return toSlice[T](obj, v)
451-
}
452-
panic(fmt.Errorf("unsupported type conversion from Python object to %T", ret))
453-
}
454-
}
455-
456-
func toSlice[T any](obj Object, v reflect.Value) T {
457-
list := Cast[List](obj)
458-
l := list.Len()
459-
v = reflect.MakeSlice(v.Type(), l, l)
460-
for i := 0; i < l; i++ {
461-
v.Index(i).Set(reflect.ValueOf(To[T](list.GetItem(i))))
462-
}
463-
return v.Interface().(T)
464-
}
465-
466395
func fromSlice(v reflect.Value) List {
467396
l := v.Len()
468397
list := newList(C.PyList_New(C.Py_ssize_t(l)))

object_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package gp
22

33
import (
4+
"bytes"
45
"reflect"
56
"testing"
67
)
@@ -377,6 +378,56 @@ def make_tuple():
377378
t.Error("Object.Obj() should return nil for nil object")
378379
}
379380
}()
381+
382+
func() {
383+
// Test AttrBytes
384+
builtins := ImportModule("types")
385+
objType := builtins.AttrFunc("SimpleNamespace")
386+
obj := objType.Call()
387+
388+
// Create a simple object with bytes attribute
389+
obj.SetAttr("bytes_val", From([]byte("hello")))
390+
391+
if !bytes.Equal(obj.AttrBytes("bytes_val").Bytes(), []byte("hello")) {
392+
t.Error("AttrBytes failed")
393+
}
394+
}()
395+
396+
func() {
397+
// Test Object.Call with kwargs
398+
pyCode := `
399+
def test_func(a, b=10, c="default"):
400+
return (a, b, c)
401+
`
402+
locals := MakeDict(nil)
403+
globals := MakeDict(nil)
404+
globals.Set(MakeStr("__builtins__"), builtins.Object)
405+
406+
code, err := CompileString(pyCode, "<string>", FileInput)
407+
if err != nil {
408+
t.Errorf("CompileString() error = %v", err)
409+
}
410+
EvalCode(code, globals, locals)
411+
412+
testFunc := locals.Get(MakeStr("test_func"))
413+
414+
// Call with positional and keyword arguments
415+
result := testFunc.Call("__call__", 1, KwArgs{
416+
"b": 20,
417+
"c": "custom",
418+
})
419+
420+
tuple := result.AsTuple()
421+
if tuple.Get(0).AsLong().Int64() != 1 {
422+
t.Error("Wrong value for first argument")
423+
}
424+
if tuple.Get(1).AsLong().Int64() != 20 {
425+
t.Error("Wrong value for keyword argument b")
426+
}
427+
if tuple.Get(2).AsStr().String() != "custom" {
428+
t.Error("Wrong value for keyword argument c")
429+
}
430+
}()
380431
}
381432

382433
func TestToValue(t *testing.T) {
@@ -517,3 +568,48 @@ func TestToValue(t *testing.T) {
517568
}
518569
}()
519570
}
571+
572+
func TestFromSpecialCases(t *testing.T) {
573+
setupTest(t)
574+
575+
func() {
576+
// Test From with uint values
577+
tests := []struct {
578+
input uint
579+
expected uint64
580+
}{
581+
{0, 0},
582+
{42, 42},
583+
{^uint(0), ^uint64(0)}, // maximum uint value
584+
}
585+
586+
for _, tt := range tests {
587+
obj := From(tt.input)
588+
if !obj.IsLong() {
589+
t.Errorf("From(uint) did not create Long object")
590+
}
591+
if got := obj.AsLong().Uint64(); got != tt.expected {
592+
t.Errorf("From(%d) = %d, want %d", tt.input, got, tt.expected)
593+
}
594+
}
595+
}()
596+
597+
func() {
598+
// Test From with Object.Obj()
599+
original := From(42)
600+
obj := From(original.Obj())
601+
602+
if !obj.IsLong() {
603+
t.Error("From(Object.Obj()) did not create Long object")
604+
}
605+
if got := obj.AsLong().Int64(); got != 42 {
606+
t.Errorf("From(Object.Obj()) = %d, want 42", got)
607+
}
608+
609+
// Test that the new object is independent
610+
original = From(100)
611+
if got := obj.AsLong().Int64(); got != 42 {
612+
t.Errorf("Object was not independent, got %d after modifying original", got)
613+
}
614+
}()
615+
}

python.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ func Initialize() {
2121
}
2222

2323
func Finalize() {
24-
C.Py_FinalizeEx()
24+
r := C.Py_FinalizeEx()
25+
check(r == 0, "failed to finalize Python")
2526
typeMetaMap = make(map[*C.PyObject]*typeMeta)
2627
pyTypeMap = make(map[reflect.Type]*C.PyObject)
2728
}
@@ -112,3 +113,11 @@ func RunString(code string) error {
112113
}
113114
return nil
114115
}
116+
117+
// ----------------------------------------------------------------------------
118+
119+
func check(b bool, msg string) {
120+
if !b {
121+
panic(msg)
122+
}
123+
}

0 commit comments

Comments
 (0)