| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- #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;
- }
|