Wie erhalte ich den aktuellen IPython / Jupyter Notebook-Namen?

85

Ich versuche, den aktuellen NoteBook-Namen zu erhalten, wenn ich das IPython-Notebook ausführe. Ich weiß, dass ich es oben auf dem Notizbuch sehen kann. Was ich nach so etwas bin

currentNotebook = IPython.foo.bar.notebookname()

Ich muss den Namen in einer Variablen erhalten.

Tooblippe
quelle
Was versuchst du damit zu machen? Der Kernel (das Bit, mit dem Code ausgeführt wird) kennt das Frontend (das Bit, mit dem Notebooks geöffnet werden) nicht.
Thomas K
7
Hallo, ich möchte es mit nbconvert verwenden, um den Erstellungsprozess von Notebook zu Latex / PDF zu automatisieren. Meine Notebooks laufen remote. Nach dem Unterricht können die Schüler eine PDF-Version ihrer Ergebnisse herunterladen.
Tooblippe
1
Die Antwort von P.Toccaceli funktioniert gut mit neueren Versionen von JupyterLab (1.1.4) (Notebook 5.6.0) und erfordert kein Javascript.
Joelostblom
Einige haben die Arbeit gemacht und ein Pip-Paket erstellt: pypi.org/project/ipynbname install bypip install ipynbname
NeoTT vor

Antworten:

24

Wie bereits erwähnt, solltest du das wahrscheinlich nicht wirklich können, aber ich habe einen Weg gefunden. Es ist ein flammender Hack, also verlassen Sie sich überhaupt nicht darauf:

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

Ich habe meine Antwort aktualisiert, um eine Lösung aufzunehmen, die in IPython 2.0 zumindest mit einem einfachen Test "funktioniert". Es ist wahrscheinlich nicht garantiert, dass die richtige Antwort gegeben wird, wenn mehrere Notebooks mit demselben Kernel usw. verbunden sind.

Leguananaut
quelle
connection_file_path = kernel.get_connection_file () funktioniert nicht mehr, Dateiname ist arg erforderlich.
Purrell
2
Einige Updates: Statt from IPython.lib import kerneljetzt ist es nur from IPython import kernel. Anstatt den Schlüssel 'Name' in den Wörterbüchern zu verwenden, verwenden Sie auch den Schlüssel 'Pfad'
Tristan Reid
1
Wie vom Antwortenden selbst angekündigt, funktioniert diese Antwort für das neueste IPython nicht. Ich habe eine Version erstellt, die mit IPython 4.2.0 in Python 3.5 zu funktionieren scheint: gist.github.com/mbdevpl/f97205b73610dd30254652e7817f99cb
mbdevpl
1
Ab Version 4.3.0 müssen Sie ein Authentifizierungstoken bereitstellen. Dies kann mit abgerufen werden notebook.notebookapp.list_running_servers().
Yingted
1
Wenn mehrere Server ausgeführt werden, können Sie überprüfen, welchen Port der übergeordnete Prozess des Kernels überwacht. Auf diesem Server sollte angegeben werden, zu welchem ​​Server eine Verbindung hergestellt werden soll (oder Sie können einfach eine Verbindung zu jedem lokalen Jupyter-Server herstellen und überprüfen, auf welchem ​​Server Ihr Kernel ausgeführt wird).
Yingted
40

Ich habe die folgenden, die mit IPython 2.0 funktioniert. Ich habe festgestellt, dass der Name des Notizbuchs als Wert des Attributs 'data-notebook-name'im <body>Tag der Seite gespeichert ist . Daher besteht die Idee darin, zuerst Javascript zu bitten, das Attribut abzurufen - Javascripts können dank der %%javascriptMagie aus einer Codezelle aufgerufen werden . Anschließend können Sie über einen Aufruf des Python-Kernels mit einem Befehl, der eine Python-Variable festlegt, auf die Javascript-Variable zugreifen. Da diese letzte Variable aus dem Kernel bekannt ist, kann auch in anderen Zellen darauf zugegriffen werden.

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

Aus einer Python-Codezelle

