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

Add support for Literal #69

Closed
pavan-uppari opened this issue Sep 17, 2024 · 3 comments
Closed

Add support for Literal #69

pavan-uppari opened this issue Sep 17, 2024 · 3 comments

Comments

@pavan-uppari
Copy link

Currently when used a field with type Literal, it is failing with below traceback

from pydantic_cli import run_and_exit, Cmd
from typing import Literal


class Options(Cmd):
    input_file: Literal[1, 2]

    def run(self) -> None:
        print(f"Mock example running with {self}")


if __name__ == "__main__":
    run_and_exit(Options, description=__doc__, version="0.1.0")
parser: CustomArgumentParser = to_parser_with_overrides(custom_default_values)
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspaces/pydantic-cli/pydantic_cli/__init__.py", line 465, in to_p
    parser = pydantic_class_to_parser(
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspaces/pydantic-cli/pydantic_cli/__init__.py", line 256, in pydantic_class_to_parser
    p = _add_pydantic_class_to_parser(p0, cls, default_value_override)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspaces/pydantic-cli/pydantic_cli/__init__.py", line 236, in _add_pydantic_class_to_parser
    _add_pydantic_field_to_parser(p, ix, field, override_value=override_value)
  File "/workspaces/pydantic-cli/pydantic_cli/__init__.py", line 208, in _add_pydantic_field_to_parser
    type_desc = __to_type_description(
                ^^^^^^^^^^^^^^^^^^^^^^
  File "/workspaces/pydantic-cli/pydantic_cli/__init__.py", line 113, in __to_type_description
    t = "" if field_type is NOT_PROVIDED else __try_to_pretty_type(field_type)
                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspaces/pydantic-cli/pydantic_cli/__init__.py", line 93, in __try_to_pretty_type
    name = "|".join(map(lambda x: x.__name__, args))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspaces/pydantic-cli/pydantic_cli/__init__.py", line 93, in <lambda>
    name = "|".join(map(lambda x: x.__name__, args))

Literal are like Enums only, when Enums are been supported, Literal can be supported too

@pavan-uppari
Copy link
Author

Everything works fine, only issue is with prettifying type at __try_to_pretty_type, if that method is fixed, then Literal can be supported. Need to find a better way to prettify type

@mpkocher
Copy link
Owner

mpkocher commented Sep 18, 2024

I believe the fundamental solution to this is to delegate this to Pydantic and call repr on FieldInfo.

https://github.com/pydantic/pydantic/blob/main/pydantic/_internal/_repr.py#L82

There's a few notes:

  1. pydantic_cli is a passthrough layer.
  2. Fundamentally solves the problem with a principled approach (but it is using an internal Pydantic API)
  3. Sacrifices how the info is displayed. Yields a very opinionated output.
  4. Might be able to leverage rich (because Pydantic supports it)
  5. Improvements to FieldInfo should probably be done within Pydantic.

For example:

from pydantic import PositiveInt, Field
from enum import Enum, auto
from pydantic_cli import run_and_exit, Cmd
from typing import Literal, Final

class State(Enum):
    a = auto()
    b = auto()


class Options(Cmd):
    input_file: Literal[1, 2]
    alpha: Final[int]
    beta: PositiveInt
    state: State = Field(..., description="The State of the Job to filter on.")
    name: None | str

    def run(self) -> None:
        print(f"Mock example running with {self}")


if __name__ == "__main__":
    run_and_exit(Options, description=__doc__, version="0.1.0")

With --help yields:

usage: example.py --input_file INPUT_FILE --alpha ALPHA --beta BETA --state STATE --name NAME [--help] [--version]

options:
  --input_file INPUT_FILE
                        FieldInfo(annotation=Literal[1, 2], required=True)
  --alpha ALPHA         FieldInfo(annotation=int, required=True, frozen=True)
  --beta BETA           FieldInfo(annotation=int, required=True, metadata=[Gt(gt=0)])
  --state STATE         FieldInfo(annotation=State, required=True, description='The State of the Job to filter on.')
  --name NAME           FieldInfo(annotation=Union[NoneType, str], required=True)
  --help                Print Help and Exit
  --version             show program's version number and exit

@mpkocher
Copy link
Owner

@pavan-uppari Thank you for your time and feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants