--- icon: log title: 'Upgrade to v2.0.0' order: 25 --- We are applying a major API refactoring in this release. The main goal is to make the API more consistent and easier to use. We are also adding new features and improvements. This release is not backward compatible with the previous versions. Please read the following guide to upgrade your project. ## Alias for `PyObject*` We are going to make a very big change in `v2.0` which may break everything! In order to make this upgrade easier, we are introducing an alias for `PyObject*` in `v1.5.0`. ```cpp using PyVar = PyObject*; ``` Please replace all `PyObject*` with `PyVar` in your code in order to be compatible with `v2.0` in the future. We will try our best to keep the compatibility with `v1.x` series. ## Old style bindings We introduced the new style bindings `vm->bind` in [`v1.1.3`](https://github.com/pocketpy/pocketpy/releases/tag/v1.1.3) and deprecated the old style bindings `vm->bind_func<>` and `vm->bind_method<>`. In this release, we added an ARGC-based binding `vm->bind_func` (without template parameter) to replace the old style bindings. If you are still using `vm->bind_func<>` and `vm->bind_method<>`, please update your code to use `vm->bind_func` instead. ```cpp // old style (removed) vm->bind_func(obj, "add", f_add); vm->bind_method(obj, "get", f_get); // new ARGC-based binding vm->bind_func(obj, "add", N, f_add); vm->bind_func(obj, "get", M+1, f_get); // +1 for `self` ``` ## Class bindings Previously, if we want to write bindings for a C++ class, we need to add `PY_CLASS` macro and implement a magic method `_register`. This way was known as an intrusive way. For example: ```cpp struct Point{ PY_CLASS(Point, test, Point) int x, y; static void _register(VM* vm, PyObject* mod, PyObject* type){ // do bindings here } }; int main(){ VM* vm = new VM(); PyObject* mod = vm->new_module("test"); Point::register_class(vm, mod); return 0; } ``` In `v1.5.0`, the `PY_CLASS` macro was deprecated. We introduced a non-intrusive way to write class bindings via `vm->register_user_class<>`. The example above can be rewritten as follows: ```cpp struct Point{ int x, y; }; int main(){ VM* vm = new VM(); PyObject* mod = vm->new_module("test"); vm->register_user_class(mod, "Point", [](VM* vm, PyVar mod, PyVar type){ // do bindings here }); return 0; } ``` The magic method `_register` is kept for users who still wants to use the intrusive way. This is achieved by an overloaded version of `vm->register_user_class<>`. For example: ```cpp struct Point{ int x, y; static void _register(VM* vm, PyObject* mod, PyObject* type){ // do bindings here (if you like the intrusive way) } }; int main(){ VM* vm = new VM(); PyObject* mod = vm->new_module("test"); vm->register_user_class(mod, "Point"); return 0; } ``` ## Signature of `_import_handler` The signature of `_import_handler` was changed from: ```cpp unsigned char* (*)(const char* name_p, int name_size, int* out_size); ``` to: ```cpp unsigned char* (*)(const char* name, int* out_size); ``` This is because `str` object was ensured to be null-terminated after `v1.4.1`. ## Signature of `bind__next__` `vm->bind__next__` is a special method that is used to implement the iterator protocol. Previously, if you want to return multiple values, you need to pack them into a tuple. For example: ```cpp vm->bind__next__(type, [](VM* vm, PyVar _0){ if(is_end) return vm->StopIteration; // ... PyVar a = VAR(1); PyVar b = VAR(2); return VAR(Tuple(a, b)); }); ``` ```python for a, b in my_iter: # There is tuple creation and destruction for each iteration ... ``` It is not efficient because unnecessary tuple creation and destruction are involved. In `v1.5.0`, you need to use stack-based style to reimplement the above code: ```cpp vm->bind__next__(type, [](VM* vm, PyVar _0) -> unsigned{ if(is_end) return 0; // return 0 indicates StopIteration // ... PyVar a = VAR(1); PyVar b = VAR(2); vm->s_data.push(a); // directly push to the stack vm->s_data.push(b); // directly push to the stack return 2; // return the number of values }); ``` In this way, the interpreter only creates a tuple when it is necessary. It can improve ~25% performance when iterating over a large array. ```python for a, b in my_iter: # No tuple creation and destruction ... for t in my_iter: # Create a tuple lazily ... ``` ## Builtin function `next()` Previously, `next()` returns `StopIteration` when the iterator is exhausted. In `v1.5.0`, it raises `StopIteration` exception instead, which is consistent with CPython. If you like the old way, you can use the following snippet to import the old `next()` function: ```python from __builtins import next ``` Related C++ APIs do not change. They still return `vm->StopIteration` to indicate the end of iteration. ## User config support We used to read `user_config.h` file to override the default configurations. In `v1.5.0`, this is no longer supported. Please use config macros before `#include "pocketpy.h"` directly. ```cpp #define PK_ENABLE_OS 1 #define PK_ENABLE_THREAD 1 #define PK_ENABLE_PROFILER 1 // for all config macros, please refer to `include/pocketpy/common/config.h` #include "pocketpy.h" ``` ## Debugger and profiler We added a macro `PK_ENABLE_PROFILER` (default is 0) to control the availability of the builtin debugger and profiler. If you want to use them, for example, you want to call `breakpoint()` or `import line_profiler`, you need to set `PK_ENABLE_PROFILER` to 1 before `#include "pocketpy.h"`. ```cpp #define PK_ENABLE_PROFILER 1 #include "pocketpy.h" ``` !!! Enabling the profiler has a performance overhead. Only enable it when you need it. !!! ## Type checking functions + `vm->is_non_tagged_type` was removed. Use `vm->is_type` instead. + `vm->check_non_tagged_type` was removed. Use `vm->check_type` instead. ## Python stringify functions The following functions now return `Str` object instead of `PyVar`: + `vm->py_str` + `vm->py_repr` + `vm->py_json` Also, `vm->bind__str__` and `vm->bind__repr__` were changed to return `Str` object. ## Catching exceptions Now you need to catch `TopLevelException` instead of `Exception`.