Skip to content

Commit

Permalink
Add context to EvalError (#163)
Browse files Browse the repository at this point in the history
* Add context to EvalError

Closes #142

* Switch to exec for defining function

* Fix argument ordering to PyBuildValue

* Actually call the function that was defined

* Correct line and column

* Add context to EvalError message
  • Loading branch information
jordemort authored Mar 15, 2023
1 parent d83f1a3 commit eb813c0
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 7 deletions.
29 changes: 28 additions & 1 deletion python_exceptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,34 @@ func raisePythonException(err error) {
backtrace := C.CString(evalErr.Backtrace())
defer C.free(unsafe.Pointer(backtrace))

exc_args = C.makeEvalErrorArgs(error_msg, error_type, backtrace)
var (
function_name *C.char
filename *C.char
line C.uint
column C.uint
)

if len(evalErr.CallStack) > 0 {
frame := evalErr.CallStack[len(evalErr.CallStack)-1]

filename = C.CString(frame.Pos.Filename())
defer C.free(unsafe.Pointer((filename)))

line = C.uint(frame.Pos.Line)
column = C.uint(frame.Pos.Col)

function_name = C.CString(frame.Name)
defer C.free(unsafe.Pointer(function_name))
} else {
filename = C.CString("unknown")
defer C.free(unsafe.Pointer(filename))

line = 0
column = 0
function_name = filename
}

exc_args = C.makeEvalErrorArgs(error_msg, error_type, filename, line, column, function_name, backtrace)
exc_type = C.EvalError
case errors.As(err, &resolveErr):
items := C.PyTuple_New(C.Py_ssize_t(len(resolveErr)))
Expand Down
45 changes: 43 additions & 2 deletions src/starlark_go/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,56 @@ class EvalError(StarlarkError):
such as adding a string to an integer.
"""

def __init__(self, error: str, error_type: str, backtrace: str):
super().__init__(error, error_type, backtrace)
def __init__(
self,
error: str,
error_type: str,
filename: str,
line: int,
column: int,
function_name: str,
backtrace: str,
):
super().__init__(
error, error_type, filename, line, column, function_name, backtrace
)
self.filename = filename
"""
The name of the file that the error occurred in.
:type: str
"""
self.line = line
"""
The line number that the error occurred on (1-based)
:type: int
"""
self.column = column
"""
The column that the error occurred on (1-based)
:type: int
"""
self.function_name = function_name
"""
The name of the function that the error occurred in
:type: str
"""
self.backtrace = backtrace
"""
A backtrace through Starlark's stack leading up to the error.
:type: str
"""

context = self.filename
if self.function_name != "<unknown>":
context += " in " + self.function_name

self.error = f"{context}:{self.line}:{self.column}: {self.error}"


class ResolveErrorItem:
"""
Expand Down
12 changes: 10 additions & 2 deletions starlark.c
Original file line number Diff line number Diff line change
Expand Up @@ -422,12 +422,20 @@ PyObject *makeSyntaxErrorArgs(
}

PyObject *makeEvalErrorArgs(
const char *error_msg, const char *error_type, const char *backtrace
const char *error_msg,
const char *error_type,
const char *filename,
const unsigned int line,
const unsigned int column,
const char *function_name,
const char *backtrace
)
{
/* Necessary because Cgo can't do varargs */
/* Three strings */
return Py_BuildValue("sss", error_msg, error_type, backtrace);
return Py_BuildValue(
"sssIIss", error_msg, error_type, filename, line, column, function_name, backtrace
);
}

PyObject *makeResolveErrorItem(
Expand Down
8 changes: 7 additions & 1 deletion starlark.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,13 @@ PyObject *makeSyntaxErrorArgs(
);

PyObject *makeEvalErrorArgs(
const char *error_msg, const char *error_type, const char *backtrace
const char *error_msg,
const char *error_type,
const char *filename,
const unsigned int line,
const unsigned int column,
const char *function_name,
const char *backtrace
);

PyObject *makeResolveErrorItem(
Expand Down
26 changes: 25 additions & 1 deletion tests/test_evalerror.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

from starlark_go import EvalError, Starlark

STARLARK_SRC = """
def wrong():
return 1 + "2"
"""


def test_raises_evalerror():
s = Starlark()
Expand All @@ -17,16 +22,35 @@ def test_eval_attrs():
s = Starlark()
raised = False

s.exec(STARLARK_SRC, filename="fake.star")

try:
s.eval('1 + "2"')
s.eval("wrong()")
except EvalError as e:
assert hasattr(e, "error")
assert isinstance(e.error, str)
assert hasattr(e, "error_type")
assert isinstance(e.error_type, str)
assert e.error_type == "*starlark.EvalError"
assert hasattr(e, "filename")
assert isinstance(e.filename, str)
assert e.filename == "fake.star"
assert hasattr(e, "line")
assert isinstance(e.line, int)
assert e.line == 3
assert hasattr(e, "column")
assert isinstance(e.column, int)
assert e.column == 12
assert hasattr(e, "function_name")
assert isinstance(e.function_name, str)
assert e.function_name == "wrong"
assert hasattr(e, "backtrace")
assert isinstance(e.backtrace, str)

strerror = str(e)
assert strerror.startswith(
f"{e.filename} in {e.function_name}:{e.line}:{e.column}: "
)
raised = True

assert raised

0 comments on commit eb813c0

Please sign in to comment.