<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Customizing IPython - Magics
IPython extends Python by adding shell-like commands called magics.
Step2:  Defining your own magic
As we have seen already, IPython has cell and line magics. You can define your own magics using any Python function and the register_magic_function method
Step3:  Exercise
Define %tic and %toc magics, which can be use for simple timings, e.g. where
python
for p in range(1,4)
Step6:  Cell Magic
Cell magics take two args
Step7:  Excercise
Can you write and register a cell magic that automates the outer iteration,
timing a block for various values of a particular variable
Step9:  Executing Notebooks
We can load a notebook into memory using IPython.nbformat.
Step10:  A notebook is just a dictionary with attribute access for convenience.
Step11:  We can see all the cells and their type
Step12:  Now I can run all of the code cells with get_ipython().run_cell
Step13:  And we can now use the function that was defined in that notebook
Step14:  Exercise
Can you write and register an %nbrun line magic to run a notebook?
python
%nbrun Sample
<ASSISTANT_TASK:>
Python Code:
%lsmagic
import numpy
%timeit A=numpy.random.random((1000,1000))
%%timeit -n 1
A=numpy.random.random((1000,1000))
b = A.sum()
Explanation: Customizing IPython - Magics
IPython extends Python by adding shell-like commands called magics.
End of explanation
ip = get_ipython()
import time
def sleep_magic(line):
    A simple function for sleeping
    t = float(line)
    time.sleep(t)
ip.register_magic_function?
ip.register_magic_function(sleep_magic, "line", "sleep")
%sleep 2
%sleep?
Explanation: Defining your own magic
As we have seen already, IPython has cell and line magics. You can define your own magics using any Python function and the register_magic_function method:
End of explanation
%load soln/tictocf.py
import numpy as np
import sys
for p in range(1,4):
    N = 10**p
    print("N=%i" % N)
    sys.stdout.flush()
    %tic
    A = np.random.random((N,N))
    np.linalg.eigvals(A)
    %toc
Explanation: Exercise
Define %tic and %toc magics, which can be use for simple timings, e.g. where
python
for p in range(1,4):
    N = 10**p
    print "N=%i" % N
    %tic
    A = np.random.random((N,N))
    np.linalg.eigvals(A)
    %toc
each %toc will print the time since the last %tic. Create separate tic and toc functions that read and write
a global time variable.
End of explanation
def dummy_cell_magic(line, cell):
    dummy cell magic for displaying the line and cell it is passed
    print("line: %r" % line)
    print("cell: %r" % cell)
ip.register_magic_function(dummy_cell_magic, "cell", "dummy")
%%dummy this is the line
this
is the
cell
def parse_magic_line(line):
    parse a magic line into a name and eval'd expression
    name, values_s = line.split(None, 1)
    values = eval(values_s, get_ipython().user_ns)
    return name, values
parse_magic_line("x range(5)")
Explanation: Cell Magic
Cell magics take two args:
the line on the same line of the magic 
the cell the multiline body of the cell after the first line
End of explanation
%load soln/scalemagic.py
%%scale N [ int(10**p) for p in range(1,4) ]
A = np.random.random((N,N))
np.linalg.eigvals(A)
%%scale N [ int(2**p) for p in np.linspace(6, 11, 11) ]
A = np.random.random((N,N))
np.linalg.eigvals(A)
Explanation: Excercise
Can you write and register a cell magic that automates the outer iteration,
timing a block for various values of a particular variable:
End of explanation
import io
import os
import IPython.nbformat as nbf
def load_notebook(filename):
    load a notebook object from a filename
    if not os.path.exists(filename) and not filename.endswith(".ipynb"):
        filename = filename + ".ipynb"
    with io.open(filename) as f:
        return nbf.read(f, as_version=4)
nb = load_notebook("_Sample")
Explanation: Executing Notebooks
We can load a notebook into memory using IPython.nbformat.
End of explanation
nb.keys()
cells = nb.cells
cells
Explanation: A notebook is just a dictionary with attribute access for convenience.
End of explanation
for cell in cells:
    print()
    print('----- %s -----' % cell.cell_type)
    print(cell.source)
Explanation: We can see all the cells and their type
End of explanation
for cell in cells:
    ip = get_ipython()
    if cell.cell_type == 'code':
        ip.run_cell(cell.source, silent=True)
Explanation: Now I can run all of the code cells with get_ipython().run_cell
End of explanation
nb_info(nb)
Explanation: And we can now use the function that was defined in that notebook:
End of explanation
%load soln/nbrun.py
%nbrun _Sample
Explanation: Exercise
Can you write and register an %nbrun line magic to run a notebook?
python
%nbrun Sample
End of explanation
<END_TASK>