ipywidgets_server icon indicating copy to clipboard operation
ipywidgets_server copied to clipboard

Jupyterlab extension: serve with 1 click

Open DougRzz opened this issue 7 years ago • 2 comments

Hi, firstly I would like to say that ipywidgets-server has really helped me out and works very well. This project deserves more publicity and stars.

It would be great to be able to serve with 1 click from a Jupyter/Jupyterlab notebook. However, I am not sure how to create a Jupyter extension. I have an alternative....I have been automatically converting my notebook session and serving it with ipywidgets-server using the code below. This was placed in the last 4 cells of my notebook. Perhaps this code could be used as a basis for a Jupyterlab extension sometime in the future.

%%javascript
var nb = IPython.notebook;
var kernel = IPython.notebook.kernel;
var commandFileName = "notebookPath = " + "'"+ nb.notebook_path +"'";
kernel.execute(commandFileName);
import nbformat
from nbconvert import PythonExporter
import os
import re
import socket
from contextlib import closing

findFreePort = False
defaultFreePort =  '39393'  # If findFreePort is False,  set this number to desired free port.  Else find a free one. 
widgetName = 'vbox'

#  Read current ipynb notebook    
with open(notebookPath) as fh:
    nb = nbformat.reads(fh.read(), nbformat.NO_CONVERT)

exporter = PythonExporter()
# Convert ipynb to Python
source, meta = exporter.from_notebook_node(nb)

#USe a regex to remove bottom of py file which includes everthing below first magic command
lines=re.sub("get_ipython\(\).*","",source, flags=re.IGNORECASE|re.MULTILINE|re.DOTALL)

# Write Python script
file_fullPathName=os.path.join((os.getcwd()), notebookPath.replace('.ipynb', '.py')) 
# file_fullPathName=os.path.join(directory, notebookPath.replace('.ipynb', '.py')) 

with open(file_fullPathName, "w") as outp:
    outp.write(lines)  #Write model data File (upto step)
def find_free_port():
    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
        s.bind(('', 0))
        return s.getsockname()[1]    

if findFreePort:
    freePort = str(find_free_port())
else:
    freePort = defaultFreePort
    
ipaddress = socket.gethostbyname(socket.gethostname())
#Open from command window from windows.  Doesn't work

commandServe = "ipywidgets-server --port " + freePort +  " " + notebookPath.replace('.ipynb', '') + ':' + widgetName
print(commandServe)

urlAddress = ipaddress + ":" + freePort
print(urlAddress)
#  Windows OS:  Open command window and serve ipywidgets 
commandServeWinOS = "start cmd.exe  /c @cmd /k \"" + r"C:\Users\\" + os.getlogin() + "\AppData\Local\Continuum\miniconda3\Scripts\ipywidgets-server.exe --port " + freePort +  " " + notebookPath.replace('.ipynb', '') + ':vbox\"'
print(commandServeWinOS)
os.system(commandServeWinOS)

import webbrowser
# generate an URL
webbrowser.get("C:/Users/" + os.getlogin() + "/AppData/Local/Google/Chrome/Application/chrome.exe %s").open("http://" + urlAddress)
# webbrowser.get("C:/Users/" + os.getlogin() + "/AppData/Local/Mozilla Firefox/firefox.exe %s").open("http://" + urlAddress)

DougRzz avatar Dec 16 '17 18:12 DougRzz

Thanks for the feedback.

This project deserves more publicity and stars.

Given the project's lack of maturity, having few users (but ones that give useful feedback, like yourself) is useful. It allows for faster iteration.

The jupyter extension is a nice idea. To make sure I understand the workflow correctly:

  • you develop widgets in a notebook
  • when you're ready to serve them, you run these scripts (from that notebook) to spin up ipywidgets-server pointing to a static version of that notebook
  • you automatically make your webbrowser point to the newly open port?

Before writing this, were you just running those commands in the windows terminal?

I note that you try to find a free port (I like the snippet you're using for that). Is this a workaround for ipywidgets-server running on port 8888 by default? On master, I have changed that to port 8866 to avoid clashes with notebook servers.

pbugnion avatar Dec 17 '17 06:12 pbugnion

I found it easier to interactively develop my ipywidgets-server dashboard in a Jupyter Notebook. Initially, I was developing my dashboards in the Notebook and in Python files in parallel. It got confusing quite quickly. So I wanted a faster and direct way of converting from the notebook to an ipywidgets-served session in the browser.

Yes, you described my current workflow well. So my scripts do the following:

  1. Convert the notebook into python code in memory
  2. I use a regular expression to remove the unnecessary Python code
  3. Write this Python code to a .py file to the current working directory on disk
  4. Find a free port
  5. Fire up ipywidgets-server in Windows using the free port (or a specified port). Previously I was opening up a Windows cmd prompt. Now I fire it up with the same commands using os.system(.....). There is most likely a better OS independent command to do this
  6. Open a browser pointing to newly opened port. Again using os.system()

Ideally, it would be nice to have the option of serving straight from the notebook (without having a python file written to disk).

It is probably not worth anyone creating a classic Notebook extension. I think it would be better to wait a couple of months for Jupyterlab to reach Beta. Maybe I could get into Javascript and have a go myself.

I note that you try to find a free port (I like the snippet you're using for that). Is this a workaround for ipywidgets-server running on port 8888 by default?

Pretty much. I didn't really understand what ports were except I noticed it was conflicting with the standard Jupyter port. So I thought it would be safer to find a free port so it would not conflict with any other software on my machine.

DougRzz avatar Dec 17 '17 11:12 DougRzz