Package: ipython-blocking
ipython_blockingipython_blockingis a context manager for capturing cell execution messages in a Jupyter notebook, along with magic commands%blockand%blockrunfor convenience.  The primary use-case for blocking notebook execution is to wait for users to interact withipywidgetsand then reference the values entered in those Widgets.Installipython_blockingis onPyPI, install with pip.pipinstallipython_blockingUsageTry out the demo notebooks in Binder to seeipython_blockingin action.  The most common way to useipython_blockingis with the%blockrunmagic and running a notebook with "cell -> run all".%blockrun buttonstops 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 #1importipywidgetsaswidgetsimportipython_blocking# enables %block and %blockrun magictext=widgets.Text()dropdown=widgets.Dropdown(options=['','foo','bar','baz'])button=widgets.Button(description='Run')box=widgets.VBox(children=[text,dropdown,button])box### cell #2%blockrunbutton### cell #3 -- doesn't execute until the 'Run' button is pressed### This gives the user a chance to interact with the Text and Dropdown widgetsprint(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!)CaptureExecutionThe wayipython_blocking"blocks" cell execution is by creating a context manager that changes the behavior of theIPython.shell.kernel['execute_request']handler.  When you execute a cell in a Jupyter notebook, it sends aexecute_requestcomms message to the kernel with that code.While theCaptureExecutionmanager 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.importipython_blockingctx=ipython_blocking.CaptureExecution(replay=True)withctx:whileTrue:ifbreak_function():breakctx.step()# handles all other messages that aren't 'execute_request' including widget value changes%blockThe%blockmagic is enabled upon importingipython_blocking.  It takes either a function or widget object and creates theCaptureExecutionmanager to block until that function returns True or the widget value changes.# cell 1importipywidgetsaswidgetsimportipython_blockingdd=widgets.Dropdown(options=['','foo','bar','baz'])dd# cell 2%blockdd# cell 3# Won't actually be executed until the user chooses an option in the dd widgetprint(dd.value)%blockrunThe%blockrunmagic is similar to%blockbut it only accepts anipywidgets.Buttontarget 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%blockrunis the better magic to use.It is often handy to make the targetButtonunclickable when the Notebook first renders, then add.observehandlers on other Widgets that can make theButtonclickable once some input validation has happened.# cell 1importipywidgetsaswidgetsimportipython_blockingtext=widgets.Text()dd=widgets.Dropdown(options=['','foo','bar','baz'])button=widgets.Button(description='Run',disabled=True)defvalidation(ev):"make button clickable if user has put in more than 5 characters and chosen a dropdown option"iflen(text.value)>5anddd.value:button.disabled=Falseelse:button.disabled=Truetext.observe(validation)dd.observe(validation)box=widgets.VBox(children=[text,dd,button])box# cell 2%blockrunbutton# cell 3print(text.value)print(dd.value)AlternativesThe other ways to get the value of a widget after a user has interacted with the widget is to structure your notebook withevent callbacksor to write your codeasynchronously.I believe there are major benefits to writing theapplication logicof a Jupyter Notebook in alinear and synchronousfashion, 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 withglobalandprintstatements)More direct debug when something goes wrongEasier to break code into small blocks/cells