Skip to content

Commit 9c421eb

Browse files
committed
Add workaround for import endless loop in python loader.
1 parent ad7ed1b commit 9c421eb

File tree

3 files changed

+72
-34
lines changed

3 files changed

+72
-34
lines changed

source/loaders/py_loader/source/py_loader_impl.c

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,6 +1810,15 @@ void py_loader_impl_module_destroy(loader_impl_py_handle_module module)
18101810
PyObject_DelItem(system_modules, module->name);
18111811
}
18121812

1813+
// TODO: Sometimes this fails, seems that sys.modules does not contain the item.
1814+
// Probably is because of the new import system which is using importlib, but
1815+
// it does not seem something problematic although it will be interesting
1816+
// to check it out so we are sure there's no leaked memory
1817+
if (PyErr_Occurred() != NULL)
1818+
{
1819+
PyErr_Clear();
1820+
}
1821+
18131822
Py_DECREF(module->name);
18141823
module->name = NULL;
18151824
}
@@ -1835,7 +1844,7 @@ void py_loader_impl_handle_destroy(loader_impl_py_handle py_handle)
18351844
free(py_handle);
18361845
}
18371846

1838-
int py_loader_impl_load_from_file_module(loader_impl_py py_impl, loader_impl_py_handle_module module, const loader_naming_path path)
1847+
int py_loader_impl_load_from_file_path(loader_impl_py py_impl, loader_impl_py_handle_module module, const loader_naming_path path)
18391848
{
18401849
loader_naming_name name;
18411850
size_t size = loader_path_get_fullname(path, name);
@@ -1852,14 +1861,22 @@ int py_loader_impl_load_from_file_module(loader_impl_py py_impl, loader_impl_py_
18521861
goto error_name_create;
18531862
}
18541863

1855-
PyObject *args_tuple = PyTuple_New(1);
1864+
PyObject *py_path = PyUnicode_DecodeFSDefault(path);
1865+
1866+
if (py_path == NULL)
1867+
{
1868+
goto error_path_create;
1869+
}
1870+
1871+
PyObject *args_tuple = PyTuple_New(2);
18561872

18571873
if (args_tuple == NULL)
18581874
{
18591875
goto error_tuple_create;
18601876
}
18611877

18621878
PyTuple_SetItem(args_tuple, 0, module->name);
1879+
PyTuple_SetItem(args_tuple, 1, py_path);
18631880

18641881
module->instance = PyObject_Call(py_impl->import_function, args_tuple, NULL);
18651882

@@ -1875,53 +1892,56 @@ int py_loader_impl_load_from_file_module(loader_impl_py py_impl, loader_impl_py_
18751892
}
18761893

18771894
Py_DECREF(args_tuple);
1895+
Py_DECREF(py_path);
18781896

18791897
return 0;
18801898

18811899
error_module_instance:
18821900
Py_DECREF(args_tuple);
18831901
error_tuple_create:
1902+
Py_DECREF(py_path);
1903+
error_path_create:
18841904
Py_XDECREF(module->name);
18851905
module->name = NULL;
18861906
error_name_create:
18871907
return 1;
18881908
}
18891909

