import { readFileSync, existsSync } from 'fs'; import { join } from 'path'; /** * Detect Python project type and configuration */ export function detectPython(projectPath) { const requirementsPath = join(projectPath, 'requirements.txt'); const pyprojectPath = join(projectPath, 'pyproject.toml'); const setupPath = join(projectPath, 'setup.py'); const hasRequirements = existsSync(requirementsPath); const hasPyproject = existsSync(pyprojectPath); const hasSetup = existsSync(setupPath); if (!hasRequirements && !hasPyproject && !hasSetup) { return null; } // Read requirements to detect project type let requirements = ''; if (hasRequirements) { requirements = readFileSync(requirementsPath, 'utf-8').toLowerCase(); } // Read pyproject.toml for additional info let pyproject = ''; if (hasPyproject) { pyproject = readFileSync(pyprojectPath, 'utf-8').toLowerCase(); } // Detect ML/PyTorch project const mlIndicators = [ 'torch', 'pytorch', 'tensorflow', 'keras', 'transformers', 'opencv', 'faster-whisper', 'whisper', 'scikit-learn', 'numpy' ]; const isML = mlIndicators.some(ind => requirements.includes(ind) || pyproject.includes(ind)); if (isML) { return { type: 'python-ml-pytorch', dockerizable: true, template: 'python/ml-pytorch', port: detectPythonPort(projectPath) || 8000, entryPoint: detectPythonEntryPoint(projectPath), buildCommand: null, description: 'Python ML/AI application', systemDeps: detectSystemDeps(requirements) }; } // Check for web frameworks const hasFlask = requirements.includes('flask') || pyproject.includes('flask'); const hasFastAPI = requirements.includes('fastapi') || pyproject.includes('fastapi'); const hasDjango = requirements.includes('django') || pyproject.includes('django'); let description = 'Python application'; let port = 8000; if (hasFlask) { description = 'Flask web application'; port = 5000; } else if (hasFastAPI) { description = 'FastAPI web application'; port = 8000; } else if (hasDjango) { description = 'Django web application'; port = 8000; } return { type: 'python-standard', dockerizable: true, template: 'python/standard', port: detectPythonPort(projectPath) || port, entryPoint: detectPythonEntryPoint(projectPath), buildCommand: null, description, framework: hasFlask ? 'flask' : hasFastAPI ? 'fastapi' : hasDjango ? 'django' : null }; } /** * Detect Python entry point */ function detectPythonEntryPoint(projectPath) { const commonEntries = [ 'main.py', 'app.py', 'server.py', 'run.py', 'src/main.py', 'src/app.py' ]; for (const entry of commonEntries) { if (existsSync(join(projectPath, entry))) { return entry; } } return 'main.py'; } /** * Detect port from Python files */ function detectPythonPort(projectPath) { const mainFiles = ['main.py', 'app.py', 'server.py', 'run.py']; for (const file of mainFiles) { const filePath = join(projectPath, file); if (existsSync(filePath)) { const content = readFileSync(filePath, 'utf-8'); // Look for port definitions const portMatch = content.match(/port[=\s:]+(\d{4})/i) || content.match(/PORT[=\s:]+(\d{4})/i); if (portMatch) { return parseInt(portMatch[1], 10); } } } return null; } /** * Detect system dependencies needed for ML projects */ function detectSystemDeps(requirements) { const deps = []; if (requirements.includes('opencv') || requirements.includes('cv2')) { deps.push('libgl1-mesa-glx', 'libglib2.0-0'); } if (requirements.includes('whisper') || requirements.includes('faster-whisper')) { deps.push('ffmpeg'); } if (requirements.includes('soundfile') || requirements.includes('librosa')) { deps.push('libsndfile1'); } if (requirements.includes('pillow') || requirements.includes('pil')) { deps.push('libjpeg-dev', 'zlib1g-dev'); } return deps; } /** * Get additional info about Python project */ export function getPythonInfo(projectPath) { const requirementsPath = join(projectPath, 'requirements.txt'); const pyprojectPath = join(projectPath, 'pyproject.toml'); const info = { hasRequirements: existsSync(requirementsPath), hasPyproject: existsSync(pyprojectPath), hasSetupPy: existsSync(join(projectPath, 'setup.py')), hasVenv: existsSync(join(projectPath, 'venv')) || existsSync(join(projectPath, '.venv')), dependencies: [] }; if (info.hasRequirements) { const content = readFileSync(requirementsPath, 'utf-8'); info.dependencies = content .split('\n') .filter(line => line.trim() && !line.startsWith('#')) .map(line => line.split('==')[0].split('>=')[0].split('<=')[0].trim()); } return info; }