klmr / box

Write reusable, composable and modular R code
https://klmr.me/box/
MIT License
862 stars 48 forks source link

Show a readable error message when there’s an error in a module #286

Open klmr opened 2 years ago

klmr commented 2 years ago

Currently, box::use() assumes that any error raised inside it must come from ‘box’ itself, and the error message/traceback is completely useless for module developers if there was an error in the loaded module source code:

x.r

f = function () stop('nope')
f()

〉box::use(./x)
Error in box::use(./x) : nope

〉traceback()
12: stop(box_error(message, call = call, subclass = subclass))
11: rethrow(error, call)
10: value[[3L]](cond)
9: tryCatchOne(expr, names, parentenv, handlers[[1L]])
8: tryCatchList(expr, classes, parentenv, handlers)
7: tryCatch(expr, error = function(error) rethrow(error, call))
6: rethrow_on_error({
       spec = parse_spec(declaration, alias)
       info = find_mod(spec, caller)
       load_and_register(spec, info, caller)
   }, call = use_call)
5: (function (declaration, alias, caller, use_call)
   {
       if (identical(declaration, quote(expr = )) && identical(alias,
           ""))
           return()
       rethrow_on_error({
           spec = parse_spec(declaration, alias)
           info = find_mod(spec, caller)
           load_and_register(spec, info, caller)
       }, call = use_call)
   })(dots[[1L]][[1L]], dots[[2L]][[1L]], dots[[3L]][[1L]], use_call = dots[[4L]][[1L]])
4: mapply(FUN = f, ..., SIMPLIFY = FALSE)
3: Map(.f, ..., USE.NAMES = FALSE)
2: map(use_one, imports, aliases, list(caller), use_call = list(sys.call()))
1: box::use(./x)

Expected result

Something along these lines:

〉box::use(./x)
Error in box::use(./x) :
  Error in f() : nope

〉traceback()
3: stop("nope") at ./x.r#1
2: f() at ./x.r#2
1: box::use(./x)