All files / lib/detectors python-detector.js

81.81% Statements 18/22
76.66% Branches 23/30
100% Functions 1/1
88.23% Lines 15/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49          4x 4x   4x 1x     3x     3x 3x 1x     3x         3x 3x 2x     3x 3x   3x                              
import fs from 'fs-extra';
import path from 'path';
import { parseRequirements } from '../parsers/requirements-parser.js';
 
export async function detectPythonStack(cwd) {
  const requirementsPath = path.join(cwd, 'requirements.txt');
  const pyprojectPath = path.join(cwd, 'pyproject.toml');
 
  if (!(await fs.pathExists(requirementsPath)) && !(await fs.pathExists(pyprojectPath))) {
    return { detected: false };
  }
 
  const deps = await parseRequirements(cwd);
 
  // Detect framework
  let framework = null;
  if (deps.fastapi || deps['starlette']) framework = 'fastapi';
  else if (deps.django || deps['Django']) framework = 'django';
  else Eif (deps.flask) framework = 'flask';
 
  Iif (!framework) {
    return { detected: false };
  }
 
  // Detect test runner
  let testRunner = null;
  if (deps.pytest) testRunner = 'pytest';
  else Iif (deps.unittest) testRunner = 'unittest';
 
  // Detect linter/formatter
  const linter = deps.ruff || deps['ruff'] ? 'ruff' : deps.pylint ? 'pylint' : null;
  const formatter = deps.black || deps['black'] ? 'black' : null;
 
  return {
    detected: true,
    stack: 'python-fastapi',
    confidence: 'high',
    language: 'python',
    framework,
    testRunner,
    linter,
    formatter,
    buildTool: 'pip',
    evidence: {
      files: [requirementsPath],
      dependencies: Object.keys(deps).slice(0, 5),
    },
  };
}