Save a copy of a notebook from within the notebook itself - jupyter-notebook

I would like to save a copy of a notebook (or rename it) from within a cell of the notebook.
Preferably without too much JavaScript. Actually I guess something of this form should work
from IPython.display import display_html
display_html("script>Jupyter....???...()</script>")

Here is a solution only in Python. The notebook_path function comes from P.Toccaceli's solution on How do I get the current IPython Notebook name.
from notebook import notebookapp
import urllib
import json
import os
import ipykernel
from shutil import copy2
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
def copy_current_nb(new_name):
nb = notebook_path()
if nb:
new_path = os.path.join(os.path.dirname(nb), new_name+'.ipynb')
copy2(nb, new_path)
else:
print("Current notebook path cannot be determined.")
Then, simply use copy_current_nb('Save1') to create a copy named Save1.ipynb in the same directory.

Related

How to override getpass function in a jupyter notebook for testing?

I'm trying to test a jupyter notebook with nbval.
py.test --nbval ~/MyNotebook.ipynb
Howver, the notebook has a getpass() function in it. This blocks the test:
========================================================================================= FAILURES =========================================================================================
__________________________________________________________________________________ Untitled.ipynb::Cell 1 __________________________________________________________________________________
Notebook cell execution failed
Cell 1: Cell execution caused an exception
Input:
getpass.getpass()
Traceback:
---------------------------------------------------------------------------
StdinNotImplementedError Traceback (most recent call last)
<ipython-input-2-b4e90adc512e> in <module>
----> 1 getpass.getpass()
/opt/miniconda/lib/python3.8/site-packages/ipykernel/kernelbase.py in getpass(self, prompt, stream)
834 """
835 if not self._allow_stdin:
--> 836 raise StdinNotImplementedError(
837 "getpass was called, but this frontend does not support input requests."
838 )
StdinNotImplementedError: getpass was called, but this frontend does not support input requests.
I've tried to override the function, however it still prompts for input:
How can I override the getpass() function or provide Stdin so that it doesn't block for user input?
What you tried would seem work, here it is in the Python interpreter
>>> import getpass
>>> getpass.getpass = lambda: "abc"
>>> getpass.getpass()
'abc'
However, on Jupyter, IPython overrides getpass between blocks.
The solution is either to monkeypatch at the start of every block, or monkeypatch IPython directly:
The IPython version of getpass is defined here and here, I hacked accordingly.
import IPython
from ipykernel.kernelapp import IPythonKernel
import builtins
# Modified version of original function
def _forward_input_hacked(self, allow_stdin=False):
"""Forward raw_input and getpass to the current frontend.
via input_request (hacked)
"""
self._allow_stdin = allow_stdin
self._sys_raw_input = builtins.input
builtins.input = self.raw_input
self._save_getpass = getpass.getpass
getpass.getpass = lambda: "xyz"
#getpass.getpass = self.getpass
ipyk = IPython.Application.instance().kernel
ipyk._forward_input = _forward_input_hacked.__get__(ipyk, IPythonKernel)
import getpass
This should persist across blocks.
Note: a previous version of my answer did not answer the question properly, this monkeypatch should do the trick.

Jupyter (Lab): Browsing the remote file system inside a notebook

I have several Jupyter notebooks which perform analysis on datasets. Right now, a dataset is specified by its filename. Every time the user wants to perform analysis on a new dataset, she/he has to edit the appropriate line in the notebook and modify dataset path string. The datasets can be located in different directories. The notebooks can also be located in different directories. In each notebook I would like to provide a widget that allows the user to browse the remote file system and pick the dataset he/she wants to analyse.
Are there any open source projects that support the above functionality? I am looking for something that is still active/supported and has some basic documentation. I did quick search on Google and surprisingly I didn't find anything.
Then I realised that JupyterLab, the evolution of Jupyter, has something very similar to what I want. It already has a very capable file browser but it is a bit "isolated" from everything else.
Is it possible somehow to get the relative (to the currently opened notebook) path of the selected file in the JupyterLab file browser?
Thank you.
Here's code for a server-side file browsing widget. Only tested in regular Jypter notebook - not Jupyter Lab. Also, must use a fairly recent version. Hope this helps.
import sys
import os
import ipywidgets as ui
from IPython.display import display
class PathSelector():
def __init__(self,start_dir,select_file=True):
self.file = None
self.select_file = select_file
self.cwd = start_dir
self.select = ui.SelectMultiple(options=['init'],value=(),rows=10,description='')
self.accord = ui.Accordion(children=[self.select])
self.accord.selected_index = None # Start closed (showing path only)
self.refresh(self.cwd)
self.select.observe(self.on_update,'value')
def on_update(self,change):
if len(change['new']) > 0:
self.refresh(change['new'][0])
def refresh(self,item):
path = os.path.abspath(os.path.join(self.cwd,item))
if os.path.isfile(path):
if self.select_file:
self.accord.set_title(0,path)
self.file = path
self.accord.selected_index = None
else:
self.select.value = ()
else: # os.path.isdir(path)
self.file = None
self.cwd = path
# Build list of files and dirs
keys = ['[..]'];
for item in os.listdir(path):
if item[0] == '.':
continue
elif os.path.isdir(os.path.join(path,item)):
keys.append('['+item+']');
else:
keys.append(item);
# Sort and create list of output values
keys.sort(key=str.lower)
vals = []
for k in keys:
if k[0] == '[':
vals.append(k[1:-1]) # strip off brackets
else:
vals.append(k)
# Update widget
self.accord.set_title(0,path)
self.select.options = list(zip(keys,vals))
with self.select.hold_trait_notifications():
self.select.value = ()
f = PathSelector('/some/data')
display(f.accord)

bokeh development workflow with interaction

I'm developing a visualization using
% bokeh serve --show myapp.py
The problem is that when I change myapp.py I have to kill the above command and restart it. Is there a better workflow for this kind of development?
Thanks!
Not yet. As of Bokeh 0.11.1 (and soon to be 0.12 too), this is a planned, but still open, feature request There are only a few folks working a huge pile of work for Bokeh. New contributors can help accelerate new features and fixes. If you're able to help, please reach out to us on the project gitter channel.
I was unable to understand enough of the bokeh internals to make this work nicer, but here is a hacky script that does what I want anyway.
# bokeh_watcher.py
#
# Watches specific files in directory and restarts bokeh server upon change.
#
# % python bokeh_watcher filename.py
#
# Note that you stil have to navigate your browser to localhost:5006/filename
# to see your Bokeh visualization and you might have to refresh the browser.
import sys
import time
import logging
from watchdog.observers import Observer
from watchdog.events import RegexMatchingEventHandler
from bokeh.command.bootstrap import main
import multiprocessing
import os
JOBS = []
FILE = []
def spawn_bokeh(args):
main(args)
class BokehHandler(RegexMatchingEventHandler):
'''
kills and restarts bokeh server upon filechange.
'''
def on_modified(self, event):
super(BokehHandler, self).on_modified(event)
what = 'directory' if event.is_directory else 'file'
logging.info("Modified %s: %s"% (what, event.src_path))
p=JOBS.pop()
p.terminate()
time.sleep(1) # time to die
logging.info('terminated')
logging.info('initiating restart')
p = multiprocessing.Process(target=spawn_bokeh,
args=(self.args,))
p.start()
JOBS.append(p)
if __name__ == "__main__":
here = os.path.realpath(__file__)
fullpathname=os.path.dirname(here)+os.sep+sys.argv[1]
# local logger
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
filemod_handler = BokehHandler(['.*%s'%(sys.argv[1])])
filemod_handler.args = ['','serve',fullpathname, '--log-level','info']
# fire up bokeh server
p = multiprocessing.Process(target=spawn_bokeh,args=(filemod_handler.args,))
p.start()
# store object in global for later
JOBS.append(p)
observer = Observer()
observer.schedule(filemod_handler, '.', recursive=False)
observer.start()
try:
while True:
time.sleep(3)
except KeyboardInterrupt:
observer.stop()
observer.join()

ipython notebook: custom cells and execute hook

I would like to override what happens when run is pressed for certain cells in ipython notebook.
For example, I would like to be able to write an SQL query directly in a cell and define a function that processes it.
It seems it should be possible to do this as with ipython-notebook extensions. Does anyone know of a similar extension? An easy way to do this directly from ipython?
Ideally this would involve defining a custom cell type, but I would be happy to use special tags to separate the usual python code from, say, a custom SQL query cell.
I've once had the similar desire and I ended up with the following solution:
from sqlalchemy import create_engine
import pandas as pd
from IPython.core.magic import register_cell_magic
from IPython import get_ipython
con = create_engine(DB_URL)
#register_cell_magic
def sql(line, cell):
cell = cell.format(**globals())
if line.strip() != '-':
res = pd.read_sql(cell, con)
if line.strip() != '': get_ipython().user_ns[line.strip()] = res
return res
else:
con.execute(cell)
del sql
You can now write in other cells:
%sql outputvar
select * from whatever where ...
For example:

How can I set up default startup commands in iPython notebooks?

I want to put couple of cells with commands I need in almost every new notebook in every new notebook I create.
For example when I create a new notebook it should put a
%matplotlib inline
import matplotlib.pyplot as plt
in a cell by default but not execute it.
How could I set something like that up?
This will work for both terminal based IPython shell and Browser based Notebook:
Navigate to ~/.ipython/profile_default
Create a folder called startup if it’s not already there
Add a new Python file called start.py
Put your favorite imports (and custom functions may be) in this file
Launch IPython or a Jupyter Notebook and your favorite libraries will be automatically loaded every time!
Here is my sample for start.py:
Another Source
To define set of commands on default startup, you need to add the commands in the templete ipy_user_conf.py file in your ~/.ipython directory.
This module is imported during IPython startup. So you can easily do : import modules, configure extensions, change options, define magic commands, put variables and functions in the IPython namespace etc.
Here is the sample ipy_user_conf.py :
# Most of your config files and extensions will probably start
# with this import
import IPython.ipapi
ip = IPython.ipapi.get()
# You probably want to uncomment this if you did %upgrade -nolegacy
# import ipy_defaults
import os
def main():
#ip.dbg.debugmode = True
ip.dbg.debug_stack()
# uncomment if you want to get ipython -p sh behaviour
# without having to use command line switches
import ipy_profile_sh
import jobctrl
# Configure your favourite editor?
# Good idea e.g. for %edit os.path.isfile
#import ipy_editors
# Choose one of these:
#ipy_editors.scite()
#ipy_editors.scite('c:/opt/scite/scite.exe')
#ipy_editors.komodo()
#ipy_editors.idle()
# ... or many others, try 'ipy_editors??' after import to see them
# Or roll your own:
#ipy_editors.install_editor("c:/opt/jed +$line $file")
o = ip.options
# An example on how to set options
#o.autocall = 1
o.system_verbose = 0
#import_all("os sys")
#execf('~/_ipython/ns.py')
# -- prompt
# A different, more compact set of prompts from the default ones, that
# always show your current location in the filesystem:
#o.prompt_in1 = r'\C_LightBlue[\C_LightCyan\Y2\C_LightBlue]\C_Normal\n\C_Green|\#>'
#o.prompt_in2 = r'.\D: '
#o.prompt_out = r'[\#] '
# Try one of these color settings if you can't read the text easily
# autoexec is a list of IPython commands to execute on startup
#o.autoexec.append('%colors LightBG')
#o.autoexec.append('%colors NoColor')
o.autoexec.append('%colors Linux')
# some config helper functions you can use
def import_all(modules):
""" Usage: import_all("os sys") """
for m in modules.split():
ip.ex("from %s import *" % m)
def execf(fname):
""" Execute a file in user namespace """
ip.ex('execfile("%s")' % os.path.expanduser(fname))
main()
For more details, please refer the link : Customization of IPython.
I hope this is what you wanted to know.
JupyterLab
In a comment to one of the other answers, the OP pointed out the need to insert the actual code instead of having it load in the background. One way is to create a text keyboard shortcut by going to Settings -> Advanced settings editor -> JSON settings Editor and adding the following under User Preferences:
{
"shortcuts": [
{
"command": "apputils:run-first-enabled",
"selector": "body",
"keys": ["Alt I"],
"args": {
"commands": [
"console:replace-selection",
"fileeditor:replace-selection",
"notebook:replace-selection",
],
"args": {"text": "import pandas as pd\nimport altair as alt\n\n"}
}
}
]
}
This will insert the following snippet each time you press Alt + i in the notebook:
import pandas as pd
import altair as alt
# <-- Cursor placed here
More on text shortcuts in jupyterlab
IPython console
If you are interested in automatically importing commonly used libraries in the IPython console only so that they are there for interactive use, but not in the notebook to avoid issues with sharing notebooks lacking some imports, you can launch IPython like so (and set up an alias to not have to type this each time):
ipython -c "import pandas as pd; import numpy as np" -i
(This was what I was looking for when I originally found this question)

Resources