Skip to main content
Component: CodeFormattingMixin Module: gaia.agents.code.tools.code_formatting Import: from gaia.agents.code.tools.code_formatting import CodeFormattingMixin

Overview

CodeFormattingMixin provides Python code formatting and combined linting tools for the Code Agent, integrating Black formatter with pylint analysis for comprehensive code quality checks. Key Features:
  • Black formatting with configurable line length
  • Check-only mode for validation without modification
  • Combined lint + format analysis
  • Auto-fix capability with re-validation
  • Unified diff generation for changes

API Specification

class CodeFormattingMixin:
    """
    Mixin providing code formatting and linting tools.

    Tools provided:
    - format_with_black: Format code using Black
    - lint_and_format: Combined pylint + Black analysis
    """

    @tool
    def format_with_black(
        file_path: str = None,
        code: str = None,
        line_length: int = 88,
        check_only: bool = False
    ) -> Dict[str, Any]:
        """
        Format Python code with Black.

        Args:
            file_path: Path to Python file (optional)
            code: Python code string (optional)
            line_length: Max line length (default: 88)
            check_only: Only check, don't modify (default: False)

        Returns:
            {
                "status": "success" | "error",
                "formatted": bool,  # Whether formatting was applied
                "needs_formatting": bool,
                "formatted_code": str | None,  # If formatted
                "diff": str | None,  # Unified diff
                "command": str,
                "stdout": str,
                "stderr": str,
                "return_code": int,
                "message": str
            }
        """
        pass

    @tool
    def lint_and_format(
        file_path: str = None,
        code: str = None,
        fix: bool = False,
        line_length: int = 88
    ) -> Dict[str, Any]:
        """
        Combined linting and formatting analysis.

        Workflow:
        1. Validate syntax
        2. Run pylint analysis
        3. Check/apply Black formatting
        4. If fix=True, apply lint fixes and re-validate

        Args:
            file_path: Path to Python file (optional)
            code: Python code string (optional)
            fix: Apply fixes (default: False)
            line_length: Max line length (default: 88)

        Returns:
            {
                "status": "success" | "error",
                "file_path": str,
                "syntax_valid": bool,
                "syntax_errors": List[str],
                "pylint": {
                    "total_issues": int,
                    "errors": int,
                    "warnings": int,
                    "conventions": int,
                    "issues": List[dict]  # First 10
                },
                "formatting": {
                    "needs_formatting": bool,
                    "formatted": bool
                },
                "formatted_code": str,  # If fix=True
                "file_updated": bool,
                "lint_fixes_applied": List[str],
                "pylint_after_fixes": {...},  # If fix=True
                "formatting_diff": str,
                "clean": bool,
                "message": str
            }
        """
        pass

    def _generate_unified_diff(
        self,
        original: str,
        modified: str,
        original_name: str = "original",
        modified_name: str = "modified",
        context_lines: int = 3
    ) -> str:
        """Generate unified diff between two strings."""
        pass

Implementation Details

Black Integration

def format_with_black(file_path=None, code=None, line_length=88, check_only=False):
    # Determine source (file or code string)
    if file_path:
        target_file = str(Path(file_path))
        original_content = Path(file_path).read_text()
    elif code:
        with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
            f.write(code)
            target_file = f.name
        original_content = code

    # Run black
    cmd = ["python", "-m", "black", f"--line-length={line_length}", "--quiet"]
    if check_only:
        cmd.extend(["--check", "--diff"])
    cmd.append(target_file)

    result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)

    # Read formatted content
    formatted_content = original_content
    if not check_only and result.returncode == 0:
        formatted_content = Path(target_file).read_text()

    # Generate diff
    needs_formatting = (result.returncode != 0 if check_only
                       else original_content != formatted_content)
    diff = _generate_unified_diff(original_content, formatted_content) if needs_formatting else None

    return {
        "formatted": not check_only and needs_formatting,
        "needs_formatting": needs_formatting,
        "formatted_code": formatted_content if not check_only else None,
        "diff": diff
    }

Combined Analysis

def lint_and_format(file_path=None, code=None, fix=False, line_length=88):
    # 1. Syntax check
    syntax_check = self._validate_python_syntax(code or Path(file_path).read_text())
    if not syntax_check["is_valid"]:
        return {"syntax_valid": False, "syntax_errors": syntax_check["errors"]}

    # 2. Pylint analysis
    pylint_result = self._execute_tool("analyze_with_pylint", {...})
    results["pylint"] = {
        "total_issues": pylint_result["total_issues"],
        "errors": pylint_result["errors"],
        "issues": pylint_result["issues"][:10]
    }

    # 3. Black formatting
    black_result = format_with_black(file_path, code, line_length, check_only=not fix)
    results["formatting"] = {
        "needs_formatting": black_result["needs_formatting"],
        "formatted": black_result["formatted"]
    }

    # 4. Apply fixes if requested
    if fix and black_result["formatted_code"]:
        Path(file_path).write_text(black_result["formatted_code"])
        results["file_updated"] = True

        # Fix linting errors
        if pylint_result["total_issues"] > 0:
            fix_lint_result = self._execute_tool("fix_linting_errors", {...})
            results["lint_fixes_applied"] = fix_lint_result["fixes_applied"]

    results["clean"] = results["pylint"]["total_issues"] == 0 and not results["formatting"]["needs_formatting"]
    return results

Testing Requirements

File: tests/agents/code/test_code_formatting_mixin.py
def test_format_with_black_formats_code():
    """Test Black formatting is applied."""
    code = "x=1;y=2"  # Poorly formatted
    result = agent.format_with_black(code=code)
    assert result["formatted"] is True
    assert result["formatted_code"] == "x = 1\ny = 2\n"

def test_format_with_black_check_only():
    """Test check-only mode doesn't modify."""
    code = "x=1"
    result = agent.format_with_black(code=code, check_only=True)
    assert result["needs_formatting"] is True
    assert result["formatted_code"] is None

def test_lint_and_format_combined():
    """Test combined linting and formatting."""
    code = "import os\nx=1"  # Unused import + bad formatting
    result = agent.lint_and_format(code=code)
    assert result["syntax_valid"] is True
    assert result["pylint"]["total_issues"] > 0
    assert result["formatting"]["needs_formatting"] is True

def test_lint_and_format_with_fix(tmp_path):
    """Test auto-fix capability."""
    file = tmp_path / "test.py"
    file.write_text("x=1")
    result = agent.lint_and_format(file_path=str(file), fix=True)
    assert result["file_updated"] is True
    assert result["formatting"]["formatted"] is True

Usage Examples

# Format code
result = agent.format_with_black(file_path="src/main.py")
if result["formatted"]:
    print("Code formatted successfully")
    print(f"Diff:\n{result['diff']}")

# Check formatting without modifying
result = agent.format_with_black(code=code_string, check_only=True)
if result["needs_formatting"]:
    print("Code needs formatting")

# Combined analysis
result = agent.lint_and_format(file_path="src/main.py", fix=True)
print(f"Clean: {result['clean']}")
print(f"Pylint issues: {result['pylint']['total_issues']}")
print(f"Formatted: {result['formatting']['formatted']}")

Acceptance Criteria

  • Black formatting integration
  • Check-only mode
  • Combined lint + format analysis
  • Auto-fix with re-validation
  • Unified diff generation
  • Temp file handling for code strings
  • Line length configuration
  • Error handling for missing Black

CodeFormattingMixin Technical Specification