Come posso ottenere il nome corrente di IPython / Jupyter Notebook


87

Sto cercando di ottenere il nome corrente del NoteBook durante l'esecuzione del notebook IPython. So di poterlo vedere nella parte superiore del taccuino. Quello che sto cercando qualcosa di simile

currentNotebook = IPython.foo.bar.notebookname()

Ho bisogno di ottenere il nome in una variabile.


Cosa stai cercando di fare con esso? In base alla progettazione, il kernel (il bit che esegue il codice) non conosce il frontend (il bit che apre i notebook).
Thomas K,

7
Ciao, voglio usarlo con nbconvert per automatizzare il processo di creazione del notebook in latex / pdf. I miei notebook funzionano in remoto. dopo una lezione gli studenti possono scaricare una versione pdf dei loro risultati.
Tooblippe

1
La risposta di P.Toccaceli funziona bene con le versioni recenti di JupyterLab (1.1.4) (notebook 5.6.0) e non richiede javascript.
joelostblom


Alcuni hanno fatto il lavoro e hanno creato un pacchetto pip: pypi.org/project/ipynbname install bypip install ipynbname
NeoTT

Risposte:


26

Come già accennato, probabilmente non dovresti essere in grado di farlo, ma ho trovato un modo. È un trucco fiammeggiante, quindi non fare affidamento su questo:

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

Ho aggiornato la mia risposta per includere una soluzione che "funziona" in IPython 2.0 almeno con un semplice test. Probabilmente non è garantito che fornisca la risposta corretta se ci sono più notebook collegati allo stesso kernel, ecc.


connection_file_path = kernel.get_connection_file () non funziona più, il nome del file è richiesto arg.
Purrell

2
Alcuni aggiornamenti: invece di from IPython.lib import kerneladesso è solo from IPython import kernel. Inoltre, invece di utilizzare la chiave "nome" nei dizionari, utilizzare la chiave "percorso"
Tristan Reid

1
Come pubblicizzato dallo stesso risponditore, questa risposta non funziona per l'ultimo IPython. Ho creato una versione che sembra funzionare con IPython 4.2.0 in Python 3.5: gist.github.com/mbdevpl/f97205b73610dd30254652e7817f99cb
mbdevpl

1
A partire dalla versione 4.3.0, è necessario fornire un token di autenticazione. Questo può essere recuperato utilizzando notebook.notebookapp.list_running_servers().
yingted

1
Se hai più server in esecuzione, puoi controllare su quale porta è in ascolto il processo genitore del kernel, che dovrebbe dirti a quale server connetterti (oppure puoi semplicemente connetterti a ogni server Jupyter locale e controllare quale sta eseguendo il tuo kernel).
yingted

41

Ho il seguente che funziona con IPython 2.0. Ho osservato che il nome del taccuino è memorizzato come valore dell'attributo 'data-notebook-name'nel <body>tag della pagina. Quindi l'idea è prima di chiedere a Javascript di recuperare l'attributo --javascripts può essere invocato da una codecell grazie alla %%javascriptmagia. Successivamente è possibile accedere alla variabile Javascript tramite una chiamata al Kernel Python, con un comando che imposta una variabile Python. Poiché quest'ultima variabile è nota dal kernel, è possibile accedervi anche in altre celle.

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

Da una cella di codice Python

print(theNotebook)

Out []: HowToGetTheNameOfTheNoteBook.ipynb

