Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable JupyterKernel to print traceback on error #2530

Closed

Conversation

ssiccha
Copy link
Contributor

@ssiccha ssiccha commented Jun 7, 2018

This is a bit dirty. Setting BreakOnError := false suppresses
both the break loop and the traceback. So to make the traceback
available under jupyter ErrorInner checks whether a JupyterKernel
is currently running.

See new commit message below.

@ssiccha
Copy link
Contributor Author

ssiccha commented Jun 7, 2018

This builds on #2529.

@ssiccha
Copy link
Contributor Author

ssiccha commented Jun 7, 2018

This seemed to me to be the easiest way to get this done for now.

Another idea would be to have ErrorInner always print a traceback, except for when a Test is running. Test could e.g. define a local variable IsTesting which is checked. This would maybe also benefit libGAP?
It is not documented that BreakOnError := false implies that the traceback is suppressed, to the contrary:

-T
     suppresses  the  usual  break  loop  behaviour of GAP. With this option GAP behaves as if the user quit
     immediately  from  every  break loop. This is intended for automated testing of GAP. This option may be
     repeated to toggle this behavior on and off.

I can't imagine a situation where a traceback would not be helpful. Maybe I'm suffering a spontaneous lack of imagination though. :)

@olexandr-konovalov olexandr-konovalov added the gapdays2018-spring Issues and PRs that arose at http://www.gapdays.de/gap-jupyter-days2018 label Jun 8, 2018
@markuspf
Copy link
Member

I think its a bad idea to have a variable for specific IsJupyterKernelRunning or IsTesting.

What's wrong with a variable that enables or disables printing of backtraces? I struggle imagining a case in which one'd want to suppress backtraces, but then at least one could.

I also agree that BreakOnError := false should not suppress printing backtraces unless this breaks some bit of testing in a major way.

@fingolfin
Copy link
Member

Uhm, I think Test actually relies on BreakOnError disabling back traces. So at the very least, there still must be a mechanism to disable the backtrace, because we really don't want them in tests (else they'd become super brittle; even if we suppressed line numbers, they'd still depend on implementation details and call orders, etc.).

