github.com/pygolin/runtime@v0.0.0-20201208210830-a62e3cd39798/object_test.go (about) 1 // Copyright 2016 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package runtime 16 17 import ( 18 "fmt" 19 "reflect" 20 "regexp" 21 "testing" 22 ) 23 24 func TestObjectCall(t *testing.T) { 25 arg0 := newObject(ObjectType) 26 arg1 := newObject(ObjectType) 27 args := wrapArgs(arg0, arg1) 28 kwargs := wrapKWArgs("kwarg", newObject(ObjectType)) 29 kwargsDict := kwargs.makeDict() 30 fn := func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 31 kwargsOrNone := None 32 if len(kwargs) > 0 { 33 kwargsOrNone = kwargs.makeDict().ToObject() 34 } 35 return newTestTuple(NewTuple(args.makeCopy()...), kwargsOrNone).ToObject(), nil 36 } 37 foo := newBuiltinFunction("foo", fn).ToObject() 38 typ := newTestClass("Foo", []*Type{ObjectType}, newStringDict(map[string]*Object{ 39 "__call__": newBuiltinFunction("__call__", fn).ToObject(), 40 })) 41 callable := newObject(typ) 42 raisesFunc := newBuiltinFunction("bar", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 43 return nil, f.RaiseType(RuntimeErrorType, "bar") 44 }).ToObject() 45 cases := []struct { 46 callable *Object 47 invokeTestCase 48 }{ 49 {foo, invokeTestCase{args: args, kwargs: kwargs, want: newTestTuple(NewTuple(args...), kwargsDict).ToObject()}}, 50 {foo, invokeTestCase{args: args, want: newTestTuple(NewTuple(args...).ToObject(), None).ToObject()}}, 51 {foo, invokeTestCase{kwargs: kwargs, want: newTestTuple(NewTuple(), kwargsDict).ToObject()}}, 52 {foo, invokeTestCase{want: newTestTuple(NewTuple(), None).ToObject()}}, 53 {foo, invokeTestCase{args: wrapArgs(arg0), want: newTestTuple(NewTuple(arg0), None).ToObject()}}, 54 {callable, invokeTestCase{args: args, kwargs: kwargs, want: newTestTuple(NewTuple(callable, arg0, arg1), kwargsDict).ToObject()}}, 55 {newObject(ObjectType), invokeTestCase{wantExc: mustCreateException(TypeErrorType, "'object' object is not callable")}}, 56 {raisesFunc, invokeTestCase{wantExc: mustCreateException(RuntimeErrorType, "bar")}}, 57 } 58 for _, cas := range cases { 59 if err := runInvokeTestCase(cas.callable, &cas.invokeTestCase); err != "" { 60 t.Error(err) 61 } 62 } 63 } 64 65 func TestNewObject(t *testing.T) { 66 cases := []*Type{DictType, ObjectType, StrType, TypeType} 67 for _, c := range cases { 68 if o := newObject(c); o.Type() != c { 69 t.Errorf("new object has type %q, want %q", o.Type().Name(), c.Name()) 70 } 71 } 72 } 73 74 func TestObjectString(t *testing.T) { 75 typ := newTestClass("Foo", []*Type{ObjectType}, newStringDict(map[string]*Object{ 76 "__repr__": newBuiltinFunction("__repr__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 77 return nil, f.RaiseType(ExceptionType, "ruh roh") 78 }).ToObject(), 79 })) 80 cases := []struct { 81 o *Object 82 wantPattern string 83 }{ 84 {newObject(ObjectType), `^<object object at \w+>$`}, 85 {NewTuple(NewStr("foo").ToObject(), NewStr("bar").ToObject()).ToObject(), `^\('foo', 'bar'\)$`}, 86 {ExceptionType.ToObject(), "^<type 'Exception'>$"}, 87 {NewStr("foo\nbar").ToObject(), `^'foo\\nbar'$`}, 88 {newObject(typ), `^<Foo object \(repr raised Exception\)>$`}, 89 } 90 for _, cas := range cases { 91 re := regexp.MustCompile(cas.wantPattern) 92 s := cas.o.String() 93 if matched := re.MatchString(s); !matched { 94 t.Errorf("%v.String() = %q, doesn't match pattern %q", cas.o, s, re) 95 } 96 } 97 } 98 99 func TestObjectDelAttr(t *testing.T) { 100 fun := wrapFuncForTest(func(f *Frame, o *Object, name *Str) (*Object, *BaseException) { 101 if raised := DelAttr(f, o, name); raised != nil { 102 return nil, raised 103 } 104 return GetAttr(f, o, name, None) 105 }) 106 dellerType := newTestClass("Deller", []*Type{ObjectType}, newStringDict(map[string]*Object{ 107 "__get__": newBuiltinFunction("__get__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 108 attr, raised := args[1].Dict().GetItemString(f, "attr") 109 if raised != nil { 110 return nil, raised 111 } 112 if attr == nil { 113 return nil, f.RaiseType(AttributeErrorType, "attr") 114 } 115 return attr, nil 116 }).ToObject(), 117 "__delete__": newBuiltinFunction("__delete__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 118 deleted, raised := args[1].Dict().DelItemString(f, "attr") 119 if raised != nil { 120 return nil, raised 121 } 122 if !deleted { 123 return nil, f.RaiseType(AttributeErrorType, "attr") 124 } 125 return None, nil 126 }).ToObject(), 127 })) 128 fooType := newTestClass("Foo", []*Type{ObjectType}, newStringDict(map[string]*Object{"deller": newObject(dellerType)})) 129 foo := newObject(fooType) 130 if raised := foo.Dict().SetItemString(NewRootFrame(), "attr", NewInt(123).ToObject()); raised != nil { 131 t.Fatal(raised) 132 } 133 cases := []invokeTestCase{ 134 {args: wrapArgs(foo, "deller"), want: None}, 135 {args: wrapArgs(newObject(fooType), "foo"), wantExc: mustCreateException(AttributeErrorType, "'Foo' object has no attribute 'foo'")}, 136 {args: wrapArgs(newObject(fooType), "deller"), wantExc: mustCreateException(AttributeErrorType, "attr")}, 137 } 138 for _, cas := range cases { 139 if err := runInvokeTestCase(fun, &cas); err != "" { 140 t.Error(err) 141 } 142 } 143 } 144 145 func TestObjectGetAttribute(t *testing.T) { 146 fun := wrapFuncForTest(func(f *Frame, o *Object, name *Str) (*Object, *BaseException) { 147 return GetAttr(f, o, name, nil) 148 }) 149 // class Getter(object): 150 // def __get__(self, *args): 151 // return "got getter" 152 getterType := newTestClass("Getter", []*Type{ObjectType}, newStringDict(map[string]*Object{ 153 "__get__": newBuiltinFunction("__get__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 154 return NewStr("got getter").ToObject(), nil 155 }).ToObject(), 156 })) 157 getter := newObject(getterType) 158 // class Setter(object): 159 // def __get__(self, *args): 160 // return "got setter" 161 // def __set__(self, *args): 162 // pass 163 setterType := newTestClass("Setter", []*Type{ObjectType}, newStringDict(map[string]*Object{ 164 "__get__": newBuiltinFunction("__get__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 165 return NewStr("got setter").ToObject(), nil 166 }).ToObject(), 167 "__set__": newBuiltinFunction("__set__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 168 return None, nil 169 }).ToObject(), 170 })) 171 setter := newObject(setterType) 172 // class Foo(object): 173 // pass 174 // foo = Foo() 175 fooType := newTestClass("Foo", []*Type{ObjectType}, newStringDict(map[string]*Object{ 176 "bar": NewInt(42).ToObject(), 177 "baz": NewStr("Foo's baz").ToObject(), 178 "foogetter": getter, 179 "foo": NewInt(101).ToObject(), 180 "barsetter": setter, 181 })) 182 foo := newObject(fooType) 183 if raised := foo.Dict().SetItemString(NewRootFrame(), "fooattr", True.ToObject()); raised != nil { 184 t.Fatal(raised) 185 } 186 if raised := foo.Dict().SetItemString(NewRootFrame(), "barattr", NewInt(-1).ToObject()); raised != nil { 187 t.Fatal(raised) 188 } 189 if raised := foo.Dict().SetItemString(NewRootFrame(), "barsetter", NewStr("NOT setter").ToObject()); raised != nil { 190 t.Fatal(raised) 191 } 192 cases := []invokeTestCase{ 193 {args: wrapArgs(foo, "bar"), want: NewInt(42).ToObject()}, 194 {args: wrapArgs(foo, "fooattr"), want: True.ToObject()}, 195 {args: wrapArgs(foo, "foogetter"), want: NewStr("got getter").ToObject()}, 196 {args: wrapArgs(foo, "bar"), want: NewInt(42).ToObject()}, 197 {args: wrapArgs(foo, "foo"), want: NewInt(101).ToObject()}, 198 {args: wrapArgs(foo, "barattr"), want: NewInt(-1).ToObject()}, 199 {args: wrapArgs(foo, "barsetter"), want: NewStr("got setter").ToObject()}, 200 } 201 for _, cas := range cases { 202 if err := runInvokeTestCase(fun, &cas); err != "" { 203 t.Error(err) 204 } 205 } 206 } 207 208 func TestObjectGetDict(t *testing.T) { 209 fooType := newTestClass("Foo", []*Type{ObjectType}, NewDict()) 210 foo := newObject(fooType) 211 if raised := SetAttr(NewRootFrame(), foo, NewStr("bar"), NewInt(123).ToObject()); raised != nil { 212 panic(raised) 213 } 214 fun := wrapFuncForTest(func(f *Frame, o *Object) (*Object, *BaseException) { 215 return GetAttr(f, o, NewStr("__dict__"), nil) 216 }) 217 cases := []invokeTestCase{ 218 {args: wrapArgs(newObject(ObjectType)), wantExc: mustCreateException(AttributeErrorType, "'object' object has no attribute '__dict__'")}, 219 {args: wrapArgs(newObject(fooType)), want: NewDict().ToObject()}, 220 {args: wrapArgs(foo), want: newStringDict(map[string]*Object{"bar": NewInt(123).ToObject()}).ToObject()}, 221 } 222 for _, cas := range cases { 223 if err := runInvokeTestCase(fun, &cas); err != "" { 224 t.Error(err) 225 } 226 } 227 } 228 229 func TestObjectSetDict(t *testing.T) { 230 fooType := newTestClass("Foo", []*Type{ObjectType}, NewDict()) 231 testDict := newStringDict(map[string]*Object{"bar": NewInt(123).ToObject()}) 232 fun := wrapFuncForTest(func(f *Frame, o, val *Object) (*Object, *BaseException) { 233 if raised := SetAttr(f, o, NewStr("__dict__"), val); raised != nil { 234 return nil, raised 235 } 236 d := o.Dict() 237 if d == nil { 238 return None, nil 239 } 240 return d.ToObject(), nil 241 }) 242 cases := []invokeTestCase{ 243 {args: wrapArgs(newObject(ObjectType), NewDict()), wantExc: mustCreateException(AttributeErrorType, "'object' object has no attribute '__dict__'")}, 244 {args: wrapArgs(newObject(fooType), testDict), want: testDict.ToObject()}, 245 {args: wrapArgs(newObject(fooType), 123), wantExc: mustCreateException(TypeErrorType, "'_set_dict' requires a 'dict' object but received a 'int'")}, 246 } 247 for _, cas := range cases { 248 if err := runInvokeTestCase(fun, &cas); err != "" { 249 t.Error(err) 250 } 251 } 252 } 253 254 func TestObjectNew(t *testing.T) { 255 foo := makeTestType("Foo", ObjectType) 256 foo.flags &= ^typeFlagInstantiable 257 prepareType(foo) 258 cases := []invokeTestCase{ 259 {args: wrapArgs(ExceptionType), want: newObject(ExceptionType)}, 260 {args: wrapArgs(IntType), want: NewInt(0).ToObject()}, 261 {wantExc: mustCreateException(TypeErrorType, "'__new__' requires 1 arguments")}, 262 {args: wrapArgs(None), wantExc: mustCreateException(TypeErrorType, `'__new__' requires a 'type' object but received a "NoneType"`)}, 263 {args: wrapArgs(foo), wantExc: mustCreateException(TypeErrorType, "object.__new__(Foo) is not safe, use Foo.__new__()")}, 264 } 265 for _, cas := range cases { 266 if err := runInvokeMethodTestCase(ObjectType, "__new__", &cas); err != "" { 267 t.Error(err) 268 } 269 } 270 } 271 272 func TestObjectReduce(t *testing.T) { 273 fun := wrapFuncForTest(func(f *Frame, method *Str, o *Object, args Args) (*Object, *BaseException) { 274 // Call __reduce/reduce_ex__. 275 reduce, raised := GetAttr(f, o, method, nil) 276 if raised != nil { 277 return nil, raised 278 } 279 result, raised := reduce.Call(f, args, nil) 280 if raised != nil { 281 return nil, raised 282 } 283 msg := NewStr(fmt.Sprintf("reduce must return a tuple, got %s", result.Type().Name())).ToObject() 284 if raised := Assert(f, GetBool(result.isInstance(TupleType)).ToObject(), msg); raised != nil { 285 return nil, raised 286 } 287 elems := toTupleUnsafe(result).elems 288 numElems := len(elems) 289 msg = NewStr(fmt.Sprintf("reduce must return a tuple with 2 <= len <= 5, got %d", numElems)).ToObject() 290 if raised := Assert(f, GetBool(numElems >= 2 && numElems <= 5).ToObject(), msg); raised != nil { 291 return nil, raised 292 } 293 newArgs := elems[1] 294 msg = NewStr(fmt.Sprintf("reduce second return value must be tuple, got %s", newArgs.Type().Name())).ToObject() 295 if raised := Assert(f, GetBool(newArgs.isInstance(TupleType)).ToObject(), msg); raised != nil { 296 return nil, raised 297 } 298 // Call the reconstructor function with the args returned. 299 reduced, raised := elems[0].Call(f, toTupleUnsafe(newArgs).elems, nil) 300 if raised != nil { 301 return nil, raised 302 } 303 // Return the reconstructed object, object state, list items 304 // and dict items. 305 state, list, dict := None, None, None 306 if numElems > 2 && elems[2] != None { 307 state = elems[2] 308 } 309 if numElems > 3 && elems[3] != None { 310 if list, raised = ListType.Call(f, Args{elems[3]}, nil); raised != nil { 311 return nil, raised 312 } 313 } 314 if numElems > 4 && elems[4] != None { 315 if dict, raised = DictType.Call(f, Args{elems[4]}, nil); raised != nil { 316 return nil, raised 317 } 318 } 319 return NewTuple(reduced, state, list, dict).ToObject(), nil 320 }) 321 fooType := newTestClass("Foo", []*Type{StrType}, NewDict()) 322 fooNoDict := &Str{Object: Object{typ: fooType}, value: "fooNoDict"} 323 // Calling __reduce_ex__ on a type that overrides __reduce__ should 324 // forward to the call to __reduce__. 325 reduceOverrideType := newTestClass("ReduceOverride", []*Type{ObjectType}, newStringDict(map[string]*Object{ 326 "__reduce__": newBuiltinFunction("__reduce__", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { 327 strNew, raised := GetAttr(f, StrType.ToObject(), NewStr("__new__"), nil) 328 if raised != nil { 329 return nil, raised 330 } 331 return newTestTuple(strNew, newTestTuple(StrType, "ReduceOverride")).ToObject(), nil 332 }).ToObject(), 333 })) 334 getNewArgsRaisesType := newTestClass("GetNewArgsRaises", []*Type{ObjectType}, newStringDict(map[string]*Object{ 335 "__getnewargs__": newBuiltinFunction("__getnewargs__", func(f *Frame, _ Args, _ KWArgs) (*Object, *BaseException) { 336 return nil, f.RaiseType(RuntimeErrorType, "uh oh") 337 }).ToObject(), 338 })) 339 getNewArgsReturnsNonTupleType := newTestClass("GetNewArgsReturnsNonTuple", []*Type{ObjectType}, newStringDict(map[string]*Object{ 340 "__getnewargs__": newBuiltinFunction("__getnewargs__", func(f *Frame, _ Args, _ KWArgs) (*Object, *BaseException) { 341 return NewInt(123).ToObject(), nil 342 }).ToObject(), 343 })) 344 // Attempting to reduce an int will fail with "can't pickle" but 345 // subclasses can be reduced. 346 intSubclass := newTestClass("IntSubclass", []*Type{IntType}, NewDict()) 347 intSubclassInst := &Int{Object{typ: intSubclass}, 123} 348 cases := []invokeTestCase{ 349 {args: wrapArgs("__reduce__", 42, Args{}), wantExc: mustCreateException(TypeErrorType, "can't pickle int objects")}, 350 {args: wrapArgs("__reduce__", 42, wrapArgs(2)), want: newTestTuple(42, None, None, None).ToObject()}, 351 {args: wrapArgs("__reduce_ex__", 42, Args{}), wantExc: mustCreateException(TypeErrorType, "can't pickle int objects")}, 352 {args: wrapArgs("__reduce__", 3.14, wrapArgs("bad proto")), wantExc: mustCreateException(TypeErrorType, "'__reduce__' requires a 'int' object but received a 'str'")}, 353 {args: wrapArgs("__reduce_ex__", 3.14, wrapArgs("bad proto")), wantExc: mustCreateException(TypeErrorType, "'__reduce_ex__' requires a 'int' object but received a 'str'")}, 354 {args: wrapArgs("__reduce__", newObject(fooType), Args{}), want: newTestTuple("", NewDict(), None, None).ToObject()}, 355 {args: wrapArgs("__reduce__", newObject(fooType), wrapArgs(2)), want: newTestTuple("", NewDict(), None, None).ToObject()}, 356 {args: wrapArgs("__reduce_ex__", newObject(fooType), Args{}), want: newTestTuple("", NewDict(), None, None).ToObject()}, 357 {args: wrapArgs("__reduce_ex__", newObject(reduceOverrideType), Args{}), want: newTestTuple("ReduceOverride", None, None, None).ToObject()}, 358 {args: wrapArgs("__reduce__", fooNoDict, Args{}), want: newTestTuple("fooNoDict", None, None, None).ToObject()}, 359 {args: wrapArgs("__reduce__", newTestList(1, 2, 3), wrapArgs(2)), want: newTestTuple(NewList(), None, newTestList(1, 2, 3), None).ToObject()}, 360 {args: wrapArgs("__reduce__", newTestDict("a", 1, "b", 2), wrapArgs(2)), want: newTestTuple(NewDict(), None, None, newTestDict("a", 1, "b", 2)).ToObject()}, 361 {args: wrapArgs("__reduce__", newObject(getNewArgsRaisesType), wrapArgs(2)), wantExc: mustCreateException(RuntimeErrorType, "uh oh")}, 362 {args: wrapArgs("__reduce__", newObject(getNewArgsReturnsNonTupleType), wrapArgs(2)), wantExc: mustCreateException(TypeErrorType, "__getnewargs__ should return a tuple, not 'int'")}, 363 {args: wrapArgs("__reduce__", newTestTuple("foo", "bar"), wrapArgs(2)), want: newTestTuple(newTestTuple("foo", "bar"), None, None, None).ToObject()}, 364 {args: wrapArgs("__reduce__", 3.14, wrapArgs(2)), want: newTestTuple(3.14, None, None, None).ToObject()}, 365 {args: wrapArgs("__reduce__", NewUnicode("abc"), wrapArgs(2)), want: newTestTuple(NewUnicode("abc"), None, None, None).ToObject()}, 366 {args: wrapArgs("__reduce__", intSubclassInst, Args{}), want: newTestTuple(intSubclassInst, None, None, None).ToObject()}, 367 } 368 for _, cas := range cases { 369 if err := runInvokeTestCase(fun, &cas); err != "" { 370 t.Error(err) 371 } 372 } 373 } 374 375 func TestObjectSetAttr(t *testing.T) { 376 fun := wrapFuncForTest(func(f *Frame, o *Object, name *Str, value *Object) (*Object, *BaseException) { 377 if raised := SetAttr(f, o, name, value); raised != nil { 378 return nil, raised 379 } 380 return GetAttr(f, o, name, None) 381 }) 382 setterType := newTestClass("Setter", []*Type{ObjectType}, newStringDict(map[string]*Object{ 383 "__get__": newBuiltinFunction("__get__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 384 item, raised := args[1].Dict().GetItemString(f, "attr") 385 if raised != nil { 386 return nil, raised 387 } 388 if item == nil { 389 return nil, raiseKeyError(f, NewStr("attr").ToObject()) 390 } 391 return item, nil 392 }).ToObject(), 393 "__set__": newBuiltinFunction("__set__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 394 if raised := args[1].Dict().SetItemString(f, "attr", NewTuple(args.makeCopy()...).ToObject()); raised != nil { 395 return nil, raised 396 } 397 return None, nil 398 }).ToObject(), 399 })) 400 setter := newObject(setterType) 401 fooType := newTestClass("Foo", []*Type{ObjectType}, newStringDict(map[string]*Object{"setter": setter})) 402 foo := newObject(fooType) 403 cases := []invokeTestCase{ 404 {args: wrapArgs(newObject(fooType), "foo", "abc"), want: NewStr("abc").ToObject()}, 405 {args: wrapArgs(foo, "setter", "baz"), want: NewTuple(setter, foo, NewStr("baz").ToObject()).ToObject()}, 406 {args: wrapArgs(newObject(ObjectType), "foo", 10), wantExc: mustCreateException(AttributeErrorType, "'object' has no attribute 'foo'")}, 407 } 408 for _, cas := range cases { 409 if err := runInvokeTestCase(fun, &cas); err != "" { 410 t.Error(err) 411 } 412 } 413 } 414 415 func TestObjectStrRepr(t *testing.T) { 416 fun := wrapFuncForTest(func(f *Frame, o *Object, wantPattern string) *BaseException { 417 re := regexp.MustCompile(wantPattern) 418 s, raised := ToStr(f, o) 419 if raised != nil { 420 return raised 421 } 422 if !re.MatchString(s.Value()) { 423 t.Errorf("str(%v) = %v, want %q", o, s, re) 424 } 425 s, raised = Repr(f, o) 426 if raised != nil { 427 return raised 428 } 429 if !re.MatchString(s.Value()) { 430 t.Errorf("repr(%v) = %v, want %q", o, s, re) 431 } 432 return nil 433 }) 434 type noReprMethodBasis struct{ Object } 435 noReprMethodType := newType(TypeType, "noReprMethod", reflect.TypeOf(noReprMethodBasis{}), []*Type{}, NewDict()) 436 noReprMethodType.mro = []*Type{noReprMethodType} 437 fooType := newTestClass("Foo", []*Type{ObjectType}, newTestDict("__module__", "foo.bar")) 438 cases := []invokeTestCase{ 439 {args: wrapArgs(newObject(ObjectType), `^<object object at \w+>$`), want: None}, 440 {args: wrapArgs(newObject(noReprMethodType), `^<noReprMethod object at \w+>$`), want: None}, 441 {args: wrapArgs(newObject(fooType), `<foo\.bar\.Foo object at \w+>`), want: None}, 442 } 443 for _, cas := range cases { 444 if err := runInvokeTestCase(fun, &cas); err != "" { 445 t.Error(err) 446 } 447 } 448 }