@zgxsuerwtmrhjzt/n8n-nodes-python-raw
Version:
Run Python scripts in n8n with raw output (exitCode, stdout, stderr) - Fork of naskio/n8n-nodes-python
217 lines (211 loc) • 7.56 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PythonFunction = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const child_process_1 = require("child_process");
const fs = require("fs");
const tempy = require("tempy");
class PythonFunction {
constructor() {
this.description = {
displayName: 'Python Function (Raw)',
name: 'pythonFunctionRaw',
icon: 'fa:code',
group: ['transform'],
version: 1,
description: 'Run custom Python script once and return raw output (exitCode, stdout, stderr)',
defaults: {
name: 'PythonFunctionRaw',
color: '#4B8BBE',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'pythonEnvVars',
required: false,
},
],
properties: [
{
displayName: 'Python Code',
name: 'functionCode',
typeOptions: {
alwaysOpenEditWindow: true,
rows: 15,
},
type: 'string',
default: `# This script runs once and receives input data as 'input_items' variable
# Available variables:
# - input_items: list of all input data
# - env_vars: dict of environment variables
import json
import sys
# Your Python code here
print("Input items count:", len(input_items))
for i, item in enumerate(input_items):
print(f"Item {i}: {item}")
# Example: process data and print results
result = {"processed_count": len(input_items), "status": "success"}
print(json.dumps(result))
# Exit with success
sys.exit(0)
`,
description: 'Pure Python script that will be executed once. Input data available as input_items variable.',
noDataExpression: true,
},
{
displayName: 'Python Executable',
name: 'pythonPath',
type: 'string',
default: 'python3',
description: 'Path to Python executable (python3, python, or full path)',
},
],
};
}
async execute() {
var _a;
let items = this.getInputData();
items = JSON.parse(JSON.stringify(items));
const functionCode = this.getNodeParameter('functionCode', 0);
const pythonPath = this.getNodeParameter('pythonPath', 0);
let pythonEnvVars = {};
try {
pythonEnvVars = parseEnvFile(String(((_a = (await this.getCredentials('pythonEnvVars'))) === null || _a === void 0 ? void 0 : _a.envFileContent) || ''));
}
catch (_) {
}
let scriptPath = '';
try {
scriptPath = await getTemporaryScriptPath(functionCode, unwrapJsonField(items), pythonEnvVars);
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Could not generate temporary script file: ${error.message}`);
}
try {
const execResults = await execPythonSpawn(scriptPath, pythonPath, this.sendMessageToUI);
const { error: returnedError, exitCode, stdout, stderr, } = execResults;
const resultItem = {
json: {
exitCode: exitCode,
stdout: stdout,
stderr: stderr,
success: exitCode === 0,
error: returnedError ? returnedError.message : null,
inputItemsCount: items.length,
executedAt: new Date().toISOString(),
}
};
if (returnedError !== undefined && !this.continueOnFail()) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Python script failed with exit code ${exitCode}: ${returnedError.message}`);
}
return this.prepareOutputData([resultItem]);
}
catch (error) {
if (this.continueOnFail()) {
const errorItem = {
json: {
exitCode: -1,
stdout: '',
stderr: error.message || String(error),
success: false,
error: error.message || String(error),
inputItemsCount: items.length,
executedAt: new Date().toISOString(),
}
};
return this.prepareOutputData([errorItem]);
}
else {
throw error;
}
}
}
}
exports.PythonFunction = PythonFunction;
function execPythonSpawn(scriptPath, pythonPath, stdoutListener) {
const returnData = {
error: undefined,
exitCode: 0,
stderr: '',
stdout: '',
};
return new Promise((resolve, reject) => {
const child = child_process_1.spawn(pythonPath, [scriptPath], {
cwd: process.cwd(),
});
child.stdout.on('data', data => {
returnData.stdout += data.toString();
if (stdoutListener) {
stdoutListener(data.toString());
}
});
child.stderr.on('data', data => {
returnData.stderr += data.toString();
});
child.on('error', (error) => {
returnData.error = error;
resolve(returnData);
});
child.on('close', code => {
returnData.exitCode = code || 0;
if (code !== 0) {
returnData.error = new Error(`Process exited with code ${code}`);
}
resolve(returnData);
});
});
}
function parseEnvFile(envFileContent) {
if (!envFileContent || envFileContent === '') {
return {};
}
const envLines = envFileContent.split('\n');
const envVars = {};
for (const line of envLines) {
const parts = line.split('=');
if (parts.length === 2) {
envVars[parts[0]] = parts[1];
}
}
return envVars;
}
function formatCodeSnippet(code) {
return code
.replace(/\n/g, '\n\t')
.replace(/\r/g, '\n\t')
.replace(/\r\n\t/g, '\n\t')
.replace(/\r\n/g, '\n\t');
}
function getScriptCode(codeSnippet, data, envVars) {
const script = `#!/usr/bin/env python3
# Auto-generated script for n8n Python Function (Raw)
import json
import sys
# Input data and environment variables
input_items = ${JSON.stringify(data)}
env_vars = ${JSON.stringify(envVars)}
# User code starts here
${codeSnippet}
`;
return script;
}
async function getTemporaryScriptPath(codeSnippet, data, envVars) {
const tmpPath = tempy.file({ extension: 'py' });
const codeStr = getScriptCode(codeSnippet, data, envVars);
fs.writeFileSync(tmpPath, codeStr);
return tmpPath;
}
function unwrapJsonField(list = []) {
return list.reduce((acc, item) => {
if ('json' in item) {
acc.push(item.json);
}
else {
acc.push(item);
}
return acc;
}, []);
}
//# sourceMappingURL=PythonFunction.node.js.map