Skip to content

Commit 40835c8

Browse files
committed
Implements Python error hint when importing from src.
1 parent 48990e7 commit 40835c8

File tree

1 file changed

+39
-8
lines changed

1 file changed

+39
-8
lines changed

src/pyodide/python-entrypoint-helper.ts

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,27 @@ export function setDoAnImport(
9595
};
9696
}
9797

98+
function handleSrcImport(pyodide: Pyodide, e: any): never {
99+
// Users may be expecting to import local modules via the `src` directory, which for a default
100+
// project structure will fail. This code will add some extra info to the error message to help
101+
// them fix it.
102+
if (e.name === 'PythonError' && e.type === 'ModuleNotFoundError') {
103+
pyodide.runPython(`
104+
try:
105+
import sys
106+
exc = sys.last_value
107+
if exc.name == "src":
108+
exc.add_note(
109+
"If your main module is inside the 'src' directory then your import " +
110+
"statement shouldn't include a 'src.' prefix")
111+
raise exc
112+
finally:
113+
del exc
114+
`);
115+
}
116+
throw e;
117+
}
118+
98119
async function pyimportMainModule(pyodide: Pyodide): Promise<PyModule> {
99120
if (!MAIN_MODULE_NAME.endsWith('.py')) {
100121
throw new PythonUserError(
@@ -243,6 +264,8 @@ function getMainModule(): Promise<PyModule> {
243264
return await enterJaegerSpan('pyimport_main_module', () =>
244265
pyimportMainModule(pyodide)
245266
);
267+
} catch (e: any) {
268+
handleSrcImport(pyodide, e);
246269
} finally {
247270
Limiter.finishStartup(LOADED_SNAPSHOT_TYPE);
248271
}
@@ -264,18 +287,26 @@ async function preparePython(): Promise<PyModule> {
264287
}
265288
}
266289

267-
function doPyCallHelper(
290+
async function doPyCallHelper(
268291
relaxed: boolean,
269292
pyfunc: PyCallable,
270293
args: any[]
271-
): any {
272-
if (pyfunc.callWithOptions) {
273-
return pyfunc.callWithOptions({ relaxed, promising: true }, ...args);
274-
}
275-
if (relaxed) {
276-
return pyfunc.callRelaxed(...args);
294+
): Promise<any> {
295+
try {
296+
if (pyfunc.callWithOptions) {
297+
return await pyfunc.callWithOptions(
298+
{ relaxed, promising: true },
299+
...args
300+
);
301+
}
302+
if (relaxed) {
303+
return await pyfunc.callRelaxed(...args);
304+
}
305+
return await pyfunc(...args);
306+
} catch (e: any) {
307+
const pyodide = await getPyodide();
308+
handleSrcImport(pyodide, e);
277309
}
278-
return pyfunc(...args);
279310
}
280311

281312
function doPyCall(pyfunc: PyCallable, args: any[]): any {

0 commit comments

Comments
 (0)