BLUELOVETH пре 2 година
родитељ
комит
1614502741
8 измењених фајлова са 80 додато и 3 уклоњено
  1. 12 0
      docs/modules/traceback.md
  2. 33 1
      docs/quick-start/bind.md
  3. 1 0
      src/ceval.h
  4. 1 1
      src/compiler.h
  5. 1 0
      src/opcodes.h
  6. 17 0
      src/pocketpy.h
  7. 5 1
      src/vm.h
  8. 10 0
      tests/80_traceback.py

+ 12 - 0
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.

+ 33 - 1
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.
+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;
+    }));
+  }
+};
+```

+ 1 - 0
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]);

+ 1 - 1
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);

+ 1 - 0
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)

+ 17 - 0
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);

+ 5 - 1
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){

+ 10 - 0
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'''