Executing Code

In this notebook we'll look at some of the issues surrounding executing code in the notebook.

Backtraces

When you interrupt a computation, or if an exception is raised but not caught, you will see a backtrace of what was happening when the program halted. The backtrace is color highlighted to help you find the information you need to debug the problem.


In [1]:
def f(x):
    return 1.0 / x

def g(x):
    return x - 1.0

f(g(1.0))


---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-1-2383327a1aba> in <module>()
      5     return x - 1.0
      6 
----> 7 f(g(1.0))

<ipython-input-1-2383327a1aba> in f(x)
      1 def f(x):
----> 2     return 1.0 / x
      3 
      4 def g(x):
      5     return x - 1.0

ZeroDivisionError: float division by zero

Python Debugging

You can also turn on the Python debugger inside a notebook using the magic invocation %pdb on. When an exception occurs, the debugger will activate inside the output cell. You can then type commands and see responses from the stopped state of the program.

Some commands:

  • h help
  • w print stack trace
  • p expr print expressions
  • q quit
  • r restart

Full documentation on the debugger can be found at Python debugger pdb.


In [2]:
%pdb on

f(g(1.0))


Automatic pdb calling has been turned ON
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-2-5cc466c822a2> in <module>()
      1 get_ipython().magic(u'pdb on')
      2 
----> 3 f(g(1.0))

<ipython-input-1-2383327a1aba> in f(x)
      1 def f(x):
----> 2     return 1.0 / x
      3 
      4 def g(x):
      5     return x - 1.0

ZeroDivisionError: float division by zero
> <ipython-input-1-2383327a1aba>(2)f()
      1 def f(x):
----> 2     return 1.0 / x
      3 

ipdb> q

Output

Normal output is shown after the In[] area. Output written to stdout is shown in one color, while output written to stderr is shown with a red background.


In [3]:
import sys
print('Hello, world!')
sys.stdout.write('We meet again, stdout.')
sys.stderr.write('Error, you appear to have created a black hole.')


Hello, world!
We meet again, stdout.
Error, you appear to have created a black hole.

Asynchronous Output

Output written to stdout and stderr shows up immediately in the notebook, you don't have to wait for the evaluation to finish before you see anything. Here is demo.


In [4]:
import time
for i in range(10):
    print(i)
    time.sleep(0.5)


0
1
2
3
4
5
6
7
8
9

Threads

You can start multiple threads and use the standard Python threading libraries such as threads and threading to coordinate between them.

Note that because of the global interpreter lock in CPython two threads with work to do will never run at the same time.


In [5]:
import threading

class SummingThread(threading.Thread):
     def __init__(self, low, high):
         super(SummingThread, self).__init__()
         self.low = low
         self.high = high
         self.total = 0

     def run(self):
         for i in range(self.low, self.high):
             self.total += i

def sequential_sum(n):
    total = 0
    for i in range(0, n):
        total += i
    return total
            
def parallel_sum(n):
    thread1 = SummingThread(0, n/2)
    thread2 = SummingThread(n/2, n)
    thread1.start()
    thread2.start()
    thread1.join()
    thread2.join()
    return thread1.total + thread2.total

%timeit sequential_sum(100000)
%timeit parallel_sum(1000000)


100 loops, best of 3: 6.68 ms per loop
1 loops, best of 3: 263 ms per loop

Multiprocessing

It is possible to use the multiprocessing library inside Pineapple notebooks. The multiprocessing library spawns multiple interpreters which can actually run in parallel. Of course this is still no guarantee of higher performance.


In [ ]:
from time import sleep
from multiprocessing import Pool

def f(p):
    low, high = p
    total = 0
    for i in range(low, high):
        total += i
    return total

def sequential_sum(n):
    total = 0
    for i in range(0, n):
        total += i
    return total

def parallel_sum(n):
    p = Pool(2)
    results = p.map(f, [[0, n/2], [n/2, n]])
    return results[0] + results[1]

if __name__ == "__main__":
    %timeit sequential_sum(1000)
    %timeit parallel_sum(1000)