@badc0d3/piece-python-runner
Version:
Run Python code with imports in Activepieces
210 lines (209 loc) • 9.88 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.runPythonCode = void 0;
const pieces_framework_1 = require("@activepieces/pieces-framework");
const child_process_1 = require("child_process");
const fs_1 = require("fs");
const path_1 = require("path");
const crypto_1 = require("crypto");
const util_1 = require("util");
const execAsync = (0, util_1.promisify)(child_process_1.exec);
exports.runPythonCode = (0, pieces_framework_1.createAction)({
name: 'run-python-code',
displayName: 'Run Python Code',
description: 'Execute Python code with optional package installation',
props: {
code: pieces_framework_1.Property.LongText({
displayName: 'Python Code',
description: 'The Python code to execute',
required: true,
defaultValue: `# Example: Get external IP address
import urllib.request
import json
response = urllib.request.urlopen('https://api.ipify.org?format=json')
data = json.loads(response.read())
print(f"Your IP: {data['ip']}")`,
}),
requirements: pieces_framework_1.Property.LongText({
displayName: 'Requirements',
description: 'Python packages to install (one per line, like requirements.txt)',
required: false,
defaultValue: 'requests\nnumpy\npandas',
}),
timeout: pieces_framework_1.Property.Number({
displayName: 'Timeout (seconds)',
description: 'Maximum execution time in seconds',
required: false,
defaultValue: 30,
}),
pythonVersion: pieces_framework_1.Property.StaticDropdown({
displayName: 'Python Version',
description: 'Python version to use',
required: false,
defaultValue: 'python3',
options: {
options: [
{ label: 'Python 3 (Default)', value: 'python3' },
{ label: 'Python 3.9', value: 'python3.9' },
{ label: 'Python 3.10', value: 'python3.10' },
{ label: 'Python 3.11', value: 'python3.11' },
],
},
}),
captureOutput: pieces_framework_1.Property.Checkbox({
displayName: 'Capture Output',
description: 'Capture stdout and stderr separately',
required: false,
defaultValue: true,
}),
},
async run(context) {
const { code, requirements, timeout, pythonVersion, captureOutput } = context.propsValue;
// Create a temporary directory for this execution
const tempDir = (0, path_1.join)('/tmp', `python-exec-${(0, crypto_1.randomBytes)(8).toString('hex')}`);
const scriptPath = (0, path_1.join)(tempDir, 'script.py');
const venvPath = (0, path_1.join)(tempDir, 'venv');
const requirementsPath = (0, path_1.join)(tempDir, 'requirements.txt');
let useVenv = true;
let pipCommand = '';
let pythonCommand = '';
// Check if running in Docker
const isDocker = process.env.DOCKER_CONTAINER === 'true' ||
(0, fs_1.existsSync)('/.dockerenv') ||
((0, fs_1.existsSync)('/proc/1/cgroup') && (0, fs_1.readFileSync)('/proc/1/cgroup', 'utf8').includes('docker'));
if (isDocker) {
console.log('Detected Docker environment - skipping virtual environment creation');
useVenv = false;
}
try {
// Create temporary directory
(0, fs_1.mkdirSync)(tempDir, { recursive: true });
// Write the Python script
(0, fs_1.writeFileSync)(scriptPath, code);
// Create virtual environment only if not in Docker and useVenv is true
if (useVenv) {
try {
// First check if Python is available and get version info
const pythonCheck = await execAsync(`${pythonVersion} --version`, {
timeout: 5000,
});
console.log(`Python version check: ${pythonCheck.stdout}`);
// Check if venv module is available
const venvCheck = await execAsync(`${pythonVersion} -m venv --help`, {
timeout: 5000,
}).catch(err => {
throw new Error(`Python venv module not available. Please ensure python3-venv is installed. Error: ${err.message}`);
});
// Try creating venv with symlinks first
await execAsync(`${pythonVersion} -m venv ${venvPath}`, {
timeout: 10000,
});
}
catch (error) {
console.log('Primary venv creation failed:', error.message);
// If symlink fails, try without symlinks
try {
console.log('Trying venv creation with --copies flag...');
await execAsync(`${pythonVersion} -m venv --copies ${venvPath}`, {
timeout: 10000,
});
}
catch (fallbackError) {
// If both methods fail, try using the system Python directly
console.log('Venv creation failed. Error details:', fallbackError.message);
console.log('Falling back to system Python without virtual environment...');
useVenv = false;
}
}
}
if (useVenv) {
// Determine pip path based on OS
const pipPath = process.platform === 'win32'
? (0, path_1.join)(venvPath, 'Scripts', 'pip')
: (0, path_1.join)(venvPath, 'bin', 'pip');
const pythonPath = process.platform === 'win32'
? (0, path_1.join)(venvPath, 'Scripts', 'python')
: (0, path_1.join)(venvPath, 'bin', 'python');
pipCommand = pipPath;
pythonCommand = pythonPath;
}
else {
// Use system Python and pip
pipCommand = `${pythonVersion} -m pip`;
pythonCommand = pythonVersion;
}
// Install requirements if provided
if (requirements && requirements.trim()) {
(0, fs_1.writeFileSync)(requirementsPath, requirements);
try {
// First check if pip is available
const pipCheckCmd = useVenv ? `${pipCommand} --version` : `${pythonVersion} -m pip --version`;
await execAsync(pipCheckCmd, { timeout: 5000 }).catch(err => {
throw new Error(`pip is not installed. Please ensure pip is available in your Python environment. ` +
`For Debian/Ubuntu: apt-get install python3-pip. ` +
`For other systems, please install pip for ${pythonVersion}.`);
});
const pipInstallCmd = useVenv
? `${pipCommand} install -r ${requirementsPath}`
: `${pipCommand} install --user -r ${requirementsPath}`;
const { stdout: pipStdout, stderr: pipStderr } = await execAsync(pipInstallCmd, {
timeout: timeout * 1000,
});
console.log('Pip install output:', pipStdout);
if (pipStderr)
console.error('Pip install errors:', pipStderr);
}
catch (pipError) {
// If pip is not available, provide a helpful error message
if (pipError.message.includes('No module named pip') || pipError.message.includes('pip is not installed')) {
throw new Error(`Cannot install Python packages: pip is not available. ` +
`To use packages, please ensure pip is installed in your environment. ` +
`For the sandboxed version, use the "Run Python Code (Sandboxed)" action instead.`);
}
throw pipError;
}
}
// Execute the Python script
const execOptions = {
timeout: (timeout || 30) * 1000,
maxBuffer: 10 * 1024 * 1024, // 10MB buffer for output
};
const { stdout, stderr } = await execAsync(`${pythonCommand} ${scriptPath}`, execOptions);
// Parse output if it's JSON
let parsedOutput;
try {
parsedOutput = JSON.parse(stdout.trim());
}
catch {
parsedOutput = stdout.trim();
}
return {
success: true,
output: parsedOutput,
stdout: captureOutput ? stdout : undefined,
stderr: captureOutput ? stderr : undefined,
executionTime: new Date().toISOString(),
};
}
catch (error) {
return {
success: false,
error: error.message,
stdout: error.stdout || '',
stderr: error.stderr || '',
code: error.code,
executionTime: new Date().toISOString(),
};
}
finally {
// Cleanup temporary files
try {
(0, fs_1.rmdirSync)(tempDir, { recursive: true });
}
catch (cleanupError) {
console.error('Failed to cleanup temp directory:', cleanupError);
}
}
},
});
//# sourceMappingURL=run-python-code.js.map