Coverage for lintro / tools / implementations / pytest / pytest_error_handler.py: 39%

23 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""Pytest error handling. 

2 

3This module contains the PytestErrorHandler class that handles various error 

4scenarios consistently and provides standardized error messages. 

5""" 

6 

7import subprocess # nosec B404 - used safely with shell disabled 

8from dataclasses import dataclass, field 

9 

10from loguru import logger 

11 

12from lintro.models.core.tool_result import ToolResult 

13 

14 

15@dataclass 

16class PytestErrorHandler: 

17 """Handles pytest error scenarios consistently. 

18 

19 This class encapsulates error handling logic for various pytest execution 

20 failures, providing standardized error messages and ToolResult objects. 

21 

22 Attributes: 

23 tool_name: Name of the tool (e.g., "pytest"). 

24 """ 

25 

26 tool_name: str = field(default="pytest") 

27 

28 def handle_timeout_error( 

29 self, 

30 timeout_val: int, 

31 cmd: list[str], 

32 initial_count: int = 0, 

33 ) -> ToolResult: 

34 """Handle timeout errors consistently. 

35 

36 Args: 

37 timeout_val: The timeout value that was exceeded. 

38 cmd: Command that timed out. 

39 initial_count: Number of issues discovered before timeout. 

40 

41 Returns: 

42 ToolResult: Standardized timeout error result. 

43 """ 

44 # Format the command for display 

45 cmd_str = " ".join(cmd[:4]) if len(cmd) >= 4 else " ".join(cmd) 

46 if len(cmd) > 4: 

47 cmd_str += f" ... ({len(cmd) - 4} more args)" 

48 

49 error_msg = ( 

50 f"❌ pytest execution timed out after {timeout_val}s\n\n" 

51 f"Command: {cmd_str}\n\n" 

52 "Possible causes:\n" 

53 " • Tests are taking too long to run\n" 

54 " • Some tests are hanging or blocked (e.g., waiting for I/O)\n" 

55 " • Test discovery is slow or stuck\n" 

56 " • Resource exhaustion (memory, file descriptors)\n\n" 

57 "Solutions:\n" 

58 " 1. Increase timeout: lintro test --tool-options timeout=600\n" 

59 " 2. Run fewer tests: lintro test tests/unit/ (vs full test suite)\n" 

60 " 3. Run in parallel: lintro test --tool-options workers=auto\n" 

61 " 4. Skip slow tests: lintro test -m 'not slow'\n" 

62 " 5. Debug directly: pytest -v --tb=short <test_file>\n" 

63 ) 

64 logger.error(error_msg) 

65 return ToolResult( 

66 name=self.tool_name, 

67 success=False, 

68 issues=[], 

69 output=error_msg, 

70 issues_count=max(initial_count, 1), # Count timeout as execution failure 

71 ) 

72 

73 def handle_execution_error( 

74 self, 

75 error: Exception, 

76 cmd: list[str], 

77 ) -> ToolResult: 

78 """Handle execution errors consistently. 

79 

80 Args: 

81 error: The exception that occurred. 

82 cmd: Command that failed. 

83 

84 Returns: 

85 ToolResult: Standardized error result. 

86 """ 

87 if isinstance(error, FileNotFoundError): 

88 error_msg = ( 

89 f"pytest executable not found: {error}\n\n" 

90 "Please ensure pytest is installed:\n" 

91 " - Install via pip: pip install pytest\n" 

92 " - Install via uv: uv add pytest\n" 

93 " - Or install as dev dependency: uv add --dev pytest\n\n" 

94 "After installation, verify pytest is available:\n" 

95 " pytest --version" 

96 ) 

97 elif isinstance(error, subprocess.CalledProcessError): 

98 error_msg = ( 

99 f"pytest execution failed with return code {error.returncode}\n\n" 

100 "Common causes:\n" 

101 " - Syntax errors in test files\n" 

102 " - Missing dependencies or imports\n" 

103 " - Configuration issues in pytest.ini or pyproject.toml\n" 

104 " - Permission errors accessing test files\n\n" 

105 "Try running pytest directly to see detailed error:\n" 

106 f" {' '.join(cmd[:3])} ..." 

107 ) 

108 else: 

109 # Generic error handling with helpful context 

110 error_type = type(error).__name__ 

111 error_msg = ( 

112 f"Unexpected error running pytest: {error_type}: {error}\n\n" 

113 "Please report this issue if it persists. " 

114 "For troubleshooting:\n" 

115 " - Verify pytest is installed: pytest --version\n" 

116 " - Check test files for syntax errors\n" 

117 " - Review pytest configuration files\n" 

118 " - Run pytest directly to see full output" 

119 ) 

120 

121 logger.error(error_msg) 

122 return ToolResult( 

123 name=self.tool_name, 

124 success=False, 

125 issues=[], 

126 output=error_msg, 

127 issues_count=1 if isinstance(error, subprocess.TimeoutExpired) else 0, 

128 )