¿Cómo obtengo el nombre actual de IPython / Jupyter Notebook?

85

Estoy tratando de obtener el nombre actual del Cuaderno de notas cuando ejecuto el cuaderno IPython. Sé que puedo verlo en la parte superior del cuaderno. Lo que estoy buscando algo como

currentNotebook = IPython.foo.bar.notebookname()

Necesito obtener el nombre en una variable.

Tooblippe
fuente
¿Que estas intentando hacer con eso? Por diseño, el kernel (el bit que ejecuta el código) no conoce la interfaz (el bit que abre los cuadernos).
Thomas K
7
Hola, quiero usarlo con nbconvert para automatizar el proceso de creación del cuaderno a latex / pdf. Mis cuadernos funcionan de forma remota. después de una clase, los estudiantes pueden descargar una versión en pdf de sus resultados.
Tooblippe
1
La respuesta de P.Toccaceli funciona bien con versiones recientes de JupyterLab (1.1.4) (cuaderno 5.6.0) y no requiere javascript.
joelostblom
Algunos hicieron el trabajo e hicieron un paquete pip: pypi.org/project/ipynbname install bypip install ipynbname
NeoTT

Respuestas:

24

Como ya se mencionó, probablemente no se supone que pueda hacer esto, pero encontré una manera. Sin embargo, es un truco en llamas, así que no confíes en esto en absoluto:

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

Actualicé mi respuesta para incluir una solución que "funciona" en IPython 2.0 al menos con una prueba simple. Probablemente no se garantiza que dé la respuesta correcta si hay varios portátiles conectados al mismo kernel, etc.

Iguananaut
fuente
connection_file_path = kernel.get_connection_file () ya no funciona, se requiere el nombre del archivoarg.
Purrell
2
Algunas actualizaciones: en lugar de from IPython.lib import kernelahora es solo from IPython import kernel. Además, en lugar de usar la clave 'nombre' en los diccionarios, use la clave 'ruta'
Tristan Reid
1
Como lo anunció el propio respondedor, esta respuesta no funciona para el último IPython. He creado una versión que parece funcionar con IPython 4.2.0 en Python 3.5: gist.github.com/mbdevpl/f97205b73610dd30254652e7817f99cb
mbdevpl
1
A partir de la versión 4.3.0, debe proporcionar un token de autenticación. Esto se puede recuperar usando notebook.notebookapp.list_running_servers().
yingted
1
Si tiene varios servidores en ejecución, puede verificar en qué puerto está escuchando el proceso principal del kernel, lo que debería indicarle a qué servidor conectarse (o simplemente puede conectarse a cada servidor Jupyter local y verificar cuál está ejecutando su kernel).
Publicado el
40

Tengo lo siguiente que funciona con IPython 2.0. Observé que el nombre del cuaderno se almacena como el valor del atributo 'data-notebook-name'en la <body>etiqueta de la página. Por lo tanto, la idea es primero pedirle a Javascript que recupere el atributo: los javascripts se pueden invocar desde una celda de código gracias a la %%javascriptmagia. Entonces es posible acceder a la variable de Javascript a través de una llamada al Kernel de Python, con un comando que establece una variable de Python. Dado que esta última variable se conoce del kernel, también se puede acceder a ella en otras celdas.

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

Desde una celda de código Python

print(theNotebook)

Fuera []: HowToGetTheNameOfTheNoteBook.ipynb

Un defecto en esta solución es que cuando se cambia el título (nombre) de un cuaderno, entonces este nombre parece no actualizarse inmediatamente (probablemente hay algún tipo de caché) y es necesario recargar el cuaderno para acceder a la nuevo nombre.

[Editar] Reflexionando, una solución más eficiente es buscar el campo de entrada para el nombre del cuaderno en lugar de la <body>etiqueta. Al buscar en la fuente, parece que este campo tiene la identificación "notebook_name". Entonces es posible capturar este valor mediante document.getElementById()ay luego seguir el mismo enfoque que el anterior. El código se convierte, todavía usando la magia de JavaScript

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

