This commit is contained in:
blueloveTH 2026-01-26 17:24:29 +08:00
parent 979addecf9
commit 2281b9bb44
16 changed files with 151 additions and 108 deletions

View File

@ -35,7 +35,6 @@ typedef struct TypePointer {
} TypePointer;
typedef struct py_ModuleInfo {
c11_string* name;
c11_string* package;
c11_string* path;
py_GlobalRef self; // weakref to the original module object

View File

@ -57,7 +57,6 @@ MAGIC_METHOD(__exit__)
MAGIC_METHOD(__name__)
MAGIC_METHOD(__all__)
MAGIC_METHOD(__package__)
MAGIC_METHOD(__path__)
MAGIC_METHOD(__class__)
MAGIC_METHOD(__getattr__)
MAGIC_METHOD(__reduce__)

View File

@ -2462,17 +2462,50 @@ static Error* compile_decorated(Compiler* self) {
// import a [as b]
// import a [as b], c [as d]
static Error* compile_normal_import(Compiler* self) {
static Error* compile_normal_import(Compiler* self, c11_sbuf* buf) {
do {
consume(TK_ID);
c11_sv name = Token__sv(prev());
int index = Ctx__add_const_string(ctx(), name);
Ctx__emit_(ctx(), OP_IMPORT_PATH, index, prev()->line);
if(match(TK_AS)) {
c11_sbuf__write_sv(buf, name);
bool has_sub_cpnt = false;
while(match(TK_DOT)) {
has_sub_cpnt = true;
consume(TK_ID);
name = Token__sv(prev());
c11_sbuf__write_char(buf, '.');
c11_sbuf__write_sv(buf, Token__sv(prev()));
}
Ctx__emit_store_name(ctx(), name_scope(self), py_namev(name), prev()->line);
c11_string* path = c11_sbuf__submit(buf);
int path_index = Ctx__add_const_string(ctx(), c11_string__sv(path));
c11_string__delete(path);
NameScope scope = name_scope(self);
Ctx__emit_(ctx(), OP_IMPORT_PATH, path_index, prev()->line);
// [module <path>]
if(!has_sub_cpnt) {
if(match(TK_AS)) {
// import a as x
consume(TK_ID);
name = Token__sv(prev());
} else {
// import a
}
} else {
if(match(TK_AS)) {
// import a.b as x
consume(TK_ID);
name = Token__sv(prev());
} else {
// import a.b
Ctx__emit_(ctx(), OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
int index = Ctx__add_const_string(ctx(), name);
Ctx__emit_(ctx(), OP_IMPORT_PATH, index, BC_KEEPLINE);
}
}
Ctx__emit_store_name(ctx(), scope, py_namev(name), BC_KEEPLINE);
} while(match(TK_COMMA));
consume_end_stmt();
return NULL;
@ -2485,7 +2518,7 @@ static Error* compile_normal_import(Compiler* self) {
// from ..a import b [as c]
// from .a.b import c [as d]
// from xxx import *
static Error* compile_from_import(c11_sbuf* buf, Compiler* self) {
static Error* compile_from_import(Compiler* self, c11_sbuf* buf) {
int dots = 0;
while(true) {
@ -2695,11 +2728,18 @@ static Error* compile_stmt(Compiler* self) {
}
case TK_WHILE: check(compile_while_loop(self)); break;
case TK_FOR: check(compile_for_loop(self)); break;
case TK_IMPORT: check(compile_normal_import(self)); break;
case TK_IMPORT: {
c11_sbuf buf;
c11_sbuf__ctor(&buf);
err = compile_normal_import(self, &buf);
c11_sbuf__dtor(&buf);
if(err) return err;
break;
}
case TK_FROM: {
c11_sbuf buf;
c11_sbuf__ctor(&buf);
err = compile_from_import(&buf, self);
err = compile_from_import(self, &buf);
c11_sbuf__dtor(&buf);
if(err) return err;
break;

View File

@ -465,10 +465,12 @@ static bool builtins_compile(int argc, py_Ref argv) {
static bool builtins__import__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
PY_CHECK_ARG_TYPE(0, tp_str);
int res = py_import(py_tostr(argv));
const char* path = py_tostr(py_arg(0));
if(path[0] == '.') return ValueError("relative import not allowed here");
int res = py_import(path);
if(res == -1) return false;
if(res) return true;
return ImportError("module '%s' not found", py_tostr(argv));
return ImportError("module '%s' not found", path);
}
static bool NoneType__repr__(int argc, py_Ref argv) {

View File

@ -13,20 +13,18 @@ py_Ref py_getmodule(const char* path) {
return BinTree__try_get(&vm->modules, (void*)path);
}
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");
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((c11_sv){path, path_len}, '.');
if(last_dot == -1) {
mi->name = c11_string__new(path);
mi->package = c11_string__new("");
int last_dot = c11_sv__rindex(pathv, '.');
if(last_dot == -1 || is_init) {
mi->package = c11_string__new(path);
} else {
const char* start = path + last_dot + 1;
mi->name = c11_string__new(start);
// a.b.c -> a.b
mi->package = c11_string__new2(path, last_dot);
}
@ -43,16 +41,17 @@ py_Ref py_newmodule(const char* path) {
mi->self = retval;
// setup __name__
py_newstrv(py_emplacedict(retval, __name__), c11_string__sv(mi->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));
// setup __path__
py_newstrv(py_emplacedict(retval, __path__), c11_string__sv(mi->path));
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->name);
c11_string__delete(mi->package);
c11_string__delete(mi->path);
}
@ -71,27 +70,28 @@ int py_import(const char* path_cstr) {
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++;
// */__init__.py[c]
c11_sv top_filepath = c11_string__sv(vm->top_frame->co->src->filename);
c11_sv top_filename = c11_sv__filename(top_filepath);
int is_init = c11__sveq2(top_filename, "__init__.py") || c11__sveq2(top_filename, "__init__.pyc");
py_ModuleInfo* mi = py_touserdata(vm->top_frame->module);
c11_sv package_sv = c11_string__sv(mi->path);
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 = is_init; i < dot_count; i++) {
for(int i = 1; i < dot_count; i++) {
if(cpnts.length == 0){
ImportError("attempted relative import beyond top-level package");
return -1;
@ -119,7 +119,22 @@ int py_import(const char* path_cstr) {
return res;
}
assert(path.data[0] != '.' && path.data[path.size - 1] != '.');
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);
@ -143,6 +158,7 @@ int py_import(const char* path_cstr) {
bool need_free = true;
bool is_pyc = false;
bool is_init = false;
const char* data = load_kPythonLib(path_cstr);
int data_size = -1;
@ -165,13 +181,17 @@ int py_import(const char* path_cstr) {
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) goto __SUCCESS;
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;
}
@ -184,7 +204,7 @@ __SUCCESS:
do {
} while(0);
py_GlobalRef mod = py_newmodule(path_cstr);
py_GlobalRef mod = pk_newmodule(path_cstr, is_init);
bool ok;
if(is_pyc) {

View File

@ -166,16 +166,3 @@ for i in range(len(data)):
if i % 3 == 0:
y = b.pop()
delattr(a, y)
# bug test
d = {
'__name__': '__main__',
'__package__': '',
'__path__': '__main__',
'a': [],
'gc': 1,
}
del d['a']
assert 'a' not in d
assert d['gc'] == 1

View File

@ -14,14 +14,13 @@ else:
assert os.getcwd().endswith('tests')
import test1
assert test1.add(1, 2) == 13
from test2.a.g import get_value, A
assert get_value() == '123'
assert (A.__module__ == 'test2.a.g'), A.__module__
import test2
import test2.a.g
assert test2.a.g.get_value() == '123'
from test2.utils import get_value_2
@ -30,10 +29,12 @@ assert get_value_2() == '123'
from test3.a.b import value
assert value == 1
import test3.a.b as test3_ab
assert test3_ab.value == 1
from test2.utils import r
assert r.__name__ == 'r'
assert r.__name__ == 'test2.utils.r'
assert r.__package__ == 'test2.utils'
assert r.__path__ == 'test2.utils.r'
def f():
import math as m

25
tests/301_import1.py Normal file
View File

@ -0,0 +1,25 @@
try:
import os
except ImportError:
exit(0)
import sys
is_pyc = sys.argv[0].endswith('.pyc')
if is_pyc:
os.chdir('tmp/tests')
else:
os.chdir('tests')
assert os.getcwd().endswith('tests')
os.environ['STDOUT'] = ''
import test.a.g.q
assert os.environ['STDOUT'] == 'test init!!\ntest.a init!!\ntest.a.g init!!\ntest.a.g.q init!!\n'
import test
assert test.__package__ == 'test'
assert test.__name__ == 'test'

View File

@ -1,46 +0,0 @@
# a=[]
# import gc
# gc.collect()
# # a.append(a)
# print(globals().items())
# del a
# print(list(globals().items()))
# print(globals()['gc'])
# gc.collect()
d = object()
d.__name__ = '__main__'
d.__package__ = ''
d.__path__ = '__main__'
d.a = []
d.gc = 1
assert d.gc == 1
del d.a
assert not hasattr(d, 'a')
assert d.gc == 1
# [0, 1, 6, 7, 4, 5, 2, 3]
# 0 __name__ [0]
# 1 __package__ [1]
# 2 nil
# 3 nil
# 4 gc [4]
# 5 nil
# 6 __path__ [2]
# 7 a [3]
import gc
gc.collect()
a = []
del a
assert gc.collect() == 1
a = []
a.append(a)
del a
assert gc.collect() == 1

5
tests/test/__init__.py Normal file
View File

@ -0,0 +1,5 @@
assert __package__ == 'test'
assert __name__ == 'test'
import os
os.environ['STDOUT'] += 'test init!!\n'

5
tests/test/a/__init__.py Normal file
View File

@ -0,0 +1,5 @@
assert __package__ == 'test.a'
assert __name__ == 'test.a'
import os
os.environ['STDOUT'] += 'test.a init!!\n'

View File

@ -0,0 +1,7 @@
assert __package__ == 'test.a.g'
assert __name__ == 'test.a.g'
import os
os.environ['STDOUT'] += 'test.a.g init!!\n'

5
tests/test/a/g/q.py Normal file
View File

@ -0,0 +1,5 @@
assert __package__ == 'test.a.g'
assert __name__ == 'test.a.g.q'
import os
os.environ['STDOUT'] += 'test.a.g.q init!!\n'

View File

@ -1,7 +1,7 @@
D = 10
try:
import abc # does not exist
import xxxxx # does not exist
exit(1)
except ImportError:
pass

View File

@ -1,3 +0,0 @@
raise ValueError(
"test3 should not be imported"
)

View File

@ -1,3 +0,0 @@
raise ValueError(
"test3.a should not be imported"
)