Skip to content

Commit e906ee1

Browse files
committed
Redo import
1 parent e83891d commit e906ee1

File tree

4 files changed

+153
-3
lines changed

4 files changed

+153
-3
lines changed

builtin/builtin.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -345,11 +345,17 @@ is the number of parent directories to search relative to the current module.`
345345

346346
func builtin___import__(self py.Object, args py.Tuple, kwargs py.StringDict) py.Object {
347347
kwlist := []string{"name", "globals", "locals", "fromlist", "level"}
348-
var name, globals, locals, fromlist py.Object
348+
var name py.Object
349+
var globals py.Object = py.NewStringDict()
350+
var locals py.Object = py.NewStringDict()
351+
var fromlist py.Object = py.Tuple{}
349352
var level py.Object = py.Int(0)
350353

351354
py.ParseTupleAndKeywords(args, kwargs, "U|OOOi:__import__", kwlist, &name, &globals, &locals, &fromlist, &level)
352-
return py.ImportModuleLevelObject(name, globals, locals, fromlist, int(level.(py.Int)))
355+
if fromlist == py.None {
356+
fromlist = py.Tuple{}
357+
}
358+
return py.ImportModuleLevelObject(string(name.(py.String)), globals.(py.StringDict), locals.(py.StringDict), fromlist.(py.Tuple), int(level.(py.Int)))
353359
}
354360

355361
const ord_doc = `ord(c) -> integer

compile/compile.go

