Reputation: 2897
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
Upvotes: 7
Views: 4453
Reputation: 569
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
.
Upvotes: 2
Reputation: 85552
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!
Upvotes: 4