gopherdata / gophernotes

The Go kernel for Jupyter notebooks and nteract.
MIT License
3.82k stars 265 forks source link

notebook: defer is executed at the end of a cell #150

Open sbinet opened 5 years ago

sbinet commented 5 years ago

hi,

I am converting a few neugram.io-based notebooks to gopherdata+gomacro.

In these neugram notebooks, a defer would only be executed at the end of the notebook, not at the end of the cell/block. e.g.:

// cell-1
f, err := os.Open(fname)
if err != nil {
    panic(err)
}
defer f.Close()
// cell-2
_, err = f.Write([]byte("hello"))
if err != nil {
    panic(err)
}

this would work in neugram, but fails with gomacro, as f has been closed in cell-1.

could this be fixed?

thanks.

cosmos72 commented 5 years ago

There is currently no hook to run code at the end of a notebook, and each cell is executed sequentially and "separately" from the others. I understand the use case, but it's not easy to implement it.

Also, since in interactive use there may be no "end of notebook" concept, I am a bit worried that top-level defers could become basically a no-op

sbinet commented 5 years ago

in neugram, we had to introduce a top-level function, "main", where all the statements and expressions would be accumulated, including the defers.

of course, in the notebook mode the defers wouldn't be executed (or at least that wouldn't be visible from the web interface), but they would in the "simple" REPL case (when exiting the interpreter.)

I understand this may be problematic to implement with gomacro. but usually notebooks are used for either tutorials or "public relations", and having code that explicitly comments out defer statements isn't great :)

cosmos72 commented 5 years ago

If you mean that jupyter notebook distinguishes "run this existing notebook begin to end" from "open a notebook and run it interactively under user's control", that may be a solution - but I honestly don't know whether such a distinction exists: do you ?

sbinet commented 5 years ago

I don't think such a distinction exists. (by simple REPL I meant running neugram from the usual UNIX shell.)

SpencerPark commented 5 years ago

"run this existing notebook begin to end" from "open a notebook and run it interactively under user's control" ... whether such a distinction exists

Correct, it is simply the front end sending all the cells in individually for execution.

@sbinet, would it instead be alright to define a builtin like func nbdefer(func later()) (please excuse my Go, it's been a while) that is called when the kernel is shutdown? Similar to how Display is given to users.

cosmos72 commented 5 years ago

@SpencerPark: that would require changing all defer f(args) to nbdefer(func () { f(args) }) in the source code to be evaluated. While feasible, it's surprising and cumbersome.

I thought more about it, and it's possible to implement a configuration flag in gomacro that chooses behaviour of top-level defer between: 1) current behaviour: defer calls are evaluated at the end of the cell 2) new behaviour: defer calls are evaluated at the end of the notebook

gophernotes could configure behaviour 2) at startup and, clearly, tell gomacro when a notebook is finished (I have no idea how to detect when a notebook is finished - help is welcome)

Does it seem an acceptable solution?

SpencerPark commented 5 years ago

that would require changing all defer f(args) to nbdefer(func () { f(args) }) in the source code to be evaluated.

Good point, that is a bit tedious, and ugly.

how to detect when a notebook is finished

There is the shutdown_request message that we currently handle in handleShutdownRequest but would need the value for the interpreter passed in from handleShellMessage. A notebook is never "finished" but the kernel is killed when closing it properly ("Close and halt" button) or when manually shutdown. Otherwise it remains running in the background until the server is shutdown.