Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get Jupyter notebook name [duplicate]

I am trying to obtain the current NoteBook name when running the IPython notebook. I know I can see it at the top of the notebook. What I am after something like

currentNotebook = IPython.foo.bar.notebookname()

I need to get the name in a variable.

like image 379
Tooblippe Avatar asked Sep 22 '12 13:09

Tooblippe


People also ask

How do you copy a Jupyter Notebook?

Notebook 1: — Select multiple cells by holding down Shift and hit Ctrl+c to copy. Notebook 2: — Hit Esc to enter Command mode Ctrl + v to paste.

What is the file name for Jupyter Notebook?

When you use the Jupyter Notebook dashboard menu to rename Jupyter Notebook files (. ipynb), a new window will open in which you can type the new name of the file.


11 Answers

adding to previous answers,

to get the notebook name run the following in a cell:

%%javascript
IPython.notebook.kernel.execute('nb_name = "' + IPython.notebook.notebook_name + '"')

this gets you the file name in nb_name

then to get the full path you may use the following in a separate cell:

import os
nb_full_path = os.path.join(os.getcwd(), nb_name)
like image 97
Mahmoud Elagdar Avatar answered Oct 21 '22 08:10

Mahmoud Elagdar


I have the following which works with IPython 2.0. I observed that the name of the notebook is stored as the value of the attribute 'data-notebook-name' in the <body> tag of the page. Thus the idea is first to ask Javascript to retrieve the attribute --javascripts can be invoked from a codecell thanks to the %%javascript magic. Then it is possible to access to the Javascript variable through a call to the Python Kernel, with a command which sets a Python variable. Since this last variable is known from the kernel, it can be accessed in other cells as well.

%%javascript
var kernel = IPython.notebook.kernel;
var body = document.body,  
    attribs = body.attributes;
var command = "theNotebook = " + "'"+attribs['data-notebook-name'].value+"'";
kernel.execute(command);

From a Python code cell

print(theNotebook)

Out[ ]: HowToGetTheNameOfTheNoteBook.ipynb

A defect in this solution is that when one changes the title (name) of a notebook, then this name seems to not be updated immediately (there is probably some kind of cache) and it is necessary to reload the notebook to get access to the new name.

[Edit] On reflection, a more efficient solution is to look for the input field for notebook's name instead of the <body> tag. Looking into the source, it appears that this field has id "notebook_name". It is then possible to catch this value by a document.getElementById() and then follow the same approach as above. The code becomes, still using the javascript magic

%%javascript
var kernel = IPython.notebook.kernel;
var thename = window.document.getElementById("notebook_name").innerHTML;
var command = "theNotebook = " + "'"+thename+"'";
kernel.execute(command);

Then, from a ipython cell,

In [11]: print(theNotebook)
Out [11]: HowToGetTheNameOfTheNoteBookSolBis

Contrary to the first solution, modifications of notebook's name are updated immediately and there is no need to refresh the notebook.

like image 26
jfb Avatar answered Oct 21 '22 06:10

jfb


As already mentioned you probably aren't really supposed to be able to do this, but I did find a way. It's a flaming hack though so don't rely on this at all:

import json
import os
import urllib2
import IPython
from IPython.lib import kernel
connection_file_path = kernel.get_connection_file()
connection_file = os.path.basename(connection_file_path)
kernel_id = connection_file.split('-', 1)[1].split('.')[0]

# Updated answer with semi-solutions for both IPython 2.x and IPython < 2.x
if IPython.version_info[0] < 2:
    ## Not sure if it's even possible to get the port for the
    ## notebook app; so just using the default...
    notebooks = json.load(urllib2.urlopen('http://127.0.0.1:8888/notebooks'))
    for nb in notebooks:
        if nb['kernel_id'] == kernel_id:
            print nb['name']
            break
else:
    sessions = json.load(urllib2.urlopen('http://127.0.0.1:8888/api/sessions'))
    for sess in sessions:
        if sess['kernel']['id'] == kernel_id:
            print sess['notebook']['name']
            break

