Compare commits

...

4 Commits

Author SHA1 Message Date
blueloveTH
8058cee3a9 Update typeinfo.c 2025-06-22 18:02:38 +08:00
blueloveTH
e187a61624 fix module reload bug 2025-06-22 16:31:46 +08:00
blueloveTH
68a2186728 improve module 2025-06-22 13:37:20 +08:00
blueloveTH
0b09246a6d add RELOAD_MODE 2025-06-22 13:01:50 +08:00
20 changed files with 295 additions and 104 deletions

View File

@ -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" 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 if [ "$(uname)" == "Darwin" ]; then
SANITIZE_FLAGS="-fsanitize=address,undefined" SANITIZE_FLAGS="-fsanitize=address,undefined"

View File

@ -16,13 +16,26 @@ typedef struct py_TypeInfo {
bool is_python; // is it a python class? (not derived from c object) bool is_python; // is it a python class? (not derived from c object)
bool is_sealed; // can it be subclassed? bool is_sealed; // can it be subclassed?
py_Dtor dtor; // destructor for this type, NULL if no dtor
py_TValue annotations; 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 void (*on_end_subclass)(struct py_TypeInfo*); // backdoor for enum module
} py_TypeInfo; } py_TypeInfo;
py_TypeInfo* pk_typeinfo(py_Type type); py_TypeInfo* pk_typeinfo(py_Type type);
py_ItemRef pk_tpfindname(py_TypeInfo* ti, py_Name name); py_ItemRef pk_tpfindname(py_TypeInfo* ti, py_Name name);
#define pk_tpfindmagic pk_tpfindname #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);

View File

@ -34,6 +34,13 @@ typedef struct TypePointer {
py_Dtor dtor; py_Dtor dtor;
} TypePointer; } 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 { typedef struct VM {
py_Frame* top_frame; py_Frame* top_frame;
@ -100,13 +107,6 @@ typedef enum FrameResult {
FrameResult VM__run_top_frame(VM* self); 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); FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall);
const char* pk_opname(Opcode op); const char* pk_opname(Opcode op);
@ -146,6 +146,7 @@ py_Type pk_nativefunc__register();
py_Type pk_boundmethod__register(); py_Type pk_boundmethod__register();
py_Type pk_range__register(); py_Type pk_range__register();
py_Type pk_range_iterator__register(); py_Type pk_range_iterator__register();
py_Type pk_module__register();
py_Type pk_BaseException__register(); py_Type pk_BaseException__register();
py_Type pk_Exception__register(); py_Type pk_Exception__register();
py_Type pk_StopIteration__register(); py_Type pk_StopIteration__register();

View File

@ -92,7 +92,8 @@ typedef bool (*py_CFunction)(int argc, py_StackRef argv) PY_RAISE PY_RETURN;
/// + `EXEC_MODE`: for statements. /// + `EXEC_MODE`: for statements.
/// + `EVAL_MODE`: for expressions. /// + `EVAL_MODE`: for expressions.
/// + `SINGLE_MODE`: for REPL or jupyter notebook execution. /// + `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 *************/ /************* 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__`. /// NOTE: Be careful if `f` modifies the object's `__dict__`.
PK_API bool PK_API bool
py_applydict(py_Ref self, bool (*f)(py_Name name, py_Ref val, void* ctx), void* ctx) PY_RAISE; 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. /// Get the i-th slot of the object.
/// The object must have slots and `i` must be in valid range. /// 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. /// Get a module by path.
PK_API py_GlobalRef py_getmodule(const char* path); PK_API py_GlobalRef py_getmodule(const char* path);
/// Reload an existing module. /// 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. /// Import a module.
/// The result will be set to `py_retval()`. /// The result will be set to `py_retval()`.

View File

@ -1042,12 +1042,13 @@ FrameResult VM__run_top_frame(VM* self) {
goto __ERROR; goto __ERROR;
} }
py_Type type = pk_newtype(py_name2str(name), py_Type type = pk_newtypewithmode(name,
base, base,
frame->module, frame->module,
NULL, NULL,
base_ti->is_python, base_ti->is_python,
false); false,
frame->co->src->mode);
PUSH(py_tpobject(type)); PUSH(py_tpobject(type));
self->curr_class = TOP(); self->curr_class = TOP();
DISPATCH(); DISPATCH();

View File

@ -45,5 +45,108 @@ const char* py_tpname(py_Type type) {
} }
py_TypeInfo* pk_typeinfo(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; 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;
} }

View File

