mirror of
				https://github.com/pocketpy/pocketpy
				synced 2025-10-31 08:50:17 +00:00 
			
		
		
		
	Compare commits
	
		
			4 Commits
		
	
	
		
			f64885f4ca
			...
			8058cee3a9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 8058cee3a9 | ||
|  | e187a61624 | ||
|  | 68a2186728 | ||
|  | 0b09246a6d | 
| @ -6,7 +6,7 @@ SRC=$(find src/ -name "*.c") | ||||
| 
 | ||||
| FLAGS="-std=c11 -lm -ldl -lpthread -Iinclude -O0 -Wfatal-errors -g -DDEBUG -DPK_ENABLE_OS=1" | ||||
| 
 | ||||
| SANITIZE_FLAGS="-fsanitize=address,leak,undefined" | ||||
| SANITIZE_FLAGS="-fsanitize=address,leak,undefined -fno-sanitize=function" | ||||
| 
 | ||||
| if [ "$(uname)" == "Darwin" ]; then | ||||
|     SANITIZE_FLAGS="-fsanitize=address,undefined" | ||||
|  | ||||
| @ -16,13 +16,26 @@ typedef struct py_TypeInfo { | ||||
|     bool is_python;  // is it a python class? (not derived from c object)
 | ||||
|     bool is_sealed;  // can it be subclassed?
 | ||||
| 
 | ||||
|     py_Dtor dtor;  // destructor for this type, NULL if no dtor
 | ||||
| 
 | ||||
|     py_TValue annotations; | ||||
| 
 | ||||
|     py_Dtor dtor;  // destructor for this type, NULL if no dtor
 | ||||
|     void (*on_end_subclass)(struct py_TypeInfo*);  // backdoor for enum module
 | ||||
| } py_TypeInfo; | ||||
| 
 | ||||
| py_TypeInfo* pk_typeinfo(py_Type type); | ||||
| py_ItemRef pk_tpfindname(py_TypeInfo* ti, py_Name name); | ||||
| #define pk_tpfindmagic pk_tpfindname | ||||
| 
 | ||||
| py_Type pk_newtype(const char* name, | ||||
|                    py_Type base, | ||||
|                    const py_GlobalRef module, | ||||
|                    void (*dtor)(void*), | ||||
|                    bool is_python, | ||||
|                    bool is_sealed); | ||||
| 
 | ||||
| 
 | ||||
| py_Type pk_newtypewithmode(py_Name name, | ||||
|                    py_Type base, | ||||
|                    const py_GlobalRef module, | ||||
|                    void (*dtor)(void*), | ||||
|                    bool is_python, | ||||
|                    bool is_sealed, enum py_CompileMode mode); | ||||
| @ -34,6 +34,13 @@ typedef struct TypePointer { | ||||
|     py_Dtor dtor; | ||||
| } TypePointer; | ||||
| 
 | ||||
| typedef struct py_ModuleInfo { | ||||
|     c11_string* name; | ||||
|     c11_string* package; | ||||
|     c11_string* path; | ||||
|     py_GlobalRef self;  // weakref to the original module object
 | ||||
| } py_ModuleInfo; | ||||
| 
 | ||||