Luego, desde una celda ipython,

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

A diferencia de la primera solución, las modificaciones del nombre del portátil se actualizan de inmediato y no es necesario actualizar el portátil.

jfb
fuente
Tal vez me perdí algo, pero ¿cómo se invoca el código javascript de python?
Artjom B.
7
También es posible llamar al javascript desde dentro de Python usando el método de visualización aplicado a un objeto javascript comodef getname(): display(Javascript('IPython.notebook.kernel.execute("theNotebook = " + "\'"+IPython.notebook.notebook_name+"\'");'))
Jakob
¿Cómo modifico esto para obtener la ruta del portátil?
Pedro M Duarte
@PedroMDuarte: Puede usar IPython.notebook.notebook_path en javascript para 'thename' en el script anterior para obtener ese valor.
Tristan Reid
1
Para obtener la ruta del cuaderno sin trucos JS:globals()['_dh'][0]
germen
38

agregando a las respuestas anteriores,

para obtener el nombre del cuaderno, ejecute lo siguiente en una celda:

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

esto le da el nombre del archivo en nb_name

luego, para obtener la ruta completa, puede usar lo siguiente en una celda separada:

import os
nb_full_path = os.path.join(os.getcwd(), nb_name)
Mahmoud Elagdar
fuente
1
El uso de IPython.notebook.notebook_nameesto se puede hacer usando%%javascript IPython.notebook.kernel.execute('notebookName = ' + '"' + IPython.notebook.notebook_name + '"')
jfb
9
Por alguna razón, esto solo funciona si ejecuto la celda javascript "manualmente". Si ejecuto el cuaderno completo, la segunda celda falla. ¿Alguna idea de por qué?
Pierre-Antoine
Supongo que, por alguna razón, si una variable se modifica desde javascript y luego se accede desde python puro en la misma llamada, la versión de python no ve la actualización y también reemplaza la versión de javascript. Así que supongo que puede mover la celda javascript a la parte superior, ejecutarla y luego usar "Celda> Ejecutar todo abajo".
Mahmoud Elagdar
2
¿Por qué necesitamos javascript en realidad? nada mas nativo?
matanster
1
Falla en Jupyter Lab:Javascript Error: IPython is not defined
magicrebirth
27

En Jupyter 3.0 funciona lo siguiente. Aquí estoy mostrando la ruta completa en el servidor de Jupyter, no solo el nombre del cuaderno:

Para almacenar NOTEBOOK_FULL_PATHen la interfaz actual del portátil:

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

Para luego mostrarlo:

print("NOTEBOOK_FULL_PATH:\n", NOTEBOOK_FULL_PATH)

La ejecución de la primera celda de Javascript no produce resultados. Ejecutar la segunda celda de Python produce algo como:

NOTEBOOK_FULL_PATH:
 /user/zeph/GetNotebookName.ipynb
Zephaniah Grunschlag
fuente
4
Esto está muy limpio. Entonces, ¿cómo llamaría al código Javascript desde una función de Python?
Lukas
Hmmmm ... tal vez en ese caso debería agregar el puerto con dos puntos seguidos del número de puerto.
Zephaniah Grunschlag
3
Esta es una ruta relativa, no una ruta completa
Ivelin
Esto tampoco incluye la configuración de c.NotebookApp.notebook_dir.
sappjw
4
Estoy consiguiendo Javascript Error: IPython is not defined. ¿Cómo puedo cargar IPython para javascript
Zozo
25

Parece que no puedo comentar, así que tengo que publicar esto como respuesta.

La solución aceptada por @iguananaut y la actualización por @mbdevpl parecen no funcionar con versiones recientes del Notebook. Lo arreglé como se muestra a continuación. Lo comprobé en Python v3.6.1 + Notebook v5.0.0 y en Python v3.6.5 y Notebook v5.5.0.

