pythonpydantic

Initialize pydantic Literal from array of strings


I would like to inizialize a pydantic Literal from an array of strings

from typing import Literal
from pydantic import BaseModel

CLASS_NAME_VALUES = ["primary", "secondary", "success", "danger", "warning", "info", "dark"]
ICON_NAME_VALUES = ["electricity.svg", "water.svg",
                    "internet.svg", "naturalGas.svg", "noCategory.svg"]

class CategoryRequest(BaseModel):
    class_name: Literal[CLASS_NAME_VALUES]
    icon_name: Literal[ICON_NAME_VALUES]

But I am getting the following error

  File "pydantic/main.py", line 252, in pydantic.main.ModelMetaclass.__new__
  File "pydantic/fields.py", line 309, in pydantic.fields.ModelField.infer
  File "pydantic/fields.py", line 271, in pydantic.fields.ModelField.__init__
  File "pydantic/fields.py", line 351, in pydantic.fields.ModelField.prepare
  File "pydantic/fields.py", line 529, in pydantic.fields.ModelField.populate_validators
  File "pydantic/validators.py", line 566, in find_validators
  File "pydantic/validators.py", line 410, in pydantic.validators.make_literal_validator
TypeError: unhashable type: 'list'

Any suggestion?


Solution

  • At runtime, an arbitrary value is allowed as type argument to Literal[...], but type checkers may impose restrictions. For example, mypy permits only one or more literal bool, int, str, bytes, enum values, None and aliases to other Literal types. For example, Literal[3 + 4] or List[(3, 4)] are disallowed.

    As for pydantic, it permits uses values of hashable types in Literal, like tuple. For example, your sample could be rewritten using tuple as:

    from typing import Literal
    from pydantic import BaseModel
    
    CLASS_NAME_VALUES = ("primary", "secondary", "success", "danger", "warning", "info", "dark")
    ICON_NAME_VALUES = ("electricity.svg", "water.svg",
                        "internet.svg", "naturalGas.svg", "noCategory.svg")
    
    
    class CategoryRequest(BaseModel):
        class_name: Literal[CLASS_NAME_VALUES]
        icon_name: Literal[ICON_NAME_VALUES]
    

    But I would strongly advise against using this syntax, but sticking to the canonical one, which is also allowed by the type checkers:

    CLASS_NAME_VALUES = Literal["primary", "secondary", "success", "danger", "warning", "info", "dark"]
    ICON_NAME_VALUES = Literal["electricity.svg", "water.svg",
                        "internet.svg", "naturalGas.svg", "noCategory.svg"]
    
    
    class CategoryRequest(BaseModel):
        class_name: CLASS_NAME_VALUES
        icon_name: ICON_NAME_VALUES