8000 Make import statement, __import__ builtin and IMPORT_NAME opcode work… · go-python/gpython@0f6ac1b · GitHub
[go: up one dir, main page]

Skip to content

Commit 0f6ac1b

Browse files
committed
Make import statement, __import__ builtin and IMPORT_NAME opcode work (a bit)
1 parent 5511993 commit 0f6ac1b

File tree

3 files changed

+245
-2
lines changed

3 files changed

+245
-2
lines changed

builtin/builtin.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Noteworthy: None is the 'nil' object; Ellipsis represents '...' in slices.`
1515
func init() {
1616
methods := []*py.Method{
1717
py.NewMethod("__build_class__", builtin___build_class__, 0, build_class_doc),
18-
// py.NewMethod("__import__", builtin___import__, 0, import_doc),
18+
py.NewMethod("__import__", builtin___import__, 0, import_doc),
1919
py.NewMethod("abs", builtin_abs, 0, abs_doc),
2020
// py.NewMethod("all", builtin_all, 0, all_doc),
2121
// py.NewMethod("any", builtin_any, 0, any_doc),
@@ -324,3 +324,28 @@ func builtin_next(self py.Object, args py.Tuple) (res py.Object) {
324324

325325
return py.Next(it)
326326
}
327+
328+
const import_doc = `__import__(name, globals=None, locals=None, fromlist=(), level=0) -> module
329+
330+
Import a module. Because this function is meant for use by the Python
331+
interpreter and not for general use it is better to use
332+
importlib.import_module() to programmatically import a module.
333+
334+
The globals argument is only used to determine the context;
335+
they are not modified. The locals argument is unused. The fromlist
336+
should be a list of names to emulate ''from name import ...'', or an
337+
empty list to emulate ''import name''.
338+
When importing a module from a package, note that __import__('A.B', ...)
339+
returns package A when fromlist is empty, but its submodule B when
340+
fromlist is not empty. Level is used to determine whether to perform
341+
absolute or relative imports. 0 is absolute while a positive number
342+
is the number of parent directories to search relative to the current module.`
343+
344+
func builtin___import__(self py.Object, args py.Tuple, kwargs py.StringDict) py.Object {
345+
kwlist := []string{"name", "globals", "locals", "fromlist", "level"}
346+
var name, globals, locals, fromlist py.Object
347+
var level py.Object = py.Int(0)
348+
349+
py.ParseTupleAndKeywords(args, kwargs, "U|OOOi:__import__", kwlist, &name, &globals, &locals, &fromlist, &level)
350+
return py.ImportModuleLevelObject(name, globals, locals, fromlist, int(level.(py.Int)))
351+
}

py/import.go

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// Import modules
2+
3+
package py
4+
5+
import (
6+
"strings"
7+
)
8+
9+
// FIXME
10+
var importlib *Module
11+
12+
// The workings of __import__
13+
func ImportModuleLevelObject(nameObj, given_globals, locals, given_fromlist Object, level int) Object {
14+
var abs_name string
15+
var builtins_import Object
16+
var final_mod Object
17+
var mod Object
18+
var PackageObj Object
19+
var Package string
20+
var globals StringDict
21+
var fromlist Tuple
22+
var ok bool
23+
var name string
24+
25+
// Make sure to use default values so as to not have
26+
// PyObject_CallMethodObjArgs() truncate the parameter list because of a
27+
// nil argument.
28+
if given_globals == nil {
29+
globals = StringDict{}
30+
} else {
31+
// Only have to care what given_globals is if it will be used
32+
// for something.
33+
globals, ok = given_globals.(StringDict)
34+
if level > 0 && !ok {
35+
panic(ExceptionNewf(TypeError, "globals must be a dict"))
36+
}
37+
}
38+
39+
if given_fromlist == nil || given_fromlist == None {
40+
fromlist = Tuple{}
41+
} else {
42+
fromlist = SequenceTuple(given_fromlist)
43+
}
44+
if nameObj == nil {
45+
panic(ExceptionNewf(ValueError, "Empty module name"))
46+
}
47+
48+
// The below code is importlib.__import__() & _gcd_import(), ported to Go
49+
// for added performance.
50+
51+
_, ok = nameObj.(String)
52+
if !ok {
53+
panic(ExceptionNewf(TypeError, "module name must be a string"))
54+
}
55+
name = string(nameObj.(String))
56+
57+
if level < 0 {
58+
panic(ExceptionNewf(ValueError, "level must be >= 0"))
59+
} else if level > 0 {
60+
PackageObj, ok = globals["__package__"]
61+
if ok && PackageObj != None {
62+
if _, ok = PackageObj.(String); !ok {
63+
panic(ExceptionNewf(TypeError, "package must be a string"))
64+
}
65+
Package = string(PackageObj.(String))
66+
} else {
67+
PackageObj, ok = globals["__name__"]
68+
if !ok {
69+
panic(ExceptionNewf(KeyError, "'__name__' not in globals"))
70+
} else if _, ok = PackageObj.(String); !ok {
71+
panic(ExceptionNewf(TypeError, "__name__ must be a string"))
72+
}
73+
Package = string(PackageObj.(String))
74+
75+
if _, ok = globals["__path__"]; !ok {
76+
i := strings.LastIndex(string(Package), ".")
77+
if i < 0 {
78+
Package = ""
79+
} else {
80+
Package = Package[:i]
81+
}
82+
}
83+
}
84+
85+
if _, ok = modules[string(Package)]; !ok {
86+
panic(ExceptionNewf(SystemError, "Parent module %q not loaded, cannot perform relative import", Package))
87+
}
88+
} else { // level == 0 */
89+
if len(name) == 0 {
90+
panic(ExceptionNewf(ValueError, "Empty module name"))
91+
}
92+
Package = ""
93+
}
94+
95+
if level > 0 {
96+
last_dot := len(Package)
97+
var base string
98+
level_up := 1
99+
100+
for level_up = 1; level_up < level; level_up += 1 {
101+
last_dot = strings.LastIndex(string(Package[:last_dot]), ".")
102+
if last_dot < 0 {
103+
panic(ExceptionNewf(ValueError, "attempted relative import beyond top-level Package"))
104+
}
105+
}
106+
107+
base = Package[:last_dot]
108+
109+
if len(name) > 0 {
110+
abs_name = strings.Join([]string{base, name}, ".")
111+
} else {
112+
abs_name = base
113+
}
114+
} else {
115+
abs_name = name
116+
}
117+
118+
// FIXME _PyImport_AcquireLock()
119+
120+
// From this point forward, goto error_with_unlock!
121+
builtins_import, ok = globals["__import__"]
122+
if !ok {
123+
builtins_import, ok = Builtins.Globals["__import__"]
124+
if !ok {
125+
panic(ExceptionNewf(ImportError, "__import__ not found"))
126+
}
127+
}
128+
129+
mod, ok = modules[abs_name]
130+
if mod == None {
131+
panic(ExceptionNewf(ImportError, "import of %q halted; None in sys.modules", abs_name))
132+
} else if ok {
133+
var value Object
134+
initializing := false
135+
136+
// Optimization: only call _bootstrap._lock_unlock_module() if
137+
// __initializing__ is true.
138+
// NOTE: because of this, __initializing__ must be set *before*
139+
// stuffing the new module in sys.modules.
140+
141+
value = GetAttrOrNil(mod, "__initializing__")
142+
if value != nil {
143+
initializing = bool(MakeBool(value).(Bool))
144+
}
145+
if initializing {
146+
// _bootstrap._lock_unlock_module() releases the import lock */
147+
value = importlib.Call("_lock_unlock_module", Tuple{String(abs_name)}, nil)
148+
} else {
149+
// FIXME locking
150+
// if _PyImport_ReleaseLock() < 0 {
151+
// panic(ExceptionNewf(RuntimeError, "not holding the import lock"))
152+
// }
153+
}
154+
} else {
155+
// _bootstrap._find_and_load() releases the import lock
156+
mod = importlib.Call("_find_and_load", Tuple{String(abs_name), builtins_import}, nil)
157+
}
158+
// From now on we don't hold the import lock anymore.
159+
160+
if len(fromlist) == 0 {
161+
if level == 0 || len(name) > 0 {
162+
i := strings.Index(name, ".")
163+
if i < 0 {
164+
// No dot in module name, simple exit
165+
final_mod = mod
166+
goto error
167+
}
168+
front := name[:1]
169+
170+
if level == 0 {
171+
final_mod = Call(builtins_import, Tuple{String(front)}, nil)
172+
} else {
173+
cut_off := len(name) - len(front)
174+
abs_name_len := len(abs_name)
175+
to_return := abs_name[:abs_name_len-cut_off]
176+
final_mod, ok = modules[to_return]
177+
if !ok {
178+
panic(ExceptionNewf(KeyError, "%q not in sys.modules as expected", to_return))
179+
}
180+
}
181+
} else {
182+
final_mod = mod
183+
}
184+
} else {
185+
final_mod = importlib.Call("_handle_fromlist", Tuple{mod, fromlist, builtins_import}, nil)
186+
}
187+
goto error
188+
189+
//error_with_unlock:
190+
// FIXME defer?
191+
// if _PyImport_ReleaseLock() < 0 {
192+
// panic(ExceptionNewf(RuntimeError, "not holding the import lock")
193+
// }
194+
error:
195+
// FIXME defer?
196+
// if final_mod == nil {
197+
// remove_importlib_frames()
198+
// }
199+
return final_mod
200+
}

vm/eval.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,25 @@ func do_COMPARE_OP(vm *Vm, opname int32) {
859859
// STORE_FAST instruction modifies the namespace.
860860
func do_IMPORT_NAME(vm *Vm, namei int32) {
861861
defer vm.CheckException()
862-
vm.NotImplemented("IMPORT_NAME", namei)
862+
name := py.String(vm.frame.Code.Names[namei])
863+
__import__, ok := vm.frame.Builtins["__import__"]
864+
if !ok {
865+
panic(py.ExceptionNewf(py.ImportError, "__import__ not found"))
866+
}
867+
v := vm.POP()
868+
u := vm.TOP()
869+
var locals py.Object = vm.frame.Locals
870+
if locals == nil {
871+
locals = py.None
872+
}
873+
var args py.Tuple
874+
if _, ok := u.(py.Int); ok {
875+
args = py.Tuple{name, vm.frame.Globals, locals, v, u}
876+
} else {
877+
args = py.Tuple{name, vm.frame.Globals, locals, v}
878+
}
879+
x := py.Call(__import__, args, nil)
880+
vm.SET_TOP(x)
863881
}
864882

865883
// Loads the attribute co_names[namei] from the module found in

0 commit comments

Comments
 (0)
0