from notebook import notebookapp
import urllib
import json
import os
import ipykernel

def notebook_path():
    """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 notebookapp.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['notebook_dir'],sess['notebook']['path'])
        except:
            pass  # There may be stale entries in the runtime directory 
    return None

Como se indica en la cadena de documentos, esto solo funciona cuando no hay autenticación o la autenticación está basada en token.

Tenga en cuenta que, como también informaron otros, el método basado en Javascript no parece funcionar cuando se ejecuta "Ejecutar todas las celdas" (pero funciona cuando se ejecutan celdas "manualmente"), lo cual fue un factor decisivo para mí.

P.Toccaceli
fuente
¿Hay alguna biblioteca para esto?
matanster
El fracaso de los métodos de Javascript fue un factor decisivo para mí también. ¡Gracias por publicar esta alternativa!
gumption
Tengo que reemplazar srv ['notebook_dir'] con from jupyter_core.paths import jupyter_config_dir; de traitlets.config import Config; c = Config (); file_path = os.path.join (jupyter_config_dir (), 'jupyter_notebook_config.py'); exec (abrir (ruta_archivo) .read ()); root_dir = c ['FileContentsManager'] ['root_dir']
Dave Babbitt
14

El paquete ipyparams puede hacer esto con bastante facilidad.

import ipyparams
currentNotebook = ipyparams.notebook_name
Cuenta
fuente
Esta parece una mejor respuesta que la aceptada en la parte superior.
Alejandro
1

Suponiendo que tiene el host, el puerto y el token de autenticación del servidor Jupyter Notebook, esto debería funcionar para usted. Se basa en esta respuesta .

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()]

Uso de ejemplo:

get_notebook_path('127.0.0.1', 17004, '344eb91bee5742a8501cc8ee84043d0af07d42e7135bed90')
Caleb Koch
fuente
0

Otra solución hacky ya que mi servidor portátil puede cambiar. Básicamente, imprime una cadena aleatoria, la guarda y luego busca un archivo que contenga esa cadena en el directorio de trabajo. El while es necesario porque save_checkpoint es asincrónico.

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)
MartínV
fuente
0

Todas las soluciones basadas en Json fallan si ejecutamos más de una celda a la vez porque el resultado no estará listo hasta después del final de la ejecución (no se trata de usar la suspensión o esperar en cualquier momento, compruébelo usted mismo, pero recuerde reiniciar el kernel y ejecutar todas las pruebas)

Basado en soluciones anteriores, esto evita usar la magia %% en caso de que necesite ponerlo en medio de algún otro código:

from IPython.display import display, Javascript

# can have comments here :)
js_cmd = 'IPython.notebook.kernel.execute(\'nb_name = "\' + IPython.notebook.notebook_name + \'"\')'
display(Javascript(js_cmd))

Para python 3, lo siguiente basado en la respuesta de @Iguananaut y actualizado para la última python y posiblemente varios servidores funcionará:

import os
import json
try:
    from urllib2 import urlopen
except:
    from urllib.request import urlopen
import ipykernel

connection_file_path = ipykernel.get_connection_file()
connection_file = os.path.basename(connection_file_path)
kernel_id = connection_file.split('-', 1)[1].split('.')[0]    
    
running_servers = !jupyter notebook list
running_servers = [s.split('::')[0].strip() for s in running_servers[1:]]
nb_name = '???'
for serv in running_servers:
    uri_parts = serv.split('?')
    uri_parts[0] += 'api/sessions'
    sessions = json.load(urlopen('?'.join(uri_parts)))
    for sess in sessions:
        if sess['kernel']['id'] == kernel_id:
            nb_name = os.path.basename(sess['notebook']['path'])
            break
    if nb_name != '???':
        break
print (f'[{nb_name}]')
    
ntg
fuente