Validators¶
1. Feature overview¶
The validators feature allows the programmer to define a validation function that runs after the dataclass is initialized from CLI arguments. The validation functions can raise ValueError’s in cases of invalid configuration being specified which result in calls to argparse.ArgumentParser.error() result in nicely formatted errors for the user including the tools usage message and a non-zero exit code.
An example of the validators feature is shown below:
# price-checker.py
import sys
from cfgclasses import arg, parse_args, validator
from dataclasses import dataclass
ITEMS = {
"shirt": {
"red": 10.45,
"green": 11.45,
"orange": 12.60,
},
"socks": {
"blue": 5.10,
"green": 5.10,
},
}
@dataclass
class Config:
item: str = arg("Item to check the price for", choices=ITEMS)
# Note: we can't specify the choices for colour here because it depends
# on the item
colour: str = arg("Colour of the item")
@validator
def validate_available_color(self):
if self.colour not in ITEMS[self.item]:
raise ValueError(f"{self.item} is not available in {self.colour}")
def run(self):
print(f"£{ITEMS[self.item][self.colour]}")
if __name__ == '__main__':
parse_args(Config, sys.argv[1:]).run()
Example runs:
$ python3 foo.py --item shirt --colour red
£10.45
$ price-checker.py --item shirt --colour blue
usage: foo.py [-h] --item {shirt,socks} --colour COLOUR
foo.py: error: shirt is not available in blue
2. Design ammendments¶
- The following additions are made to the primary design:
A new
@validatordecorator is added to thecfgclassesmodule which accepts a single argument, the validation function. * The decorator sets a marker attribute on the validation function to indicate that it is a cfgclasses validator.A new step is added to the high-level flow after creating the
dataclassinstance: * Check all attributes of the instance (exclusing those starting__) * If the attribute has a marker attribute indicating it is a validator, call the attribute. * Repeat this process for all nesteddataclassinstances. * If any ValueError’s are raised by these methods, use theargparse.ArgumentParser.error()method to output the exception message.