Skip to content

Commit 22b9afd

Browse files
committed
Rework GetAttr and SetAttr and related parts
* Add GetDict interface * Use it to straighten out GetAttr and SetAttr * Use __get__ and __set__ on functions and methods to implement bound methods properly
1 parent 611ef13 commit 22b9afd

File tree

7 files changed

+106
-78
lines changed

7 files changed

+106
-78
lines changed

py/function.go

+15
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ func (o *Function) Type() *Type {
3838
return FunctionType
3939
}
4040

41+
// Get the Dict
42+
func (f *Function) GetDict() StringDict {
43+
return f.Dict
44+
}
45+
4146
// Define a new function
4247
//
4348
// Return a new function object associated with the code object
@@ -131,6 +136,16 @@ func (f *Function) M__call__(args Tuple, kwargs StringDict) Object {
131136
return result
132137
}
133138

139+
// Read a function from a class which makes a bound method
140+
func (f *Function) M__get__(instance, owner Object) Object {
141+
if instance != None {
142+
return NewBoundMethod(instance, f)
143+
}
144+
return f
145+
}
146+
134147
// Make sure it satisfies the interface
135148
var _ Object = (*Function)(nil)
136149
var _ I__call__ = (*Function)(nil)
150+
var _ IGetDict = (*Function)(nil)
151+
var _ I__get__ = (*Function)(nil)