1890-
int py_loader_impl_load_from_file_path(loader_impl_py py_impl, loader_impl_py_handle_module module, const loader_naming_path path)
1910+
int py_loader_impl_load_from_module(loader_impl_py_handle_module module, const loader_naming_path path)
18911911
{
1892-
loader_naming_name name;
1893-
size_t size = loader_path_get_fullname(path, name);
1912+
size_t length = strlen(path);
18941913

1895-
if (size <= 1)
1914+
if (length == 0)
18961915
{
1897-
goto error_name_create;
1916+
// TODO: Handle exception
1917+
return 1;
18981918
}
18991919

1900-
module->name = PyUnicode_DecodeFSDefaultAndSize(name, (Py_ssize_t)size - 1);
1920+
module->name = PyUnicode_DecodeFSDefaultAndSize(path, (Py_ssize_t)length);
19011921

19021922
if (module->name == NULL)
19031923
{
1904-
goto error_name_create;
1924+
// TODO: Handle exception
1925+
return 1;
19051926
}
19061927

1907-
PyObject *py_path = PyUnicode_DecodeFSDefault(path);
1928+
/* TODO: Trying to reimplement the PyImport_Import,
1929+
* check the TODO in monkey patch method in the port for more information
1930+
*/
1931+
/*
1932+
PyObject * globals = PyEval_GetGlobals();
19081933
1909-
if (py_path == NULL)
1934+
if (globals != NULL)
19101935
{
1911-
goto error_path_create;
1936+
Py_INCREF(globals);
19121937
}
19131938
1914-
PyObject *args_tuple = PyTuple_New(2);
1915-
1916-
if (args_tuple == NULL)
1917-
{
1918-
goto error_tuple_create;
1919-
}
1939+
module->instance = PyImport_ImportModuleLevelObject(module->name, globals, NULL, NULL, 0);
19201940
1921-
PyTuple_SetItem(args_tuple, 0, module->name);
1922-
PyTuple_SetItem(args_tuple, 1, py_path);
1941+
Py_XDECREF(globals);
1942+
*/
19231943

1924-
module->instance = PyObject_Call(py_impl->import_function, args_tuple, NULL);
1944+
module->instance = PyImport_Import(module->name);
19251945

19261946
if (!(module->instance != NULL && PyModule_Check(module->instance)))
19271947
{
@@ -1931,22 +1951,13 @@ int py_loader_impl_load_from_file_path(loader_impl_py py_impl, loader_impl_py_ha
19311951
module->instance = NULL;
19321952
}
19331953

1934-
goto error_module_instance;
1954+
goto error_import;
19351955
}
19361956

1937-
Py_DECREF(args_tuple);
1938-
Py_DECREF(py_path);
1939-
19401957
return 0;
1941-
1942-
error_module_instance:
1943-
Py_DECREF(args_tuple);
1944-
error_tuple_create:
1945-
Py_DECREF(py_path);
1946-
error_path_create:
1947-
Py_XDECREF(module->name);
1958+
error_import:
1959+
Py_DECREF(module->name);
19481960
module->name = NULL;
1949-
error_name_create:
19501961
return 1;
19511962
}
19521963

@@ -1998,8 +2009,13 @@ loader_handle py_loader_impl_load_from_file(loader_impl impl, const loader_namin
19982009
for (iterator = 0; iterator < size; ++iterator)
19992010
{
20002011
/* Try to load the module as it is */
2001-
if (py_loader_impl_load_from_file_module(py_impl, &py_handle->modules[iterator], paths[iterator]) != 0)
2012+
if (py_loader_impl_load_from_module(&py_handle->modules[iterator], paths[iterator]) != 0)
20022013
{
2014+
if (PyErr_Occurred() != NULL)
2015+
{
2016+
PyErr_Clear();
2017+
}
2018+
20032019
/* Otherwise, we assume it is a path so we load from path */
20042020
if (loader_path_is_absolute(paths[iterator]) == 0)
20052021
{

source/ports/node_port/test/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@ describe('metacall', () => {
157157
assert.notStrictEqual(py_encode_basestring_ascii, undefined);
158158
assert.strictEqual(py_encode_basestring_ascii('asd'), '"asd"');
159159
});
160+
it('require (py submodule dependency)', () => {
161+
// Require the 'op' submodule from 'fn' Python package
162+
const { foldr, call } = require('fn.op');
163+
164+
assert.strictEqual(foldr(call, 10)([s => s * s, k => k + 10]), 400);
165+
});
160166
it('require (rb)', () => {
161167
const cache = require('./cache.rb');
162168
assert.notStrictEqual(cache, undefined);

source/ports/py_port/metacall/api.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import os
2121
import sys
2222
import json
23+
import inspect # TODO: Remove this, check the monkey patching
2324

2425
if sys.platform == 'win32':
2526
from metacall.module_win32 import metacall_module_load
@@ -168,6 +169,21 @@ def generate_module(handle_name, handle):
168169
# Generate the module from cached handle
169170
return generate_module(handle_name, handle)
170171

172+
# This breaks a recursion loop when trying to load py files
173+
# The problem is basically that PyImport_Import in the loader tries to
174+
# load as module first, so it jumps to this method again. Probably
175+
# we should find a more efficient solution, for example reimplementing
176+
# PyImport_Import (which I failed) or checking if the port is present
177+
# and the import is monkey patched, so in this case we should populate
178+
# the real python __import__ from the port into the loader, so we can
179+
# avoid jumping always to the patched import, this is a workaround
180+
# that must be removed because it is dirty and slow (TODO)
181+
if extension == 'py':
182+
current_frame = inspect.currentframe()
183+
call_frame = inspect.getouterframes(current_frame, 2)
184+
if call_frame[1][3] == 'metacall_load_from_file' or call_frame[1][3] == 'metacall_load_from_memory':
185+
return ImportError('MetaCall could not import:', name)
186+
171187
if metacall_load_from_file(extensions_to_tag[extension], [name]):
172188
handle = find_handle(name)
173189
if handle != None:

0 commit comments

Comments
 (0)