eladrich / pyrallis

Pyrallis is a framework for structured configuration parsing from both cmd and files. Simply define your desired configuration structure as a dataclass and let pyrallis do the rest!
https://eladrich.github.io/pyrallis/
MIT License
198 stars 7 forks source link

Configurable Help Text #27

Open siddk opened 1 year ago

siddk commented 1 year ago

This is an awesome project! One small enhancement - it would be really nice to support "configurable" population orders for help text beyond the default (look for docstring above argument, then below, then inline).

A common pattern for many codebases I'm working with ends up looking like:

@dataclass
class Config:
    # fmt: off
    mode: str = "train"                         # Mode in < train | evaluate | visualize >

    # Data / Preprocessing Parameters
    data: Path = Path("data/")                  # Path to data directory with MNIST images / labels
    download: bool = True                       # Whether to download MNIST data (if doesn't already exist)

    # Model Parameters
    hidden_dim: int = 256                       # Hidden Layer Dimensionality for 2-Layer MLP
    # fmt: on

@pyrallis.wrap()
def main(cfg: Config) -> None:
    print(cfg)

Because of style directives (fmt: off) and "factored" configuration arguments, the help text ends up populated as:

usage: main.py [-h] [--config_path str] [--mode str] [--data str] [--download str] [--hidden_dim str]

optional arguments:
  -h, --help         show this help message and exit
  --config_path str  Path for a config file to parse with pyrallis

Config:

  --mode str         fmt: off
  --data str         Data / Preprocessing Parameters
  --download str     Whether to download MNIST data (if doesn't already exist)
  --hidden_dim str   Model Parameters

Notably the mode and data and hidden_dim parameters are all populated incorrectly, when ideally they'd be populated inline (e.g. `mode --> "Mode in < train | evaluate | visualize >").

Happy to PR with a fix if that would be easy! I think we just need to pass an additional argument to the initializer in dataclass_wrapper.py!