jupyter
jupyter copied to clipboard
recommendation for using plotly?
A src block like
import plotly.graph_objects as go
f = go.Figure(go.Scatter(x=[1, 2, 3], y=[5, 8, 9]))
f.show()
generates a lot of html. For me that causes a few issues, usually emacs chokes trying to fontify it, and it isn't helpful to see it, I would rather see the rendered html!
Any recommendation for handling this? I could see dumping it to a file and opening it in a browser. That isn't quite right here, it looks like the html is not complete enough to be rendered, but in general what do you do when your code outputs a lot of html code?
Would widgets be a solution?
https://github.com/nnicandro/emacs-jupyter#building-the-widget-support-experimental
https://plotly.com/python/figurewidget/
I was able to make the widgets extension, and when I ran some
import plotly.graph_objects as go
f = go.FigureWidget()
f.add_scatter(y=[2, 1, 4, 3]);
f
a browser did pop up, but it was blank. The html code looks empty too:
!DOCTYPE html>
<html>
<head>
<title>Jupyter Client</title>
<script type="application/javascript" src="/jupyter"></script>
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.min.js"></script>
<style type="text/css">
* {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
</style>
<script type="application/javascript">
var kernel;
document.addEventListener("DOMContentLoaded", function(event) {
// TODO: May not be available everywhere
var p = new URLSearchParams(window.location.search);
var kernel = new EmacsJupyter({username: p.get('username'),
clientId: p.get('clientId')},
p.get('port'));
var commManager = new CommManager(kernel);
var widgetManager = new WidgetManager(kernel, document.getElementById("widget"));
commManager.register_target(widgetManager.comm_target_name, function(comm, msg) {
widgetManager.handle_comm_open(comm, msg);
});
kernel.widgetManager = widgetManager;
kernel.commManager = commManager;
window.kernel = kernel;
});
</script>
</head>
<body>
</body>
</html>
Maybe there is some thing I am missing?
my current solution is to patch fig.show so it saves html and returns an Image and FileLink so I can see what the figure is, and click a link if I want to interact with it.
Yes, this is the widget.html.
The widgetManager where the widget is supposed to show up is placed under document.getElementById("widget"))
, which is a div created by index.js.
I don't really know much JS, but looking a bit at the jupyter-widget-client elisp code, it seems that it is the one responsible for serving the <script type="application/javascript" src="/jupyter"></script>
scripts, under which index.js is to be found.
So maybe something is off with resolving that path.
I just tried and I no longer seem to be able to use widgets either. I remember trying them out back when the project was first announced in 2019 and it worked back then - that was under Emacs 26.3 and who knows what versions of node modules and browsers.
my current solution is to patch fig.show so it saves html and returns an Image and FileLink so I can see what the figure is, and click a link if I want to interact with it.
@jkitchin thanks for mentioning this workaround, could you maybe share this solution if you still use it or have it lying around?
I think it looks like this:
import os
from hashlib import md5
from IPython.display import Image, FileLink
import plotly.graph_objects as go
import plotly.io as pio
def myshow(self, *args, **kwargs):
html = pio.to_html(self)
mhash = md5(html.encode('utf-8')).hexdigest()
if not os.path.isdir('.ob-jupyter'):
os.mkdir('.ob-jupyter')
fhtml = os.path.join('.ob-jupyter', mhash + '.html')
with open(fhtml, 'w') as f:
f.write(html)
display(FileLink(fhtml, result_html_suffix=''))
return Image(pio.to_image(self, 'png'))
go.Figure.show = myshow
Thanks! I changed the last return line to return Image(pio.to_image(self, 'png', engine="kaleido"))
to not use the deprecated orca engine. Now I get this as a result
#+RESULTS:
:RESULTS:
: c:\Users\LeimgruberF\dev\py\.ob-jupyter\06a4bacef7f9bccd239548566e03e3eb.html
: <IPython.core.display.Image object>
:END:
The file link is not clickable, but it references the correct HTML and I can interact with it after manually copying the path and opening it in a browser. Maybe the plotly API changed in the meantime? Or do I need to have different src block settings regarding the block result type?
With the code I posted, here is what I get.
The headers are set as (setq org-babel-default-header-args:jupyter-python '((:results . "value") (:session . "jupyter") (:kernel . "python3") (:pandoc . "t") (:exports . "both") (:cache . "no") (:noweb . "no") (:hlines . "no") (:tangle . "no") (:eval . "never-export")))
With the new engine='kaleido' it also seems work.
Thanks for the example! I am getting results below with
org-babel-default-header-args:jupyter-python’s value is
((:async . "yes")
(:session . "py")
(:kernel . "lg"))
maybe the difference is I have pandoc set to t. Did you try with the same settings I listed above?
Thanks for the shove, with :pandoc t
it works!
A less hacky way to do it might be the following. Set
pio.renderers.default = "png"
Works in the interactive REPL at least.
I've been using the following code for displaying HTML figures in interactive notebooks, based on @jkitchin 's code above
import hashlib
import os
import plotly.io as pio
class EmacsRenderer(pio.base_renderers.ColabRenderer):
save_dir = "ob-jupyter"
base_url = f"http://localhost:8888/files"
def to_mimebundle(self, fig_dict):
html = super().to_mimebundle(fig_dict)["text/html"]
mhash = hashlib.md5(html.encode("utf-8")).hexdigest()
if not os.path.isdir(self.save_dir):
os.mkdir(self.save_dir)
fhtml = os.path.join(self.save_dir, mhash + ".html")
with open(fhtml, "w") as f:
f.write(html)
return {"text/html": f'<a href="{self.base_url}/{fhtml}">Click to open {fhtml}</a>'}
pio.renderers["emacs"] = EmacsRenderer()
pio.renderers.default = "emacs"
Would this maybe be worth a pull request?
do you have some kind of server running on port 8888?
This works better for me, it just opens a local file in the browser.
return {"text/html": f"<a href=\"{fhtml}\" target='_blank'>Click to open {fhtml}</a>"}
Yes, the jupyter notebook is a server running on 8888, in a remote machine. I did open it by first calling on the command line jupyter nbclassic
and then connecting using function jupyter-run-server-repl
.
I agree if you don’t explicitly run the server it’s better to link to the path!
Adrià
On Mar 21, 2023, at 10:53, John Kitchin @.***> wrote:
do you have some kind of server running on port 8888?
This works better for me, it just opens a local file in the browser.
return {"text/html": f"<a href="{fhtml}" target='_blank'>Click to open {fhtml}"} — Reply to this email directly, view it on GitHub https://github.com/nnicandro/emacs-jupyter/issues/333#issuecomment-1478345625, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABFTF4VNFZVVFMGQKGL4W7LW5HTKFANCNFSM45ZUJ4XQ. You are receiving this because you commented.
maybe can be configured in the python code somehow. it is pretty non-obvious to me, as a long time jupyter user!
@rhaps0dy After connecting via jupyter-run-server-repl
how to tell jupyter org client to use that connection? If I execute an org source block then a new kernel is started in a new *jupyter-repl...*
buffer.
See the README of emacs-jupyter for connecting to an existing kernel. You should start your kernel with jupyter kernel
, note down the path of the connection .json
file, and configure your source block as follows:
#+begin_src jupyter-python :session ~/Library/Jupyter/runtime/kernel-70ab2982-89ba-4a91-8a3d-1300a850b0df.json
import sys
print(sys.executable)
#+end_src
Thanks, I was hoping to avoid configuring the explicit path of connection .json
every time and for each source block in question and hoped for a more interactive way, e.g. jupyter-org noticing the new session after jupyter-run-server-repl
and re-using that session from then on.
Yeah fair. I have no idea, I don’t use emacs-jupyter with org mode. Maybe there’s a way to make jupyter always use the same connection Json, so you have one per project.
Yes, that's at least halfway then. I will try and see what fits best - thanks for your snippets and ideas.