Hi all,
I'm working on a GUI frontend for R, and I'm looking for a good way to
catch
all warning- and error-output. The reason for this is mostly, that I would
like to know, which sections of the output are "normal" output,
warnings, and
errors. This would allow for some nice features, such as highlighting
warnings and errors in a different color, or popping up a message like e.g.
"There were these warnings, while doing this-or-that:".
Maybe a good solution for this already exists, but I have not been able to
find it. Therefore, I'll outline what I have tried/considered so far, and
what I think might be ways to add a corresonding API. Of course, I'm not
very
familiar with R, so these suggestions are probably not "the right way to do
it".
What I've tried so far:
1) Directing stderr-output to a file (using sink ()):
This solution splits warnings, and errors from "regular" output. Using
options (error=quote (myhandler ())
I can additionally tell warnings and errors apart after the fact. However,
this solution is problematic, in that then the output is handled
asynchronously, and I can not tell, at which position in the output a warning
should have been printed. Further, if the user runs one "sink ()" too
many
(the user has a sort of console for direct interaction with R), the
output-handling will be broken in my GUI.
2) Use options (warning.expression=...), and options (error=...):
Unfortunately, it turned out, that when setting warning.expression to
something non-NULL, the warning is discarded completely. There is no way to
get at the warning that would have been generated.
3) Use condition handlers:
3a) Wrap each call inside withCallingHandlers (...):
This would probably work, but add quite a bit of overhead for string
manipulation, and parsing. The GUI runs a lot of small commands during normal
operation. There are additional hazzles, such as error messages would then
all look like "Error in withCallingHandlers(...", and I'd have to
use yet
more string operations to make them look normal.
Also: Can I be sure, that all warnings (i.e. even from C-code) are signalled
as conditions? I'm afraid I do not fully understand the internal going-ons,
here.
If all else fails, I'll have to use this, but I'm not very happy with
this.
3b) Use ".Internal (.addCondHands (...))" once to set up persistent
handlers:
This worked fine, when testing it in the R-console. However, in my GUI, calls
are actually handled using R_tryEval (..., R_GlobalEnv, ...). It seems the
condition handlers do not carry over between two successive calls of
R_tryEval. So effectively, I'd once again be back at 3a.
What I would like to have:
Of course there would be many different ways to accomplish this. Here are some
solutions I can think of:
1) In my programmers' dream, there'd simply be three callbacks that I
can
override: R_WriteConsole (exists), R_WriteWarning, and R_WriteError.
R_WriteWarning, and R_WriteError would be called from vwarningcall_dflt, and
verrorcall_dflt, respectively, instead of REprintf. The default
implementation of those, would of course simply call REprintf. Drawbacks:
a) REprintf is available for use in R_ext/Print.h. I'd miss out on any
direct
calls of REprintf, while those should probably still be recorded as a
warning/error.
b) I'd have to add a fairly elaborate implementation in order to honor any
sinks the user has (purposefully) created. If I can even access all necessary
data/functions from my code (I have not investigated that, yet).
2) Similar, but add the hook a little later, in REvprintf. There, instead of
directly calling R_WriteConsole, a new callback R_WriteError would be used
(which defaults to R_WriteConsole). Using this callback I would get a stream
of both warnings, and errors, separate from "regular" output. I could
tell
warnings and errors apart, using options (error=...).
3) Yet a little less intrusive: Somehow use a fake R_ErrorCon:
There I'd simply add my own callback for con->vprintf, and
con->fflush. Then
proceed as in 2. However, it seems Rconnection and the related functions are
too tightly guarded against this approach. I simply don't see a way, how I
could fake this connection (but maybe there is one?). Maybe a public API
could be added to allow this.
Ok, so much on my helpless attempts. Could you help me with this?
Thanks!
Thomas