8

Is there a way to use the click library in a Jupyter notebook cell? I would like to pass flags to my Jupyter notebook code, within the notebook, to make it smoother transiting to a stand alone script. For instance, using OptionParser from a Jupyter notebook cell:

from optparse import OptionParser
import sys


def main():
    parser = OptionParser()
    parser.add_option('-f', '--fake',
                    default='False',
                help='Fake data')
    (options,args) = parser.parse_args()
    print('options:{} args: {}'.format(options, args))
    if options.fake:
        print('Fake detected')

def test_args():

    print('hello')

if __name__ == '__main__':

    sys.argv = ['--fake', 'True' '--help']
    main()

output: options:{'fake': 'False'} args: ['True--help'] Fake detected

Using the click library, I get a string of errors. I ran this code from a Jupyter notebook cell:

import click

@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name',
            help='The person to greet.')
def hello(count, name):
    """Simple program that greets NAME for a total of COUNT times."""
    for x in range(count):
        click.echo('Hello %s!' % name)

if __name__ == '__main__':
    hello()

Ouput (truncated):

UnsupportedOperation                      Traceback (most recent call last)
<ipython-input-6-ad31be7bf0fe> in <module>()
    12 if __name__ == '__main__':
    13     sys.argv = ['--count', '3']
---> 14     hello()

~/.local/lib/python3.6/site-packages/click/core.py in __call__(self, *args, **kwargs)
    720     def __call__(self, *args, **kwargs):
    721         """Alias for :meth:`main`."""
--> 722         return self.main(*args, **kwargs)
    723 
    724 
...
257 
    258     if message:
--> 259         file.write(message)
    260     file.flush()
    261 

UnsupportedOperation: not writable
1
  • One option I found on linux. You can use sys.stdout = open('/dev/stdout', 'w'); sys.stderr = open('/dev/stderr', 'w'), then it runs but all outputs now go to the terminal where you started jupyter. Commented Apr 1, 2018 at 6:27

2 Answers 2

5

You can use the %%python magic command to start a new Python propcess:

%%python

import sys
import click

@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name',
            help='The person to greet.')
def hello(count, name):
    """Simple program that greets NAME for a total of COUNT times."""
    with open('echo.txt', 'w') as fobj:
        for x in range(count):
            click.echo('Hello %s!' % name)

if __name__ == '__main__':
    # first element is the script name, use empty string instead
    sys.argv = ['', '--name', 'Max', '--count', '3']
    hello()

Output:

Hello Max!
Hello Max!
Hello Max!
Sign up to request clarification or add additional context in comments.

Comments

2

Jupyter hijacks the stdout/stderr/stdin feeds. You can see this using import sys; type(sys.stdin), which gives ipykernel.iostream.OutStream. A workaround that lets jupyter and click operate together is to pass sys.stdout directly to click.

def monkey_patch_jupyter_click_sreams():
    """see https://stackoverflow.com/a/49595790/221742 ."""
    import sys
    import ipykernel
    import click  
    if not click._compat.PY2 and isinstance(sys.stdout, ipykernel.iostream.OutStream):
        click._compat._force_correct_text_writer = lambda stream, encoding, errors: stream

monkey_patch_jupyter_click_sreams()
# your code here
hello()

This works on by bypassing the click wrapper for the stdout and other streams and just passes stdout._buffer. This works with click, even if stdout has been replaced with ipythons ipykernel.iostream.OutStream.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.