print(theNotebook)

Out []: HowToGetTheNameOfTheNoteBook.ipynb

Ein Fehler in dieser Lösung besteht darin, dass, wenn man den Titel (Namen) eines Notizbuchs ändert, dieser Name nicht sofort aktualisiert zu werden scheint (es gibt wahrscheinlich eine Art Cache) und das Notizbuch neu geladen werden muss, um Zugriff auf das zu erhalten neuer Name.

[Bearbeiten] Bei der Reflexion besteht eine effizientere Lösung darin, anstelle des <body>Tags nach dem Eingabefeld für den Namen des Notebooks zu suchen . Wenn Sie sich die Quelle ansehen, scheint dieses Feld die ID "notebook_name" zu haben. Es ist dann möglich, diesen Wert mit a zu erfassen document.getElementById()und dann den gleichen Ansatz wie oben zu verfolgen. Der Code wird, immer noch mit der Javascript-Magie

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

Dann aus einer Ipython-Zelle,

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

Im Gegensatz zur ersten Lösung werden Änderungen am Namen des Notebooks sofort aktualisiert, und das Notebook muss nicht aktualisiert werden.

jfb
quelle
Vielleicht habe ich etwas verpasst, aber wie ruft man den Javascript-Code von Python auf?
Artjom B.
7
Es ist auch möglich, das Javascript aus Python heraus mit der Anzeigemethode aufzurufen, die auf ein Javascript-Objekt wiedef getname(): display(Javascript('IPython.notebook.kernel.execute("theNotebook = " + "\'"+IPython.notebook.notebook_name+"\'");'))
Jakob
Wie ändere ich dies, um den Pfad des Notebooks abzurufen?
Pedro M Duarte
@PedroMDuarte: Sie können IPython.notebook.notebook_path in Javascript für 'thename' im obigen Skript verwenden, um diesen Wert zu erhalten.
Tristan Reid
1
So erhalten Sie den Notizbuchpfad ohne JS-Tricks:globals()['_dh'][0]
Keim
38

Hinzufügen zu früheren Antworten,

Um den Namen des Notizbuchs zu erhalten, führen Sie Folgendes in einer Zelle aus:

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

Dadurch erhalten Sie den Dateinamen in nb_name

Um den vollständigen Pfad zu erhalten, können Sie Folgendes in einer separaten Zelle verwenden:

import os
nb_full_path = os.path.join(os.getcwd(), nb_name)
Mahmoud Elagdar
quelle
1
Unter Verwendung IPython.notebook.notebook_namedieser kann mit Hilfe getan%%javascript IPython.notebook.kernel.execute('notebookName = ' + '"' + IPython.notebook.notebook_name + '"')
jfb
9
Aus irgendeinem Grund funktioniert dies nur, wenn ich die Javascript-Zelle "manuell" ausführe. Wenn ich das vollständige Notizbuch starte, schlägt die zweite Zelle fehl. Irgendeine Idee warum?
Pierre-Antoine
Ich denke aus irgendeinem Grund, wenn eine Variable von Javascript geändert wird und dann von reinem Python im selben Aufruf zugegriffen wird, sieht die Python-Version das Update nicht und ersetzt auch die Javascript-Version. Sie können also die Javascript-Zelle nach oben verschieben, ausführen und dann "Zelle> Alle unten ausführen" verwenden.
Mahmoud Elagdar
2
Warum brauchen wir eigentlich Javascript? nichts einheimischer?
Matanster
1
Schlägt auf Jupyter Lab Javascript Error: IPython is not defined
fehl
27

Unter Jupyter 3.0 funktioniert Folgendes. Hier zeige ich den gesamten Pfad auf dem Jupyter-Server, nicht nur den Namen des Notebooks:

So speichern Sie das NOTEBOOK_FULL_PATHauf dem aktuellen Notebook-Frontend:

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

So zeigen Sie es an:

print("NOTEBOOK_FULL_PATH:\n", NOTEBOOK_FULL_PATH)