Un difetto di questa soluzione è che quando si cambia il titolo (nome) di un notebook, questo nome sembra non essere aggiornato immediatamente (probabilmente c'è una sorta di cache) ed è necessario ricaricare il notebook per accedere al nuovo nome.

[Modifica] Riflettendoci, una soluzione più efficiente è cercare il campo di input per il nome del taccuino invece del <body>tag. Esaminando l'origine, sembra che questo campo abbia l'ID "notebook_name". È quindi possibile catturare questo valore con a document.getElementById()e quindi seguire lo stesso approccio di cui sopra. Il codice diventa, ancora usando la magia di javascript

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

Quindi, da una cella ipython,

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

Contrariamente alla prima soluzione, le modifiche al nome del notebook vengono aggiornate immediatamente e non è necessario aggiornare il notebook.


Forse mi sono perso qualcosa, ma come si invoca il codice javascript da python?
Artjom B.

7
È anche possibile chiamare il javascript dall'interno di python utilizzando il metodo di visualizzazione applicato a un oggetto javascript comedef getname(): display(Javascript('IPython.notebook.kernel.execute("theNotebook = " + "\'"+IPython.notebook.notebook_name+"\'");'))
Jakob

Come lo modifico per ottenere il percorso del notebook?
Pedro M Duarte

@PedroMDuarte: Puoi usare IPython.notebook.notebook_path in javascript per 'thename' nello script sopra per ottenere quel valore.
Tristan Reid

1
Per ottenere il percorso del notebook senza l'inganno di JS:globals()['_dh'][0]
germ

38

aggiungendo alle risposte precedenti,

per ottenere il nome del taccuino eseguire quanto segue in una cella:

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

questo ti dà il nome del file in nb_name

quindi per ottenere il percorso completo è possibile utilizzare quanto segue in una cella separata:

import os
nb_full_path = os.path.join(os.getcwd(), nb_name)

1
Utilizzando IPython.notebook.notebook_namequesto può essere fatto utilizzando%%javascript IPython.notebook.kernel.execute('notebookName = ' + '"' + IPython.notebook.notebook_name + '"')
jfb

10
Per qualche ragione questo funziona solo se eseguo la cella javascript "manualmente". Se eseguo il notebook completo, la seconda cella non funziona. Qualche idea sul perché?
Pierre-Antoine

Immagino che per qualche motivo, se una variabile viene modificata da javascript, quindi si accede da puro python nella stessa chiamata, la versione python non vede l'aggiornamento e sostituisce anche la versione javascript. Quindi immagino che potresti spostare la cella javascript in alto, eseguirla, quindi utilizzare "Cella> Esegui tutto sotto".
Mahmoud Elagdar

2
Perché in realtà abbiamo bisogno di javascript? niente di più nativo?
matanster

2
Errore su Jupyter Lab:Javascript Error: IPython is not defined
magicrebirth

27

Su Jupyter 3.0 funziona quanto segue. Qui sto mostrando l'intero percorso sul server Jupyter, non solo il nome del notebook:

Per memorizzare il NOTEBOOK_FULL_PATHfront-end del notebook corrente:

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

Per visualizzarlo quindi:

print("NOTEBOOK_FULL_PATH:\n", NOTEBOOK_FULL_PATH)

L'esecuzione della prima cella Javascript non produce alcun output. L'esecuzione della seconda cella Python produce qualcosa di simile:

NOTEBOOK_FULL_PATH:
 /user/zeph/GetNotebookName.ipynb

4
Questo è molto pulito. Come chiameresti quindi il codice Javascript da una funzione Python?
Lukas

Hmmmm ... forse in tal caso dovresti aggiungere alla porta i due punti seguiti dal numero della porta?
Zephaniah Grunschlag

3
Questo è un percorso relativo, non completo
Ivelin

Anche questo non include l'impostazione di c.NotebookApp.notebook_dir.
sappjw

4
Sto ottenendo Javascript Error: IPython is not defined. Come posso caricare IPython per javascript
zozo

26

Sembra che non posso commentare, quindi devo postare questo come risposta.

La soluzione accettata da @iguananaut e l'aggiornamento da @mbdevpl sembrano non funzionare con le versioni recenti del Notebook. L'ho risolto come mostrato di seguito. L'ho controllato su Python v3.6.1 + Notebook v5.0.0 e su Python v3.6.5 e 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

Come indicato nella docstring, funziona solo quando non c'è autenticazione o l'autenticazione è basata su token.

Nota che, come riportato anche da altri, il metodo basato su Javascript non sembra funzionare quando si esegue un "Esegui tutte le celle" (ma funziona quando si eseguono le celle "manualmente"), che è stato un problema per me.


Esiste una libreria per questo?
matante

Il fallimento dei metodi Javascript è stato un problema anche per me. Grazie per aver pubblicato questa alternativa!
partenza

Devo sostituire srv ['notebook_dir'] con from jupyter_core.paths import jupyter_config_dir; da traitlets.config import Config; c = Config (); file_path = os.path.join (jupyter_config_dir (), 'jupyter_notebook_config.py'); exec (open (file_path) .read ()); root_dir = c ['FileContentsManager'] ['root_dir']
Dave Babbitt,

15

Il pacchetto ipyparams può farlo abbastanza facilmente.

import ipyparams
currentNotebook = ipyparams.notebook_name

1
Questa sembra una risposta migliore di quella accettata in alto.
alejandro

1

Supponendo che tu abbia l'host, la porta e il token di autenticazione del server Jupyter Notebook, questo dovrebbe funzionare per te. Si basa su questa risposta .

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

Utilizzo di esempio:

get_notebook_path('127.0.0.1', 17004, '344eb91bee5742a8501cc8ee84043d0af07d42e7135bed90')

0

Ancora un'altra soluzione hacker poiché il mio server notebook può cambiare. Fondamentalmente si stampa una stringa casuale, la si salva e quindi si cerca un file contenente quella stringa nella directory di lavoro. Il tempo è necessario perché save_checkpoint è asincrono.

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)

0

Tutte le soluzioni basate su Json falliscono se eseguiamo più di una cella alla volta perché il risultato non sarà pronto fino a dopo la fine dell'esecuzione (non è questione di usare lo sleep o di aspettare in qualsiasi momento, controlla tu stesso ma ricorda di riavviare il kernel ed esegui tutti i test)

Sulla base delle soluzioni precedenti, questo evita di utilizzare la magia %% nel caso in cui sia necessario inserirla nel mezzo di un altro codice:

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

Per python 3, quanto segue basato sulla risposta di @Iguananaut e aggiornato per l'ultimo python e possibilmente più server funzioneranno:

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}]')
    
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.