fix(Code Node): Install python modules always in a user-writable folder (#6568)

* upgrade pyodide

* install pyodide modules to a custom user-writable path

* in `augmentObject` `newData` is never undefined
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2023-07-07 16:43:45 +02:00
committed by GitHub
parent 071e56f7fd
commit bf351243df
15 changed files with 98 additions and 77 deletions

View File

@@ -1,5 +1,5 @@
import type { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
import type { PyProxyDict } from 'pyodide';
import type { PyDict } from 'pyodide/ffi';
import { LoadPyodide } from './Pyodide';
import type { SandboxContext } from './Sandbox';
import { Sandbox } from './Sandbox';
@@ -18,7 +18,6 @@ export class PythonSandbox extends Sandbox {
constructor(
context: SandboxContext,
private pythonCode: string,
private moduleImports: string[],
itemIndex: number | undefined,
helpers: IExecuteFunctions['helpers'],
) {
@@ -51,47 +50,35 @@ export class PythonSandbox extends Sandbox {
}
private async runCodeInPython<T>() {
// Below workaround from here:
// https://github.com/pyodide/pyodide/discussions/3537#discussioncomment-4864345
const runCode = `
from _pyodide_core import jsproxy_typedict
from js import Object
jsproxy_typedict[0] = type(Object.new().as_object_map())
if printOverwrite:
print = printOverwrite
async def __main():
${this.pythonCode
.split('\n')
.map((line) => ' ' + line)
.join('\n')}
await __main()
`;
const pyodide = await LoadPyodide();
const moduleImportsFiltered = this.moduleImports.filter(
(importModule) => !['asyncio', 'pyodide', 'math'].includes(importModule),
);
if (moduleImportsFiltered.length) {
await pyodide.loadPackage('micropip');
const micropip = pyodide.pyimport('micropip');
await Promise.all(
moduleImportsFiltered.map((importModule) => micropip.install(importModule)),
);
}
const packageCacheDir = this.helpers.getStoragePath();
const pyodide = await LoadPyodide(packageCacheDir);
let executionResult;
try {
await pyodide.runPythonAsync('jsproxy_typedict[0] = type(Object.new().as_object_map())');
await pyodide.loadPackagesFromImports(this.pythonCode);
const dict = pyodide.globals.get('dict');
const globalsDict: PyProxyDict = dict();
const globalsDict: PyDict = dict();
for (const key of Object.keys(this.context)) {
if ((key === '_env' && envAccessBlocked) || key === '_node') continue;
const value = this.context[key];
globalsDict.set(key, value);
}
await pyodide.runPythonAsync(`
if 'printOverwrite' in globals():
print = printOverwrite
`);
const runCode = `
async def __main():
${this.pythonCode
.split('\n')
.map((line) => ' ' + line)
.join('\n')}
await __main()`;
executionResult = await pyodide.runPythonAsync(runCode, { globals: globalsDict });
globalsDict.destroy();
} catch (error) {