summaryrefslogtreecommitdiffstats
path: root/third_party/python/Click/examples
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/python/Click/examples
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/python/Click/examples')
-rw-r--r--third_party/python/Click/examples/README12
-rw-r--r--third_party/python/Click/examples/aliases/README17
-rw-r--r--third_party/python/Click/examples/aliases/aliases.ini2
-rw-r--r--third_party/python/Click/examples/aliases/aliases.py111
-rw-r--r--third_party/python/Click/examples/aliases/setup.py15
-rw-r--r--third_party/python/Click/examples/bashcompletion/README12
-rw-r--r--third_party/python/Click/examples/bashcompletion/bashcompletion.py45
-rw-r--r--third_party/python/Click/examples/bashcompletion/setup.py15
-rw-r--r--third_party/python/Click/examples/colors/README11
-rw-r--r--third_party/python/Click/examples/colors/colors.py28
-rw-r--r--third_party/python/Click/examples/colors/setup.py17
-rw-r--r--third_party/python/Click/examples/complex/README16
-rw-r--r--third_party/python/Click/examples/complex/complex/__init__.py0
-rw-r--r--third_party/python/Click/examples/complex/complex/cli.py65
-rw-r--r--third_party/python/Click/examples/complex/complex/commands/__init__.py0
-rw-r--r--third_party/python/Click/examples/complex/complex/commands/cmd_init.py13
-rw-r--r--third_party/python/Click/examples/complex/complex/commands/cmd_status.py10
-rw-r--r--third_party/python/Click/examples/complex/setup.py15
-rw-r--r--third_party/python/Click/examples/imagepipe/.gitignore1
-rw-r--r--third_party/python/Click/examples/imagepipe/README13
-rw-r--r--third_party/python/Click/examples/imagepipe/example01.jpgbin0 -> 51677 bytes
-rw-r--r--third_party/python/Click/examples/imagepipe/example02.jpgbin0 -> 39106 bytes
-rw-r--r--third_party/python/Click/examples/imagepipe/imagepipe.py266
-rw-r--r--third_party/python/Click/examples/imagepipe/setup.py16
-rw-r--r--third_party/python/Click/examples/inout/README10
-rw-r--r--third_party/python/Click/examples/inout/inout.py30
-rw-r--r--third_party/python/Click/examples/inout/setup.py15
-rw-r--r--third_party/python/Click/examples/naval/README14
-rw-r--r--third_party/python/Click/examples/naval/naval.py70
-rw-r--r--third_party/python/Click/examples/naval/setup.py15
-rw-r--r--third_party/python/Click/examples/repo/README9
-rw-r--r--third_party/python/Click/examples/repo/repo.py151
-rw-r--r--third_party/python/Click/examples/repo/setup.py15
-rw-r--r--third_party/python/Click/examples/termui/README9
-rw-r--r--third_party/python/Click/examples/termui/setup.py17
-rw-r--r--third_party/python/Click/examples/termui/termui.py156
-rw-r--r--third_party/python/Click/examples/validation/README12
-rw-r--r--third_party/python/Click/examples/validation/setup.py15
-rw-r--r--third_party/python/Click/examples/validation/validation.py44
39 files changed, 1282 insertions, 0 deletions
diff --git a/third_party/python/Click/examples/README b/third_party/python/Click/examples/README
new file mode 100644
index 0000000000..6be32961f4
--- /dev/null
+++ b/third_party/python/Click/examples/README
@@ -0,0 +1,12 @@
+Click Examples
+
+ This folder contains various Click examples. Note that
+ all of these are not runnable by themselves but should be
+ installed into a virtualenv.
+
+ This is done this way so that scripts also properly work
+ on Windows and in virtualenvs without accidentally executing
+ through the wrong interpreter.
+
+ For more information about this see the documentation:
+ https://click.palletsprojects.com/en/7.x/setuptools/
diff --git a/third_party/python/Click/examples/aliases/README b/third_party/python/Click/examples/aliases/README
new file mode 100644
index 0000000000..5a4a066566
--- /dev/null
+++ b/third_party/python/Click/examples/aliases/README
@@ -0,0 +1,17 @@
+$ aliases_
+
+ aliases is a fairly advanced example that shows how
+ to implement command aliases with Click. It uses a
+ subclass of the default group to customize how commands
+ are located.
+
+ It supports both aliases read from a config file as well
+ as automatic abbreviations.
+
+ The aliases from the config are read from the aliases.ini
+ file. Try `aliases st` and `aliases ci`!
+
+Usage:
+
+ $ pip install --editable .
+ $ aliases --help
diff --git a/third_party/python/Click/examples/aliases/aliases.ini b/third_party/python/Click/examples/aliases/aliases.ini
new file mode 100644
index 0000000000..4f1d54cd6b
--- /dev/null
+++ b/third_party/python/Click/examples/aliases/aliases.ini
@@ -0,0 +1,2 @@
+[aliases]
+ci=commit
diff --git a/third_party/python/Click/examples/aliases/aliases.py b/third_party/python/Click/examples/aliases/aliases.py
new file mode 100644
index 0000000000..38ef72c5c4
--- /dev/null
+++ b/third_party/python/Click/examples/aliases/aliases.py
@@ -0,0 +1,111 @@
+import os
+import click
+
+try:
+ import ConfigParser as configparser
+except ImportError:
+ import configparser
+
+
+class Config(object):
+ """The config in this example only holds aliases."""
+
+ def __init__(self):
+ self.path = os.getcwd()
+ self.aliases = {}
+
+ def read_config(self, filename):
+ parser = configparser.RawConfigParser()
+ parser.read([filename])
+ try:
+ self.aliases.update(parser.items('aliases'))
+ except configparser.NoSectionError:
+ pass
+
+
+pass_config = click.make_pass_decorator(Config, ensure=True)
+
+
+class AliasedGroup(click.Group):
+ """This subclass of a group supports looking up aliases in a config
+ file and with a bit of magic.
+ """
+
+ def get_command(self, ctx, cmd_name):
+ # Step one: bulitin commands as normal
+ rv = click.Group.get_command(self, ctx, cmd_name)
+ if rv is not None:
+ return rv
+
+ # Step two: find the config object and ensure it's there. This
+ # will create the config object is missing.
+ cfg = ctx.ensure_object(Config)
+
+ # Step three: lookup an explicit command aliase in the config
+ if cmd_name in cfg.aliases:
+ actual_cmd = cfg.aliases[cmd_name]
+ return click.Group.get_command(self, ctx, actual_cmd)
+
+ # Alternative option: if we did not find an explicit alias we
+ # allow automatic abbreviation of the command. "status" for
+ # instance will match "st". We only allow that however if
+ # there is only one command.
+ matches = [x for x in self.list_commands(ctx)
+ if x.lower().startswith(cmd_name.lower())]
+ if not matches:
+ return None
+ elif len(matches) == 1:
+ return click.Group.get_command(self, ctx, matches[0])
+ ctx.fail('Too many matches: %s' % ', '.join(sorted(matches)))
+
+
+def read_config(ctx, param, value):
+ """Callback that is used whenever --config is passed. We use this to
+ always load the correct config. This means that the config is loaded
+ even if the group itself never executes so our aliases stay always
+ available.
+ """
+ cfg = ctx.ensure_object(Config)
+ if value is None:
+ value = os.path.join(os.path.dirname(__file__), 'aliases.ini')
+ cfg.read_config(value)
+ return value
+
+
+@click.command(cls=AliasedGroup)
+@click.option('--config', type=click.Path(exists=True, dir_okay=False),
+ callback=read_config, expose_value=False,
+ help='The config file to use instead of the default.')
+def cli():
+ """An example application that supports aliases."""
+
+
+@cli.command()
+def push():
+ """Pushes changes."""
+ click.echo('Push')
+
+
+@cli.command()
+def pull():
+ """Pulls changes."""
+ click.echo('Pull')
+
+
+@cli.command()
+def clone():
+ """Clones a repository."""
+ click.echo('Clone')
+
+
+@cli.command()
+def commit():
+ """Commits pending changes."""
+ click.echo('Commit')
+
+
+@cli.command()
+@pass_config
+def status(config):
+ """Shows the status."""
+ click.echo('Status for %s' % config.path)
diff --git a/third_party/python/Click/examples/aliases/setup.py b/third_party/python/Click/examples/aliases/setup.py
new file mode 100644
index 0000000000..8d1d6a4068
--- /dev/null
+++ b/third_party/python/Click/examples/aliases/setup.py
@@ -0,0 +1,15 @@
+from setuptools import setup
+
+setup(
+ name='click-example-aliases',
+ version='1.0',
+ py_modules=['aliases'],
+ include_package_data=True,
+ install_requires=[
+ 'click',
+ ],
+ entry_points='''
+ [console_scripts]
+ aliases=aliases:cli
+ ''',
+)
diff --git a/third_party/python/Click/examples/bashcompletion/README b/third_party/python/Click/examples/bashcompletion/README
new file mode 100644
index 0000000000..f8a0d51ef9
--- /dev/null
+++ b/third_party/python/Click/examples/bashcompletion/README
@@ -0,0 +1,12 @@
+$ bashcompletion
+
+ bashcompletion is a simple example of an application that
+ tries to autocomplete commands, arguments and options.
+
+ This example requires Click 2.0 or higher.
+
+Usage:
+
+ $ pip install --editable .
+ $ eval "$(_BASHCOMPLETION_COMPLETE=source bashcompletion)"
+ $ bashcompletion --help
diff --git a/third_party/python/Click/examples/bashcompletion/bashcompletion.py b/third_party/python/Click/examples/bashcompletion/bashcompletion.py
new file mode 100644
index 0000000000..1072840035
--- /dev/null
+++ b/third_party/python/Click/examples/bashcompletion/bashcompletion.py
@@ -0,0 +1,45 @@
+import click
+import os
+
+
+@click.group()
+def cli():
+ pass
+
+
+def get_env_vars(ctx, args, incomplete):
+ # Completions returned as strings do not have a description displayed.
+ for key in os.environ.keys():
+ if incomplete in key:
+ yield key
+
+
+@cli.command(help='A command to print environment variables')
+@click.argument("envvar", type=click.STRING, autocompletion=get_env_vars)
+def cmd1(envvar):
+ click.echo('Environment variable: %s' % envvar)
+ click.echo('Value: %s' % os.environ[envvar])
+
+
+@click.group(help='A group that holds a subcommand')
+def group():
+ pass
+
+
+def list_users(ctx, args, incomplete):
+ # You can generate completions with descriptions by returning
+ # tuples in the form (completion, description).
+ users = [('bob', 'butcher'),
+ ('alice', 'baker'),
+ ('jerry', 'candlestick maker')]
+ # Ths will allow completion matches based on matches within the description string too!
+ return [user for user in users if incomplete in user[0] or incomplete in user[1]]
+
+
+@group.command(help='Choose a user')
+@click.argument("user", type=click.STRING, autocompletion=list_users)
+def subcmd(user):
+ click.echo('Chosen user is %s' % user)
+
+
+cli.add_command(group)
diff --git a/third_party/python/Click/examples/bashcompletion/setup.py b/third_party/python/Click/examples/bashcompletion/setup.py
new file mode 100644
index 0000000000..ad200818cd
--- /dev/null
+++ b/third_party/python/Click/examples/bashcompletion/setup.py
@@ -0,0 +1,15 @@
+from setuptools import setup
+
+setup(
+ name='click-example-bashcompletion',
+ version='1.0',
+ py_modules=['bashcompletion'],
+ include_package_data=True,
+ install_requires=[
+ 'click',
+ ],
+ entry_points='''
+ [console_scripts]
+ bashcompletion=bashcompletion:cli
+ ''',
+)
diff --git a/third_party/python/Click/examples/colors/README b/third_party/python/Click/examples/colors/README
new file mode 100644
index 0000000000..4b5b44f696
--- /dev/null
+++ b/third_party/python/Click/examples/colors/README
@@ -0,0 +1,11 @@
+$ colors_
+
+ colors is a simple example that shows how you can
+ colorize text.
+
+ For this to work on Windows, colorama is required.
+
+Usage:
+
+ $ pip install --editable .
+ $ colors
diff --git a/third_party/python/Click/examples/colors/colors.py b/third_party/python/Click/examples/colors/colors.py
new file mode 100644
index 0000000000..193b927121
--- /dev/null
+++ b/third_party/python/Click/examples/colors/colors.py
@@ -0,0 +1,28 @@
+import click
+
+
+all_colors = 'black', 'red', 'green', 'yellow', 'blue', 'magenta', \
+ 'cyan', 'white', 'bright_black', 'bright_red', \
+ 'bright_green', 'bright_yellow', 'bright_blue', \
+ 'bright_magenta', 'bright_cyan', 'bright_white'
+
+
+@click.command()
+def cli():
+ """This script prints some colors. If colorama is installed this will
+ also work on Windows. It will also automatically remove all ANSI
+ styles if data is piped into a file.
+
+ Give it a try!
+ """
+ for color in all_colors:
+ click.echo(click.style('I am colored %s' % color, fg=color))
+ for color in all_colors:
+ click.echo(click.style('I am colored %s and bold' % color,
+ fg=color, bold=True))
+ for color in all_colors:
+ click.echo(click.style('I am reverse colored %s' % color, fg=color,
+ reverse=True))
+
+ click.echo(click.style('I am blinking', blink=True))
+ click.echo(click.style('I am underlined', underline=True))
diff --git a/third_party/python/Click/examples/colors/setup.py b/third_party/python/Click/examples/colors/setup.py
new file mode 100644
index 0000000000..3f8e105fab
--- /dev/null
+++ b/third_party/python/Click/examples/colors/setup.py
@@ -0,0 +1,17 @@
+from setuptools import setup
+
+setup(
+ name='click-example-colors',
+ version='1.0',
+ py_modules=['colors'],
+ include_package_data=True,
+ install_requires=[
+ 'click',
+ # Colorama is only required for Windows.
+ 'colorama',
+ ],
+ entry_points='''
+ [console_scripts]
+ colors=colors:cli
+ ''',
+)
diff --git a/third_party/python/Click/examples/complex/README b/third_party/python/Click/examples/complex/README
new file mode 100644
index 0000000000..7eaac90372
--- /dev/null
+++ b/third_party/python/Click/examples/complex/README
@@ -0,0 +1,16 @@
+$ complex_
+
+ complex is an example of building very complex cli
+ applications that load subcommands dynamically from
+ a plugin folder and other things.
+
+ All the commands are implemented as plugins in the
+ `complex.commands` package. If a python module is
+ placed named "cmd_foo" it will show up as "foo"
+ command and the `cli` object within it will be
+ loaded as nested Click command.
+
+Usage:
+
+ $ pip install --editable .
+ $ complex --help
diff --git a/third_party/python/Click/examples/complex/complex/__init__.py b/third_party/python/Click/examples/complex/complex/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/third_party/python/Click/examples/complex/complex/__init__.py
diff --git a/third_party/python/Click/examples/complex/complex/cli.py b/third_party/python/Click/examples/complex/complex/cli.py
new file mode 100644
index 0000000000..bcfd14a132
--- /dev/null
+++ b/third_party/python/Click/examples/complex/complex/cli.py
@@ -0,0 +1,65 @@
+import os
+import sys
+import click
+
+
+CONTEXT_SETTINGS = dict(auto_envvar_prefix='COMPLEX')
+
+
+class Context(object):
+
+ def __init__(self):
+ self.verbose = False
+ self.home = os.getcwd()
+
+ def log(self, msg, *args):
+ """Logs a message to stderr."""
+ if args:
+ msg %= args
+ click.echo(msg, file=sys.stderr)
+
+ def vlog(self, msg, *args):
+ """Logs a message to stderr only if verbose is enabled."""
+ if self.verbose:
+ self.log(msg, *args)
+
+
+pass_context = click.make_pass_decorator(Context, ensure=True)
+cmd_folder = os.path.abspath(os.path.join(os.path.dirname(__file__),
+ 'commands'))
+
+
+class ComplexCLI(click.MultiCommand):
+
+ def list_commands(self, ctx):
+ rv = []
+ for filename in os.listdir(cmd_folder):
+ if filename.endswith('.py') and \
+ filename.startswith('cmd_'):
+ rv.append(filename[4:-3])
+ rv.sort()
+ return rv
+
+ def get_command(self, ctx, name):
+ try:
+ if sys.version_info[0] == 2:
+ name = name.encode('ascii', 'replace')
+ mod = __import__('complex.commands.cmd_' + name,
+ None, None, ['cli'])
+ except ImportError:
+ return
+ return mod.cli
+
+
+@click.command(cls=ComplexCLI, context_settings=CONTEXT_SETTINGS)
+@click.option('--home', type=click.Path(exists=True, file_okay=False,
+ resolve_path=True),
+ help='Changes the folder to operate on.')
+@click.option('-v', '--verbose', is_flag=True,
+ help='Enables verbose mode.')
+@pass_context
+def cli(ctx, verbose, home):
+ """A complex command line interface."""
+ ctx.verbose = verbose
+ if home is not None:
+ ctx.home = home
diff --git a/third_party/python/Click/examples/complex/complex/commands/__init__.py b/third_party/python/Click/examples/complex/complex/commands/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/third_party/python/Click/examples/complex/complex/commands/__init__.py
diff --git a/third_party/python/Click/examples/complex/complex/commands/cmd_init.py b/third_party/python/Click/examples/complex/complex/commands/cmd_init.py
new file mode 100644
index 0000000000..8c30186c3e
--- /dev/null
+++ b/third_party/python/Click/examples/complex/complex/commands/cmd_init.py
@@ -0,0 +1,13 @@
+import click
+from complex.cli import pass_context
+
+
+@click.command('init', short_help='Initializes a repo.')
+@click.argument('path', required=False, type=click.Path(resolve_path=True))
+@pass_context
+def cli(ctx, path):
+ """Initializes a repository."""
+ if path is None:
+ path = ctx.home
+ ctx.log('Initialized the repository in %s',
+ click.format_filename(path))
diff --git a/third_party/python/Click/examples/complex/complex/commands/cmd_status.py b/third_party/python/Click/examples/complex/complex/commands/cmd_status.py
new file mode 100644
index 0000000000..99e736eee6
--- /dev/null
+++ b/third_party/python/Click/examples/complex/complex/commands/cmd_status.py
@@ -0,0 +1,10 @@
+import click
+from complex.cli import pass_context
+
+
+@click.command('status', short_help='Shows file changes.')
+@pass_context
+def cli(ctx):
+ """Shows file changes in the current working directory."""
+ ctx.log('Changed files: none')
+ ctx.vlog('bla bla bla, debug info')
diff --git a/third_party/python/Click/examples/complex/setup.py b/third_party/python/Click/examples/complex/setup.py
new file mode 100644
index 0000000000..dee002c135
--- /dev/null
+++ b/third_party/python/Click/examples/complex/setup.py
@@ -0,0 +1,15 @@
+from setuptools import setup
+
+setup(
+ name='click-example-complex',
+ version='1.0',
+ packages=['complex', 'complex.commands'],
+ include_package_data=True,
+ install_requires=[
+ 'click',
+ ],
+ entry_points='''
+ [console_scripts]
+ complex=complex.cli:cli
+ ''',
+)
diff --git a/third_party/python/Click/examples/imagepipe/.gitignore b/third_party/python/Click/examples/imagepipe/.gitignore
new file mode 100644
index 0000000000..63280895b1
--- /dev/null
+++ b/third_party/python/Click/examples/imagepipe/.gitignore
@@ -0,0 +1 @@
+processed-*
diff --git a/third_party/python/Click/examples/imagepipe/README b/third_party/python/Click/examples/imagepipe/README
new file mode 100644
index 0000000000..91ec0cd26f
--- /dev/null
+++ b/third_party/python/Click/examples/imagepipe/README
@@ -0,0 +1,13 @@
+$ imagepipe_
+
+ imagepipe is an example application that implements some
+ multi commands that chain image processing instructions
+ together.
+
+ This requires pillow.
+
+Usage:
+
+ $ pip install --editable .
+ $ imagepipe open -i example01.jpg resize -w 128 display
+ $ imagepipe open -i example02.jpg blur save
diff --git a/third_party/python/Click/examples/imagepipe/example01.jpg b/third_party/python/Click/examples/imagepipe/example01.jpg
new file mode 100644
index 0000000000..f2d9397755
--- /dev/null
+++ b/third_party/python/Click/examples/imagepipe/example01.jpg
Binary files differ
diff --git a/third_party/python/Click/examples/imagepipe/example02.jpg b/third_party/python/Click/examples/imagepipe/example02.jpg
new file mode 100644
index 0000000000..b1f802ed85
--- /dev/null
+++ b/third_party/python/Click/examples/imagepipe/example02.jpg
Binary files differ
diff --git a/third_party/python/Click/examples/imagepipe/imagepipe.py b/third_party/python/Click/examples/imagepipe/imagepipe.py
new file mode 100644
index 0000000000..37a1521133
--- /dev/null
+++ b/third_party/python/Click/examples/imagepipe/imagepipe.py
@@ -0,0 +1,266 @@
+import click
+from functools import update_wrapper
+from PIL import Image, ImageFilter, ImageEnhance
+
+
+@click.group(chain=True)
+def cli():
+ """This script processes a bunch of images through pillow in a unix
+ pipe. One commands feeds into the next.
+
+ Example:
+
+ \b
+ imagepipe open -i example01.jpg resize -w 128 display
+ imagepipe open -i example02.jpg blur save
+ """
+
+
+@cli.resultcallback()
+def process_commands(processors):
+ """This result callback is invoked with an iterable of all the chained
+ subcommands. As in this example each subcommand returns a function
+ we can chain them together to feed one into the other, similar to how
+ a pipe on unix works.
+ """
+ # Start with an empty iterable.
+ stream = ()
+
+ # Pipe it through all stream processors.
+ for processor in processors:
+ stream = processor(stream)
+
+ # Evaluate the stream and throw away the items.
+ for _ in stream:
+ pass
+
+
+def processor(f):
+ """Helper decorator to rewrite a function so that it returns another
+ function from it.
+ """
+ def new_func(*args, **kwargs):
+ def processor(stream):
+ return f(stream, *args, **kwargs)
+ return processor
+ return update_wrapper(new_func, f)
+
+
+def generator(f):
+ """Similar to the :func:`processor` but passes through old values
+ unchanged and does not pass through the values as parameter.
+ """
+ @processor
+ def new_func(stream, *args, **kwargs):
+ for item in stream:
+ yield item
+ for item in f(*args, **kwargs):
+ yield item
+ return update_wrapper(new_func, f)
+
+
+def copy_filename(new, old):
+ new.filename = old.filename
+ return new
+
+
+@cli.command('open')
+@click.option('-i', '--image', 'images', type=click.Path(),
+ multiple=True, help='The image file to open.')
+@generator
+def open_cmd(images):
+ """Loads one or multiple images for processing. The input parameter
+ can be specified multiple times to load more than one image.
+ """
+ for image in images:
+ try:
+ click.echo('Opening "%s"' % image)
+ if image == '-':
+ img = Image.open(click.get_binary_stdin())
+ img.filename = '-'
+ else:
+ img = Image.open(image)
+ yield img
+ except Exception as e:
+ click.echo('Could not open image "%s": %s' % (image, e), err=True)
+
+
+@cli.command('save')
+@click.option('--filename', default='processed-%04d.png', type=click.Path(),
+ help='The format for the filename.',
+ show_default=True)
+@processor
+def save_cmd(images, filename):
+ """Saves all processed images to a series of files."""
+ for idx, image in enumerate(images):
+ try:
+ fn = filename % (idx + 1)
+ click.echo('Saving "%s" as "%s"' % (image.filename, fn))
+ yield image.save(fn)
+ except Exception as e:
+ click.echo('Could not save image "%s": %s' %
+ (image.filename, e), err=True)
+
+
+@cli.command('display')
+@processor
+def display_cmd(images):
+ """Opens all images in an image viewer."""
+ for image in images:
+ click.echo('Displaying "%s"' % image.filename)
+ image.show()
+ yield image
+
+
+@cli.command('resize')
+@click.option('-w', '--width', type=int, help='The new width of the image.')
+@click.option('-h', '--height', type=int, help='The new height of the image.')
+@processor
+def resize_cmd(images, width, height):
+ """Resizes an image by fitting it into the box without changing
+ the aspect ratio.
+ """
+ for image in images:
+ w, h = (width or image.size[0], height or image.size[1])
+ click.echo('Resizing "%s" to %dx%d' % (image.filename, w, h))
+ image.thumbnail((w, h))
+ yield image
+
+
+@cli.command('crop')
+@click.option('-b', '--border', type=int, help='Crop the image from all '
+ 'sides by this amount.')
+@processor
+def crop_cmd(images, border):
+ """Crops an image from all edges."""
+ for image in images:
+ box = [0, 0, image.size[0], image.size[1]]
+
+ if border is not None:
+ for idx, val in enumerate(box):
+ box[idx] = max(0, val - border)
+ click.echo('Cropping "%s" by %dpx' % (image.filename, border))
+ yield copy_filename(image.crop(box), image)
+ else:
+ yield image
+
+
+def convert_rotation(ctx, param, value):
+ if value is None:
+ return
+ value = value.lower()
+ if value in ('90', 'r', 'right'):
+ return (Image.ROTATE_90, 90)
+ if value in ('180', '-180'):
+ return (Image.ROTATE_180, 180)
+ if value in ('-90', '270', 'l', 'left'):
+ return (Image.ROTATE_270, 270)
+ raise click.BadParameter('invalid rotation "%s"' % value)
+
+
+def convert_flip(ctx, param, value):
+ if value is None:
+ return
+ value = value.lower()
+ if value in ('lr', 'leftright'):
+ return (Image.FLIP_LEFT_RIGHT, 'left to right')
+ if value in ('tb', 'topbottom', 'upsidedown', 'ud'):
+ return (Image.FLIP_LEFT_RIGHT, 'top to bottom')
+ raise click.BadParameter('invalid flip "%s"' % value)
+
+
+@cli.command('transpose')
+@click.option('-r', '--rotate', callback=convert_rotation,
+ help='Rotates the image (in degrees)')
+@click.option('-f', '--flip', callback=convert_flip,
+ help='Flips the image [LR / TB]')
+@processor
+def transpose_cmd(images, rotate, flip):
+ """Transposes an image by either rotating or flipping it."""
+ for image in images:
+ if rotate is not None:
+ mode, degrees = rotate
+ click.echo('Rotate "%s" by %ddeg' % (image.filename, degrees))
+ image = copy_filename(image.transpose(mode), image)
+ if flip is not None:
+ mode, direction = flip
+ click.echo('Flip "%s" %s' % (image.filename, direction))
+ image = copy_filename(image.transpose(mode), image)
+ yield image
+
+
+@cli.command('blur')
+@click.option('-r', '--radius', default=2, show_default=True,
+ help='The blur radius.')
+@processor
+def blur_cmd(images, radius):
+ """Applies gaussian blur."""
+ blur = ImageFilter.GaussianBlur(radius)
+ for image in images:
+ click.echo('Blurring "%s" by %dpx' % (image.filename, radius))
+ yield copy_filename(image.filter(blur), image)
+
+
+@cli.command('smoothen')
+@click.option('-i', '--iterations', default=1, show_default=True,
+ help='How many iterations of the smoothen filter to run.')
+@processor
+def smoothen_cmd(images, iterations):
+ """Applies a smoothening filter."""
+ for image in images:
+ click.echo('Smoothening "%s" %d time%s' %
+ (image.filename, iterations, iterations != 1 and 's' or '',))
+ for x in xrange(iterations):
+ image = copy_filename(image.filter(ImageFilter.BLUR), image)
+ yield image
+
+
+@cli.command('emboss')
+@processor
+def emboss_cmd(images):
+ """Embosses an image."""
+ for image in images:
+ click.echo('Embossing "%s"' % image.filename)
+ yield copy_filename(image.filter(ImageFilter.EMBOSS), image)
+
+
+@cli.command('sharpen')
+@click.option('-f', '--factor', default=2.0,
+ help='Sharpens the image.', show_default=True)
+@processor
+def sharpen_cmd(images, factor):
+ """Sharpens an image."""
+ for image in images:
+ click.echo('Sharpen "%s" by %f' % (image.filename, factor))
+ enhancer = ImageEnhance.Sharpness(image)
+ yield copy_filename(enhancer.enhance(max(1.0, factor)), image)
+
+
+@cli.command('paste')
+@click.option('-l', '--left', default=0, help='Offset from left.')
+@click.option('-r', '--right', default=0, help='Offset from right.')
+@processor
+def paste_cmd(images, left, right):
+ """Pastes the second image on the first image and leaves the rest
+ unchanged.
+ """
+ imageiter = iter(images)
+ image = next(imageiter, None)
+ to_paste = next(imageiter, None)
+
+ if to_paste is None:
+ if image is not None:
+ yield image
+ return
+
+ click.echo('Paste "%s" on "%s"' %
+ (to_paste.filename, image.filename))
+ mask = None
+ if to_paste.mode == 'RGBA' or 'transparency' in to_paste.info:
+ mask = to_paste
+ image.paste(to_paste, (left, right), mask)
+ image.filename += '+' + to_paste.filename
+ yield image
+
+ for image in imageiter:
+ yield image
diff --git a/third_party/python/Click/examples/imagepipe/setup.py b/third_party/python/Click/examples/imagepipe/setup.py
new file mode 100644
index 0000000000..d2d8d9911a
--- /dev/null
+++ b/third_party/python/Click/examples/imagepipe/setup.py
@@ -0,0 +1,16 @@
+from setuptools import setup
+
+setup(
+ name='click-example-imagepipe',
+ version='1.0',
+ py_modules=['imagepipe'],
+ include_package_data=True,
+ install_requires=[
+ 'click',
+ 'pillow',
+ ],
+ entry_points='''
+ [console_scripts]
+ imagepipe=imagepipe:cli
+ ''',
+)
diff --git a/third_party/python/Click/examples/inout/README b/third_party/python/Click/examples/inout/README
new file mode 100644
index 0000000000..6309bc873e
--- /dev/null
+++ b/third_party/python/Click/examples/inout/README
@@ -0,0 +1,10 @@
+$ inout_
+
+ inout is a simple example of an application that
+ can read from files and write to files but also
+ accept input from stdin or write to stdout.
+
+Usage:
+
+ $ pip install --editable .
+ $ inout input_file.txt output_file.txt
diff --git a/third_party/python/Click/examples/inout/inout.py b/third_party/python/Click/examples/inout/inout.py
new file mode 100644
index 0000000000..b93f306629
--- /dev/null
+++ b/third_party/python/Click/examples/inout/inout.py
@@ -0,0 +1,30 @@
+import click
+
+
+@click.command()
+@click.argument('input', type=click.File('rb'), nargs=-1)
+@click.argument('output', type=click.File('wb'))
+def cli(input, output):
+ """This script works similar to the Unix `cat` command but it writes
+ into a specific file (which could be the standard output as denoted by
+ the ``-`` sign).
+
+ \b
+ Copy stdin to stdout:
+ inout - -
+
+ \b
+ Copy foo.txt and bar.txt to stdout:
+ inout foo.txt bar.txt -
+
+ \b
+ Write stdin into the file foo.txt
+ inout - foo.txt
+ """
+ for f in input:
+ while True:
+ chunk = f.read(1024)
+ if not chunk:
+ break
+ output.write(chunk)
+ output.flush()
diff --git a/third_party/python/Click/examples/inout/setup.py b/third_party/python/Click/examples/inout/setup.py
new file mode 100644
index 0000000000..5c613646e2
--- /dev/null
+++ b/third_party/python/Click/examples/inout/setup.py
@@ -0,0 +1,15 @@
+from setuptools import setup
+
+setup(
+ name='click-example-inout',
+ version='0.1',
+ py_modules=['inout'],
+ include_package_data=True,
+ install_requires=[
+ 'click',
+ ],
+ entry_points='''
+ [console_scripts]
+ inout=inout:cli
+ ''',
+)
diff --git a/third_party/python/Click/examples/naval/README b/third_party/python/Click/examples/naval/README
new file mode 100644
index 0000000000..aa289a28e7
--- /dev/null
+++ b/third_party/python/Click/examples/naval/README
@@ -0,0 +1,14 @@
+$ naval_
+
+ naval is a simple example of an application that
+ is ported from the docopt example of the same name.
+
+ Unlike the original this one also runs some code and
+ prints messages and it's command line interface was
+ changed slightly to make more sense with established
+ POSIX semantics.
+
+Usage:
+
+ $ pip install --editable .
+ $ naval --help
diff --git a/third_party/python/Click/examples/naval/naval.py b/third_party/python/Click/examples/naval/naval.py
new file mode 100644
index 0000000000..2d173d84bd
--- /dev/null
+++ b/third_party/python/Click/examples/naval/naval.py
@@ -0,0 +1,70 @@
+import click
+
+
+@click.group()
+@click.version_option()
+def cli():
+ """Naval Fate.
+
+ This is the docopt example adopted to Click but with some actual
+ commands implemented and not just the empty parsing which really
+ is not all that interesting.
+ """
+
+
+@cli.group()
+def ship():
+ """Manages ships."""
+
+
+@ship.command('new')
+@click.argument('name')
+def ship_new(name):
+ """Creates a new ship."""
+ click.echo('Created ship %s' % name)
+
+
+@ship.command('move')
+@click.argument('ship')
+@click.argument('x', type=float)
+@click.argument('y', type=float)
+@click.option('--speed', metavar='KN', default=10,
+ help='Speed in knots.')
+def ship_move(ship, x, y, speed):
+ """Moves SHIP to the new location X,Y."""
+ click.echo('Moving ship %s to %s,%s with speed %s' % (ship, x, y, speed))
+
+
+@ship.command('shoot')
+@click.argument('ship')
+@click.argument('x', type=float)
+@click.argument('y', type=float)
+def ship_shoot(ship, x, y):
+ """Makes SHIP fire to X,Y."""
+ click.echo('Ship %s fires to %s,%s' % (ship, x, y))
+
+
+@cli.group('mine')
+def mine():
+ """Manages mines."""
+
+
+@mine.command('set')
+@click.argument('x', type=float)
+@click.argument('y', type=float)
+@click.option('ty', '--moored', flag_value='moored',
+ default=True,
+ help='Moored (anchored) mine. Default.')
+@click.option('ty', '--drifting', flag_value='drifting',
+ help='Drifting mine.')
+def mine_set(x, y, ty):
+ """Sets a mine at a specific coordinate."""
+ click.echo('Set %s mine at %s,%s' % (ty, x, y))
+
+
+@mine.command('remove')
+@click.argument('x', type=float)
+@click.argument('y', type=float)
+def mine_remove(x, y):
+ """Removes a mine at a specific coordinate."""
+ click.echo('Removed mine at %s,%s' % (x, y))
diff --git a/third_party/python/Click/examples/naval/setup.py b/third_party/python/Click/examples/naval/setup.py
new file mode 100644
index 0000000000..124addf430
--- /dev/null
+++ b/third_party/python/Click/examples/naval/setup.py
@@ -0,0 +1,15 @@
+from setuptools import setup
+
+setup(
+ name='click-example-naval',
+ version='2.0',
+ py_modules=['naval'],
+ include_package_data=True,
+ install_requires=[
+ 'click',
+ ],
+ entry_points='''
+ [console_scripts]
+ naval=naval:cli
+ ''',
+)
diff --git a/third_party/python/Click/examples/repo/README b/third_party/python/Click/examples/repo/README
new file mode 100644
index 0000000000..52d1fa7d0b
--- /dev/null
+++ b/third_party/python/Click/examples/repo/README
@@ -0,0 +1,9 @@
+$ repo_
+
+ repo is a simple example of an application that looks
+ and works similar to hg or git.
+
+Usage:
+
+ $ pip install --editable .
+ $ repo --help
diff --git a/third_party/python/Click/examples/repo/repo.py b/third_party/python/Click/examples/repo/repo.py
new file mode 100644
index 0000000000..2b1992d3e8
--- /dev/null
+++ b/third_party/python/Click/examples/repo/repo.py
@@ -0,0 +1,151 @@
+import os
+import sys
+import posixpath
+
+import click
+
+
+class Repo(object):
+
+ def __init__(self, home):
+ self.home = home
+ self.config = {}
+ self.verbose = False
+
+ def set_config(self, key, value):
+ self.config[key] = value
+ if self.verbose:
+ click.echo(' config[%s] = %s' % (key, value), file=sys.stderr)
+
+ def __repr__(self):
+ return '<Repo %r>' % self.home
+
+
+pass_repo = click.make_pass_decorator(Repo)
+
+
+@click.group()
+@click.option('--repo-home', envvar='REPO_HOME', default='.repo',
+ metavar='PATH', help='Changes the repository folder location.')
+@click.option('--config', nargs=2, multiple=True,
+ metavar='KEY VALUE', help='Overrides a config key/value pair.')
+@click.option('--verbose', '-v', is_flag=True,
+ help='Enables verbose mode.')
+@click.version_option('1.0')
+@click.pass_context
+def cli(ctx, repo_home, config, verbose):
+ """Repo is a command line tool that showcases how to build complex
+ command line interfaces with Click.
+
+ This tool is supposed to look like a distributed version control
+ system to show how something like this can be structured.
+ """
+ # Create a repo object and remember it as as the context object. From
+ # this point onwards other commands can refer to it by using the
+ # @pass_repo decorator.
+ ctx.obj = Repo(os.path.abspath(repo_home))
+ ctx.obj.verbose = verbose
+ for key, value in config:
+ ctx.obj.set_config(key, value)
+
+
+@cli.command()
+@click.argument('src')
+@click.argument('dest', required=False)
+@click.option('--shallow/--deep', default=False,
+ help='Makes a checkout shallow or deep. Deep by default.')
+@click.option('--rev', '-r', default='HEAD',
+ help='Clone a specific revision instead of HEAD.')
+@pass_repo
+def clone(repo, src, dest, shallow, rev):
+ """Clones a repository.
+
+ This will clone the repository at SRC into the folder DEST. If DEST
+ is not provided this will automatically use the last path component
+ of SRC and create that folder.
+ """
+ if dest is None:
+ dest = posixpath.split(src)[-1] or '.'
+ click.echo('Cloning repo %s to %s' % (src, os.path.abspath(dest)))
+ repo.home = dest
+ if shallow:
+ click.echo('Making shallow checkout')
+ click.echo('Checking out revision %s' % rev)
+
+
+@cli.command()
+@click.confirmation_option()
+@pass_repo
+def delete(repo):
+ """Deletes a repository.
+
+ This will throw away the current repository.
+ """
+ click.echo('Destroying repo %s' % repo.home)
+ click.echo('Deleted!')
+
+
+@cli.command()
+@click.option('--username', prompt=True,
+ help='The developer\'s shown username.')
+@click.option('--email', prompt='E-Mail',
+ help='The developer\'s email address')
+@click.password_option(help='The login password.')
+@pass_repo
+def setuser(repo, username, email, password):
+ """Sets the user credentials.
+
+ This will override the current user config.
+ """
+ repo.set_config('username', username)
+ repo.set_config('email', email)
+ repo.set_config('password', '*' * len(password))
+ click.echo('Changed credentials.')
+
+
+@cli.command()
+@click.option('--message', '-m', multiple=True,
+ help='The commit message. If provided multiple times each '
+ 'argument gets converted into a new line.')
+@click.argument('files', nargs=-1, type=click.Path())
+@pass_repo
+def commit(repo, files, message):
+ """Commits outstanding changes.
+
+ Commit changes to the given files into the repository. You will need to
+ "repo push" to push up your changes to other repositories.
+
+ If a list of files is omitted, all changes reported by "repo status"
+ will be committed.
+ """
+ if not message:
+ marker = '# Files to be committed:'
+ hint = ['', '', marker, '#']
+ for file in files:
+ hint.append('# U %s' % file)
+ message = click.edit('\n'.join(hint))
+ if message is None:
+ click.echo('Aborted!')
+ return
+ msg = message.split(marker)[0].rstrip()
+ if not msg:
+ click.echo('Aborted! Empty commit message')
+ return
+ else:
+ msg = '\n'.join(message)
+ click.echo('Files to be committed: %s' % (files,))
+ click.echo('Commit message:\n' + msg)
+
+
+@cli.command(short_help='Copies files.')
+@click.option('--force', is_flag=True,
+ help='forcibly copy over an existing managed file')
+@click.argument('src', nargs=-1, type=click.Path())
+@click.argument('dst', type=click.Path())
+@pass_repo
+def copy(repo, src, dst, force):
+ """Copies one or multiple files to a new location. This copies all
+ files from SRC to DST.
+ """
+ for fn in src:
+ click.echo('Copy from %s -> %s' % (fn, dst))
diff --git a/third_party/python/Click/examples/repo/setup.py b/third_party/python/Click/examples/repo/setup.py
new file mode 100644
index 0000000000..19aab7087a
--- /dev/null
+++ b/third_party/python/Click/examples/repo/setup.py
@@ -0,0 +1,15 @@
+from setuptools import setup
+
+setup(
+ name='click-example-repo',
+ version='0.1',
+ py_modules=['repo'],
+ include_package_data=True,
+ install_requires=[
+ 'click',
+ ],
+ entry_points='''
+ [console_scripts]
+ repo=repo:cli
+ ''',
+)
diff --git a/third_party/python/Click/examples/termui/README b/third_party/python/Click/examples/termui/README
new file mode 100644
index 0000000000..2c9d9dd045
--- /dev/null
+++ b/third_party/python/Click/examples/termui/README
@@ -0,0 +1,9 @@
+$ termui_
+
+ termui showcases the different terminal UI helpers that
+ Click provides.
+
+Usage:
+
+ $ pip install --editable .
+ $ termui --help
diff --git a/third_party/python/Click/examples/termui/setup.py b/third_party/python/Click/examples/termui/setup.py
new file mode 100644
index 0000000000..14558e85c1
--- /dev/null
+++ b/third_party/python/Click/examples/termui/setup.py
@@ -0,0 +1,17 @@
+from setuptools import setup
+
+setup(
+ name='click-example-termui',
+ version='1.0',
+ py_modules=['termui'],
+ include_package_data=True,
+ install_requires=[
+ 'click',
+ # Colorama is only required for Windows.
+ 'colorama',
+ ],
+ entry_points='''
+ [console_scripts]
+ termui=termui:cli
+ ''',
+)
diff --git a/third_party/python/Click/examples/termui/termui.py b/third_party/python/Click/examples/termui/termui.py
new file mode 100644
index 0000000000..793afa419b
--- /dev/null
+++ b/third_party/python/Click/examples/termui/termui.py
@@ -0,0 +1,156 @@
+# coding: utf-8
+import click
+import math
+import time
+import random
+
+try:
+ range_type = xrange
+except NameError:
+ range_type = range
+
+
+@click.group()
+def cli():
+ """This script showcases different terminal UI helpers in Click."""
+ pass
+
+
+@cli.command()
+def colordemo():
+ """Demonstrates ANSI color support."""
+ for color in 'red', 'green', 'blue':
+ click.echo(click.style('I am colored %s' % color, fg=color))
+ click.echo(click.style('I am background colored %s' % color, bg=color))
+
+
+@cli.command()
+def pager():
+ """Demonstrates using the pager."""
+ lines = []
+ for x in range_type(200):
+ lines.append('%s. Hello World!' % click.style(str(x), fg='green'))
+ click.echo_via_pager('\n'.join(lines))
+
+
+@cli.command()
+@click.option('--count', default=8000, type=click.IntRange(1, 100000),
+ help='The number of items to process.')
+def progress(count):
+ """Demonstrates the progress bar."""
+ items = range_type(count)
+
+ def process_slowly(item):
+ time.sleep(0.002 * random.random())
+
+ def filter(items):
+ for item in items:
+ if random.random() > 0.3:
+ yield item
+
+ with click.progressbar(items, label='Processing accounts',
+ fill_char=click.style('#', fg='green')) as bar:
+ for item in bar:
+ process_slowly(item)
+
+ def show_item(item):
+ if item is not None:
+ return 'Item #%d' % item
+
+ with click.progressbar(filter(items), label='Committing transaction',
+ fill_char=click.style('#', fg='yellow'),
+ item_show_func=show_item) as bar:
+ for item in bar:
+ process_slowly(item)
+
+ with click.progressbar(length=count, label='Counting',
+ bar_template='%(label)s %(bar)s | %(info)s',
+ fill_char=click.style(u'█', fg='cyan'),
+ empty_char=' ') as bar:
+ for item in bar:
+ process_slowly(item)
+
+ with click.progressbar(length=count, width=0, show_percent=False,
+ show_eta=False,
+ fill_char=click.style('#', fg='magenta')) as bar:
+ for item in bar:
+ process_slowly(item)
+
+ # 'Non-linear progress bar'
+ steps = [math.exp( x * 1. / 20) - 1 for x in range(20)]
+ count = int(sum(steps))
+ with click.progressbar(length=count, show_percent=False,
+ label='Slowing progress bar',
+ fill_char=click.style(u'█', fg='green')) as bar:
+ for item in steps:
+ time.sleep(item)
+ bar.update(item)
+
+
+@cli.command()
+@click.argument('url')
+def open(url):
+ """Opens a file or URL In the default application."""
+ click.launch(url)
+
+
+@cli.command()
+@click.argument('url')
+def locate(url):
+ """Opens a file or URL In the default application."""
+ click.launch(url, locate=True)
+
+
+@cli.command()
+def edit():
+ """Opens an editor with some text in it."""
+ MARKER = '# Everything below is ignored\n'
+ message = click.edit('\n\n' + MARKER)
+ if message is not None:
+ msg = message.split(MARKER, 1)[0].rstrip('\n')
+ if not msg:
+ click.echo('Empty message!')
+ else:
+ click.echo('Message:\n' + msg)
+ else:
+ click.echo('You did not enter anything!')
+
+
+@cli.command()
+def clear():
+ """Clears the entire screen."""
+ click.clear()
+
+
+@cli.command()
+def pause():
+ """Waits for the user to press a button."""
+ click.pause()
+
+
+@cli.command()
+def menu():
+ """Shows a simple menu."""
+ menu = 'main'
+ while 1:
+ if menu == 'main':
+ click.echo('Main menu:')
+ click.echo(' d: debug menu')
+ click.echo(' q: quit')
+ char = click.getchar()
+ if char == 'd':
+ menu = 'debug'
+ elif char == 'q':
+ menu = 'quit'
+ else:
+ click.echo('Invalid input')
+ elif menu == 'debug':
+ click.echo('Debug menu')
+ click.echo(' b: back')
+ char = click.getchar()
+ if char == 'b':
+ menu = 'main'
+ else:
+ click.echo('Invalid input')
+ elif menu == 'quit':
+ return
diff --git a/third_party/python/Click/examples/validation/README b/third_party/python/Click/examples/validation/README
new file mode 100644
index 0000000000..a69e3f4276
--- /dev/null
+++ b/third_party/python/Click/examples/validation/README
@@ -0,0 +1,12 @@
+$ validation_
+
+ validation is a simple example of an application that
+ performs custom validation of parameters in different
+ ways.
+
+ This example requires Click 2.0 or higher.
+
+Usage:
+
+ $ pip install --editable .
+ $ validation --help
diff --git a/third_party/python/Click/examples/validation/setup.py b/third_party/python/Click/examples/validation/setup.py
new file mode 100644
index 0000000000..9491f709c7
--- /dev/null
+++ b/third_party/python/Click/examples/validation/setup.py
@@ -0,0 +1,15 @@
+from setuptools import setup
+
+setup(
+ name='click-example-validation',
+ version='1.0',
+ py_modules=['validation'],
+ include_package_data=True,
+ install_requires=[
+ 'click',
+ ],
+ entry_points='''
+ [console_scripts]
+ validation=validation:cli
+ ''',
+)
diff --git a/third_party/python/Click/examples/validation/validation.py b/third_party/python/Click/examples/validation/validation.py
new file mode 100644
index 0000000000..00fa0a6001
--- /dev/null
+++ b/third_party/python/Click/examples/validation/validation.py
@@ -0,0 +1,44 @@
+import click
+try:
+ from urllib import parse as urlparse
+except ImportError:
+ import urlparse
+
+
+def validate_count(ctx, param, value):
+ if value < 0 or value % 2 != 0:
+ raise click.BadParameter('Should be a positive, even integer.')
+ return value
+
+
+class URL(click.ParamType):
+ name = 'url'
+
+ def convert(self, value, param, ctx):
+ if not isinstance(value, tuple):
+ value = urlparse.urlparse(value)
+ if value.scheme not in ('http', 'https'):
+ self.fail('invalid URL scheme (%s). Only HTTP URLs are '
+ 'allowed' % value.scheme, param, ctx)
+ return value
+
+
+@click.command()
+@click.option('--count', default=2, callback=validate_count,
+ help='A positive even number.')
+@click.option('--foo', help='A mysterious parameter.')
+@click.option('--url', help='A URL', type=URL())
+@click.version_option()
+def cli(count, foo, url):
+ """Validation.
+
+ This example validates parameters in different ways. It does it
+ through callbacks, through a custom type as well as by validating
+ manually in the function.
+ """
+ if foo is not None and foo != 'wat':
+ raise click.BadParameter('If a value is provided it needs to be the '
+ 'value "wat".', param_hint=['--foo'])
+ click.echo('count: %s' % count)
+ click.echo('foo: %s' % foo)
+ click.echo('url: %s' % repr(url))