Como obtenho o nome do Notebook IPython / Jupyter atual

87

Estou tentando obter o nome do Bloco de notas atual ao executar o bloco de notas IPython. Eu sei que posso ver na parte superior do caderno. O que eu estou atrás de algo como

currentNotebook = IPython.foo.bar.notebookname()

Preciso colocar o nome em uma variável.

Tooblippe
fonte
O que você está tentando fazer com isso? Por design, o kernel (o bit que executa o código) não conhece o front-end (o bit que abre notebooks).
Thomas K
7
Olá, quero usá-lo com o nbconvert para automatizar o processo de criação do notebook para latex / pdf. Meus notebooks são executados remotamente. depois de uma aula, os alunos podem baixar uma versão em pdf de seus resultados.
Tooblippe
1
A resposta de P.Toccaceli funciona bem com versões recentes do JupyterLab (1.1.4) (notebook 5.6.0) e não requer javascript.
joelostblom
Alguns fizeram o trabalho e criaram um pacote pip: pypi.org/project/ipynbname install bypip install ipynbname
NeoTT

Respostas:

26

Como já mencionei, provavelmente você não deveria ser capaz de fazer isso, mas encontrei uma maneira. É um hack inflamado, então não confie nisso:

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

Atualizei minha resposta para incluir uma solução que "funciona" no IPython 2.0 pelo menos com um teste simples. Provavelmente não é garantido que a resposta correta seja dada se houver vários notebooks conectados ao mesmo kernel, etc.

Iguananaut
fonte
connection_file_path = kernel.get_connection_file () não funciona mais, o nome do arquivo é necessário arg.
Purrell
2
Algumas atualizações: Em vez de from IPython.lib import kernelagora é só from IPython import kernel. Além disso, em vez de usar a chave 'nome' nos dicionários, use a chave 'caminho'
Tristan Reid
1
Conforme anunciado pelo próprio respondente, esta resposta não funciona para o IPython mais recente. Eu criei uma versão que parece funcionar com IPython 4.2.0 em Python 3.5: gist.github.com/mbdevpl/f97205b73610dd30254652e7817f99cb
mbdevpl
1
A partir da versão 4.3.0, você precisa fornecer um token de autenticação. Isso pode ser recuperado usando notebook.notebookapp.list_running_servers().
lançado em
1
Se você tiver vários servidores em execução, poderá verificar em qual porta o processo pai do kernel está escutando, o que deve informar a qual servidor se conectar (ou você pode apenas se conectar a cada servidor Jupyter local e verificar qual está executando o kernel).
yingted de
41

Eu tenho o seguinte que funciona com IPython 2.0. Observei que o nome do notebook fica armazenado como o valor do atributo 'data-notebook-name'na <body>tag da página. Portanto, a ideia é primeiro pedir ao Javascript para recuperar o atributo - os javascripts podem ser invocados a partir de um codecell graças à %%javascriptmagia. Então é possível acessar a variável Javascript através de uma chamada ao Kernel Python, com um comando que define uma variável Python. Como esta última variável é conhecida no kernel, ela pode ser acessada em outras células também.

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

De uma célula de código Python

print(theNotebook)

Out []: HowToGetTheNameOfTheNoteBook.ipynb

Um defeito desta solução é que quando alguém muda o título (nome) de um notebook, então este nome parece não ser atualizado imediatamente (provavelmente existe algum tipo de cache) e é necessário recarregar o notebook para ter acesso ao novo nome.

[Editar] Pensando bem, uma solução mais eficiente é procurar o campo de entrada para o nome do caderno em vez da <body>tag. Olhando para a fonte, parece que este campo tem id "notebook_name". Então, é possível capturar esse valor por a document.getElementById()e seguir a mesma abordagem acima. O código se torna, ainda usando a magia do javascript

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

Então, a partir de uma célula ipython,

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

Ao contrário da primeira solução, as modificações do nome do notebook são atualizadas imediatamente e não há necessidade de atualizar o notebook.

jfb
fonte
Talvez eu tenha esquecido algo, mas como você invoca o código javascript do python?
Artjom B.
7
Também é possível chamar o javascript de dentro do python usando o método de exibição aplicado a um objeto javascript comodef getname(): display(Javascript('IPython.notebook.kernel.execute("theNotebook = " + "\'"+IPython.notebook.notebook_name+"\'");'))
Jakob
Como modifico isso para obter o caminho do notebook?
Pedro M Duarte
@PedroMDuarte: Você pode usar IPython.notebook.notebook_path em javascript para 'thename' no script acima para obter esse valor.
Tristan Reid
1
Para obter o caminho do notebook sem truques de JS:globals()['_dh'][0]
germe
38

adicionando às respostas anteriores,