I updated my answer to include a solution that "works" in IPython 2.0 at least with a simple test. It probably isn't guaranteed to give the correct answer if there are multiple notebooks connected to the same kernel, etc.

like image 40
Iguananaut Avatar answered Oct 21 '22 08:10

Iguananaut


It seems I cannot comment, so I have to post this as an answer.

The accepted solution by @iguananaut and the update by @mbdevpl appear not to be working with recent versions of the Notebook. I fixed it as shown below. I checked it on Python v3.6.1 + Notebook v5.0.0 and on Python v3.6.5 and Notebook v5.5.0.

import jupyterlab
if jupyterlab.__version__.split(".")[0] == "3":
    from jupyter_server import serverapp as app
    key_srv_directory = 'root_dir'
else : 
    from notebook import notebookapp as app
    key_srv_directory = 'notebook_dir'
import urllib
import json
import os
import ipykernel

def notebook_path(key_srv_directory, ):
    """Returns the absolute path of the Notebook or None if it cannot be determined
    NOTE: works only when the security is token-based or there is also no password
    """
    connection_file = os.path.basename(ipykernel.get_connection_file())
    kernel_id = connection_file.split('-', 1)[1].split('.')[0]

    for srv in app.list_running_servers():
        try:
            if srv['token']=='' and not srv['password']:  # No token and no password, ahem...
                req = urllib.request.urlopen(srv['url']+'api/sessions')
            else:
                req = urllib.request.urlopen(srv['url']+'api/sessions?token='+srv['token'])
            sessions = json.load(req)
            for sess in sessions:
                if sess['kernel']['id'] == kernel_id:
                    return os.path.join(srv[key_srv_directory],sess['notebook']['path'])
        except:
            pass  # There may be stale entries in the runtime directory 
    return None

As stated in the docstring, this works only when either there is no authentication or the authentication is token-based.

Note that, as also reported by others, the Javascript-based method does not seem to work when executing a "Run all cells" (but works when executing cells "manually"), which was a deal-breaker for me.

like image 36
P.Toccaceli Avatar answered Oct 21 '22 07:10

P.Toccaceli


On Jupyter 3.0 the following works. Here I'm showing the entire path on the Jupyter server, not just the notebook name:

To store the NOTEBOOK_FULL_PATH on the current notebook front end:

%%javascript
var nb = IPython.notebook;
var kernel = IPython.notebook.kernel;
var command = "NOTEBOOK_FULL_PATH = '" + nb.base_url + nb.notebook_path + "'";
kernel.execute(command);

To then display it:

print("NOTEBOOK_FULL_PATH:\n", NOTEBOOK_FULL_PATH)

Running the first Javascript cell produces no output. Running the second Python cell produces something like:

NOTEBOOK_FULL_PATH:
 /user/zeph/GetNotebookName.ipynb
like image 42
Zephaniah Grunschlag Avatar answered Oct 21 '22 08:10

Zephaniah Grunschlag


The ipyparams package can do this pretty easily.

import ipyparams
currentNotebook = ipyparams.notebook_name
like image 44
Bill Avatar answered Oct 21 '22 08:10

Bill


Yet another hacky solution since my notebook server can change. Basically you print a random string, save it and then search for a file containing that string in the working directory. The while is needed because save_checkpoint is asynchronous.

from time import sleep
from IPython.display import display, Javascript
import subprocess
import os
import uuid

def get_notebook_path_and_save():
    magic = str(uuid.uuid1()).replace('-', '')
    print(magic)
    # saves it (ctrl+S)
    display(Javascript('IPython.notebook.save_checkpoint();'))
    nb_name = None
    while nb_name is None:
        try:
            sleep(0.1)
            nb_name = subprocess.check_output(f'grep -l {magic} *.ipynb', shell=True).decode().strip()
        except:
            pass
    return os.path.join(os.getcwd(), nb_name)
like image 44
MartínV Avatar answered Oct 21 '22 07:10

MartínV


There is no real way yet to do this in Jupyterlab. But there is an official way that's now under active discussion/development as of August 2021:

https://github.com/jupyter/jupyter_client/pull/656

