UNPKG

@badc0d3/piece-python-runner

Version:

Run Python code with imports in Activepieces

210 lines (209 loc) 9.88 kB
"use strict"; 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