Skip to content

Commit 40e3f41

Browse files
committed
Return bound methods on attribute access
1 parent 9510b77 commit 40e3f41

File tree

3 files changed

+66
-11
lines changed

3 files changed

+66
-11
lines changed

py/boundmethod.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// BoundMethod object
2+
//
3+
// Combines an object and a callable
4+
5+
package py
6+
7+
// A python BoundMethod object
8+
type BoundMethod struct {
9+
Self Object
10+
Method Object
11+
}
12+
13+
var BoundMethodType = NewType("boundmethod", "boundmethod object")
14+
15+
// Type of this object
16+
func (o *BoundMethod) Type() *Type {
17+
return BoundMethodType
18+
}
19+
20+
// Define a new boundmethod
21+
func NewBoundMethod(self, method Object) *BoundMethod {
22+
return &BoundMethod{Self: self, Method: method}
23+
}
24+
25+
// Call the bound method
26+
func (bm *BoundMethod) M__call__(args Tuple, kwargs StringDict) Object {
27+
newArgs := make(Tuple, len(args)+1)
28+
newArgs[0] = bm.Self
29+
copy(newArgs[1:], args)
30+
return Call(bm.Method, newArgs, kwargs)
31+
}

py/function.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func (f *Function) LocalsForCall(args Tuple) StringDict {
8888
// fmt.Printf("call f %#v with %v\n", f, args)
8989
if len(args) != int(f.Code.Argcount) {
9090
// FIXME don't know how to deal with default args
91-
panic("Wrong number of arguments")
91+
panic(fmt.Sprintf("Wrong number of arguments: expecting %d but got %d: %#v", f.Code.Argcount, len(args), args))
9292
}
9393
// FIXME not sure this is right!
9494
// Copy the args into the local variables

py/internal.go

+34-10
Original file line numberDiff line numberDiff line change
@@ -100,34 +100,58 @@ func SetItem(self Object, key Object, value Object) Object {
100100
}
101101

102102
// GetAttrOrNil - returns the result nil if attribute not found
103-
func GetAttrOrNil(self Object, key string) Object {
103+
func GetAttrOrNil(self Object, key string) (res Object) {
104104
// Call __getattribute unconditionally if it exists
105105
if I, ok := self.(I__getattribute__); ok {
106-
return I.M__getattribute__(Object(String(key)))
107-
} else if res, ok := TypeCall1(self, "__getattribute__", Object(String(key))); ok {
106+
res = I.M__getattribute__(Object(String(key)))
107+
goto found
108+
} else if res, ok = TypeCall1(self, "__getattribute__", Object(String(key))); ok {
108109
// FIXME catch AttributeError here
109-
return res
110+
goto found
110111
}
111112

112113
if t, ok := self.(*Type); ok {
113114
// Now look in the instance dictionary etc
114-
res := t.GetAttrOrNil(key)
115+
res = t.GetAttrOrNil(key)
115116
if res != nil {
116-
return res
117+
goto found
117118
}
118119
} else {
119120
// FIXME introspection for M__methods__ on non *Type objects
120121
}
121122

122123
// And now only if not found call __getattr__
123124
if I, ok := self.(I__getattr__); ok {
124-
return I.M__getattr__(Object(String(key)))
125-
} else if res, ok := TypeCall1(self, "__getitem__", Object(String(key))); ok {
126-
return res
125+
res = I.M__getattr__(Object(String(key)))
126+
goto found
127+
} else if res, ok = TypeCall1(self, "__getattr__", Object(String(key))); ok {
128+
goto found
127129
}
128130

129131
// Not found - return nil
130-
return nil
132+
res = nil
133+
return
134+
135+
found:
136+
// FIXME if self is an instance then if it returning a function then it needs to return a bound method?
137+
// otherwise it should return a function
138+
//
139+
// >>> str.find
140+
// <method 'find' of 'str' objects>
141+
// >>> "".find
142+
// <built-in method find of str object at 0x7f929bd54c00>
143+
// >>>
144+
//
145+
// created by PyMethod_New defined in classobject.c
146+
// called by type.tp_descr_get
147+
148+
// FIXME Not completely correct!
149+
// Should be using __get__
150+
switch res.(type) {
151+
case *Function, *Method:
152+
res = NewBoundMethod(self, res)
153+
}
154+
return
131155
}
132156

133157
// GetAttrString

0 commit comments

Comments
 (0)