In the meantime, hitting the api/sessions REST endpoint of jupyter_server seems like the best bet. Here's a cleaned-up version of that approach:

from jupyter_server import serverapp
from jupyter_server.utils import url_path_join
from pathlib import Path
import re
import requests

kernelIdRegex = re.compile(r"(?<=kernel-)[\w\d\-]+(?=\.json)")

def getNotebookPath():
    kernelId = kernelIdRegex.search(get_ipython().config["IPKernelApp"]["connection_file"])[0]
    
    for jupServ in serverapp.list_running_servers():
        for session in requests.get(url_path_join(jupServ["url"], "api/sessions"), params={"token": jupServ["token"]}).json():
            if kernelId == session["kernel"]["id"]:
                return Path(jupServ["root_dir"]) / session["notebook"]['path']

Tested working with

python==3.9
jupyter_server==1.8.0
jupyterlab==4.0.0a7
like image 39
tel Avatar answered Oct 21 '22 07:10

tel


Modifying @jfb method, gives the function below which worked fine on ipykernel-5.3.4.

def getNotebookName():
    display(Javascript('IPython.notebook.kernel.execute("NotebookName = " + "\'"+window.document.getElementById("notebook_name").innerHTML+"\'");'))
    try:
        _ = type(NotebookName)
        return NotebookName
    except:
        return None

Note that the display javascript will take some time to reach the browser, and it will take some time to execute the JS and get back to the kernel. I know it may sound stupid, but it's better to run the function in two cells, like this:

nb_name = getNotebookName()

and in the following cell:

for i in range(10):
    nb_name = getNotebookName()
    if nb_name is not None:
        break

However, if you don't need to define a function, the wise method is to run display(Javascript(..)) in one cell, and check the notebook name in another cell. In this way, the browser has enough time to execute the code and return the notebook name.

If you don't mind to use a library, the most robust way is:

import ipynbname
nb_name = ipynbname.name()
like image 38
Mehdi Avatar answered Oct 21 '22 08:10

Mehdi


Assuming you have the Jupyter Notebook server's host, port, and authentication token, this should work for you. It's based off of this answer.

import os
import json
import posixpath
import subprocess
import urllib.request
import psutil

def get_notebook_path(host, port, token):
    process_id = os.getpid();
    notebooks = get_running_notebooks(host, port, token)
    for notebook in notebooks:
        if process_id in notebook['process_ids']:
            return notebook['path']

def get_running_notebooks(host, port, token):
    sessions_url = posixpath.join('http://%s:%d' % (host, port), 'api', 'sessions')
    sessions_url += f'?token={token}'
    response = urllib.request.urlopen(sessions_url).read()
    res = json.loads(response)
    notebooks = [{'kernel_id': notebook['kernel']['id'],
                  'path': notebook['notebook']['path'],
                  'process_ids': get_process_ids(notebook['kernel']['id'])} for notebook in res]
    return notebooks

def get_process_ids(name):
    child = subprocess.Popen(['pgrep', '-f', name], stdout=subprocess.PIPE, shell=False)
    response = child.communicate()[0]
    return [int(pid) for pid in response.split()]

Example usage:

get_notebook_path('127.0.0.1', 17004, '344eb91bee5742a8501cc8ee84043d0af07d42e7135bed90')
like image 39
Caleb Koch Avatar answered Oct 21 '22 07:10

Caleb Koch


To realize why you can't get notebook name using these JS-based solutions, run this code and notice the delay it takes for the message box to appear after python has finished execution of the cell / entire notebook:

%%javascript

function sayHello() {
    alert('Hello world!');
}

setTimeout(sayHello, 1000);
  • More info

Javascript calls are async and hence not guaranteed to complete before python starts running another cell containing the code expecting this notebook name variable to be already created... resulting in NameError when trying to access non-existing variables that should contain notebook name.

I suspect some upvotes on this page became locked before voters could discover that all %%javascript-based solutions ultimately don't work... when the producer and consumer notebook cells are executed together (or in a quick succession).

like image 30
mirekphd Avatar answered Oct 21 '22 08:10

mirekphd