@ -50,9 +50,9 @@ void VM__ctor(VM* self) {
const static BinTreeConfig modules_config = { const static BinTreeConfig modules_config = {
.f_cmp = BinTree__cmp_cstr, .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)); c11_vector__ctor(&self->types, sizeof(TypePointer));
self->builtins = NULL; self->builtins = NULL;
@ -114,7 +114,7 @@ void VM__ctor(VM* self) {
validate(tp_slice, pk_slice__register()); validate(tp_slice, pk_slice__register());
validate(tp_range, pk_range__register()); validate(tp_range, pk_range__register());
validate(tp_range_iterator, pk_range_iterator__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_function, pk_function__register());
validate(tp_nativefunc, pk_nativefunc__register()); validate(tp_nativefunc, pk_nativefunc__register());
@ -364,47 +364,6 @@ bool pk__normalize_index(int* index, int length) {
return true; 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 static bool
prepare_py_call(py_TValue* buffer, py_Ref argv, py_Ref p1, int kwargc, const FuncDecl* decl) { prepare_py_call(py_TValue* buffer, py_Ref argv, py_Ref p1, int kwargc, const FuncDecl* decl) {
const CodeObject* co = &decl->code; const CodeObject* co = &decl->code;

View File

@ -37,8 +37,8 @@ void pk_print_stack(VM* self, py_Frame* frame, Bytecode byte) {
break; break;
} }
case tp_module: { case tp_module: {
py_Ref path = py_getdict(p, __path__); py_ModuleInfo* mi = py_touserdata(p);
pk_sprintf(&buf, "<module '%v'>", py_tosv(path)); pk_sprintf(&buf, "<module '%v'>", c11_string__sv(mi->path));
break; break;
} }
default: { default: {

View File

@ -97,6 +97,8 @@ void pk__add_module_os() {
py_ItemRef path_object = py_emplacedict(mod, py_name("path")); py_ItemRef path_object = py_emplacedict(mod, py_name("path"));
py_newobject(path_object, tp_object, -1, 0); py_newobject(path_object, tp_object, -1, 0);
py_bindfunc(path_object, "exists", os_path_exists); py_bindfunc(path_object, "exists", os_path_exists);
py_newdict(py_emplacedict(mod, py_name("environ")));
} }
typedef struct { typedef struct {

View File

@ -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)); c11_sbuf__write_cstr(path_buf, py_name2str(ti->name));
return; return;
} }
const char* mod_path = py_tostr(py_getdict(ti->module, __path__)); py_ModuleInfo* mi = py_touserdata(ti->module);
c11_sbuf__write_cstr(path_buf, mod_path); c11_sbuf__write_sv(path_buf, c11_string__sv(mi->path));
c11_sbuf__write_char(path_buf, '.'); 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) { static void pkl__emit_op(PickleObject* buf, PickleOp op) {

View File

@ -197,4 +197,5 @@ void Function__dtor(Function* self) {
// self->decl->code.src->filename->data); // self->decl->code.src->filename->data);
PK_DECREF(self->decl); PK_DECREF(self->decl);
if(self->closure) NameDict__delete(self->closure); if(self->closure) NameDict__delete(self->closure);
memset(self, 0, sizeof(Function));
} }

View File

@ -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); } void py_setglobal(py_Name name, py_Ref val) { py_setdict(pk_current_vm->main, name, val); }
py_Ref py_newmodule(const char* path) { static void py_ModuleInfo__dtor(py_ModuleInfo* mi) {
ManagedHeap* heap = &pk_current_vm->heap; 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); int path_len = strlen(path);
if(path_len > PK_MAX_MODULE_PATH_LEN) c11__abort("module path too long: %s", 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"); if(path_len == 0) c11__abort("module path cannot be empty");
py_Ref r0 = py_pushtmp(); py_ModuleInfo* mi = py_newobject(py_retval(), tp_module, -1, sizeof(py_ModuleInfo));
py_Ref r1 = py_pushtmp();
*r0 = (py_TValue){
.type = tp_module,
.is_ptr = true,
._obj = ManagedHeap__new(heap, tp_module, -1, 0),
};
int last_dot = c11_sv__rindex((c11_sv){path, path_len}, '.'); int last_dot = c11_sv__rindex((c11_sv){path, path_len}, '.');
if(last_dot == -1) { if(last_dot == -1) {
py_newstr(r1, path); mi->name = c11_string__new(path);
py_setdict(r0, __name__, r1); mi->package = c11_string__new("");
py_newstr(r1, "");
py_setdict(r0, __package__, r1);
} else { } else {
const char* start = path + last_dot + 1; const char* start = path + last_dot + 1;
py_newstr(r1, start); mi->name = c11_string__new(start);
py_setdict(r0, __name__, r1); mi->package = c11_string__new2(path, last_dot);
py_newstrv(r1, (c11_sv){path, last_dot});
py_setdict(r0, __package__, r1);
} }
py_newstr(r1, path); mi->path = c11_string__new(path);
py_setdict(r0, __path__, r1); path = mi->path->data;
// we do not allow override in order to avoid memory leak // we do not allow override in order to avoid memory leak
// it is because Module objects are not garbage collected // it is because Module objects are not garbage collected
bool exists = BinTree__contains(&pk_current_vm->modules, (void*)path); bool exists = BinTree__contains(&pk_current_vm->modules, (void*)path);
if(exists) c11__abort("module '%s' already exists", path); if(exists) c11__abort("module '%s' already exists", path);
// convert to a weak (const char*) BinTree__set(&pk_current_vm->modules, (void*)path, py_retval());
path = py_tostr(py_getdict(r0, __path__)); py_GlobalRef retval = py_getmodule(path);
BinTree__set(&pk_current_vm->modules, c11_strdup(path), r0); mi->self = retval;
return retval;
py_shrink(2);
return py_getmodule(path);
} }
int load_module_from_dll_desktop_only(const char* path) PY_RAISE PY_RETURN; 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); 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}); int is_init = c11_sv__endswith(top_filename, (c11_sv){"__init__.py", 11});
py_Ref package = py_getdict(vm->top_frame->module, __path__); py_ModuleInfo* mi = py_touserdata(vm->top_frame->module);
c11_sv package_sv = py_tosv(package); c11_sv package_sv = c11_string__sv(mi->path);
if(package_sv.size == 0) { if(package_sv.size == 0) {
return ImportError("attempted relative import with no known parent package"); return ImportError("attempted relative import with no known parent package");
} }
@ -163,9 +183,12 @@ __SUCCESS:
return ok ? 1 : -1; return ok ? 1 : -1;
} }
bool py_importlib_reload(py_GlobalRef module) { bool py_importlib_reload(py_Ref module) {
VM* vm = pk_current_vm; 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* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP);
c11_string* filename = c11_string__new3("%s.py", slashed_path->data); c11_string* filename = c11_string__new3("%s.py", slashed_path->data);
char* data = vm->callbacks.importfile(filename->data); char* data = vm->callbacks.importfile(filename->data);
@ -176,7 +199,8 @@ bool py_importlib_reload(py_GlobalRef module) {
} }
c11_string__delete(slashed_path); c11_string__delete(slashed_path);
if(data == NULL) return ImportError("module '%v' not found", 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); c11_string__delete(filename);
PK_FREE(data); PK_FREE(data);
py_assign(py_retval(), module); py_assign(py_retval(), module);

