Skip to content

py/builtinimport: support relative import in custom __import__ callbacks #6665

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 35 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
483b2e9
py/builtinimport: support relative import in custom __import__ callbacks
zsquareplusc Nov 30, 2020
823eefb
py/builtinimport: fix CI fails
zsquareplusc Nov 30, 2020
3bf3066
extmod/uasyncio: support for overriden __import__
zsquareplusc Dec 1, 2020
0109a24
tests/extmod: Add test for the precision of utime functions.
Dec 16, 2020
bd1e1e7
unix/modtime: Fix time() precision on unix ports with non-double floats.
Dec 16, 2020
3ae7d8e
unix/modtime: Remove unnecessary parenthesis to keep a consistent style.
Dec 20, 2020
81c9376
Merge branch 'fix-utime-time-precision' into dev
Dec 29, 2020
8413c46
Merge remote-tracking branch 'zsquareplusc/fix-override-relative-impo…
Dec 29, 2020
e2640ea
Merge branch 'master' into dev
Jan 19, 2021
f416bd8
extmod/modujson.c: Make ujson-dump/dumps kw-functions
peterzuger Feb 3, 2021
765a494
extmod/modujson.c: Catch and re-raise exceptions during printing
peterzuger Feb 3, 2021
70ef973
py/obj: Add global separators for ujson
peterzuger Feb 3, 2021
16df159
extmod/modujson.c: Added dump/dumps separators
peterzuger Feb 3, 2021
483b91e
Merge branch 'master' into dev
Feb 4, 2021
4a36076
Merge remote-tracking branch 'peterzuger/ujson-dump-separators' into dev
Feb 4, 2021
e73ff98
Merge branch 'master' into dev
Mar 12, 2021
209981c
Merge branch 'master' into dev
Apr 8, 2021
2c19cd5
Merge branch 'master' into dev
May 15, 2021
87e6387
Merge branch 'master' into dev
May 22, 2021
ff58f5a
Merge branch 'master' into dev
May 25, 2021
721bd30
Merge branch 'master' into dev
Jun 25, 2021
b4a2153
Merge branch 'master' into dev
Jul 7, 2021
db57e34
Revert "Merge remote-tracking branch 'peterzuger/ujson-dump-separator…
Feb 4, 2021
5585562
Merge branch 'master' into dev
Aug 18, 2021
0579aba
Merge branch 'master' into dev
Aug 19, 2021
600ca07
Merge branch 'master' into dev
oliver-joos Sep 14, 2021
d978379
Merge branch 'master' into dev
oliver-joos Sep 24, 2021
4e16027
Merge branch 'master' into dev
oliver-joos Oct 28, 2021
f64b34f
Merge branch 'master' into dev
oliver-joos Nov 15, 2021
bab170b
Merge branch 'master' into dev
oliver-joos Nov 28, 2021
2aaacbd
Revert "Merge remote-tracking branch 'zsquareplusc/fix-override-relat…
Dec 29, 2020
5143d37
Merge branch 'master' into dev
oliver-joos Mar 12, 2022
3a46dc0
py/builtinimport: Support relative import in custom __import__ callback.
oliver-joos Mar 12, 2022
9a08ff6
extmod/uasyncio: Support for overriden __import__.
zsquareplusc Dec 1, 2020
cc31c03
Merge pull request #1 from oliver-joos/fix-override-relative-import
zsquareplusc Mar 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion extmod/uasyncio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ def __getattr__(attr):
mod = _attrs.get(attr, None)
if mod is None:
raise AttributeError(attr)
value = getattr(__import__(mod, None, None, True, 1), attr)
value = getattr(__import__(mod, globals(), None, True, 1), attr)
globals()[attr] = value
return value
21 changes: 16 additions & 5 deletions py/builtinimport.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ STATIC void do_load(mp_module_context_t *module_obj, vstr_t *file) {

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

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__));
mp_obj_t current_module_name_obj = mp_obj_dict_get(globals, MP_OBJ_NEW_QSTR(MP_QSTR___name__));
assert(current_module_name_obj != MP_OBJ_NULL);

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

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

#if DEBUG_PRINT
DEBUG_printf("Current module/package: ");
Expand Down Expand Up @@ -480,6 +480,17 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
// "from ...foo.bar import baz" --> module_name="foo.bar"
mp_obj_t module_name_obj = args[0];

// This is the dict with all global symbols.
mp_obj_t globals = mp_const_none;
if (n_args >= 2) {
globals = args[1];
}
if (globals == mp_const_none) {
globals = MP_OBJ_FROM_PTR(mp_globals_get());
} else if (!mp_obj_is_type(globals, &mp_type_dict)) {
mp_raise_TypeError(MP_ERROR_TEXT("globals must be dict"));
}

// These are the imported names.
// i.e. "from foo.bar import baz, zap" --> fromtuple=("baz", "zap",)
// Note: There's a special case on the Unix port, where this is set to mp_const_false which means that it's __main__.
Expand All @@ -505,7 +516,7 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {

if (level != 0) {
// Turn "foo.bar" into "<current module minus 3 components>.foo.bar".
evaluate_relative_import(level, &module_name, &module_name_len);
evaluate_relative_import(level, &module_name, &module_name_len, globals);
}

if (module_name_len == 0) {
Expand Down
2 changes: 1 addition & 1 deletion py/runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -1408,7 +1408,7 @@ mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) {
// build args array
mp_obj_t args[5];
args[0] = MP_OBJ_NEW_QSTR(name);
args[1] = mp_const_none; // TODO should be globals
args[1] = MP_OBJ_FROM_PTR(mp_globals_get()); // globals of the current context
args[2] = mp_const_none; // TODO should be locals
args[3] = fromlist;
args[4] = level;
Expand Down
29 changes: 29 additions & 0 deletions tests/import/import_override2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# test overriding __import__ combined with importing from the filesystem


def custom_import(name, globals, locals, fromlist, level):
print("import", name, fromlist, level)
return orig_import(name, globals, locals, fromlist, level)


orig_import = __import__
try:
__import__("builtins").__import__ = custom_import
except AttributeError:
print("SKIP")
raise SystemExit

# import calls __import__ behind the scenes
import pkg7.subpkg1.subpkg2.mod3


try:
# globals must be a dict or None, not a string
orig_import("builtins", "globals", None, None, 0)
except TypeError:
print("TypeError")
try:
# ... same for relative imports (level > 0)
orig_import("builtins", "globals", None, None, 1)
except TypeError:
print("TypeError")
15 changes: 15 additions & 0 deletions tests/import/import_override2.py.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import pkg7.subpkg1.subpkg2.mod3 None 0
pkg __name__: pkg7
pkg __name__: pkg7.subpkg1
pkg __name__: pkg7.subpkg1.subpkg2
import ('mod1',) 3
import pkg7.mod1 True 0
mod1
import mod2 ('bar',) 3
mod2
mod1.foo
mod2.bar
import ('mod1',) 4
ImportError
TypeError
TypeError