Skip to main content

Context manager for blocking cell execution within a Jupyter notebook

Project description

ipython_blocking

Binder

ipython_blocking is a context manager for capturing cell execution messages in a Jupyter notebook, along with magic commands %block and %blockrun for convenience. The primary use-case for blocking notebook execution is to wait for users to interact with ipywidgets and then reference the values entered in those Widgets.

Install

ipython_blocking is on PyPI, install with pip.

pip install ipython_blocking

Usage

Try out the demo notebooks in Binder to see ipython_blocking in action. The most common way to use ipython_blocking is with the %blockrun magic and running a notebook with "cell -> run all". %blockrun button stops the cell execution messages from the initial "cell -> run all", and attaches a "cell -> run all below" handler to the button so that a notebook can be run in a linear fashion without callback functions after a user has filled out other Widget values.

### cell #1
import ipywidgets as widgets
import ipython_blocking # enables %block and %blockrun magic

text = widgets.Text()
dropdown = widgets.Dropdown(options=['', 'foo', 'bar', 'baz'])
button = widgets.Button(description='Run')
box = widgets.VBox(children=[text, dropdown, button])
box

### cell #2
%blockrun button

### cell #3 -- doesn't execute until the 'Run' button is pressed
### This gives the user a chance to interact with the Text and Dropdown widgets
print(text.value)
print(dropdown.value)

(The dropdown menu doesn't appear in this .gif because its treated as a "separate window" in Windows screen capture, sorry!)

CaptureExecution

The way ipython_blocking "blocks" cell execution is by creating a context manager that changes the behavior of the IPython.shell.kernel['execute_request'] handler. When you execute a cell in a Jupyter notebook, it sends a execute_request comms message to the kernel with that code.

While the CaptureExecution manager is "blocking", it stores those messages in a list instead of actually executing them. When the context manager exits, it resets the handler to its original behavior and then either replays the stored messages or drops them.

import ipython_blocking
ctx = ipython_blocking.CaptureExecution(replay=True)
with ctx:
    while True:
        if break_function():
            break
        ctx.step() # handles all other messages that aren't 'execute_request' including widget value changes

%block

The %block magic is enabled upon importing ipython_blocking. It takes either a function or widget object and creates the CaptureExecution manager to block until that function returns True or the widget value changes.

# cell 1
import ipywidgets as widgets
import ipython_blocking
dd = widgets.Dropdown(options=['', 'foo', 'bar', 'baz'])
dd

# cell 2
%block dd

# cell 3
# Won't actually be executed until the user chooses an option in the dd widget
print(dd.value)

%blockrun

The %blockrun magic is similar to %block but it only accepts an ipywidgets.Button target and it attaches a "cell -> run all below" handler to the button. If you expect the application logic of your Notebook to be run more than once (and/or don't want to reinitialize the Widgets because the user might only change one of many options), then %blockrun is the better magic to use.

It is often handy to make the target Button unclickable when the Notebook first renders, then add .observe handlers on other Widgets that can make the Button clickable once some input validation has happened.

# cell 1
import ipywidgets as widgets
import ipython_blocking
text = widgets.Text()
dd = widgets.Dropdown(options=['', 'foo', 'bar', 'baz'])
button = widgets.Button(description='Run', disabled=True)

def validation(ev):
    "make button clickable if user has put in more than 5 characters and chosen a dropdown option"
    if len(text.value) > 5 and dd.value:
        button.disabled = False
    else:
        button.disabled = True
text.observe(validation)
dd.observe(validation)

box = widgets.VBox(children=[text, dd, button])
box

# cell 2
%blockrun button

# cell 3
print(text.value)
print(dd.value)

Alternatives

The other ways to get the value of a widget after a user has interacted with the widget is to structure your notebook with event callbacks or to write your code asynchronously.

I believe there are major benefits to writing the application logic of a Jupyter Notebook in a linear and synchronous fashion, with as many variables as possible in the notebook global scope. Those benefits include:

  • Better introspection and comprehension of the workflow (without littering your code with global and print statements)
  • More direct debug when something goes wrong
  • Easier to break code into small blocks/cells

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ipython_blocking-0.3.0.tar.gz (6.3 kB view details)

Uploaded Source

Built Distribution

ipython_blocking-0.3.0-py3-none-any.whl (7.1 kB view details)

Uploaded Python 3

File details

Details for the file ipython_blocking-0.3.0.tar.gz.

File metadata

  • Download URL: ipython_blocking-0.3.0.tar.gz
  • Upload date:
  • Size: 6.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.7.1 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.2 CPython/3.9.6

File hashes

Hashes for ipython_blocking-0.3.0.tar.gz
Algorithm Hash digest
SHA256 b23a254570bbe6c032e6fcb48597ed68252b627a80de6e527aaf016508eececd
MD5 8085e0347993c48e7e5e08343102604b
BLAKE2b-256 33029fd294ab6ccda4ef5e096465c186e7846b88d0dd28ceccfa0951b147de21

See more details on using hashes here.

File details

Details for the file ipython_blocking-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: ipython_blocking-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 7.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.7.1 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.2 CPython/3.9.6

File hashes

Hashes for ipython_blocking-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7e20505c4b1615d548feb1c41b658c28bd347649190fa188dcdf13276c1dfa8e
MD5 a4210fcf562a106356b495af1c6764dc
BLAKE2b-256 a4abbf47afac896cffb579c886d0ccb411fb48d7194da20c954e98755d2b2d1f

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page