And I also I have quite some custom code which relies on the current behavior of BreakOnError. E.g. my UnitTests package (although that's rarely used these days, and I hope I can replace it by e.g. the QuickCheck code one of your students is working on).

Don't get me wrong, I'd survive if this changed, but I am not convinced by the argument given here that this is necessary or even that desirable, nor do I agree that -T implies that what we do right now is wrong -- and even if, this has been the behaviour of GAP for decades, and so I'd argue that the documentation for -T should be changed, if at all.

Can't you, instead of changing this code, instead install a custom OnBreak handler, which basically replicates the code from the BreakOnError = false code path? I.e. something like this (untested):

OnBreak := function()
        ErrorLevel := ErrorLevel-1;
        ErrorLVars := errorLVars;
        if ErrorLevel = 0 then LEAVE_ALL_NAMESPACES(); fi;
        JUMP_TO_CATCH(0);
end;

That said, I'd generally agree that ErrorInner could/should be refactored further (and also be merged with its HPC-GAP twin in hpcgap/lib/error.g. But I am not sure this is the way...

lib/error.g Outdated
if IsBound(IsRunningJupyterKernel) then
location := CURRENT_STATEMENT_LOCATION(context);
if location <> fail then
PrintTo(GAP_ERROR_STREAM, " at ", location[1], ":", location[2]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This causes the test suite to fail, as GAP_ERROR_STREAM is not defined. Perhaps you meant "*errout*"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

he did mean GAP_ERROR_STREAM, because I used that global variable in my prototype code for error printing in the JupyterKernel package.

Whether it's useful to use GAP_ERROR_STREAM over "*errout*" is debatable, as long as it's search-and-replaceable, either is fine for me, with preference for "*errout*" because its consistent with the remainder of the codebase.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then this pull request is incomplete, as GAP_ERROR_STREAM is not defined. It cannot be merged until this is resolved, either by defining GAP_ERROR_STREAM, or by not using it.

@ssiccha
Copy link
Contributor Author

ssiccha commented Jun 12, 2018

Sorry for being absent. I neglected to mention that this needs #2529 to be merged and that printing tracebacks while testing would lead to "false errors" when e.g. changing filenames.

wrt this PR:
@markuspf I will get rid of the local variable IsRunningJupyterKernel and try what @fingolfin suggested.

@fingolfin You mean when running a JupyterKernel we don't use -T but instead overwrite OnBreak() with the function you suggested? That should work. I'll test it.

More general (I made separate issues for these points):
I think the documentation of -T should be changed to say that it disables the break loop and the traceback. Do we want a command line option (like --nobreakloop ) that disables the break loop but still prints tracebacks? Then JupyterKernel wouldn't need to overwrite OnBreak() anymore.

I think with a global variable TracebackOnError (or any better name) one should be able to refactor ErrorInner in such a way that the behaviour of ErrorInner for BreakOnError := false could afterwards be achieved with BreakOnError := false; TracebackOnError := false;. Would that make it easy to adapt your UnitTests package @fingolfin?

@ssiccha ssiccha force-pushed the ss/jupyter-print-traceback branch from 77978f8 to da9690b Compare June 12, 2018 23:33
@codecov
Copy link

codecov bot commented Jun 12, 2018

Codecov Report

Merging #2530 into master will decrease coverage by <.01%.
The diff coverage is 66.66%.

@@            Coverage Diff             @@
##           master    #2530      +/-   ##
==========================================
- Coverage   74.46%   74.46%   -0.01%     
==========================================
  Files         481      481              
  Lines      242743   242743              
==========================================
- Hits       180770   180769       -1     
- Misses      61973    61974       +1
Impacted Files Coverage Δ
lib/error.g 35.79% <66.66%> (ø) ⬆️
hpcgap/lib/hpc/stdtasks.g 64.19% <0%> (-0.26%) ⬇️

@ssiccha
Copy link
Contributor Author

ssiccha commented Jun 12, 2018

@fingolfin I made it work with your suggestion.

On the GAP site now I did the following (new commit's message):
This makes it possible for the JupyterKernel package to
override the function OnBreak. Namely errorLvars needs to
be global so that the alternative OnBreak function can also
read from and write to it.

@ssiccha ssiccha changed the title Print traceback on error if a JupyterKernel runs Enable JupyterKernel to print traceback on error Jun 12, 2018
@ssiccha
Copy link
Contributor Author

ssiccha commented Jun 12, 2018

I'm not sure whether making errorLVars global has any unintended side effects. It basically is a buffer for the original ErrorLVars variable. The original value ofErrorLVars is always restored before exiting the function via a return or a JUMP_TO_CATCH. I think this is problematic if an error occurs inside ErrorInner. This could be alleviated by making errorLVars into a stack. Should I do this or ignore this for now?
This should only be a temporary hack, so I don't mind if this is not merged in at all. I would rather like to introduce TracebackOnError and then there would be no need for the JupyterKernel package to overwrite OnBreak() anymore and this could be closed / reverted.

This makes it possible for the JupyterKernel package to
override the function OnBreak. Namely `errorLVars` becomes
the global variable `ErrorLVarsOld` so that the alternative
OnBreak function can also read from and write to it.
@ssiccha ssiccha force-pushed the ss/jupyter-print-traceback branch from da9690b to 75f968a Compare June 13, 2018 11:06
@ssiccha
Copy link
Contributor Author

ssiccha commented Jun 13, 2018

I renamed errorLVars to ErrorLVarsOld to please the test suite.

New commit message:

Enable JupyterKernel to print traceback on error
    
This makes it possible for the JupyterKernel package to
override the function OnBreak. Namely `errorLVars` becomes
the global variable `ErrorLVarsOld` so that the alternative
OnBreak function can also read from and write to it.

@@ -242,7 +243,7 @@ BIND_GLOBAL("ErrorInner",
res := fail;
fi;
ErrorLevel := ErrorLevel-1;
ErrorLVars := errorLVars;
ErrorLVars := ErrorLVarsOld;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this change, nested errors won't work -- i.e. nested break loops.

All you need is the correct ErrorLVars in your OnBreak handler, right? So you simply leave errorLVars as it is, but insert ErrorLVars := errorLVars; right before the call to OnBreak. That should never cause harms, but would remove the need for your OnBreak handler to do anything.

I.e. this PR would essentially insert a single line.

Does that work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the nested break loops don't work like this this can't be merged.

I think the problem with setting ErrorLVars := errorLVars; right before calling OnBreak() is that currently, when entering OnBreak, ErrorLVars is set to context. Where uses ErrorLVars to access context to print the traceback. (if only OnBreak could take context as an argument..)

Or did I misunderstand something there?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I just re-read the whole of ErrorInner (as I should have done before my comments sigh), and yes, you are right.

But what should work is adding a new global ErrorLVarsOld as you did, but retaining errorLVars; then set ErrorLVarsOld only just before calling OnBreak. That's not very nice, because we are essentially passing an argument through a global variable, but it'd work.

But yeah, at this point, we might as well introduce AlwaysShowTracebackOnError as discussed in issue #2547.

And refactor this code later on again ;-).

@ssiccha ssiccha added the do not merge PRs which are not yet ready to be merged (e.g. submitted for discussion, or test results) label Jun 13, 2018
@ssiccha
Copy link
Contributor Author

ssiccha commented Jun 13, 2018

I will close this in favor of a global variable AlwaysPrintTracebackOnError as discussed in #2547.

@ssiccha ssiccha closed this Jun 13, 2018
@ssiccha ssiccha deleted the ss/jupyter-print-traceback branch June 28, 2018 22:42
@fingolfin fingolfin added kind: discussion discussions, questions, requests for comments, and so on and removed kind: request for comments labels Mar 21, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
do not merge PRs which are not yet ready to be merged (e.g. submitted for discussion, or test results) gapdays2018-spring Issues and PRs that arose at http://www.gapdays.de/gap-jupyter-days2018 kind: discussion discussions, questions, requests for comments, and so on
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants