github.com/xyproto/orbiton/v2@v2.65.12-0.20240516144430-e10a419274ec/pyparserr.go (about)

     1  package main
     2  
     3  import (
     4  	"strconv"
     5  	"strings"
     6  )
     7  
     8  // ParsePythonError parses a Python error message and returns the line number and first error message.
     9  // If no error message is found, -1 and an empty string will be returned.
    10  func ParsePythonError(msg, filename string) (int, int, string) {
    11  	var (
    12  		foundLineNumber bool   // ... ", line N"
    13  		foundHat        bool   // ^
    14  		errorMessage    string // Typically after "SyntaxError: "
    15  		lineNumber      = -1   // The line number with the Python error, if any
    16  		columnNumber    = -1   // The column number, from the position of the "^" in the error message, if any
    17  		err             error  // Only used within the loop below
    18  	)
    19  	for _, line := range strings.Split(msg, "\n") {
    20  		if foundHat && strings.Contains(line, ": ") {
    21  			errorMessage = strings.SplitN(line, ": ", 2)[1]
    22  			// break since this is usually the end of the approximately 5 line error message from Python
    23  			break
    24  		} else if foundLineNumber && len(line) > 4 {
    25  			// de-indent the line before finding the hat column number
    26  			if hatPos := strings.Index(line[4:], "^"); hatPos != -1 {
    27  				foundHat = true
    28  				// this is the column number (not index),
    29  				columnNumber = hatPos + 1
    30  			} else {
    31  				continue
    32  			}
    33  		} else if strippedLine := strings.TrimSpace(line); strings.Contains(line, "\""+filename+"\"") || (strings.HasPrefix(strippedLine, "File ") && strings.Contains(line, "\", line ")) {
    34  			fields := strings.Split(strippedLine, ", line ")
    35  			if len(fields) < 2 {
    36  				continue
    37  			}
    38  			lineNumber, err = strconv.Atoi(fields[1])
    39  			if err != nil {
    40  				continue
    41  			}
    42  			foundLineNumber = true
    43  		} else if strippedLine := strings.TrimSpace(line); strings.Contains(line, "("+filename+", ") && strings.Contains(line, "Error: ") {
    44  			fields := strings.SplitN(strippedLine, "Error: ", 2)
    45  			errorMessageFileAndLine := fields[1]
    46  			fields = strings.SplitN(errorMessageFileAndLine, "("+filename+", ", 2)
    47  			errorMessage = fields[0]
    48  			lineNumberString := fields[1]
    49  			lineNumberString = strings.TrimPrefix(lineNumberString, "line ")
    50  			lineNumberString = strings.TrimSuffix(lineNumberString, ")")
    51  			if n, err := strconv.Atoi(lineNumberString); err == nil {
    52  				lineNumber = n
    53  			}
    54  		}
    55  	}
    56  
    57  	// Strip the "(detected at line N)" message at the end
    58  	if strings.HasSuffix(errorMessage, ")") && strings.Contains(errorMessage, "(detected at line ") {
    59  		fields := strings.SplitN(errorMessage, "(detected at line ", 2)
    60  		errorMessage = strings.TrimSpace(fields[0])
    61  	}
    62  
    63  	return lineNumber, columnNumber, errorMessage
    64  }