79470233

Date: 2025-02-26 15:23:13
Score: 0.5
Natty:
Report link

This is fully expected behavior, in my opinion, because you has basically the line where exactly in file the error happens, so jump to the line and see it. And if you'd catch it inside or outside the interpreter, you'd also get the full info there in the -errorinfo of the result dict. The frame info collected in a backtrace (call-stack) is basically provided from every frame (file, namespace, eval/catch, proc/apply, etc).

it creates difficulties in the context of implementing something like a plugin system that interp evals scripts, as the writers of the plugin scripts would receive less useful information than might be desired.

Well normally nobody (even not plugin interface writers) developing everything at global level, one would rather use namespaces/procs, which then would provide the line number relative the proc.

is there a way to write this code differently that could provide better error messages in scripts run in child interpreters?

Sure, if you need the info of the code evaluating in child-interp only, here you go:

#!/usr/bin/env tclsh

interp create -safe i

set code {
  # multi-line code
  # more comments
  expr {1/0}; # this is line 4 in code
}

if { [ i eval [list catch $code res opt] ] } {
  lassign [i eval {list $res $opt}] res opt
  puts stderr "ERROR: Plug-in code failed in line [dict get $opt -errorline]:\n[dict get $opt -errorinfo]
    while executing plug-in\n\"code-content [list [string range $code 0 255]...]\"
   (\"eval\" body line [dict get $opt -errorline])"
}

interp delete i

Output:

$ example.tcl
ERROR: Plug-in code failed in line 4:
divide by zero
    invoked from within
"expr {1/0}"
    while executing plug-in
"code-content {
  # multi-line code
  # more comments
  expr {1/0}; # this is line 4 in code
...}"
   ("eval" body line 4)   <-- line in code

If you rather don't want catch the error in the code, but rather rethrow it (e. g. from proc evaluating some safe code), it is also very simple:

#!/usr/bin/env tclsh

interp create -safe i

proc safe_eval {code} {
  if { [ i eval [list catch $code res opt] ] } {
    lassign [i eval {list $res $opt}] res opt
    # add the line (info of catch) into stack trace:
    dict append opt -errorinfo "\n   (\"eval\" body line [dict get $opt -errorline])"
    return {*}$opt -level 2 $res
  }
}

safe_eval {
  # multi-line code
  # more comments
  expr {1/0}; # this is line 4 in code
}

interp delete i

Output:

$ example.tcl
divide by zero
    invoked from within
"expr {1/0}"
   ("eval" body line 4)   <-- line in code
    invoked from within
"safe_eval {
  # multi-line code
  # more comments
  expr {1/0}; # this is line 4 in code
}"
    (file "example.tcl" line 14)
Reasons:
  • Blacklisted phrase (1): is there a way
  • Long answer (-1):
  • Has code block (-0.5):
  • Contains question mark (0.5):
  • Low reputation (0.5):
Posted by: sebres