diff --git a/docs/modules/traceback.md b/docs/modules/traceback.md new file mode 100644 index 00000000..8cdfef11 --- /dev/null +++ b/docs/modules/traceback.md @@ -0,0 +1,12 @@ +--- +icon: package +label: traceback +--- + +### `trackback.print_exc() -> None` + +Print the last exception and its traceback. + +### `trackback.format_exc() -> str` + +Return the last exception and its traceback as a string. \ No newline at end of file diff --git a/docs/quick-start/bind.md b/docs/quick-start/bind.md index e1ddb346..30f38ada 100644 --- a/docs/quick-start/bind.md +++ b/docs/quick-start/bind.md @@ -103,4 +103,36 @@ For example, `vm->bind__add__` is preferred over `vm->bind_method<1>(type, "__ad ### Bind a property -You can use `vm->property(...)` to create a `property` object and assign it to an type object. \ No newline at end of file +a property is a python's `property` that attached to a type instance with a getter and an optional setter. It is a data descriptor. A property redirects attribute access to specific functions. + +You can use `@property` to create python property or use `vm->property` to create native property. + +```cpp +struct Point { + PY_CLASS(Point, test, Point); + + int x; + int y; + + Point(int x, int y) : x(x), y(y) {} + + static void _register(VM *vm, auto mod, auto type) { + vm->bind_constructor<3>(type, [](VM *vm, auto args) { + auto x = CAST(i64, args[1]); + auto y = CAST(i64, args[2]); + return VAR_T(Point, x, y); + }); + + // getter and setter of property `x` + type->attr().set("x", vm->property([](VM* vm, ArgsView args){ + Point& self = CAST(Point&, args[0]); + return VAR(self.x); + }, + [](VM* vm, ArgsView args){ + Point& self = CAST(Point&, args[0]); + self.x = CAST(int, args[1]); + return vm->None; + })); + } +}; +``` \ No newline at end of file diff --git a/src/ceval.h b/src/ceval.h index bcac0722..523339b6 100644 --- a/src/ceval.h +++ b/src/ceval.h @@ -583,6 +583,7 @@ __NEXT_STEP:; _error(StrName(byte.arg), msg); } DISPATCH(); TARGET(RE_RAISE) _raise(); DISPATCH(); + TARGET(POP_EXCEPTION) _last_exception = POPX(); DISPATCH(); /*****************************************/ TARGET(SETUP_DOCSTRING) TOP()->attr().set(__doc__, co_consts[byte.arg]); diff --git a/src/compiler.h b/src/compiler.h index 468137f0..6189d92b 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -624,7 +624,7 @@ __SUBSCR_END: } int patch = ctx()->emit(OP_POP_JUMP_IF_FALSE, BC_NOARG, BC_KEEPLINE); // pop the exception on match - ctx()->emit(OP_POP_TOP, BC_NOARG, BC_KEEPLINE); + ctx()->emit(OP_POP_EXCEPTION, BC_NOARG, BC_KEEPLINE); compile_block_body(); patches.push_back(ctx()->emit(OP_JUMP_ABSOLUTE, BC_NOARG, BC_KEEPLINE)); ctx()->patch_jump(patch); diff --git a/src/opcodes.h b/src/opcodes.h index ae9d2139..739b2c37 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -113,6 +113,7 @@ OPCODE(ASSERT) OPCODE(EXCEPTION_MATCH) OPCODE(RAISE) OPCODE(RE_RAISE) +OPCODE(POP_EXCEPTION) /**************************/ OPCODE(SETUP_DOCSTRING) OPCODE(FORMAT_STRING) diff --git a/src/pocketpy.h b/src/pocketpy.h index 9752e919..2b2c0f09 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -1155,6 +1155,22 @@ inline void add_module_math(VM* vm){ }); } +inline void add_module_traceback(VM* vm){ + PyObject* mod = vm->new_module("traceback"); + vm->bind_func<0>(mod, "print_exc", [](VM* vm, ArgsView args) { + if(vm->_last_exception==nullptr) vm->ValueError("no exception"); + Exception& e = CAST(Exception&, vm->_last_exception); + vm->_stdout(vm, e.summary()); + return vm->None; + }); + + vm->bind_func<0>(mod, "format_exc", [](VM* vm, ArgsView args) { + if(vm->_last_exception==nullptr) vm->ValueError("no exception"); + Exception& e = CAST(Exception&, vm->_last_exception); + return VAR(e.summary()); + }); +} + inline void add_module_dis(VM* vm){ PyObject* mod = vm->new_module("dis"); vm->bind_func<1>(mod, "dis", [](VM* vm, ArgsView args) { @@ -1306,6 +1322,7 @@ inline void VM::post_init(){ init_builtins(this); #if !DEBUG_NO_BUILTIN_MODULES add_module_sys(this); + add_module_traceback(this); add_module_time(this); add_module_json(this); add_module_math(this); diff --git a/src/vm.h b/src/vm.h index eadf283b..5ed6ce33 100644 --- a/src/vm.h +++ b/src/vm.h @@ -123,6 +123,8 @@ public: PyObject* StopIteration; PyObject* _main; // __main__ module + PyObject* _last_exception; + PrintFunc _stdout; PrintFunc _stderr; @@ -142,6 +144,7 @@ public: _stderr = [](VM* vm, const Str& s) { std::cerr << s; }; callstack.reserve(8); _main = nullptr; + _last_exception = nullptr; init_builtin_types(); } @@ -1448,7 +1451,8 @@ inline void ManagedHeap::mark() { for(PyObject* obj: _no_gc) OBJ_MARK(obj); for(auto& frame : vm->callstack.data()) frame._gc_mark(); for(PyObject* obj: vm->s_data) OBJ_MARK(obj); - if(_gc_marker_ex != nullptr) _gc_marker_ex(vm); + if(_gc_marker_ex) _gc_marker_ex(vm); + if(vm->_last_exception) OBJ_MARK(vm->_last_exception); } inline Str obj_type_name(VM *vm, Type type){ diff --git a/tests/80_traceback.py b/tests/80_traceback.py new file mode 100644 index 00000000..25fbfec7 --- /dev/null +++ b/tests/80_traceback.py @@ -0,0 +1,10 @@ +import traceback + +try: + a = {'123': 4} + b = a[6] +except KeyError: + s = traceback.format_exc() + +assert s == r'''Traceback (most recent call last): +KeyError: 6''' \ No newline at end of file