View File

@ -20,6 +20,15 @@ static bool namedict__getitem__(int argc, py_Ref argv) {
return true; 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) { static bool namedict__setitem__(int argc, py_Ref argv) {
PY_CHECK_ARGC(3); PY_CHECK_ARGC(3);
PY_CHECK_ARG_TYPE(1, tp_str); 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_setdict(py_tpobject(type), __hash__, py_None());
py_bindmethod(type, "items", namedict_items); py_bindmethod(type, "items", namedict_items);
py_bindmethod(type, "clear", namedict_clear); py_bindmethod(type, "clear", namedict_clear);
py_bindmethod(type, "get", namedict__get);
return type; return type;
} }

View File

@ -104,8 +104,8 @@ static bool type__module__(int argc, py_Ref argv) {
if(py_isnil(ti->module)) { if(py_isnil(ti->module)) {
py_newnone(py_retval()); py_newnone(py_retval());
} else { } else {
py_Ref path = py_getdict(ti->module, __path__); py_ModuleInfo* mi = py_touserdata(ti->module);
py_assign(py_retval(), path); py_newstrv(py_retval(), c11_string__sv(mi->path));
} }
return true; return true;
} }

View File

@ -166,10 +166,10 @@ bool py_getattr(py_Ref self, py_Name name) {
} }
if(self->type == tp_module) { if(self->type == tp_module) {
py_Ref path = py_getdict(self, __path__); py_ModuleInfo* mi = py_touserdata(self);
c11_sbuf buf; c11_sbuf buf;
c11_sbuf__ctor(&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); c11_string* new_path = c11_sbuf__submit(&buf);
int res = py_import(new_path->data); int res = py_import(new_path->data);
c11_string__delete(new_path); c11_string__delete(new_path);

View File

@ -35,6 +35,12 @@ bool py_applydict(py_Ref self, bool (*f)(py_Name, py_Ref, void*), void* ctx) {
return true; 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) { bool py_deldict(py_Ref self, py_Name name) {
assert(self && self->is_ptr); assert(self && self->is_ptr);
return NameDict__del(PyObject__dict(self->_obj), name); return NameDict__del(PyObject__dict(self->_obj), name);

View File

@ -23,6 +23,11 @@ assert get_value_2() == '123'
from test3.a.b import value from test3.a.b import value
assert value == 1 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(): def f():
import math as m import math as m
assert m.pi > 3 assert m.pi > 3

43
tests/31_modulereload.py Normal file
View 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()

View File

@ -0,0 +1,2 @@
from .a import MyClass
from . import a

18
tests/testreload/a.py Normal file
View 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