Skip to content

Commit 48d96b4

Browse files
zsquarepluscdpgeorge
authored andcommitted
py/builtinimport: Support relative import in custom __import__ callback.
The globals need to be forwarded from the caller's context. Signed-off-by: Damien George <damien@micropython.org>
1 parent c07fda7 commit 48d96b4

File tree

4 files changed

+42
-6
lines changed

4 files changed

+42
-6
lines changed

py/builtinimport.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ static void do_load(mp_module_context_t *module_obj, vstr_t *file) {
267267

268268
// Convert a relative (to the current module) import, going up "level" levels,
269269
// into an absolute import.
270-
static void evaluate_relative_import(mp_int_t level, const char **module_name, size_t *module_name_len) {
270+
static void evaluate_relative_import(mp_int_t level, const char **module_name, size_t *module_name_len, mp_obj_t globals) {
271271
// What we want to do here is to take the name of the current module,
272272
// remove <level> trailing components, and concatenate the passed-in
273273
// module name.
@@ -276,20 +276,20 @@ static void evaluate_relative_import(mp_int_t level, const char **module_name, s
276276
// module's position in the package hierarchy."
277277
// http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name
278278

279-
mp_obj_t current_module_name_obj = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___name__));
279+
mp_obj_t current_module_name_obj = mp_obj_dict_get(globals, MP_OBJ_NEW_QSTR(MP_QSTR___name__));
280280
assert(current_module_name_obj != MP_OBJ_NULL);
281281

282282
#if MICROPY_MODULE_OVERRIDE_MAIN_IMPORT && MICROPY_CPYTHON_COMPAT
283283
if (MP_OBJ_QSTR_VALUE(current_module_name_obj) == MP_QSTR___main__) {
284284
// This is a module loaded by -m command-line switch (e.g. unix port),
285285
// and so its __name__ has been set to "__main__". Get its real name
286286
// that we stored during import in the __main__ attribute.
287-
current_module_name_obj = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___main__));
287+
current_module_name_obj = mp_obj_dict_get(globals, MP_OBJ_NEW_QSTR(MP_QSTR___main__));
288288
}
289289
#endif
290290

291291
// If we have a __path__ in the globals dict, then we're a package.
292-
bool is_pkg = mp_map_lookup(&mp_globals_get()->map, MP_OBJ_NEW_QSTR(MP_QSTR___path__), MP_MAP_LOOKUP);
292+
bool is_pkg = mp_map_lookup(mp_obj_dict_get_map(globals), MP_OBJ_NEW_QSTR(MP_QSTR___path__), MP_MAP_LOOKUP);
293293

294294
#if DEBUG_PRINT
295295
DEBUG_printf("Current module/package: ");
@@ -569,10 +569,19 @@ mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) {
569569
const char *module_name = mp_obj_str_get_data(module_name_obj, &module_name_len);
570570

571571
if (level != 0) {
572+
// This is the dict with all global symbols.
573+
mp_obj_t globals = MP_OBJ_FROM_PTR(mp_globals_get());
574+
if (n_args >= 2 && args[1] != mp_const_none) {
575+
globals = args[1];
576+
if (!mp_obj_is_type(globals, &mp_type_dict)) {
577+
mp_raise_TypeError(NULL);
578+
}
579+
}
580+
572581
// Turn "foo.bar" with level=3 into "<current module 3 components>.foo.bar".
573582
// Current module name is extracted from globals().__name__.
574-
evaluate_relative_import(level, &module_name, &module_name_len);
575583
// module_name is now an absolute module path.
584+
evaluate_relative_import(level, &module_name, &module_name_len, globals);
576585
}
577586

578587
if (module_name_len == 0) {

py/runtime.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1522,7 +1522,7 @@ mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) {
15221522
// build args array
15231523
mp_obj_t args[5];
15241524
args[0] = MP_OBJ_NEW_QSTR(name);
1525-
args[1] = mp_const_none; // TODO should be globals
1525+
args[1] = MP_OBJ_FROM_PTR(mp_globals_get()); // globals of the current context
15261526
args[2] = mp_const_none; // TODO should be locals
15271527
args[3] = fromlist;
15281528
args[4] = level;

tests/import/builtin_import.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,12 @@
2020
__import__("xyz", None, None, None, -1)
2121
except ValueError:
2222
print("ValueError")
23+
24+
# globals is not checked for level=0
25+
__import__("builtins", "globals")
26+
27+
# globals must be a dict (or None) for level>0
28+
try:
29+
__import__("builtins", "globals", None, None, 1)
30+
except TypeError:
31+
print("TypeError")

tests/import/import_override2.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# test overriding __import__ combined with importing from the filesystem
2+
3+
4+
def custom_import(name, globals, locals, fromlist, level):
5+
if level > 0:
6+
print("import", name, fromlist, level)
7+
return orig_import(name, globals, locals, fromlist, level)
8+
9+
10+
orig_import = __import__
11+
try:
12+
__import__("builtins").__import__ = custom_import
13+
except AttributeError:
14+
print("SKIP")
15+
raise SystemExit
16+
17+
# import calls __import__ behind the scenes
18+
import pkg7.subpkg1.subpkg2.mod3

0 commit comments

Comments
 (0)