Skip to content

Commit f18c29d

Browse files
committed
Define exception heirachy
1 parent 317101a commit f18c29d

File tree

3 files changed

+259
-25
lines changed

3 files changed

+259
-25
lines changed

builtin/builtin.go

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,11 @@ func init() {
5757
// py.NewMethod("vars", builtin_vars, 0, vars_doc),
5858
}
5959
globals := py.StringDict{
60-
"None": py.None,
61-
"Ellipsis": py.Ellipsis,
62-
"NotImplemented": py.NotImplemented,
63-
"False": py.False,
64-
"True": py.True,
65-
"bool": py.BoolType,
60+
"None": py.None,
61+
"Ellipsis": py.Ellipsis,
62+
"False": py.False,
63+
"True": py.True,
64+
"bool": py.BoolType,
6665
// "memoryview": py.MemoryViewType,
6766
// "bytearray": py.ByteArrayType,
6867
"bytes": py.BytesType,
@@ -77,7 +76,7 @@ func init() {
7776
"int": py.IntType, // FIXME LongType?
7877
"list": py.ListType,
7978
// "map": py.MapType,
80-
"object": py.BaseObjectType,
79+
"object": py.ObjectType,
8180
"range": py.RangeType,
8281
// "reversed": py.ReversedType,
8382
"set": py.SetType,
@@ -88,6 +87,72 @@ func init() {
8887
"tuple": py.TupleType,
8988
"type": py.TypeType,
9089
// "zip": py.ZipType,
90+
91+
// Exceptions
92+
"ArithmeticError": py.ArithmeticError,
93+
"AssertionError": py.AssertionError,
94+
"AttributeError": py.AttributeError,
95+
"BaseException": py.BaseException,
96+
"BlockingIOError": py.BlockingIOError,
97+
"BrokenPipeError": py.BrokenPipeError,
98+
"BufferError": py.BufferError,
99+
"BytesWarning": py.BytesWarning,
100+
"ChildProcessError": py.ChildProcessError,
101+
"ConnectionAbortedError": py.ConnectionAbortedError,
102+
"ConnectionError": py.ConnectionError,
103+
"ConnectionRefusedError": py.ConnectionRefusedError,
104+
"ConnectionResetError": py.ConnectionResetError,
105+
"DeprecationWarning": py.DeprecationWarning,
106+
"EOFError": py.EOFError,
107+
"EnvironmentError": py.OSError,
108+
"Exception": py.ExceptionType,
109+
"FileExistsError": py.FileExistsError,
110+
"FileNotFoundError": py.FileNotFoundError,
111+
"FloatingPointError": py.FloatingPointError,
112+
"FutureWarning": py.FutureWarning,
113+
"GeneratorExit": py.GeneratorExit,
114+
"IOError": py.OSError,
115+
"ImportError": py.ImportError,
116+
"ImportWarning": py.ImportWarning,
117+
"IndentationError": py.IndentationError,
118+
"IndexError": py.IndexError,
119+
"InterruptedError": py.InterruptedError,
120+
"IsADirectoryError": py.IsADirectoryError,
121+
"KeyError": py.KeyError,
122+
"KeyboardInterrupt": py.KeyboardInterrupt,
123+
"LookupError": py.LookupError,
124+
"MemoryError": py.MemoryError,
125+
"NameError": py.NameError,
126+
"NotADirectoryError": py.NotADirectoryError,
127+
"NotImplemented": py.NotImplemented,
128+
"NotImplementedError": py.NotImplementedError,
129+
"OSError": py.OSError,
130+
"OverflowError": py.OverflowError,
131+
"PendingDeprecationWarning": py.PendingDeprecationWarning,
132+
"PermissionError": py.PermissionError,
133+
"ProcessLookupError": py.ProcessLookupError,
134+
"ReferenceError": py.ReferenceError,
135+
"ResourceWarning": py.ResourceWarning,
136+
"RuntimeError": py.RuntimeError,
137+
"RuntimeWarning": py.RuntimeWarning,
138+
"StopIteration": py.StopIteration,
139+
"SyntaxError": py.SyntaxError,
140+
"SyntaxWarning": py.SyntaxWarning,
141+
"SystemError": py.SystemError,
142+
"SystemExit": py.SystemExit,
143+
"TabError": py.TabError,
144+
"TimeoutError": py.TimeoutError,
145+
"TypeError": py.TypeError,
146+
"UnboundLocalError": py.UnboundLocalError,
147+
"UnicodeDecodeError": py.UnicodeDecodeError,
148+
"UnicodeEncodeError": py.UnicodeEncodeError,
149+
"UnicodeError": py.UnicodeError,
150+
"UnicodeTranslateError": py.UnicodeTranslateError,
151+
"UnicodeWarning": py.UnicodeWarning,
152+
"UserWarning": py.UserWarning,
153+
"ValueError": py.ValueError,
154+
"Warning": py.Warning,
155+
"ZeroDivisionError": py.ZeroDivisionError,
91156
}
92157
py.NewModule("builtins", builtin_doc, methods, globals)
93158
}

py/exception.go

Lines changed: 102 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
package py
44

5+
import (
6+
"fmt"
7+
)
8+
59
// A python Exception object
610
type Exception struct {
7-
Name string // name of the exception FIXME should be part of class machinery
11+
Base *Type
812
Args Object
913
Traceback Object
1014
Context Object
@@ -14,27 +18,108 @@ type Exception struct {
1418
}
1519

1620
var (
17-
// FIXME should be a class probably
18-
ExceptionType = NewType("exception", "Common base class for all exceptions")
19-
20-
// Some well known exceptions - these should be types?
21-
// FIXME exceptions should be created in builtins probably
22-
// their names should certainly go in there!
23-
NotImplemented = NewException("NotImplemented")
24-
StopIteration = NewException("StopIteration")
21+
// Exception heirachy
22+
BaseException = ObjectType.NewType("BaseException", "Common base class for all exceptions", ExceptionNew, nil)
23+
SystemExit = BaseException.NewType("SystemExit", "Request to exit from the interpreter.", nil, nil)
24+
KeyboardInterrupt = BaseException.NewType("KeyboardInterrupt", "Program interrupted by user.", nil, nil)
25+
GeneratorExit = BaseException.NewType("GeneratorExit", "Request that a generator exit.", nil, nil)
26+
ExceptionType = BaseException.NewType("Exception", "Common base class for all non-exit exceptions.", nil, nil)
27+
StopIteration = ExceptionType.NewType("StopIteration", "Signal the end from iterator.__next__().", nil, nil)
28+
ArithmeticError = ExceptionType.NewType("ArithmeticError", "Base class for arithmetic errors.", nil, nil)
29+
FloatingPointError = ArithmeticError.NewType("FloatingPointError", "Floating point operation failed.", nil, nil)
30+
OverflowError = ArithmeticError.NewType("OverflowError", "Result too large to be represented.", nil, nil)
31+
ZeroDivisionError = ArithmeticError.NewType("ZeroDivisionError", "Second argument to a division or modulo operation was zero.", nil, nil)
32+
AssertionError = ExceptionType.NewType("AssertionError", "Assertion failed.", nil, nil)
33+
AttributeError = ExceptionType.NewType("AttributeError", "Attribute not found.", nil, nil)
34+
BufferError = ExceptionType.NewType("BufferError", "Buffer error.", nil, nil)
35+
EOFError = ExceptionType.NewType("EOFError", "Read beyond end of file.", nil, nil)
36+
ImportError = ExceptionType.NewType("ImportError", "Import can't find module, or can't find name in module.", nil, nil)
37+
LookupError = ExceptionType.NewType("LookupError", "Base class for lookup errors.", nil, nil)
38+
IndexError = LookupError.NewType("IndexError", "Sequence index out of range.", nil, nil)
39+
KeyError = LookupError.NewType("KeyError", "Mapping key not found.", nil, nil)
40+
MemoryError = ExceptionType.NewType("MemoryError", "Out of memory.", nil, nil)
41+
NameError = ExceptionType.NewType("NameError", "Name not found globally.", nil, nil)
42+
UnboundLocalError = NameError.NewType("UnboundLocalError", "Local name referenced but not bound to a value.", nil, nil)
43+
OSError = ExceptionType.NewType("OSError", "Base class for I/O related errors.", nil, nil)
44+
BlockingIOError = OSError.NewType("BlockingIOError", "I/O operation would block.", nil, nil)
45+
ChildProcessError = OSError.NewType("ChildProcessError", "Child process error.", nil, nil)
46+
ConnectionError = OSError.NewType("ConnectionError", "Connection error.", nil, nil)
47+
BrokenPipeError = ConnectionError.NewType("BrokenPipeError", "Broken pipe.", nil, nil)
48+
ConnectionAbortedError = ConnectionError.NewType("ConnectionAbortedError", "Connection aborted.", nil, nil)
49+
ConnectionRefusedError = ConnectionError.NewType("ConnectionRefusedError", "Connection refused.", nil, nil)
50+
ConnectionResetError = ConnectionError.NewType("ConnectionResetError", "Connection reset.", nil, nil)
51+
FileExistsError = OSError.NewType("FileExistsError", "File already exists.", nil, nil)
52+
FileNotFoundError = OSError.NewType("FileNotFoundError", "File not found.", nil, nil)
53+
InterruptedError = OSError.NewType("InterruptedError", "Interrupted by signal.", nil, nil)
54+
IsADirectoryError = OSError.NewType("IsADirectoryError", "Operation doesn't work on directories.", nil, nil)
55+
NotADirectoryError = OSError.NewType("NotADirectoryError", "Operation only works on directories.", nil, nil)
56+
PermissionError = OSError.NewType("PermissionError", "Not enough permissions.", nil, nil)
57+
ProcessLookupError = OSError.NewType("ProcessLookupError", "Process not found.", nil, nil)
58+
TimeoutError = OSError.NewType("TimeoutError", "Timeout expired.", nil, nil)
59+
ReferenceError = ExceptionType.NewType("ReferenceError", "Weak ref proxy used after referent went away.", nil, nil)
60+
RuntimeError = ExceptionType.NewType("RuntimeError", "Unspecified run-time error.", nil, nil)
61+
NotImplementedError = RuntimeError.NewType("NotImplementedError", "Method or function hasn't been implemented yet.", nil, nil)
62+
SyntaxError = ExceptionType.NewType("SyntaxError", "Invalid syntax.", nil, nil)
63+
IndentationError = SyntaxError.NewType("IndentationError", "Improper indentation.", nil, nil)
64+
TabError = IndentationError.NewType("TabError", "Improper mixture of spaces and tabs.", nil, nil)
65+
SystemError = ExceptionType.NewType("SystemError", "Internal error in the Python interpreter.\n\nPlease report this to the Python maintainer, along with the traceback,\nthe Python version, and the hardware/OS platform and version.", nil, nil)
66+
TypeError = ExceptionType.NewType("TypeError", "Inappropriate argument type.", nil, nil)
67+
ValueError = ExceptionType.NewType("ValueError", "Inappropriate argument value (of correct type).", nil, nil)
68+
UnicodeError = ValueError.NewType("UnicodeError", "Unicode related error.", nil, nil)
69+
UnicodeDecodeError = UnicodeError.NewType("UnicodeDecodeError", "Unicode decoding error.", nil, nil)
70+
UnicodeEncodeError = UnicodeError.NewType("UnicodeEncodeError", "Unicode encoding error.", nil, nil)
71+
UnicodeTranslateError = UnicodeError.NewType("UnicodeTranslateError", "Unicode translation error.", nil, nil)
72+
Warning = ExceptionType.NewType("Warning", "Base class for warning categories.", nil, nil)
73+
DeprecationWarning = Warning.NewType("DeprecationWarning", "Base class for warnings about deprecated features.", nil, nil)
74+
PendingDeprecationWarning = Warning.NewType("PendingDeprecationWarning", "Base class for warnings about features which will be deprecated\nin the future.", nil, nil)
75+
RuntimeWarning = Warning.NewType("RuntimeWarning", "Base class for warnings about dubious runtime behavior.", nil, nil)
76+
SyntaxWarning = Warning.NewType("SyntaxWarning", "Base class for warnings about dubious syntax.", nil, nil)
77+
UserWarning = Warning.NewType("UserWarning", "Base class for warnings generated by user code.", nil, nil)
78+
FutureWarning = Warning.NewType("FutureWarning", "Base class for warnings about constructs that will change semantically\nin the future.", nil, nil)
79+
ImportWarning = Warning.NewType("ImportWarning", "Base class for warnings about probable mistakes in module imports", nil, nil)
80+
UnicodeWarning = Warning.NewType("UnicodeWarning", "Base class for warnings about Unicode related problems, mostly\nrelated to conversion problems.", nil, nil)
81+
BytesWarning = Warning.NewType("BytesWarning", "Base class for warnings about bytes and buffer related problems, mostly\nrelated to conversion from str or comparing to str.", nil, nil)
82+
ResourceWarning = Warning.NewType("ResourceWarning", "Base class for warnings about resource usage.", nil, nil)
83+
// Singleton exceptions
84+
NotImplemented = NotImplementedError.M__call__(nil, nil)
2585
)
2686

2787
// Type of this object
2888
func (o *Exception) Type() *Type {
29-
return ExceptionType
89+
return o.Base
3090
}
3191

32-
// Define a new exception
33-
//
34-
// FIXME need inheritance machinery to make this work properly
35-
func NewException(name string) *Exception {
36-
m := &Exception{
37-
Name: name,
92+
// RangeNew
93+
func ExceptionNew(metatype *Type, args Tuple, kwargs StringDict) Object {
94+
if len(kwargs) != 0 {
95+
// TypeError
96+
panic(fmt.Sprintf("TypeError: %s does not take keyword arguments", metatype.Name))
97+
}
98+
return &Exception{
99+
Base: metatype,
100+
Args: args.Copy(),
38101
}
39-
return m
40102
}
103+
104+
/*
105+
#define PyType_HasFeature(t,f) (((t)->tp_flags & (f)) != 0)
106+
107+
#define PyType_FastSubclass(t,f) PyType_HasFeature(t,f)
108+
109+
#define PyType_Check(op) \
110+
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TYPE_SUBCLASS)
111+
112+
#define PyType_CheckExact(op) (Py_TYPE(op) == &PyType_Type)
113+
114+
#define PyExceptionClass_Check(x) \
115+
(PyType_Check((x)) && \
116+
PyType_FastSubclass((PyTypeObject*)(x), Py_TPFLAGS_BASE_EXC_SUBCLASS))
117+
118+
#define PyExceptionInstance_Check(x) \
119+
PyType_FastSubclass((x)->ob_type, Py_TPFLAGS_BASE_EXC_SUBCLASS)
120+
121+
#define PyExceptionClass_Name(x) \
122+
((char *)(((PyTypeObject*)(x))->tp_name))
123+
124+
#define PyExceptionInstance_Class(x) ((PyObject*)((x)->ob_type))
125+
*/

vm/eval.go

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,7 +692,7 @@ func do_JUMP_ABSOLUTE(vm *Vm, target int32) {
692692
func do_FOR_ITER(vm *Vm, delta int32) {
693693
defer func() {
694694
if r := recover(); r != nil {
695-
if ex, ok := r.(*py.Exception); ok && ex == py.StopIteration {
695+
if ex, ok := r.(*py.Exception); ok && ex.Base == py.StopIteration {
696696
// StopIteration raised
697697
vm.DROP()
698698
vm.frame.Lasti += delta
@@ -788,10 +788,94 @@ func do_DELETE_DEREF(vm *Vm, i int32) {
788788
vm.NotImplemented("DELETE_DEREF", i)
789789
}
790790

791+
// Logic for the raise statement
792+
func do_raise(exc, cause py.Object) vmExit {
793+
/*
794+
// FIXME need to keep the current exception somewhere as this needs to change it
795+
// in the vm like cpython or maybe return it
796+
var t *py.Type
797+
var value py.Object
798+
if exc == nil {
799+
// Reraise
800+
panic("FIXME can't re-raise yet")
801+
// PyThreadState * tstate = PyThreadState_GET()
802+
// PyObject * tb
803+
// t = tstate.exc_type
804+
// value = tstate.exc_value
805+
// tb = tstate.exc_traceback
806+
// if t == Py_None {
807+
// PyErr_SetString(PyExc_RuntimeError, "No active exception to reraise")
808+
// return exitException
809+
// }
810+
// PyErr_Restore(t, value, tb)
811+
return exitReraise
812+
}
813+
814+
// We support the following forms of raise:
815+
// raise
816+
// raise <instance>
817+
// raise <type>
818+
if py.ExceptionClassCheck(exc) {
819+
t = exc.(*py.Type)
820+
value = py.Call(exc, nil, nil)
821+
if value == nil {
822+
return exitException
823+
}
824+
if !py.ExceptionInstanceCheck(value) {
825+
PyErr_Format(PyExc_TypeError, "calling %s should have returned an instance of BaseException, not %s", t.Name, value.Type().Name)
826+
return exitException
827+
}
828+
} else if t = py.ExceptionInstanceCheck(exc); t != nil {
829+
value = exc
830+
} else {
831+
// Not something you can raise. You get an exception
832+
// anyway, just not what you specified :-)
833+
PyErr_SetString(PyExc_TypeError, "exceptions must derive from BaseException")
834+
return exitException
835+
}
836+
837+
if cause != nil {
838+
var fixed_cause py.Object
839+
if py.ExceptionClassCheck(cause) {
840+
fixed_cause = py.Call(cause, nil, nil)
841+
if fixed_cause == nil {
842+
return exitException
843+
}
844+
} else if py.ExceptionInstanceCheck(cause) {
845+
fixed_cause = cause
846+
} else if cause == py.None {
847+
fixed_cause = nil
848+
} else {
849+
PyErr_SetString(PyExc_TypeError, "exception causes must derive from BaseException")
850+
return exitException
851+
}
852+
PyException_SetCause(value, fixed_cause)
853+
}
854+
855+
PyErr_SetObject(t, value)
856+
return WHY_EXCEPTION
857+
*/
858+
return exitException
859+
}
860+
791861
// Raises an exception. argc indicates the number of parameters to the
792862
// raise statement, ranging from 0 to 3. The handler will find the
793863
// traceback as TOS2, the parameter as TOS1, and the exception as TOS.
794864
func do_RAISE_VARARGS(vm *Vm, argc int32) {
865+
// var v, w py.Object
866+
// switch argc {
867+
// case 2:
868+
// v = vm.POP() // cause
869+
// fallthrough
870+
// case 1:
871+
// w = vm.POP() // exc
872+
// fallthrough
873+
// case 0:
874+
// why = do_raise(w, v)
875+
// default:
876+
// PyErr_SetString(PyExc_SystemError, "bad RAISE_VARARGS oparg")
877+
// why = WHY_EXCEPTION
878+
// }
795879
vm.NotImplemented("RAISE_VARARGS", argc)
796880
}
797881

0 commit comments

Comments
 (0)