omegaconf icon indicating copy to clipboard operation
omegaconf copied to clipboard

Allow the use of typing.Literal as a pythonic alternative to Enums

Open tmke8 opened this issue 4 years ago • 9 comments

Is your feature request related to a problem? Please describe.

Enums are a bit awkward to define in Python. On the other hand, using strings to represent a set of choices is very common in Python code.

Describe the solution you'd like

from typing import Literal  # before Python 3.8, import from `typing_extensions`

@dataclass
class SvmConfig:
  kernel: Literal["linear", "poly", "rbf"] = "linear"

config: SvmConfig = OmegaConf.structured(SvmConfig(kernel="rbf"))

config.kernel = "poly"  # ok!
config.kernel = "exponential"  # error!

Describe alternatives you've considered

Enums, see above.

Additional context

My main motivation is to use this with Hydra.

tmke8 avatar Oct 27 '20 23:10 tmke8

This functionality is covered well by enums. I am open to adding support for it but it's low pri. One implementation idea is to convert it to an Enum internally and use EnumNode (you can define enums dynamically). Feel free to send a pull request if you would like this supported.

omry avatar Oct 28 '20 07:10 omry

Sorry, linked to wrong issue.

omry avatar Feb 03 '21 01:02 omry

Upvoting the issue - the main use case is configuration serialization. When using Literal, pickling works without a hitch. When using Enum, we must have the appropriate class available in the scope where we unpickle the config. There are workarounds, but the easiest one is to just assert in runtime instead of work with an Enum... which defeats the purpose imo. Thanks!

yuvsier avatar Oct 02 '22 12:10 yuvsier

how do use Enum instead of Litreral?

ramshi236 avatar Nov 14 '22 13:11 ramshi236

@ramshi236 See the docs:

  • https://omegaconf.readthedocs.io/en/2.2_branch/usage.html?highlight=enum#from-a-dictionary
  • https://omegaconf.readthedocs.io/en/2.2_branch/structured_config.html?highlight=enum#simple-types

Jasha10 avatar Nov 14 '22 17:11 Jasha10

Edit: oops I thought I was posting on the hydra repo, I think my question is still valid for OmegaConf but if not I can move this over to a hydra issue

Does this also work if you're using the dataclass to define a schema (as described here)? It doesn't seem to actually do any type checking for me; i.e. given an app script:

# app.py
import hydra
from dataclasses import dataclass

from hydra.core.config_store import ConfigStore
from omegaconf import OmegaConf, MISSING
from enum import Enum


OPTIONS = ["model_a", "model_b", "model_c"]
OPTIONS_ENUM = Enum("OPTIONS", {k: k for k in OPTIONS})

@dataclass
class Config:
    lr: float = MISSING
    model: OPTIONS_ENUM = MISSING

cs = ConfigStore.instance()
cs.store(name="Config", node=Config)


@hydra.main(config_path="./", config_name="config", version_base='1.3')
def my_main(cfg: Config) -> None:
    print(OmegaConf.to_yaml(cfg))


if __name__ == "__main__":
    my_main()

and a config file:

# config.yaml
lr: 0.1
model: should_throw_exception

this will happily run:

$ python app.py
lr: 0.1
model: should_throw_exception

busycalibrating avatar Mar 25 '23 17:03 busycalibrating

There is nothing linking your config file with your config schema. Check the last tutorial page in the Structured Configs tutorial.

omry avatar Apr 19 '23 08:04 omry

Looks like there's a PR for this https://github.com/omry/omegaconf/pull/865 that's gone stale. I'd love to add a vote for this to get reviewed and merged as it would be useful for me as well.

daturkel avatar Sep 27 '23 19:09 daturkel