Coverage for formkit_ninja / parser / formatter.py: 17.65%

30 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-02-20 04:40 +0000

1""" 

2Code formatter using ruff. 

3 

4This module provides a CodeFormatter class that uses ruff format 

5to format Python code strings. 

6""" 

7 

8import subprocess 

9from typing import Optional 

10 

11 

12class FormattingError(Exception): 

13 """Exception raised when code formatting fails.""" 

14 

15 def __init__(self, message: str, original_error: Optional[Exception] = None) -> None: 

16 """Initialize FormattingError with a message and optional original error. 

17 

18 Args: 

19 message: Error message describing what went wrong 

20 original_error: The original exception that caused the formatting error 

21 """ 

22 super().__init__(message) 

23 self.message = message 

24 self.original_error = original_error 

25 

26 

27class CodeFormatter: 

28 """Formatter for Python code using ruff format.""" 

29 

30 def format(self, code: str) -> str: 

31 """Format Python code using ruff format. 

32 

33 Args: 

34 code: The Python code string to format 

35 

36 Returns: 

37 The formatted Python code string 

38 

39 Raises: 

40 FormattingError: If formatting fails (invalid syntax, ruff not found, etc.) 

41 """ 

42 if not code.strip(): 

43 # Handle empty or whitespace-only strings 

44 return code 

45 

46 try: 

47 # Use ruff format via subprocess 

48 # --stdin-filename ensures ruff treats input as Python code 

49 result = subprocess.run( 

50 ["ruff", "format", "--stdin-filename", "code.py"], 

51 input=code.encode("utf-8"), 

52 capture_output=True, 

53 check=True, 

54 text=False, # We handle encoding manually 

55 ) 

56 

57 # Decode the formatted output 

58 formatted = result.stdout.decode("utf-8") 

59 

60 return formatted 

61 

62 except FileNotFoundError as e: 

63 raise FormattingError( 

64 "ruff command not found. Please ensure ruff is installed and in your PATH.", 

65 original_error=e, 

66 ) from e 

67 

68 except subprocess.CalledProcessError as e: 

69 # Ruff returned non-zero exit code (usually syntax errors) 

70 error_message = "Code formatting failed" 

71 if e.stderr: 

72 try: 

73 stderr_text = e.stderr.decode("utf-8") 

74 error_message = f"Code formatting failed: {stderr_text}" 

75 except (UnicodeDecodeError, AttributeError): 

76 error_message = f"Code formatting failed (exit code {e.returncode})" 

77 

78 raise FormattingError(error_message, original_error=e) from e 

79 

80 except subprocess.SubprocessError as e: 

81 raise FormattingError( 

82 f"Subprocess error during formatting: {str(e)}", 

83 original_error=e, 

84 ) from e 

85 

86 except Exception as e: 

87 raise FormattingError( 

88 f"Unexpected error during formatting: {str(e)}", 

89 original_error=e, 

90 ) from e