Skip to content

Commit 4dedb41

Browse files
committed
Implement builtin.next and py.IsException
1 parent 84c03fa commit 4dedb41

File tree

3 files changed

+57
-7
lines changed

3 files changed

+57
-7
lines changed

builtin/builtin.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func init() {
4444
// py.NewMethod("locals", builtin_locals, py.METH_NOARGS, locals_doc),
4545
// py.NewMethod("max", builtin_max, 0, max_doc),
4646
// py.NewMethod("min", builtin_min, 0, min_doc),
47-
// py.NewMethod("next", builtin_next, 0, next_doc),
47+
py.NewMethod("next", builtin_next, 0, next_doc),
4848
// py.NewMethod("oct", builtin_oct, 0, oct_doc),
4949
// py.NewMethod("ord", builtin_ord, 0, ord_doc),
5050
py.NewMethod("pow", builtin_pow, 0, pow_doc),
@@ -301,3 +301,30 @@ func builtin___build_class__(self py.Object, args py.Tuple, kwargs py.StringDict
301301
fmt.Printf("Globals = %v, Locals = %v\n", fn.Globals, ns)
302302
return cls
303303
}
304+
305+
const next_doc = `next(iterator[, default])
306+
307+
Return the next item from the iterator. If default is given and the iterator
308+
is exhausted, it is returned instead of raising StopIteration.`
309+
310+
func builtin_next(self py.Object, args py.Tuple) (res py.Object) {
311+
var it, def py.Object
312+
313+
py.UnpackTuple(args, nil, "next", 1, 2, &it, &def)
314+
315+
if def != nil {
316+
defer func() {
317+
if r := recover(); r != nil {
318+
if py.IsException(py.StopIteration, r) {
319+
// Return defult on StopIteration
320+
res = def
321+
} else {
322+
// Re-raise
323+
panic(r)
324+
}
325+
}
326+
}()
327+
}
328+
329+
return py.Next(it)
330+
}

py/exception.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,33 @@ func ExceptionGivenMatches(err, exc Object) bool {
253253
return err == exc
254254
}
255255

256+
// IsException matches the result of recover to an exception
257+
//
258+
// For use to catch a single python exception from go code
259+
//
260+
// It can be an instance or the class itself
261+
func IsException(exception *Type, r interface{}) bool {
262+
var t *Type
263+
switch ex := r.(type) {
264+
case *Exception:
265+
t = ex.Type()
266+
case *Type:
267+
t = ex
268+
default:
269+
return false
270+
}
271+
// Exact instance or subclass match
272+
if t == exception {
273+
return true
274+
}
275+
// Can't be a subclass of exception
276+
if t.Flags&TPFLAGS_BASE_EXC_SUBCLASS == 0 {
277+
return false
278+
}
279+
// Now the full match
280+
return t.IsSubtype(exception)
281+
}
282+
256283
// Check Interfaces
257284
var _ error = (*Exception)(nil)
258285
var _ error = (*ExceptionInfo)(nil)

vm/eval.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,8 @@ func (vm *Vm) CheckException() {
117117
//
118118
// Otherwise deals with the as per vm.CheckException and returns false
119119
func (vm *Vm) catchStopIteration(r interface{}) bool {
120-
// FIXME match subclasses of StopIteration too?
121-
if ex, ok := r.(*py.Exception); ok && ex.Type() == py.StopIteration {
122-
// StopIteration() raised
123-
return true
124-
} else if ex, ok := r.(*py.Type); ok && ex == py.StopIteration {
125-
// StopIteration raised
120+
if py.IsException(py.StopIteration, r) {
121+
// StopIteration or subclass raises
126122
return true
127123
} else {
128124
// Deal with the exception as normal

0 commit comments

Comments
 (0)