Skip to content
This repository was archived by the owner on Mar 23, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion runtime/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,13 +219,23 @@ func GetItem(f *Frame, o, key *Object) (*Object, *BaseException) {
// GetAttr returns the named attribute of o. Equivalent to the Python expression
// getattr(o, name, def).
func GetAttr(f *Frame, o *Object, name *Str, def *Object) (*Object, *BaseException) {
// TODO: Fall back to __getattr__.
getAttribute := o.typ.slots.GetAttribute
if getAttribute == nil {
msg := fmt.Sprintf("'%s' has no attribute '%s'", o.typ.Name(), name.Value())
return nil, f.RaiseType(AttributeErrorType, msg)
}

result, raised := getAttribute.Fn(f, o, name)
if raised != nil && raised.isInstance(AttributeErrorType) {
// Fall back to __getattr__ when __getattribute__ raised AttributeError
getAttr := o.typ.slots.GetAttr
if getAttr != nil {
f.RestoreExc(nil, nil)
result, raised = getAttr.Fn(f, o, name)
}
}

// Last resort: return the default
if raised != nil && raised.isInstance(AttributeErrorType) && def != nil {
f.RestoreExc(nil, nil)
result, raised = def, nil
Expand Down
21 changes: 21 additions & 0 deletions runtime/slots.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,26 @@ func (s *getAttributeSlot) wrapCallable(callable *Object) bool {
return true
}

type getAttrSlot struct {
Fn func(*Frame, *Object, *Str) (*Object, *BaseException)
}

func (s *getAttrSlot) makeCallable(t *Type, slotName string) *Object {
return newBuiltinFunction(slotName, func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
if raised := checkMethodArgs(f, slotName, args, t, StrType); raised != nil {
return nil, raised
}
return s.Fn(f, args[0], toStrUnsafe(args[1]))
}).ToObject()
}

func (s *getAttrSlot) wrapCallable(callable *Object) bool {
s.Fn = func(f *Frame, o *Object, name *Str) (*Object, *BaseException) {
return callable.Call(f, Args{o, name.ToObject()}, nil)
}
return true
}

type getSlot struct {
Fn func(*Frame, *Object, *Object, *Type) (*Object, *BaseException)
}
Expand Down Expand Up @@ -388,6 +408,7 @@ type typeSlots struct {
GE *binaryOpSlot
Get *getSlot
GetAttribute *getAttributeSlot
GetAttr *getAttrSlot
GetItem *binaryOpSlot
GT *binaryOpSlot
Hash *unaryOpSlot
Expand Down
18 changes: 18 additions & 0 deletions testing/class_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ def bar(self):
assert foo.baz() == 'bar'

foo.b = 10
assert foo.b == 10

del foo.b
assert not hasattr(foo, 'b')
try:
Expand All @@ -48,3 +50,19 @@ def bar(self):
pass
else:
raise AssertionError


spamdict = {}
class Spam(object):
def __getattr__(self, key):
return spamdict[key]
def __setattr__(self, key, value):
spamdict[key] = value

spam = Spam()
spam.eggs = 'with eggs'
assert spamdict == {'eggs': 'with eggs'}, "Change spam.eggs should update spamdict"
assert spam.eggs == 'with eggs', "spam.eggs value should came from spamdict['eggs']"

spamdict['eggs'] = 'with more spam'
assert spam.eggs == 'with more spam', "Change spamdict['eggs'] should update spam.eggs"