Skip to content

Commit

Permalink
Add case_sensitive=False as an option to Choice
Browse files Browse the repository at this point in the history
Adds a test that validates both the new and the old behavior WRT case
sensitivity. Applies lowercasing *after* token normalization, in case
someone has a `token_normalize_func` which is, itself, case-sensitive.

Closes pallets#569
  • Loading branch information
sirosen authored and zacbir committed May 15, 2018
1 parent bd59717 commit 138a6e3
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 6 deletions.
28 changes: 22 additions & 6 deletions click/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,15 @@ class Choice(ParamType):
generators) may lead to surprising results.
See :ref:`choice-opts` for an example.
:param case_sensitive: Set to false to make choices case insensitive.
Defaults to true.
"""
name = 'choice'

def __init__(self, choices):
def __init__(self, choices, case_sensitive=True):
self.choices = choices
self.case_sensitive = case_sensitive

def get_metavar(self, param):
return '[%s]' % '|'.join(self.choices)
Expand All @@ -150,13 +154,25 @@ def convert(self, value, param, ctx):
if value in self.choices:
return value

# Match through normalization
# Match through normalization and case sensitivity
# first do token_normalize_func, then lowercase
# preserve original `value` to produce an accurate message in
# `self.fail`
normed_value = value
normed_choices = self.choices

if ctx is not None and \
ctx.token_normalize_func is not None:
value = ctx.token_normalize_func(value)
for choice in self.choices:
if ctx.token_normalize_func(choice) == value:
return choice
normed_value = ctx.token_normalize_func(value)
normed_choices = [ctx.token_normalize_func(choice) for choice in
self.choices]

if not self.case_sensitive:
normed_value = normed_value.lower()
normed_choices = [choice.lower() for choice in normed_choices]

if normed_value in normed_choices:
return normed_value

self.fail('invalid choice: %s. (choose from %s)' %
(value, ', '.join(self.choices)), param, ctx)
Expand Down
31 changes: 31 additions & 0 deletions tests/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,37 @@ def cmd(foo):
in result.output


def test_case_insensitive_choice(runner):
@click.command()
@click.option('--foo', type=click.Choice(
['Orange', 'Apple'], case_sensitive=False))
def cmd(foo):
click.echo(foo)

result = runner.invoke(cmd, ['--foo', 'apple'])
assert result.exit_code == 0

result = runner.invoke(cmd, ['--foo', 'oRANGe'])
assert result.exit_code == 0

result = runner.invoke(cmd, ['--foo', 'Apple'])
assert result.exit_code == 0

@click.command()
@click.option('--foo', type=click.Choice(['Orange', 'Apple']))
def cmd2(foo):
click.echo(foo)

result = runner.invoke(cmd2, ['--foo', 'apple'])
assert result.exit_code == 2

result = runner.invoke(cmd2, ['--foo', 'oRANGe'])
assert result.exit_code == 2

result = runner.invoke(cmd2, ['--foo', 'Apple'])
assert result.exit_code == 0


def test_multiline_help(runner):
@click.command()
@click.option('--foo', help="""
Expand Down

0 comments on commit 138a6e3

Please sign in to comment.