Two fun facts:
First, let's create the hello world click command from the click docs:
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!' % click.style(name, fg='green'))
which we can call:
hello(['--count', '2'])
Your name: min Hello min! Hello min!
An exception has occurred, use %tb to see the full traceback. SystemExit: 0
/Users/benjaminrk/dev/ip/ipython/IPython/core/interactiveshell.py:3283: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D. warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)
Two things:
SystemExit
, andmin
is not greenThe latter is because click checks if sys.stdin is a tty, which it isn't in the notebook, and only enables colors if it is.
The check is in click.utils.should_strip_ansi
:
s = click.style('Hello World!', fg='green')
s
'\x1b[32mHello World!\x1b[0m'
click.utils.should_strip_ansi??
Signature: click.utils.should_strip_ansi(stream=None, color=None) Docstring: <no docstring> Source: def should_strip_ansi(stream=None, color=None): if color is None: if stream is None: stream = sys.stdin return not isatty(stream) return not color File: ~/conda/lib/python3.6/site-packages/click/_compat.py Type: function
click.echo(s)
Hello World!
We can force click not to check this and then echo should produce colored output:
try:
from unittest import mock
except ImportError: # py2
import mock
with mock.patch(
'click.utils.should_strip_ansi',
lambda *args, **kwargs: False
):
click.echo(s)
Hello World!
Next, we can write the transform to turn a click command into an IPython magic.
An IPython magic is a Python function called where the rest of the line
is passed as a simple string.
The click function wants a list of arguments, sys.argv
-style.
So we need to do the following things:
shlex.split
%magicname
click.echo
that it should always color outputSystemExit
errors and turning them into IPython magic UsageError
sregister_magic_function
import shlex
try:
from unittest import mock
except ImportError: # py2
import mock
from IPython.core.error import UsageError
def click_magic(click_command, name=None):
if name is None:
name = click_command.name
def magic_func(line):
args = shlex.split(line)
# bypass click's check for whether colors should be enabled
with mock.patch(
'click.utils.should_strip_ansi',
lambda *args, **kwargs: False
):
try:
click_command(
shlex.split(line),
prog_name='%' + name,
)
except SystemExit as e:
if e.code != 0:
raise UsageError("Command exited with status=%s" % e.code)
get_ipython().register_magic_function(magic_func, magic_name=name)
click_magic(hello)
%hello --help
Usage: %hello [OPTIONS] Simple program that greets NAME for a total of COUNT times. Options: --count INTEGER Number of greetings. --name TEXT The person to greet. --help Show this message and exit.
%hello --count 2 --name myname
Hello myname! Hello myname!
%hello --unrecognized
Error: no such option: --unrecognized UsageError: Command exited with status=2