mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-26 06:20:16 +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
|
||||
#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),
|
||||
base,
|
||||
frame->module,
|
||||
NULL,
|
||||
base_ti->is_python,
|
||||
false);
|
||||
py_Type type = pk_newtypewithmode(name,
|
||||
base,
|
||||
frame->module,
|
||||
NULL,
|
||||
base_ti->is_python,
|
||||
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