Das Ausführen der ersten Javascript- Zelle erzeugt keine Ausgabe. Das Ausführen der zweiten Python- Zelle erzeugt Folgendes:

NOTEBOOK_FULL_PATH:
 /user/zeph/GetNotebookName.ipynb
Zephaniah Grunschlag
quelle
4
Das ist sehr sauber. Wie würden Sie dann den Javascript-Code von einer Python-Funktion aus aufrufen?
Lukas
Hmmmm ... vielleicht sollten Sie in diesem Fall den Port mit einem Doppelpunkt gefolgt von der Portnummer versehen?
Zephaniah Grunschlag
3
Dies ist relativer Pfad nicht vollständiger Pfad
Ivelin
Dies beinhaltet auch nicht die Einstellung von c.NotebookApp.notebook_dir.
Sappjw
4
Ich bekomme Javascript Error: IPython is not defined. Wie kann ich IPython für Javascript laden
zozo
25

Es scheint, dass ich keinen Kommentar abgeben kann, daher muss ich dies als Antwort posten.

Die von @iguananaut akzeptierte Lösung und das Update von @mbdevpl scheinen mit neueren Versionen des Notebooks nicht zu funktionieren. Ich habe es wie unten gezeigt behoben. Ich habe es auf Python v3.6.1 + Notebook v5.0.0 und auf Python v3.6.5 und Notebook v5.5.0 überprüft.

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

Wie in der Dokumentzeichenfolge angegeben, funktioniert dies nur, wenn entweder keine Authentifizierung vorliegt oder die Authentifizierung tokenbasiert ist.

Beachten Sie, dass die Javascript-basierte Methode, wie auch von anderen berichtet, nicht zu funktionieren scheint, wenn "Alle Zellen ausführen" ausgeführt wird (funktioniert jedoch, wenn Zellen "manuell" ausgeführt werden), was für mich ein Deal-Breaker war.

P. Toccaceli
quelle
Gibt es dafür eine Bibliothek?
Matanster
Das Versagen von Javascript-Methoden war auch für mich ein Deal-Breaker. Vielen Dank für die Veröffentlichung dieser Alternative!
Kaugummi
Ich muss srv ['notebook_dir'] durch jupyter_core.paths ersetzen. Import jupyter_config_dir; from traitlets.config import Config; c = Config (); file_path = os.path.join (jupyter_config_dir (), 'jupyter_notebook_config.py'); exec (open (Dateipfad) .read ()); root_dir = c ['FileContentsManager'] ['root_dir']
Dave Babbitt
14

Das ipyparams- Paket kann dies ziemlich einfach tun.

import ipyparams
currentNotebook = ipyparams.notebook_name
Rechnung
quelle
Dies scheint eine bessere Antwort zu sein als die oben akzeptierte.
Alejandro
1

Angenommen, Sie haben den Host, den Port und das Authentifizierungstoken des Jupyter Notebook-Servers, sollte dies für Sie funktionieren. Es basiert auf dieser Antwort .

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

Anwendungsbeispiel:

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

Noch eine hackige Lösung, da sich mein Notebook-Server ändern kann. Grundsätzlich drucken Sie eine zufällige Zeichenfolge, speichern sie und suchen dann im Arbeitsverzeichnis nach einer Datei, die diese Zeichenfolge enthält. Die Weile wird benötigt, da save_checkpoint asynchron ist.

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
quelle
0

Alle Json-basierten Lösungen schlagen fehl, wenn mehr als eine Zelle gleichzeitig ausgeführt wird, da das Ergebnis erst nach dem Ende der Ausführung verfügbar ist (es geht nicht darum, den Ruhezustand zu verwenden oder zu warten, überprüfen Sie es selbst, aber denken Sie daran, den Kernel neu zu starten und alle Tests ausführen)

Basierend auf früheren Lösungen wird die Verwendung der %% -Magie vermieden, falls Sie sie in die Mitte eines anderen Codes stellen müssen:

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

Für Python 3 funktioniert Folgendes, basierend auf der Antwort von @Iguananaut und aktualisiert für das neueste Python und möglicherweise mehrere Server:

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
quelle