| typedef struct VM { | ||||
|     py_Frame* top_frame; | ||||
| 
 | ||||
| @ -100,13 +107,6 @@ typedef enum FrameResult { | ||||
| 
 | ||||
| FrameResult VM__run_top_frame(VM* self); | ||||
| 
 | ||||
| py_Type pk_newtype(const char* name, | ||||
|                    py_Type base, | ||||
|                    const py_GlobalRef module, | ||||
|                    void (*dtor)(void*), | ||||
|                    bool is_python, | ||||
|                    bool is_sealed); | ||||
| 
 | ||||
| FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall); | ||||
| 
 | ||||
| const char* pk_opname(Opcode op); | ||||
| @ -146,6 +146,7 @@ py_Type pk_nativefunc__register(); | ||||
| py_Type pk_boundmethod__register(); | ||||
| py_Type pk_range__register(); | ||||
| py_Type pk_range_iterator__register(); | ||||
| py_Type pk_module__register(); | ||||
| py_Type pk_BaseException__register(); | ||||
| py_Type pk_Exception__register(); | ||||
| py_Type pk_StopIteration__register(); | ||||
|  | ||||
| @ -92,7 +92,8 @@ typedef bool (*py_CFunction)(int argc, py_StackRef argv) PY_RAISE PY_RETURN; | ||||
| /// + `EXEC_MODE`: for statements.
 | ||||
| /// + `EVAL_MODE`: for expressions.
 | ||||
| /// + `SINGLE_MODE`: for REPL or jupyter notebook execution.
 | ||||
| enum py_CompileMode { EXEC_MODE, EVAL_MODE, SINGLE_MODE }; | ||||
| /// + `RELOAD_MODE`: for reloading a module without allocating new types if possible.
 | ||||
| enum py_CompileMode { EXEC_MODE, EVAL_MODE, SINGLE_MODE, RELOAD_MODE }; | ||||
| 
 | ||||
| /************* Global Setup *************/ | ||||
| 
 | ||||
| @ -405,6 +406,8 @@ PK_API py_ItemRef py_emplacedict(py_Ref self, py_Name name); | ||||
| /// NOTE: Be careful if `f` modifies the object's `__dict__`.
 | ||||
| PK_API bool | ||||
|     py_applydict(py_Ref self, bool (*f)(py_Name name, py_Ref val, void* ctx), void* ctx) PY_RAISE; | ||||
| /// Clear the object's `__dict__`. This function is dangerous.
 | ||||
| PK_API void py_cleardict(py_Ref self); | ||||
| 
 | ||||
| /// Get the i-th slot of the object.
 | ||||
| /// The object must have slots and `i` must be in valid range.
 | ||||
| @ -555,7 +558,7 @@ PK_API py_GlobalRef py_newmodule(const char* path); | ||||
| /// Get a module by path.
 | ||||
| PK_API py_GlobalRef py_getmodule(const char* path); | ||||
| /// Reload an existing module.
 | ||||
| PK_API bool py_importlib_reload(py_GlobalRef module) PY_RAISE PY_RETURN; | ||||
| PK_API bool py_importlib_reload(py_Ref module) PY_RAISE PY_RETURN; | ||||
| 
 | ||||
| /// Import a module.
 | ||||
| /// The result will be set to `py_retval()`.
 | ||||
|  | ||||
| @ -1042,12 +1042,13 @@ FrameResult VM__run_top_frame(VM* self) { | ||||
|                     goto __ERROR; | ||||
|                 } | ||||
| 
 | ||||
|                 py_Type type = pk_newtype(py_name2str(name), | ||||
|                 py_Type type = pk_newtypewithmode(name, | ||||
|                                                   base, | ||||
|                                                   frame->module, | ||||
|                                                   NULL, | ||||
|                                                   base_ti->is_python, | ||||
|                                           false); | ||||
|                                                   false, | ||||
|                                                   frame->co->src->mode); | ||||
|                 PUSH(py_tpobject(type)); | ||||
|                 self->curr_class = TOP(); | ||||
|                 DISPATCH(); | ||||
|  | ||||
| @ -45,5 +45,108 @@ const char* py_tpname(py_Type type) { | ||||
| } | ||||
| 
 | ||||
| py_TypeInfo* pk_typeinfo(py_Type type) { | ||||
| #ifndef NDEBUG | ||||
|     int length = pk_current_vm->types.length; | ||||
|     if(type < 0 || type >= length) { | ||||
|         c11__abort("type index %d is out of bounds [0, %d)", type, length); | ||||
|     } | ||||
| #endif | ||||
|     return c11__getitem(TypePointer, &pk_current_vm->types, type).ti; | ||||
| } | ||||
| 
 | ||||
| static void py_TypeInfo__common_init(py_Name name, | ||||
|                                      py_Type base, | ||||
|                                      py_Type index, | ||||
|                                      const py_GlobalRef module, | ||||
|                                      void (*dtor)(void*), | ||||
|                                      bool is_python, | ||||
|                                      bool is_sealed, | ||||
|                                      py_TypeInfo* self, | ||||
|                                      py_TValue* typeobject) { | ||||
|     py_TypeInfo* base_ti = base ? pk_typeinfo(base) : NULL; | ||||
|     if(base_ti && base_ti->is_sealed) { | ||||
|         c11__abort("type '%s' is not an acceptable base type", py_name2str(base_ti->name)); | ||||
|     } | ||||
| 
 | ||||
|     self->name = name; | ||||
|     self->index = index; | ||||
|     self->base = base; | ||||
|     self->base_ti = base_ti; | ||||
| 
 | ||||
|     py_assign(&self->self, typeobject); | ||||
|     self->module = module ? module : py_NIL(); | ||||
| 
 | ||||
|     if(!dtor && base) dtor = base_ti->dtor; | ||||
|     self->is_python = is_python; | ||||
|     self->is_sealed = is_sealed; | ||||
| 
 | ||||
|     self->annotations = *py_NIL(); | ||||
|     self->dtor = dtor; | ||||
|     self->on_end_subclass = NULL; | ||||
| } | ||||
| 
 | ||||
| py_Type pk_newtype(const char* name, | ||||
|                    py_Type base, | ||||
|                    const py_GlobalRef module, | ||||
|                    void (*dtor)(void*), | ||||
|                    bool is_python, | ||||
|                    bool is_sealed) { | ||||
|     py_Type index = pk_current_vm->types.length; | ||||
|     py_TypeInfo* self = py_newobject(py_retval(), tp_type, -1, sizeof(py_TypeInfo)); | ||||
|     py_TypeInfo__common_init(py_name(name), | ||||
|                              base, | ||||
|                              index, | ||||
|                              module, | ||||
|                              dtor, | ||||
|                              is_python, | ||||
|                              is_sealed, | ||||
|                              self, | ||||
|                              py_retval()); | ||||
|     TypePointer* pointer = c11_vector__emplace(&pk_current_vm->types); | ||||
|     pointer->ti = self; | ||||
|     pointer->dtor = self->dtor; | ||||
|     return index; | ||||
| } | ||||
| 
 | ||||
| py_Type pk_newtypewithmode(py_Name name, | ||||
|                            py_Type base, | ||||
|                            const py_GlobalRef module, | ||||
|                            void (*dtor)(void*), | ||||
|                            bool is_python, | ||||
|                            bool is_sealed, | ||||
|                            enum py_CompileMode mode) { | ||||
|     if(mode == RELOAD_MODE && module != NULL) { | ||||
|         py_ItemRef old_class = py_getdict(module, name); | ||||
|         if(old_class != NULL && py_istype(old_class, tp_type)) { | ||||
| #ifndef NDEBUG | ||||
|             const char* name_cstr = py_name2str(name); | ||||
|             (void)name_cstr;  // avoid unused warning
 | ||||
| #endif | ||||
|             py_cleardict(old_class); | ||||
|             py_TypeInfo* self = py_touserdata(old_class); | ||||
|             py_Type index = self->index; | ||||
|             py_TypeInfo__common_init(name, | ||||
|                                      base, | ||||
|                                      index, | ||||
|                                      module, | ||||
|                                      dtor, | ||||
|                                      is_python, | ||||
|                                      is_sealed, | ||||
|                                      self, | ||||
|                                      &self->self); | ||||
|             TypePointer* pointer = c11__at(TypePointer, &pk_current_vm->types, index); | ||||
|             pointer->ti = self; | ||||
|             pointer->dtor = self->dtor; | ||||
|             return index; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return pk_newtype(py_name2str(name), base, module, dtor, is_python, is_sealed); | ||||
| } | ||||
| 
 | ||||
| py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, void (*dtor)(void*)) { | ||||
|     if(strlen(name) == 0) c11__abort("type name cannot be empty"); | ||||
|     py_Type type = pk_newtype(name, base, module, dtor, false, false); | ||||
|     if(module) py_setdict(module, py_name(name), py_tpobject(type)); | ||||
|     return type; | ||||
| } | ||||
| @ -50,9 +50,9 @@ void VM__ctor(VM* self) { | ||||
| 
 | ||||
|     const static BinTreeConfig modules_config = { | ||||
|         .f_cmp = BinTree__cmp_cstr, | ||||
|         .need_free_key = true, | ||||
|         .need_free_key = false, | ||||
|     }; | ||||
|     BinTree__ctor(&self->modules, c11_strdup(""), py_NIL(), &modules_config); | ||||
|     BinTree__ctor(&self->modules, "", py_NIL(), &modules_config); | ||||
|     c11_vector__ctor(&self->types, sizeof(TypePointer)); | ||||
| 
 | ||||
|     self->builtins = NULL; | ||||
| @ -114,7 +114,7 @@ void VM__ctor(VM* self) { | ||||
|     validate(tp_slice, pk_slice__register()); | ||||
|     validate(tp_range, pk_range__register()); | ||||
|     validate(tp_range_iterator, pk_range_iterator__register()); | ||||
|     validate(tp_module, pk_newtype("module", tp_object, NULL, NULL, false, true)); | ||||
|     validate(tp_module, pk_module__register()); | ||||
| 
 | ||||
|     validate(tp_function, pk_function__register()); | ||||
|     validate(tp_nativefunc, pk_nativefunc__register()); | ||||
| @ -364,47 +364,6 @@ bool pk__normalize_index(int* index, int length) { | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| py_Type pk_newtype(const char* name, | ||||
|                    py_Type base, | ||||
|                    const py_GlobalRef module, | ||||
|                    void (*dtor)(void*), | ||||
|                    bool is_python, | ||||
|                    bool is_sealed) { | ||||
|     py_Type index = pk_current_vm->types.length; | ||||
|     py_TypeInfo* self = py_newobject(py_retval(), tp_type, -1, sizeof(py_TypeInfo)); | ||||
|     py_TypeInfo* base_ti = base ? pk_typeinfo(base) : NULL; | ||||
|     if(base_ti && base_ti->is_sealed) { | ||||
|         c11__abort("type '%s' is not an acceptable base type", py_name2str(base_ti->name)); | ||||
|     } | ||||
| 
 | ||||
|     memset(self, 0, sizeof(py_TypeInfo)); | ||||
|     self->name = py_name(name); | ||||
|     self->index = index; | ||||
|     self->base = base; | ||||
|     self->base_ti = base_ti; | ||||
| 
 | ||||
|     self->self = *py_retval(); | ||||
|     self->module = module ? module : py_NIL(); | ||||
|     self->annotations = *py_NIL(); | ||||
| 
 | ||||
|     if(!dtor && base) dtor = base_ti->dtor; | ||||
|     self->is_python = is_python; | ||||
|     self->is_sealed = is_sealed; | ||||
|     self->dtor = dtor; | ||||
| 
 | ||||
|     TypePointer* pointer = c11_vector__emplace(&pk_current_vm->types); | ||||
|     pointer->ti = self; | ||||
|     pointer->dtor = dtor; | ||||
|     return index; | ||||
| } | ||||
| 
 | ||||
| py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, void (*dtor)(void*)) { | ||||
|     if(strlen(name) == 0) c11__abort("type name cannot be empty"); | ||||
|     py_Type type = pk_newtype(name, base, module, dtor, false, false); | ||||
|     if(module) py_setdict(module, py_name(name), py_tpobject(type)); | ||||
|     return type; | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
|     prepare_py_call(py_TValue* buffer, py_Ref argv, py_Ref p1, int kwargc, const FuncDecl* decl) { | ||||
|     const CodeObject* co = &decl->code; | ||||
|  | ||||
| @ -37,8 +37,8 @@ void pk_print_stack(VM* self, py_Frame* frame, Bytecode byte) { | ||||
|                 break; | ||||
|             } | ||||
|             case tp_module: { | ||||
|                 py_Ref path = py_getdict(p, __path__); | ||||
|                 pk_sprintf(&buf, "<module '%v'>", py_tosv(path)); | ||||
|                 py_ModuleInfo* mi = py_touserdata(p); | ||||
|                 pk_sprintf(&buf, "<module '%v'>", c11_string__sv(mi->path)); | ||||
|                 break; | ||||
|             } | ||||
|             default: { | ||||
|  | ||||
| @ -97,6 +97,8 @@ void pk__add_module_os() { | ||||
|     py_ItemRef path_object = py_emplacedict(mod, py_name("path")); | ||||
|     py_newobject(path_object, tp_object, -1, 0); | ||||
|     py_bindfunc(path_object, "exists", os_path_exists); | ||||
| 
 | ||||
|     py_newdict(py_emplacedict(mod, py_name("environ"))); | ||||
| } | ||||
| 
 | ||||
| typedef struct { | ||||
|  | ||||
| @ -66,10 +66,10 @@ static void c11_sbuf__write_type_path(c11_sbuf* path_buf, py_Type type) { | ||||
|         c11_sbuf__write_cstr(path_buf, py_name2str(ti->name)); | ||||
|         return; | ||||
|     } | ||||
|     const char* mod_path = py_tostr(py_getdict(ti->module, __path__)); | ||||
|     c11_sbuf__write_cstr(path_buf, mod_path); | ||||
|     py_ModuleInfo* mi = py_touserdata(ti->module); | ||||
|     c11_sbuf__write_sv(path_buf, c11_string__sv(mi->path)); | ||||
|     c11_sbuf__write_char(path_buf, '.'); | ||||
|     c11_sbuf__write_cstr(path_buf, py_name2str(ti->name)); | ||||
|     c11_sbuf__write_sv(path_buf, py_name2sv(ti->name)); | ||||
| } | ||||
| 
 | ||||
| static void pkl__emit_op(PickleObject* buf, PickleOp op) { | ||||
|  | ||||
| @ -197,4 +197,5 @@ void Function__dtor(Function* self) { | ||||
|     // self->decl->code.src->filename->data);
 | ||||
|     PK_DECREF(self->decl); | ||||
|     if(self->closure) NameDict__delete(self->closure); | ||||
|     memset(self, 0, sizeof(Function)); | ||||
| } | ||||
| @ -22,50 +22,70 @@ py_Ref py_getglobal(py_Name name) { return py_getdict(pk_current_vm->main, name) | ||||
| 
 | ||||
| void py_setglobal(py_Name name, py_Ref val) { py_setdict(pk_current_vm->main, name, val); } | ||||
| 
 | ||||
| py_Ref py_newmodule(const char* path) { | ||||
|     ManagedHeap* heap = &pk_current_vm->heap; | ||||
| static void py_ModuleInfo__dtor(py_ModuleInfo* mi) { | ||||
|     c11_string__delete(mi->name); | ||||
|     c11_string__delete(mi->package); | ||||
|     c11_string__delete(mi->path); | ||||
| } | ||||
| 
 | ||||
| static bool module__name__(int argc, py_Ref argv) { | ||||
|     PY_CHECK_ARGC(1); | ||||
|     py_ModuleInfo* mi = py_touserdata(argv); | ||||
|     py_newstrv(py_retval(), c11_string__sv(mi->name)); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool module__package__(int argc, py_Ref argv) { | ||||
|     PY_CHECK_ARGC(1); | ||||
|     py_ModuleInfo* mi = py_touserdata(argv); | ||||
|     py_newstrv(py_retval(), c11_string__sv(mi->package)); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool module__path__(int argc, py_Ref argv) { | ||||
|     PY_CHECK_ARGC(1); | ||||
|     py_ModuleInfo* mi = py_touserdata(argv); | ||||
|     py_newstrv(py_retval(), c11_string__sv(mi->path)); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| py_Type pk_module__register() { | ||||
|     py_Type type = pk_newtype("module", tp_object, NULL, (py_Dtor)py_ModuleInfo__dtor, false, true); | ||||
|     py_bindproperty(type, "__name__", module__name__, NULL); | ||||
|     py_bindproperty(type, "__package__", module__package__, NULL); | ||||
|     py_bindproperty(type, "__path__", module__path__, NULL); | ||||
|     return type; | ||||
| } | ||||
| 
 | ||||
| py_Ref py_newmodule(const char* path) { | ||||
|     int path_len = strlen(path); | ||||
|     if(path_len > PK_MAX_MODULE_PATH_LEN) c11__abort("module path too long: %s", path); | ||||
|     if(path_len == 0) c11__abort("module path cannot be empty"); | ||||
| 
 | ||||
|     py_Ref r0 = py_pushtmp(); | ||||
|     py_Ref r1 = py_pushtmp(); | ||||
| 
 | ||||
|     *r0 = (py_TValue){ | ||||
|         .type = tp_module, | ||||
|         .is_ptr = true, | ||||
|         ._obj = ManagedHeap__new(heap, tp_module, -1, 0), | ||||
|     }; | ||||
|     py_ModuleInfo* mi = py_newobject(py_retval(), tp_module, -1, sizeof(py_ModuleInfo)); | ||||
| 
 | ||||
|     int last_dot = c11_sv__rindex((c11_sv){path, path_len}, '.'); | ||||
|     if(last_dot == -1) { | ||||
|         py_newstr(r1, path); | ||||
|         py_setdict(r0, __name__, r1); | ||||
|         py_newstr(r1, ""); | ||||
|         py_setdict(r0, __package__, r1); | ||||
|         mi->name = c11_string__new(path); | ||||
|         mi->package = c11_string__new(""); | ||||
|     } else { | ||||
|         const char* start = path + last_dot + 1; | ||||
|         py_newstr(r1, start); | ||||
|         py_setdict(r0, __name__, r1); | ||||
|         py_newstrv(r1, (c11_sv){path, last_dot}); | ||||
|         py_setdict(r0, __package__, r1); | ||||
|         mi->name = c11_string__new(start); | ||||
|         mi->package = c11_string__new2(path, last_dot); | ||||
|     } | ||||
| 
 | ||||
|     py_newstr(r1, path); | ||||
|     py_setdict(r0, __path__, r1); | ||||
|     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); | ||||
| 
 | ||||
|     // convert to a weak (const char*)
 | ||||
|     path = py_tostr(py_getdict(r0, __path__)); | ||||
|     BinTree__set(&pk_current_vm->modules, c11_strdup(path), r0); | ||||
| 
 | ||||
|     py_shrink(2); | ||||
|     return py_getmodule(path); | ||||
|     BinTree__set(&pk_current_vm->modules, (void*)path, py_retval()); | ||||
|     py_GlobalRef retval = py_getmodule(path); | ||||
|     mi->self = retval; | ||||
|     return retval; | ||||
| } | ||||
| 
 | ||||
| int load_module_from_dll_desktop_only(const char* path) PY_RAISE PY_RETURN; | ||||
| @ -84,8 +104,8 @@ int py_import(const char* path_cstr) { | ||||
|         c11_sv top_filename = c11_string__sv(vm->top_frame->co->src->filename); | ||||
|         int is_init = c11_sv__endswith(top_filename, (c11_sv){"__init__.py", 11}); | ||||
| 
 | ||||
|         py_Ref package = py_getdict(vm->top_frame->module, __path__); | ||||
|         c11_sv package_sv = py_tosv(package); | ||||
|         py_ModuleInfo* mi = py_touserdata(vm->top_frame->module); | ||||
|         c11_sv package_sv = c11_string__sv(mi->path); | ||||
|         if(package_sv.size == 0) { | ||||
|             return ImportError("attempted relative import with no known parent package"); | ||||
|         } | ||||
| @ -163,9 +183,12 @@ __SUCCESS: | ||||
|     return ok ? 1 : -1; | ||||
| } | ||||
| 
 | ||||
| bool py_importlib_reload(py_GlobalRef module) { | ||||
| bool py_importlib_reload(py_Ref module) { | ||||
|     VM* vm = pk_current_vm; | ||||
|     c11_sv path = py_tosv(py_getdict(module, __path__)); | ||||
|     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); | ||||
|     char* data = vm->callbacks.importfile(filename->data); | ||||
| @ -176,7 +199,8 @@ bool py_importlib_reload(py_GlobalRef module) { | ||||
|     } | ||||
|     c11_string__delete(slashed_path); | ||||
|     if(data == NULL) return ImportError("module '%v' not found", path); | ||||
|     bool ok = py_exec(data, filename->data, EXEC_MODE, module); | ||||
|     // 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); | ||||
|  | ||||
| @ -20,6 +20,15 @@ static bool namedict__getitem__(int argc, py_Ref argv) { | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool namedict__get(int argc, py_Ref argv) { | ||||
|     PY_CHECK_ARGC(3); | ||||
|     PY_CHECK_ARG_TYPE(1, tp_str); | ||||
|     py_Name name = py_namev(py_tosv(py_arg(1))); | ||||
|     py_Ref res = py_getdict(py_getslot(argv, 0), name); | ||||
|     py_assign(py_retval(), res ? res : py_arg(2)); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool namedict__setitem__(int argc, py_Ref argv) { | ||||
|     PY_CHECK_ARGC(3); | ||||
|     PY_CHECK_ARG_TYPE(1, tp_str); | ||||
| @ -82,5 +91,6 @@ py_Type pk_namedict__register() { | ||||
|     py_setdict(py_tpobject(type), __hash__, py_None()); | ||||
|     py_bindmethod(type, "items", namedict_items); | ||||
|     py_bindmethod(type, "clear", namedict_clear); | ||||
|     py_bindmethod(type, "get", namedict__get); | ||||
|     return type; | ||||
| } | ||||
|  | ||||
| @ -104,8 +104,8 @@ static bool type__module__(int argc, py_Ref argv) { | ||||
|     if(py_isnil(ti->module)) { | ||||
|         py_newnone(py_retval()); | ||||
|     } else { | ||||
|         py_Ref path = py_getdict(ti->module, __path__); | ||||
|         py_assign(py_retval(), path); | ||||
|         py_ModuleInfo* mi = py_touserdata(ti->module); | ||||
|         py_newstrv(py_retval(), c11_string__sv(mi->path)); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @ -166,10 +166,10 @@ bool py_getattr(py_Ref self, py_Name name) { | ||||
|     } | ||||
| 
 | ||||
|     if(self->type == tp_module) { | ||||
|         py_Ref path = py_getdict(self, __path__); | ||||
|         py_ModuleInfo* mi = py_touserdata(self); | ||||
|         c11_sbuf buf; | ||||
|         c11_sbuf__ctor(&buf); | ||||
|         pk_sprintf(&buf, "%v.%n", py_tosv(path), name); | ||||
|         pk_sprintf(&buf, "%v.%n", c11_string__sv(mi->path), name); | ||||
|         c11_string* new_path = c11_sbuf__submit(&buf); | ||||
|         int res = py_import(new_path->data); | ||||
|         c11_string__delete(new_path); | ||||
|  | ||||
| @ -35,6 +35,12 @@ bool py_applydict(py_Ref self, bool (*f)(py_Name, py_Ref, void*), void* ctx) { | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void py_cleardict(py_Ref self) { | ||||
|     assert(self && self->is_ptr); | ||||
|     NameDict* dict = PyObject__dict(self->_obj); | ||||
|     NameDict__clear(dict); | ||||
| } | ||||
| 
 | ||||
| bool py_deldict(py_Ref self, py_Name name) { | ||||
|     assert(self && self->is_ptr); | ||||
|     return NameDict__del(PyObject__dict(self->_obj), name); | ||||
|  | ||||
| @ -23,6 +23,11 @@ assert get_value_2() == '123' | ||||
| from test3.a.b import value | ||||
| assert value == 1 | ||||
| 
 | ||||
| from test2.utils import r | ||||
| assert r.__name__ == 'r' | ||||
| assert r.__package__ == 'test2.utils' | ||||
| assert r.__path__ == 'test2.utils.r' | ||||
| 
 | ||||
| def f(): | ||||
|     import math as m | ||||
|     assert m.pi > 3 | ||||
|  | ||||
							
								
								
									
										43
									
								
								tests/31_modulereload.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								tests/31_modulereload.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| try: | ||||
|     import os | ||||
| except ImportError: | ||||
|     exit(0) | ||||
| 
 | ||||
| import importlib | ||||
| 
 | ||||
| os.chdir('tests') | ||||
| assert os.getcwd().endswith('tests') | ||||
| 
 | ||||
| # test | ||||
| os.environ['TEST_RELOAD_VALUE'] = '123' | ||||
| os.environ['SET_X'] = '1' | ||||
| os.environ['SET_Y'] = '0' | ||||
| 
 | ||||
| from testreload import MyClass, a | ||||
| 
 | ||||
| objid = id(MyClass) | ||||
| funcid = id(MyClass.some_func) | ||||
| getxyid = id(MyClass.get_xy) | ||||
| 
 | ||||
| assert MyClass.value == '123' | ||||
| assert MyClass.get_xy() == (1, 0) | ||||
| 
 | ||||
| inst = MyClass() | ||||
| assert inst.some_func() == '123' | ||||
| 
 | ||||
| # reload | ||||
| os.environ['TEST_RELOAD_VALUE'] = '456' | ||||
| os.environ['SET_X'] = '0' | ||||
| os.environ['SET_Y'] = '1' | ||||
| 
 | ||||
| importlib.reload(a) | ||||
| 
 | ||||
| assert id(MyClass) == objid | ||||
| assert id(MyClass.some_func) != funcid | ||||
| assert id(MyClass.get_xy) != getxyid | ||||
| 
 | ||||
| assert MyClass.value == '456' | ||||
| assert inst.some_func() == '456' | ||||
| assert (MyClass.get_xy() == (1, 1)), MyClass.get_xy() | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										2
									
								
								tests/testreload/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/testreload/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| from .a import MyClass | ||||
| from . import a | ||||
							
								
								
									
										18
									
								
								tests/testreload/a.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								tests/testreload/a.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| import os | ||||
| 
 | ||||
| class MyClass: | ||||
|     value = os.environ['TEST_RELOAD_VALUE'] | ||||
| 
 | ||||
|     def some_func(self): | ||||
|         return self.value | ||||
|      | ||||
|     @staticmethod | ||||
|     def get_xy(): | ||||
|         g = globals() | ||||
|         return g.get('x', 0), g.get('y', 0) | ||||
| 
 | ||||
| 
 | ||||
| if os.environ['SET_X'] == '1': | ||||
|     x = 1 | ||||
| elif os.environ['SET_Y'] == '1': | ||||
|     y = 1 | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user