Coder Social home page Coder Social logo

kafonek / ipython_blocking Goto Github PK

View Code? Open in Web Editor NEW
45.0 2.0 6.0 4.11 MB

A Python library that offers a context manager to turn on cell execution capture/blocking in Jupyter notebooks/IPykernel. Useful when you need a "blocking widget"

License: BSD 3-Clause "New" or "Revised" License

Jupyter Notebook 84.05% Python 15.95%

ipython_blocking's Introduction

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

ipython_blocking's People

Contributors

gregd33 avatar jeffyjefflabs avatar kafonek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

ipython_blocking's Issues

how to block python code execution till js snippet returns in Jupyter notebook

I am trying to execute a JS code to get the notebook name in Jupyter

import IPython
from IPython.core.magics.display import Javascript

def func():
    return Javascript("IPython.notebook.kernel.execute(`notebook_name = '${IPython.notebook.notebook_name}'`)")
func()

The problem is that this code is executed asynchronously and I do not have the result until all cells have finished executing whereas I need the result much before. This creates a problem when I am trying to do a Run all cells or %run notebook.ipynb from another notebook where the notebook.ipynb file holds this js code. I tried using

%block func

but it doesn't work.

Help is much obliged.

How To Import into Jupyter Notebook

Hello @kafonek ! Great work with this project-this is exactly what I'm looking for! However, I'm pretty new with using the online Jupyter Notebook interface, and I'm not sure what I'm missing but the ipython_blocking module is not being found. Here is the current cell I am running:

import os
import sys
from IPython.display import display
module_path = os.path.abspath(os.path.join('..'))
if module_path not in sys.path:
    sys.path.append(module_path)
import ipywidgets as widgets
import ipython_blocking 

However, I get an error saying:

ImportError: No module named 'ipython_blocking'

I can't seem to find an answer to this online-any suggestions? Thank you in advance!

Great job!

Hi Mat,

This is not so much an issue as it is an encouragement on your work with ipython_blocking. Its exactly what I was looking for, for hours today.

I will definitely send feedback if I have any and submit PR's if I can.

Thanks again!

Sohail

After %blockrun <button>, notebook jumps to bottom

Hi,

Functionality-wise, I've been looking for this exact functionality for a while, so thanks for making this so easy with one line of code!!

One thing I've noticed though is that after the %blockrun cell executes, it immediately jumps to the end of the notebook. However the reason I use this is to wait for ipywidgets input. This means the user has to scroll all the way back up to the input cells to actually enter input. Do you know if there is a way to force the web page to "stop" at the %blockrun cell so that users can enter the input right then and there?

Thanks :)

%blockrun button executes even after clicking the button on google colab

Error reproducible at
cell 1
try:
import ipython_blocking
except ImportError:
!pip install ipython_blocking
import ipython_blocking

cell 2
from ipywidgets import Dropdown, Button, Output
from IPython.display import display, clear_output

outs = Output()
query_selector = Dropdown(
options=['Select', 'Q1', 'Q2'],
value='Select',
description='Query Type:'
)

process_button = Button(
description='Process',
disabled=False
)

def proc_button(bb):
with outs:
clear_output()
display(process_button)

display(query_selector)
query_selector.observe(proc_button, names='value')
display(outs)

cell 3
%blockrun process_button

cell 4
print(query_selector.value)

causing the cell 3 to run continuously even after the button is selected.

Kernel.do_one_iteration never awaited

Ran into this while working on ideas to solve #10. Was on WSL / dockerized Jupyter based off the docker-stacks minimal-notebook image.

Code:

# cell 1
import ipython_blocking
import ipywidgets as widgets

button = widgets.Button(description="click me", button_style="info")
button

# cell 2
%blockrun button

Error:

/home/jovyan/ipython_blocking/ipython_blocking/ipython_blocking.py:21: RuntimeWarning: coroutine 'Kernel.do_one_iteration' was never awaited
  self.kernel.do_one_iteration()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

Versions:

jupyter core     : 4.7.1
jupyter-notebook : 6.4.0
qtconsole        : not installed
ipython          : 7.25.0
ipykernel        : 6.0.3
jupyter client   : 6.1.12
jupyter lab      : 3.0.16
nbconvert        : 6.1.0
ipywidgets       : 7.6.3
nbformat         : 5.1.3
traitlets        : 5.0.5

Support reading other properties than value?

Now my problem is that I want to use it with from ipyfilechooser import FileChooser, which doesn't inherit from ValueWidget or have a value property or something:

/opt/conda/lib/python3.7/site-packages/ipython_blocking/ipython_magic.py in block(self, line)
     57             func = lambda: obj._has_been_clicked
     58         else:
---> 59             raise Exception('The positional argument to %block should be a ValueWidget, Button, or a function/method')
     60         return self.capture(func, args.timeout)
     61 

If there were some way to point at a property: %block myobject.property, and somehow introspect that it was indeed a value or property to do change detection on, then it could be a good general-purpose solution. Also, FileChooser should probably actually inherit and provide a value, but that's another matter.

Yes, I know I can write a change-detecting function, as per the workaround in #5:

# previous cell
config_selector = FileChooser(str(root_working_dir), use_dir_icons=True)
display(config_selector)

# next cell
ctx = ipython_blocking.CaptureExecution()
with ctx:
    init_val = config_selector.selected
    while True:
        if init_val != config_selector.selected:
            break
        ctx.step()

...but I was hoping for an easy-to-read magic one-liner.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.