Skip to content

Commit

Permalink
Merge pull request #50 from Julian-Brendel/feature/enum-by-name
Browse files Browse the repository at this point in the history
Add support for Enum by name
  • Loading branch information
mpkocher authored Mar 3, 2022
2 parents 8cac9e4 + ddb6eb3 commit 50d1eda
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 14 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

## Version 4.3.0

- Leverage Pydantic validation for enum choices, enabling more complex use-cases

## Version 4.0.0

- Backward incompatible change for semantics of boolean options
Expand Down Expand Up @@ -27,4 +31,4 @@

## Version 2.3.0

- Internals now leverage `mypy` and can catch more Type related errors
- Internals now leverage `mypy` and can catch more Type related errors
8 changes: 0 additions & 8 deletions pydantic_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,13 +307,6 @@ def _add_pydantic_field_to_parser(
else:
shape_kwargs = {}

choices: T.Optional[T.List[T.Any]] = None
try:
if issubclass(field.type_, Enum):
choices = [x.value for x in field.type_.__members__.values()]
except TypeError:
pass

if field.type_ == bool:
# see comments above
# case #1 and has different semantic meaning with how the tuple[str,str] is
Expand Down Expand Up @@ -342,7 +335,6 @@ def _add_pydantic_field_to_parser(
default=default_value,
dest=field_id,
required=is_required,
choices=choices, # type: ignore
**shape_kwargs, # type: ignore
)

Expand Down
2 changes: 1 addition & 1 deletion pydantic_cli/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "4.2.1"
__version__ = "4.3.0"
54 changes: 54 additions & 0 deletions pydantic_cli/examples/simple_with_enum_by_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from enum import Enum, auto, IntEnum
from typing import Set

from pydantic import BaseModel, Field

from pydantic_cli import run_and_exit


class CastAbleEnum(Enum):
"""Example enum mixin that will cast enum from case-insensitive name"""

@classmethod
def __get_validators__(cls):
yield cls.validate

@classmethod
def validate(cls, v):
try:
lookup = {k.lower(): item.value for k, item in cls.__members__.items()}
return lookup[v.lower()]
except KeyError:
raise ValueError(f"Invalid value {v}. {cls.cli_help()}")

@classmethod
def cli_help(cls) -> str:
return f"Allowed={list(cls.__members__.keys())}"


class Mode(CastAbleEnum, IntEnum):
alpha = auto()
beta = auto()


class State(CastAbleEnum, str, Enum):
RUNNING = "RUNNING"
FAILED = "FAILED"
SUCCESSFUL = "SUCCESSFUL"


class Options(BaseModel):
states: Set[State] = Field(
..., description=f"States to filter on. {State.cli_help()}"
)
mode: Mode = Field(..., description=f"Processing Mode to select. {Mode.cli_help()}")
max_records: int = 100


def example_runner(opts: Options) -> int:
print(f"Mock example running with {opts}")
return 0


if __name__ == "__main__":
run_and_exit(Options, example_runner, description=__doc__, version="0.1.0")
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ def test_simple_02(self):
def test_simple_03(self):
self.run_config(
["-i", "/path/to/file.txt", "-m", "1234", "--log_level", "BAD_LOG_LEVEL"],
exit_code=2,
exit_code=1,
)
2 changes: 1 addition & 1 deletion pydantic_cli/tests/test_examples_simple_with_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ def test_bad_enum_value(self):
"--mode",
"1",
]
self.run_config(args, exit_code=2)
self.run_config(args, exit_code=1)
34 changes: 34 additions & 0 deletions pydantic_cli/tests/test_examples_simple_with_enum_by_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from . import _TestHarness, HarnessConfig

from pydantic_cli.examples.simple_with_enum_by_name import Options, example_runner


class TestExamples(_TestHarness[Options]):

CONFIG = HarnessConfig(Options, example_runner)

def test_simple_01(self):
args = ["--states", "RUNNING", "FAILED", "--mode", "alpha"]
self.run_config(args)

def test_case_insensitive(self):
args = ["--states", "successful", "failed", "--mode", "ALPHA"]
self.run_config(args)

def test_bad_enum_by_value(self):
args = [
"--states",
"RUNNING",
"--mode",
"1",
]
self.run_config(args, exit_code=1)

def test_bad_enum_value(self):
args = [
"--states",
"RUNNING",
"--mode",
"DRAGON",
]
self.run_config(args, exit_code=1)
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ def get_version():
install_requires=_get_requirements("REQUIREMENTS.txt"),
packages=['pydantic_cli', 'pydantic_cli.examples'],
package_data={"pydantic_cli": ["py.typed"]},
tests_require=_get_requirements("REQUIREMENTS-TEST.txt"),
extras_require={"shtab": "shtab>=1.3.1"},
extras_require={"test": _get_requirements("REQUIREMENTS-TEST.txt"),
"shtab": "shtab>=1.3.1"},
zip_safe=False,
classifiers=[
"Programming Language :: Python :: 3",
Expand Down

0 comments on commit 50d1eda

Please sign in to comment.