Coverage for lintro / parsers / taplo / taplo_parser.py: 100%

41 statements  

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

1"""Parser for taplo output. 

2 

3This module provides parsing functionality for taplo TOML linter/formatter 

4text output format. 

5""" 

6 

7from __future__ import annotations 

8 

9import re 

10 

11from lintro.parsers.base_parser import strip_ansi_codes 

12from lintro.parsers.taplo.taplo_issue import TaploIssue 

13 

14# Pre-compiled regex patterns for taplo output parsing 

15# Pattern for taplo error header: error[code]: message or warning[code]: message 

16_HEADER_PATTERN: re.Pattern[str] = re.compile( 

17 r"^(error|warning)\[([^\]]+)\]:\s*(.+)$", 

18) 

19 

20# Pattern for location line: --> file:line:column 

21_LOCATION_PATTERN: re.Pattern[str] = re.compile( 

22 r"^\s*-->\s*(.+):(\d+):(\d+)\s*$", 

23) 

24 

25# Pattern for taplo fmt --check output: 

26# ERROR taplo:format_files: the file is not properly formatted path="..." 

27# Also handles RUST_LOG=error format: 

28# ERROR the file is not properly formatted path="..." 

29_FMT_CHECK_PATTERN: re.Pattern[str] = re.compile( 

30 r'^ERROR\s+(?:taplo:format_files:\s*)?(.+?)\s+path="([^"]+)"', 

31) 

32 

33 

34def parse_taplo_output(output: str | None) -> list[TaploIssue]: 

35 """Parse taplo output into a list of TaploIssue objects. 

36 

37 Taplo outputs errors in the format: 

38 error[code]: message 

39 --> file:line:column 

40 | 

41 N | code line content 

42 | ^ error indicator 

43 

44 Example output: 

45 error[invalid_value]: invalid value 

46 --> pyproject.toml:5:10 

47 | 

48 5 | version = 

49 | ^ expected a value 

50 

51 Args: 

52 output: The raw output from taplo, or None. 

53 

54 Returns: 

55 List of TaploIssue objects parsed from the output. 

56 """ 

57 issues: list[TaploIssue] = [] 

58 

59 # Handle None or empty output 

60 if not output or not output.strip(): 

61 return issues 

62 

63 # Strip ANSI codes for consistent parsing across environments 

64 output = strip_ansi_codes(output) 

65 

66 lines: list[str] = output.splitlines() 

67 i: int = 0 

68 

69 while i < len(lines): 

70 line: str = lines[i] 

71 

72 # Try to match taplo fmt --check output first 

73 fmt_match: re.Match[str] | None = _FMT_CHECK_PATTERN.match(line) 

74 if fmt_match: 

75 message: str = fmt_match.group(1).strip() 

76 file_path: str = fmt_match.group(2) 

77 issues.append( 

78 TaploIssue( 

79 file=file_path, 

80 line=0, 

81 column=0, 

82 level="error", 

83 code="format", 

84 message=message, 

85 ), 

86 ) 

87 i += 1 

88 continue 

89 

90 # Try to match error/warning header 

91 header_match: re.Match[str] | None = _HEADER_PATTERN.match(line) 

92 if header_match: 

93 level: str = header_match.group(1) 

94 code: str = header_match.group(2) 

95 message = header_match.group(3).strip() 

96 

97 # Look for location in next lines 

98 file_path = "" 

99 line_num: int = 0 

100 column: int = 0 

101 

102 # Search ahead for the location line (usually within 1-2 lines) 

103 for j in range(i + 1, min(i + 5, len(lines))): 

104 location_match: re.Match[str] | None = _LOCATION_PATTERN.match(lines[j]) 

105 if location_match: 

106 file_path = location_match.group(1) 

107 line_num = int(location_match.group(2)) 

108 column = int(location_match.group(3)) 

109 break 

110 

111 issues.append( 

112 TaploIssue( 

113 file=file_path, 

114 line=line_num, 

115 column=column, 

116 level=level, 

117 code=code, 

118 message=message, 

119 ), 

120 ) 

121 

122 i += 1 

123 

124 return issues