#include "pocketpy/common/str.h" #include "pocketpy/objects/base.h" #include "pocketpy/objects/codeobject.h" #include "pocketpy/pocketpy.h" #include "pocketpy/common/utils.h" #include "pocketpy/common/sstream.h" #include "pocketpy/interpreter/vm.h" #include "pocketpy/common/_generated.h" py_Ref py_getmodule(const char* path) { VM* vm = pk_current_vm; return BinTree__try_get(&vm->modules, (void*)path); } static py_Ref pk_newmodule(const char* path, bool is_init) { c11_sv pathv = {path, strlen(path)}; if(pathv.size > PK_MAX_MODULE_PATH_LEN) c11__abort("module path too long: %s", path); if(pathv.size == 0) c11__abort("module path cannot be empty"); py_ModuleInfo* mi = py_newobject(py_retval(), tp_module, -1, sizeof(py_ModuleInfo)); int last_dot = c11_sv__rindex(pathv, '.'); if(last_dot == -1 || is_init) { mi->package = c11_string__new(path); } else { // a.b.c -> a.b mi->package = c11_string__new2(path, last_dot); } mi->path = c11_string__new(path); path = mi->path->data; // we do not allow override in order to avoid memory leak // it is because Module objects are not garbage collected bool exists = BinTree__contains(&pk_current_vm->modules, (void*)path); if(exists) c11__abort("module '%s' already exists", path); BinTree__set(&pk_current_vm->modules, (void*)path, py_retval()); py_GlobalRef retval = py_getmodule(path); mi->self = retval; // setup __name__ py_newstrv(py_emplacedict(retval, __name__), c11_string__sv(mi->path)); // setup __package__ py_newstrv(py_emplacedict(retval, __package__), c11_string__sv(mi->package)); return retval; } py_Ref py_newmodule(const char* path) { return pk_newmodule(path, false); } static void py_ModuleInfo__dtor(py_ModuleInfo* mi) { c11_string__delete(mi->package); c11_string__delete(mi->path); } py_Type pk_module__register() { py_Type type = pk_newtype("module", tp_object, NULL, (py_Dtor)py_ModuleInfo__dtor, false, true); return type; } int load_module_from_dll_desktop_only(const char* path) PY_RAISE PY_RETURN; int py_import(const char* path_cstr) { VM* vm = pk_current_vm; c11_sv path = {path_cstr, strlen(path_cstr)}; if(path.size == 0) { ValueError("empty module name"); return -1; } if(path.size > PK_MAX_MODULE_PATH_LEN) { ValueError("module name too long: %v", path); return -1; } if(path.data[0] == '.') { c11__rtassert(vm->top_frame != NULL && vm->top_frame->module != NULL); // try relative import int dot_count = 1; while(dot_count < path.size && path.data[dot_count] == '.') dot_count++; py_ModuleInfo* mi = py_touserdata(vm->top_frame->module); c11_sv package_sv = c11_string__sv(mi->package); if(package_sv.size == 0) { ImportError("attempted relative import with no known parent package"); return -1; } c11_vector /* T=c11_sv */ cpnts = c11_sv__split(package_sv, '.'); for(int i = 1; i < dot_count; i++) { if(cpnts.length == 0){ ImportError("attempted relative import beyond top-level package"); return -1; } c11_vector__pop(&cpnts); } if(dot_count < path.size) { c11_sv last_cpnt = c11_sv__slice(path, dot_count); c11_vector__push(c11_sv, &cpnts, last_cpnt); } // join cpnts c11_sbuf buf; c11_sbuf__ctor(&buf); for(int i = 0; i < cpnts.length; i++) { if(i > 0) c11_sbuf__write_char(&buf, '.'); c11_sbuf__write_sv(&buf, c11__getitem(c11_sv, &cpnts, i)); } c11_vector__dtor(&cpnts); c11_string* new_path = c11_sbuf__submit(&buf); int res = py_import(new_path->data); c11_string__delete(new_path); return res; } c11__rtassert(path.data[0] != '.' && path.data[path.size - 1] != '.'); // import parent module (implicit recursion) int last_dot_index = c11_sv__rindex(path, '.'); if(last_dot_index >= 0) { c11_sv ppath = c11_sv__slice2(path, 0, last_dot_index); py_GlobalRef ext_mod = py_getmodule(ppath.data); if(!ext_mod) { char buf[PK_MAX_MODULE_PATH_LEN + 1]; memcpy(buf, ppath.data, ppath.size); buf[ppath.size] = '\0'; int res = py_import(buf); if(res != 1) return res; py_newnil(py_retval()); } } // check existing module py_GlobalRef ext_mod = py_getmodule(path.data); if(ext_mod) { py_assign(py_retval(), ext_mod); return 1; } if(vm->callbacks.lazyimport) { py_GlobalRef lazymod = vm->callbacks.lazyimport(path_cstr); if(lazymod) { c11__rtassert(py_istype(lazymod, tp_module)); py_assign(py_retval(), lazymod); return 1; } } // try import c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP); c11_string* filename = c11_string__new3("%s.py", slashed_path->data); bool need_free = true; bool is_pyc = false; bool is_init = false; const char* data = load_kPythonLib(path_cstr); int data_size = -1; if(data != NULL) { need_free = false; goto __SUCCESS; } data = vm->callbacks.importfile(filename->data, &data_size); if(data != NULL) goto __SUCCESS; c11_string__delete(filename); filename = c11_string__new3("%s.pyc", slashed_path->data); data = vm->callbacks.importfile(filename->data, &data_size); if(data != NULL) { is_pyc = true; goto __SUCCESS; } c11_string__delete(filename); filename = c11_string__new3("%s%c__init__.py", slashed_path->data, PK_PLATFORM_SEP); data = vm->callbacks.importfile(filename->data, &data_size); if(data != NULL) { is_init = true; goto __SUCCESS; } c11_string__delete(filename); filename = c11_string__new3("%s%c__init__.pyc", slashed_path->data, PK_PLATFORM_SEP); data = vm->callbacks.importfile(filename->data, &data_size); if(data != NULL) { is_pyc = true; is_init = true; goto __SUCCESS; } c11_string__delete(filename); c11_string__delete(slashed_path); // not found return load_module_from_dll_desktop_only(path_cstr); __SUCCESS: do { } while(0); py_GlobalRef mod = pk_newmodule(path_cstr, is_init); bool ok; if(is_pyc) { ok = py_execo(data, data_size, filename->data, mod); } else { ok = py_exec(data, filename->data, EXEC_MODE, mod); } py_assign(py_retval(), mod); c11_string__delete(filename); c11_string__delete(slashed_path); if(need_free) PK_FREE((void*)data); return ok ? 1 : -1; } bool py_importlib_reload(py_Ref module) { VM* vm = pk_current_vm; py_ModuleInfo* mi = py_touserdata(module); // We should ensure that the module is its original py_GlobalRef module = mi->self; c11_sv path = c11_string__sv(mi->path); c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP); c11_string* filename = c11_string__new3("%s.py", slashed_path->data); // Here we only consider source modules. // Because compiled modules have no source file (it cannot be reloaded) char* data = vm->callbacks.importfile(filename->data, NULL); if(data == NULL) { c11_string__delete(filename); filename = c11_string__new3("%s%c__init__.py", slashed_path->data, PK_PLATFORM_SEP); data = vm->callbacks.importfile(filename->data, NULL); } c11_string__delete(slashed_path); if(data == NULL) return ImportError("module '%v' not found", path); // py_cleardict(module); BUG: removing old classes will cause RELOAD_MODE to fail bool ok = py_exec(data, filename->data, RELOAD_MODE, module); c11_string__delete(filename); PK_FREE(data); py_assign(py_retval(), module); return ok; }