blueloveTH 1 miesiąc temu
rodzic
commit
2281b9bb44

+ 0 - 1
include/pocketpy/interpreter/vm.h

@@ -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

+ 0 - 1
include/pocketpy/xmacros/magics.h

@@ -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__)

+ 49 - 9
src/compiler/compiler.c

@@ -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;

+ 4 - 2
src/modules/builtins.c

@@ -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) {

+ 45 - 25
src/public/ModuleSystem.c

@@ -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) {

+ 0 - 13
tests/080_dict.py

@@ -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

+ 5 - 4
tests/300_import.py

@@ -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 - 0
tests/301_import1.py

@@ -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'

+ 0 - 46
tests/712_gc_bug.py

@@ -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 - 0
tests/test/__init__.py

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

+ 5 - 0
tests/test/a/__init__.py

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

+ 7 - 0
tests/test/a/g/__init__.py

@@ -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 - 0
tests/test/a/g/q.py

@@ -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'

+ 1 - 1
tests/test2/b.py

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

+ 0 - 3
tests/test3/__init__.py

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

+ 0 - 3
tests/test3/a/__init__.py

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