para obter o nome do notebook, execute o seguinte em uma célula:

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

isso dá a você o nome do arquivo em nb_name

então, para obter o caminho completo, você pode usar o seguinte em uma célula separada:

import os
nb_full_path = os.path.join(os.getcwd(), nb_name)
Mahmoud Elagdar
fonte
1
Usar IPython.notebook.notebook_nameisso pode ser feito usando%%javascript IPython.notebook.kernel.execute('notebookName = ' + '"' + IPython.notebook.notebook_name + '"')
jfb 01 de
10
Por alguma razão, isso só funcionará se eu executar a célula javascript "manualmente". Se eu executar o notebook completo, a segunda célula falha. Alguma ideia do porquê?
Pierre-Antoine de
Eu acho que por algum motivo, se uma variável for modificada do javascript e acessada do python puro na mesma chamada, a versão do python não verá a atualização e também substituirá a versão do javascript. Então, acho que você pode mover a célula javascript para o topo, executá-la e usar "Cell> Run All Bellow".
Mahmoud Elagdar
2
Por que realmente precisamos de javascript? nada mais nativo?
matanster
2
Falha no Jupyter Lab:Javascript Error: IPython is not defined
magicrebirth
27

No Jupyter 3.0 funciona o seguinte. Aqui, estou mostrando todo o caminho no servidor Jupyter, não apenas o nome do notebook:

Para armazenar NOTEBOOK_FULL_PATHno front-end do notebook atual:

%%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 então exibi-lo:

print("NOTEBOOK_FULL_PATH:\n", NOTEBOOK_FULL_PATH)

A execução da primeira célula Javascript não produz saída. Executar a segunda célula Python produz algo como:

NOTEBOOK_FULL_PATH:
 /user/zeph/GetNotebookName.ipynb
Zephaniah Grunschlag
fonte
4
Isso é muito limpo. Como você chamaria o código Javascript de uma função Python então?
Lukas
Hmmmm ... talvez nesse caso você deva acrescentar à porta dois pontos seguido pelo número da porta?
Zephaniah Grunschlag 01 de
3
Este é o caminho relativo, não o caminho completo
Ivelin
Isso também não inclui a configuração de c.NotebookApp.notebook_dir.
sappjw
4
Eu estou conseguindo Javascript Error: IPython is not defined. Como posso carregar IPython para javascript
zozo
26

Parece que não posso comentar, então tenho que postar isso como uma resposta.

A solução aceita por @iguananaut e a atualização por @mbdevpl parecem não estar funcionando com as versões recentes do Notebook. Eu consertei como mostrado abaixo. Eu verifiquei no Python v3.6.1 + Notebook v5.0.0 e no 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

Conforme declarado na docstring, isso funciona apenas quando não há autenticação ou a autenticação é baseada em token.

Observe que, como também relatado por outros, o método baseado em Javascript não parece funcionar ao executar um "Executar todas as células" (mas funciona ao executar células "manualmente"), o que foi um obstáculo para mim.

P.Toccaceli
fonte
Existe alguma biblioteca para isso?
matanster
O fracasso dos métodos Javascript também foi um obstáculo para mim. Obrigado por postar esta alternativa!
bom senso
Tenho que substituir srv ['notebook_dir'] com from jupyter_core.paths import jupyter_config_dir; from traitlets.config import Config; c = Config (); file_path = os.path.join (jupyter_config_dir (), 'jupyter_notebook_config.py'); exec (abrir (caminho_de_arquivo) .read ()); root_dir = c ['FileContentsManager'] ['root_dir']
Dave Babbitt
15

O pacote ipyparams pode fazer isso facilmente.

import ipyparams
currentNotebook = ipyparams.notebook_name
Conta
fonte
1
Esta parece uma resposta melhor do que a aceita no topo.
alejandro
1

Supondo que você tenha o host, a porta e o token de autenticação do servidor do Notebook Jupyter, isso deve funcionar para você. É baseado nesta resposta .

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

Exemplo de uso:

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

Mais uma solução hacky, já que meu servidor de notebook pode mudar. Basicamente, você imprime uma string aleatória, salva-a e, a seguir, procura um arquivo que contém essa string no diretório de trabalho. O while é necessário porque save_checkpoint é assíncrono.

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

Todas as soluções baseadas em Json falham se executarmos mais de uma célula ao mesmo tempo porque o resultado não estará pronto até depois do final da execução (não é uma questão de usar o sono ou esperar qualquer momento, verifique você mesmo, mas lembre-se de reiniciar o kernel e execute todos os testes)

Com base em soluções anteriores, isso evita usar a magia %% caso você precise colocá-la no meio de algum outro 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 o python 3, o seguinte com base na resposta de @Iguananaut e atualizado para o python mais recente e possivelmente vários 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
fonte