+5
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,8 @@ sys.stdout.close()`,
6868
}
6969
return obj
7070
}
71+
72+
// Set in py to avoid circular import
73+
func init() {
74+
py.Compile = Compile
75+
}

py/import.go

+138-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,148 @@
33
package py
44

55
import (
6+
"io/ioutil"
7+
"os"
8+
"path"
9+
"path/filepath"
610
"strings"
711
)
812

13+
var (
14+
// This will become sys.path one day ;-)
15+
modulePath = []string{"", "/usr/lib/python3.3", "/usr/local/lib/python3.3/dist-packages", "/usr/lib/python3/dist-packages"}
16+
)
17+
918
// The workings of __import__
10-
func ImportModuleLevelObject(nameObj, given_globals, locals, given_fromlist Object, level int) Object {
19+
//
20+
// __import__(name, globals=None, locals=None, fromlist=(), level=0)
21+
//
22+
// This function is invoked by the import statement. It can be
23+
// replaced (by importing the builtins module and assigning to
24+
// builtins.__import__) in order to change semantics of the import
25+
// statement, but doing so is strongly discouraged as it is usually
26+
// simpler to use import hooks (see PEP 302) to attain the same goals
27+
// and does not cause issues with code which assumes the default
28+
// import implementation is in use. Direct use of __import__() is also
29+
// discouraged in favor of importlib.import_module().
30+
//
31+
// The function imports the module name, potentially using the given
32+
// globals and locals to determine how to interpret the name in a
33+
// package context. The fromlist gives the names of objects or
34+
// submodules that should be imported from the module given by
35+
// name. The standard implementation does not use its locals argument
36+
// at all, and uses its globals only to determine the package context
37+
// of the import statement.
38+
//
39+
// level specifies whether to use absolute or relative imports. 0 (the
40+
// default) means only perform absolute imports. Positive values for
41+
// level indicate the number of parent directories to search relative
42+
// to the directory of the module calling __import__() (see PEP 328
43+
// for the details).
44+
//
45+
// When the name variable is of the form package.module, normally, the
46+
// top-level package (the name up till the first dot) is returned, not
47+
// the module named by name. However, when a non-empty fromlist
48+
// argument is given, the module named by name is returned.
49+
//
50+
// For example, the statement import spam results in bytecode
51+
// resembling the following code:
52+
//
53+
// spam = __import__('spam', globals(), locals(), [], 0)
54+
// The statement import spam.ham results in this call:
55+
//
56+
// spam = __import__('spam.ham', globals(), locals(), [], 0)
57+
//
58+
// Note how __import__() returns the toplevel module here because this
59+
// is the object that is bound to a name by the import statement.
60+
//
61+
// On the other hand, the statement from spam.ham import eggs, sausage
62+
// as saus results in
63+
//
64+
// _temp = __import__('spam.ham', globals(), locals(), ['eggs', 'sausage'], 0)
65+
// eggs = _temp.eggs
66+
// saus = _temp.sausage
67+
//
68+
// Here, the spam.ham module is returned from __import__(). From this
69+
// object, the names to import are retrieved and assigned to their
70+
// respective names.
71+
//
72+
// If you simply want to import a module (potentially within a
73+
// package) by name, use importlib.import_module().
74+
//
75+
// Changed in version 3.3: Negative values for level are no longer
76+
// supported (which also changes the default value to 0).
77+
func ImportModuleLevelObject(name string, globals, locals StringDict, fromlist Tuple, level int) Object {
78+
// Module already loaded - return that
79+
if module, ok := modules[name]; ok {
80+
return module
81+
}
82+
83+
if level != 0 {
84+
panic("Relative import not supported yet")
85+
}
86+
87+
parts := strings.Split(name, ".")
88+
pathParts := path.Join(parts...)
89+
90+
for _, mpath := range modulePath {
91+
if mpath == "" {
92+
mpathObj, ok := globals["__file__"]
93+
if !ok {
94+
panic(ExceptionNewf(SystemError, "Couldn't find __file__ in globals"))
95+
}
96+
mpath = string(mpathObj.(String))
97+
}
98+
fullPath := path.Join(mpath, pathParts)
99+
// FIXME Read pyc/pyo too
100+
fullPath, err := filepath.Abs(fullPath + ".py")
101+
if err != nil {
102+
continue
103+
}
104+
// Check if file exists
105+
if _, err := os.Stat(fullPath); err == nil {
106+
str, err := ioutil.ReadFile(fullPath)
107+
if err != nil {
108+
panic(ExceptionNewf(OSError, "Couldn't read %q: %v", fullPath, err))
109+
}
110+
codeObj := Compile(string(str), fullPath, "exec", 0, true)
111+
code, ok := codeObj.(*Code)
112+
if !ok {
113+
panic(ExceptionNewf(ImportError, "Compile didn't return code object"))
114+
}
115+
module := NewModule(name, "", nil, nil)
116+
_, err = Run(module.Globals, module.Globals, code, nil)
117+
if err != nil {
118+
panic(err)
119+
}
120+
return module
121+
}
122+
}
123+
panic(ExceptionNewf(ImportError, "No module named '%s'", name))
124+
125+
// Convert to absolute path if relative
126+
// Use __file__ from globals to work out what we are relative to
127+
128+
// '' in path seems to mean use the current __file__
129+
130+
// Find a valid path which we need to check for the correct __init__.py in subdirectories etc
131+
132+
// Look for .py and .pyc files
133+
134+
// Make absolute module path too if we can for sys.modules
135+
136+
//How do we uniquely identify modules?
137+
138+
// SystemError: Parent module '' not loaded, cannot perform relative import
139+
140+
}
141+
142+
// Straight port of the python code
143+
//
144+
// This calls functins from _bootstrap.py which is a frozen module
145+
//
146+
// Too much functionality for the moment
147+
func XImportModuleLevelObject(nameObj, given_globals, locals, given_fromlist Object, level int) Object {
11148
var abs_name string
12149
var builtins_import Object
13150
var final_mod Object

py/py.go

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ var (
1717
// See vm/eval.go - set to avoid circular import
1818
Run func(globals, locals StringDict, code *Code, closure Tuple) (res Object, err error)
1919
RunFrame func(frame *Frame) (res Object, err error)
20+
// See compile/compile.go - set to avoid circular import
21+
Compile func(str, filename, mode string, flags int, dont_inherit bool) Object
2022
)
2123

2224
// Called to create a new instance of class cls. __new__() is a static method (special-cased so you need not declare it as such) that takes the class of which an instance was requested as its first argument. The remaining arguments are those passed to the object constructor expression (the call to the class). The return value of __new__() should be the new object instance (usually an instance of cls).

0 commit comments

Comments
 (0)