py/import.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,16 @@ func ImportModuleLevelObject(nameObj, given_globals, locals, given_fromlist Obje
128128
panic(ExceptionNewf(ImportError, "import of %q halted; None in sys.modules", abs_name))
129129
} else if ok {
130130
var value Object
131+
var err error
131132
initializing := false
132133

133134
// Optimization: only call _bootstrap._lock_unlock_module() if
134135
// __initializing__ is true.
135136
// NOTE: because of this, __initializing__ must be set *before*
136137
// stuffing the new module in sys.modules.
137138

138-
value = GetAttrOrNil(mod, "__initializing__")
139-
if value != nil {
139+
value, err = GetAttrErr(mod, "__initializing__")
140+
if err == nil {
140141
initializing = bool(MakeBool(value).(Bool))
141142
}
142143
if initializing {

py/internal.go

+52-52
Original file line numberDiff line numberDiff line change
@@ -123,88 +123,74 @@ func SetItem(self Object, key Object, value Object) Object {
123123
panic(ExceptionNewf(TypeError, "'%s' object does not support item assignment", self.Type().Name))
124124
}
125125

126-
// GetAttrOrNil - returns the result nil if attribute not found
127-
func GetAttrOrNil(self Object, key string) (res Object) {
126+
// GetAttrErr - returns the result or an err to be raised if not found
127+
func GetAttrErr(self Object, key string) (res Object, err error) {
128128
defer func() {
129129
if r := recover(); r != nil {
130130
if IsException(AttributeError, r) {
131-
// AttributeError caught - return nil
131+
// AttributeError caught - return nil and error
132132
res = nil
133+
err = r.(error)
133134
} else {
134135
// Propagate the exception
135136
panic(r)
136137
}
137138
}
138139
}()
139140

140-
// Call __getattribute unconditionally if it exists
141+
// Call __getattribute__ unconditionally if it exists
141142
if I, ok := self.(I__getattribute__); ok {
142143
res = I.M__getattribute__(key)
143-
goto found
144+
return
144145
} else if res, ok = TypeCall1(self, "__getattribute__", Object(String(key))); ok {
145-
// FIXME catch AttributeError here
146-
goto found
146+
return
147147
}
148148

149-
if t, ok := self.(*Type); ok {
150-
// Now look in the instance dictionary etc
151-
res = t.GetAttrOrNil(key)
152-
if res != nil {
153-
goto found
149+
// Look in the instance dictionary if it exists
150+
if I, ok := self.(IGetDict); ok {
151+
dict := I.GetDict()
152+
res, ok = dict[key]
153+
if ok {
154+
return
154155
}
155-
} else {
156-
// Now look in type's dictionary etc
157-
res = self.Type().NativeGetAttrOrNil(key)
158-
if res != nil {
159-
goto found
156+
}
157+
158+
// Now look in type's dictionary etc
159+
t := self.Type()
160+
res = t.NativeGetAttrOrNil(key)
161+
if res != nil {
162+
// Call __get__ which creates bound methods, reads properties etc
163+
if I, ok := res.(I__get__); ok {
164+
res = I.M__get__(self, t)
160165
}
161-
// FIXME introspection for M__methods__ on non *Type objects
166+
return
162167
}
163168

164169
// And now only if not found call __getattr__
165170
if I, ok := self.(I__getattr__); ok {
166171
res = I.M__getattr__(key)
167-
goto found
172+
return
168173
} else if res, ok = TypeCall1(self, "__getattr__", Object(String(key))); ok {
169-
goto found
174+
return
170175
}
171176

172177
// Not found - return nil
173178
res = nil
174-
return
175-
176-
found:
177-
// FIXME if self is an instance then if it returning a function then it needs to return a bound method?
178-
// otherwise it should return a function
179-
//
180-
// >>> str.find
181-
// <method 'find' of 'str' objects>
182-
// >>> "".find
183-
// <built-in method find of str object at 0x7f929bd54c00>
184-
// >>>
185-
//
186-
// created by PyMethod_New defined in classobject.c
187-
// called by type.tp_descr_get
188-
189-
// FIXME Not completely correct!
190-
// Should be using __get__
191-
switch res.(type) {
192-
case *Function, *Method:
193-
res = NewBoundMethod(self, res)
194-
}
179+
err = ExceptionNewf(AttributeError, "'%s' has no attribute '%s'", self.Type().Name, key)
195180
return
196181
}
197182

198-
// GetAttrString
183+
// GetAttrString gets the attribute, raising an error if not found
199184
func GetAttrString(self Object, key string) Object {
200-
res := GetAttrOrNil(self, key)
201-
if res == nil {
202-
panic(ExceptionNewf(AttributeError, "'%s' has no attribute '%s'", self.Type().Name, key))
185+
res, err := GetAttrErr(self, key)
186+
if err != nil {
187+
panic(err)
203188
}
204189
return res
205190
}
206191

207-
// GetAttr
192+
// GetAttr gets the attribute rasing an error if key isn't a string or
193+
// attribute not found
208194
func GetAttr(self Object, keyObj Object) Object {
209195
if key, ok := keyObj.(String); ok {
210196
return GetAttrString(self, string(key))
@@ -214,22 +200,36 @@ func GetAttr(self Object, keyObj Object) Object {
214200

215201
// SetAttrString
216202
func SetAttrString(self Object, key string, value Object) Object {
203+
// First look in type's dictionary etc for a property that could
204+
// be set - do this before looking in the instance dictionary
205+
setter := self.Type().NativeGetAttrOrNil(key)
206+
if setter != nil {
207+
// Call __set__ which writes properties etc
208+
if I, ok := setter.(I__set__); ok {
209+
return I.M__set__(self, value)
210+
}
211+
}
212+
213+
// If we have __setattr__ then use that
217214
if I, ok := self.(I__setattr__); ok {
218215
return I.M__setattr__(key, value)
219216
} else if res, ok := TypeCall2(self, "__setattr__", String(key), value); ok {
220217
return res
221218
}
222219

223-
// Set the attribute on *Type
224-
if t, ok := self.(*Type); ok {
225-
if t.Dict == nil {
226-
t.Dict = make(StringDict)
220+
// Otherwise set the attribute in the instance dictionary if
221+
// possible
222+
if I, ok := self.(IGetDict); ok {
223+
dict := I.GetDict()
224+
if dict == nil {
225+
panic(ExceptionNewf(SystemError, "nil Dict in %s", self.Type().Name))
227226
}
228-
t.Dict[key] = value
227+
dict[key] = value
229228
return None
230229
}
231230

232-
panic(ExceptionNewf(TypeError, "'%s' object does not support setting attributes", self.Type().Name))
231+
// If not blow up
232+
panic(ExceptionNewf(AttributeError, "'%s' object has no attribute '%s'", self.Type().Name, key))
233233
}
234234

235235
// SetAttr

py/method.go

+9
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,15 @@ func (m *Method) M__call__(args Tuple, kwargs StringDict) Object {
137137
return result
138138
}
139139

140+
// Read a method from a class which makes a bound method
141+
func (m *Method) M__get__(instance, owner Object) Object {
142+
if instance != None {
143+
return NewBoundMethod(instance, m)
144+
}
145+
return m
146+
}
147+
140148
// Make sure it satisfies the interface
141149
var _ Object = (*Method)(nil)
142150
var _ I__call__ = (*Method)(nil)
151+
var _ I__get__ = (*Method)(nil)

py/module.go

+7-17
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ func (o *Module) Type() *Type {
3131
return ModuleType
3232
}
3333

34+
// Get the Dict
35+
func (m *Module) GetDict() StringDict {
36+
return m.Globals
37+
}
38+
3439
// Define a new module
3540
func NewModule(name, doc string, methods []*Method, globals StringDict) *Module {
3641
m := &Module{
@@ -61,23 +66,8 @@ func NewModule(name, doc string, methods []*Method, globals StringDict) *Module
6166

6267
// Calls a named method of a module
6368
func (m *Module) Call(name string, args Tuple, kwargs StringDict) Object {
64-
return Call(m.M__getattribute__(name), args, kwargs)
65-
}
66-
67-
// Get an attribute from the module
68-
func (m *Module) M__getattribute__(name string) Object {
69-
res, ok := m.Globals[name]
70-
if !ok {
71-
panic(ExceptionNewf(AttributeError, "module '%s' has no attribute '%s'", m.Name, name))
72-
}
73-
return res
74-
}
75-
76-
func (m *Module) M__setattr__(name string, value Object) Object {
77-
m.Globals[name] = value
78-
return None
69+
return Call(GetAttrString(m, name), args, kwargs)
7970
}
8071

8172
// Interfaces
82-
var _ I__getattribute__ = (*Module)(nil)
83-
var _ I__setattr__ = (*Module)(nil)
73+
var _ IGetDict = (*Module)(nil)

py/py.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ type Object interface {
66
Type() *Type
77
}
88

9+
// Optional interfaces
10+
type IGetDict interface {
11+
GetDict() StringDict
12+
}
13+
914
// Some well known objects
1015
var (
1116
Ellipsis Object
@@ -390,8 +395,8 @@ type I__get__ interface {
390395
M__get__(instance, owner Object) Object
391396
}
392397

393-
// Called to set the attribute on an instance instance of the owner
394-
// class to a new value, value.
398+
// Called to set the attribute on an instance of the owner
399+
// class to a new value.
395400
//object.__set__(self, instance, value)
396401
type I__set__ interface {
397402
M__set__(instance, value Object) Object

py/type.go

+13-5
Original file line numberDiff line numberDiff line change
@@ -171,12 +171,14 @@ type Type struct {
171171
var TypeType *Type = &Type{
172172
Name: "type",
173173
Doc: "type(object) -> the object's type\ntype(name, bases, dict) -> a new type",
174+
Dict: StringDict{},
174175
}
175176

176177
var ObjectType = &Type{
177178
Name: "object",
178179
Doc: "The most base type",
179180
Flags: TPFLAGS_BASETYPE,
181+
Dict: StringDict{},
180182
}
181183

182184
func init() {
@@ -196,6 +198,11 @@ func (t *Type) Type() *Type {
196198
return t.ObjectType
197199
}
198200

201+
// Get the Dict
202+
func (t *Type) GetDict() StringDict {
203+
return t.Dict
204+
}
205+
199206
// Make a new type from a name
200207
//
201208
// For making Go types
@@ -1091,12 +1098,12 @@ func best_base(bases Tuple) *Type {
10911098

10921099
// Generic object allocator
10931100
func (t *Type) Alloc() *Type {
1094-
obj := new(Type)
1095-
10961101
// Set the type of the new object to this type
1097-
obj.ObjectType = t
1098-
obj.Base = t
1099-
1102+
obj := &Type{
1103+
ObjectType: t,
1104+
Base: t,
1105+
Dict: StringDict{},
1106+
}
11001107
return obj
11011108
}
11021109

@@ -1585,3 +1592,4 @@ func ObjectNew(t *Type, args Tuple, kwargs StringDict) Object {
15851592
// Make sure it satisfies the interface
15861593
var _ Object = (*Type)(nil)
15871594
var _ I__call__ = (*Type)(nil)
1595+
var _ IGetDict = (*Type)(nil)

0 commit comments

Comments
 (0)