Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support enum param type #10

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions click_params/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__version__ = '0.2.0'

from .base import BaseParamType, ValidatorParamType, RangeParamType, ListParamType, UnionParamType
from .base import BaseParamType, ValidatorParamType, RangeParamType, ListParamType, UnionParamType, EnumParamType
from .domain import (
DOMAIN, PUBLIC_URL, URL, EMAIL, SLUG, EmailParamType, DomainListParamType, PublicUrlListParamType,
UrlListParamType, EmailListParamType, SlugListParamType
Expand All @@ -21,7 +21,7 @@

__all__ = [
# base
'BaseParamType', 'ValidatorParamType', 'RangeParamType', 'ListParamType', 'UnionParamType',
'BaseParamType', 'ValidatorParamType', 'RangeParamType', 'ListParamType', 'UnionParamType', 'EnumParamType',

# domain
'DOMAIN', 'PUBLIC_URL', 'URL', 'EmailParamType', 'EMAIL', 'SLUG', 'DomainListParamType', 'PublicUrlListParamType',
Expand Down
33 changes: 32 additions & 1 deletion click_params/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Base classes to implement various parameter types"""
from typing import Union, Tuple, Callable, List, Any, Optional, Sequence
import enum
from typing import Union, Tuple, Callable, List, Any, Optional, Sequence, Type

import click

Expand Down Expand Up @@ -139,3 +140,33 @@ def convert(self, value, param, ctx):

def __repr__(self):
return self.name.upper()


class EnumParamType(CustomParamType):
Copy link
Collaborator

@lewoudar lewoudar Mar 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we probably want to implement __repr__, get_missing_message and shell_complete methods from the click.Choice class


def __init__(self, enum_type: Type[enum.Enum], transform_upper: bool = True):
self.enum_type = enum_type
self.transform_upper = transform_upper

def convert(self, value, param, ctx):
if self.transform_upper:
value = value.upper()
try:
return self.enum_type[value]
except KeyError:
raise click.BadParameter(
"Unknown {enum_type} value: {value}".format(
enum_type=self.enum_type.__name__,
value=value
)
)

def get_metavar(self, param):
choices_str = '|'.join([element.name for element in self.enum_type])

# Use curly braces to indicate a required argument.
if param.required and param.param_type_name == 'argument':
return '{{{choices_str}}}'.format(choices_str=choices_str)

# Use square braces to indicate an option or optional argument.
return '[{choices_str}]'.format(choices_str=choices_str)
40 changes: 39 additions & 1 deletion docs/usage/miscellaneous.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,42 @@ Two remarks compared to the last script.

- The order of parameter types in the union is the order click will try to parse the value.
- In the last two examples click was unable to parse because they were neither an integer nor a string from allowed
choices.
choices.

## EnumParamType

Signature: `EnumParamType(enum_type: Type[enum.Enum], transform_upper: bool = True)`

Converts string to an enum value. if `transform_upper` is set to be true, convert name
to uppercase before getting the enum value.

````python
import enum
import click
from click_params import EnumParamType


class MyEnum(enum.Enum):

ONE = "one"
TWO = "two"
THREE = "three"
ONE_ALIAS = ONE


@click.command()
@click.option("-c", "--choice", type=EnumParamType(MyEnum))
def f(choice):
click.echo("You have chosen {choice}".format(choice=choice))
````

````bash
$ python cli.py -c one
You have chosen MyEnum.ONE

$ python cli.py -c three
You have chosen MyEnum.Three

$ python cli.py -c unreal
Error: Unknown MyEnum value: UNREAL
````
49 changes: 48 additions & 1 deletion tests/test_base.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import enum
from fractions import Fraction

import click
import pytest
from validators.utils import validator

from click_params.base import RangeParamType, BaseParamType, ValidatorParamType, ListParamType, UnionParamType
from click_params.base import RangeParamType, BaseParamType, ValidatorParamType, ListParamType, UnionParamType, \
EnumParamType
from click_params.numeric import DECIMAL, FRACTION, COMPLEX


Expand Down Expand Up @@ -218,3 +220,48 @@ def test_parse_expression_unsuccessfully(self, expression, param_types):
union_type = UnionParamType(param_types=param_types)
with pytest.raises(click.BadParameter):
union_type.convert(expression, None, None)


class MyEnum(enum.Enum):

ONE = 1
TWO = 2
THREE = 3
ONE_ALIAS = 1


class TestEnumParamType:
"""Test class EnumParamType"""

@pytest.mark.parametrize(('expression', 'transform_upper', 'value'), [
('one', True, MyEnum.ONE),
('One', True, MyEnum.ONE),
('ONE', True, MyEnum.ONE),
('ONE', False, MyEnum.ONE),
('two', True, MyEnum.TWO),
('TWO', True, MyEnum.TWO),
('TWO', False, MyEnum.TWO),
('three', True, MyEnum.THREE),
('THREE', True, MyEnum.THREE),
('THREE', False, MyEnum.THREE),
('one_alias', True, MyEnum.ONE),
])
def test_parse_expression_successfully(self, expression, transform_upper, value):
enum_type = EnumParamType(MyEnum, transform_upper=transform_upper)
converted_value = enum_type.convert(expression, None, None)
assert type(value) == MyEnum
assert value == converted_value

@pytest.mark.parametrize(('expression', 'transform_upper'), [
('one', False),
('One', False),
('two', False),
('three', False),
('one_alias', False),
('unreal', True),
('unreal', False),
])
def test_parse_expression_unsuccessfully(self, expression, transform_upper):
enum_type = EnumParamType(MyEnum, transform_upper=transform_upper)
with pytest.raises(click.BadParameter):
enum_type.convert(expression, None, None)