From 8537807d5c10ce425e564130732a06dfe69f4f44 Mon Sep 17 00:00:00 2001 From: Alan Justino Date: Fri, 28 Apr 2017 13:21:54 -0300 Subject: [PATCH 1/4] Tests for __getattr__ and __setattr__ --- testing/class_test.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/testing/class_test.py b/testing/class_test.py index bc8ec06e..f812d850 100644 --- a/testing/class_test.py +++ b/testing/class_test.py @@ -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: @@ -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" From dd8ca9a714a082a153bb06e47cfd7841eecfe216 Mon Sep 17 00:00:00 2001 From: Alan Justino Date: Sun, 18 Jun 2017 16:31:38 -0300 Subject: [PATCH 2/4] slot for __getattr__, fallback of __getattribute__ --- runtime/core.go | 15 ++++++++++++++- runtime/slots.go | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/runtime/core.go b/runtime/core.go index 966a2dc9..c37340a0 100644 --- a/runtime/core.go +++ b/runtime/core.go @@ -219,13 +219,26 @@ 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 { + // No __getattr__: reraise + return nil, raised + } + result, raised := getAttr.Fn(f, o, name) + if raised == nil { + return result, raised + } + } + + // Last resort: return the default if raised != nil && raised.isInstance(AttributeErrorType) && def != nil { f.RestoreExc(nil, nil) result, raised = def, nil diff --git a/runtime/slots.go b/runtime/slots.go index b2dd2426..d222e8f7 100644 --- a/runtime/slots.go +++ b/runtime/slots.go @@ -388,6 +388,7 @@ type typeSlots struct { GE *binaryOpSlot Get *getSlot GetAttribute *getAttributeSlot + GetAttr *getAttrSlot GetItem *binaryOpSlot GT *binaryOpSlot Hash *unaryOpSlot From 615408eba075f74c33c2347a0df390bacfc26016 Mon Sep 17 00:00:00 2001 From: Alan Justino Date: Sun, 18 Jun 2017 16:51:56 -0300 Subject: [PATCH 3/4] Reuse defined result,raised --- runtime/core.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/runtime/core.go b/runtime/core.go index c37340a0..12479dc1 100644 --- a/runtime/core.go +++ b/runtime/core.go @@ -224,17 +224,14 @@ func GetAttr(f *Frame, o *Object, name *Str, def *Object) (*Object, *BaseExcepti 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 { - // No __getattr__: reraise - return nil, raised - } - result, raised := getAttr.Fn(f, o, name) - if raised == nil { - return result, raised + if getAttr != nil { + f.RestoreExc(nil, nil) + result, raised = getAttr.Fn(f, o, name) } } From e333cbe44c18023674eb9e36e3bc2554b2b7704f Mon Sep 17 00:00:00 2001 From: Alan Justino Date: Sun, 18 Jun 2017 17:06:35 -0300 Subject: [PATCH 4/4] Oops: Forgot to commit the getAttrSlot --- runtime/slots.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/runtime/slots.go b/runtime/slots.go index d222e8f7..8d002ccf 100644 --- a/runtime/slots.go +++ b/runtime/slots.go @@ -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) }