|
38 | 38 |
|
39 | 39 | class Validator(Protocol[ValueT_contra]):
|
40 | 40 | """
|
41 |
| - Structura type for an option validator. |
| 41 | + Structural type for an option validator. |
| 42 | + There are two ways to signal that a value is invalid: |
| 43 | +
|
| 44 | + - returning :obj:`False` |
| 45 | + - raising any :class:`Exception` |
| 46 | +
|
| 47 | + There are two ways to signal that a value is valid: |
| 48 | +
|
| 49 | + - returning :obj:`True` (as opposed to :obj:`False`) |
| 50 | + - returning :obj:`None` (as opposed to raising an exception) |
| 51 | +
|
| 52 | + In the context of option validation, a :obj:`ValueError` will be raised |
| 53 | + if validation fails: if the validator raised an exception, the |
| 54 | + :obj:`ValueError` is re-raised from it. |
| 55 | +
|
42 | 56 | """
|
43 | 57 |
|
44 |
| - def __call__(self, value: ValueT_contra, /) -> bool: |
| 58 | + def __call__(self, value: ValueT_contra, /) -> bool|None: |
45 | 59 | ...
|
46 | 60 |
|
47 | 61 |
|
@@ -84,9 +98,7 @@ def __new__(
|
84 | 98 |
|
85 | 99 | :param ty: The type of the option.
|
86 | 100 | :param default: The default value for the option.
|
87 |
| - :param validator: A callable that takes a value and returns whether |
88 |
| - it is valid for this option. If not specified, |
89 |
| - defaults to :obj:`None` (no validation) |
| 101 | + :param validator: An optional validator function, see :class:`Validator` |
90 | 102 |
|
91 | 103 | :meta public:
|
92 | 104 | """
|
@@ -151,16 +163,25 @@ def validate(self, value: ValueT) -> None:
|
151 | 163 |
|
152 | 164 | - Checks that the value has the correct type.
|
153 | 165 | - If a validator is specified, checks that the value is valid.
|
| 166 | +
|
| 167 | + If the value is not valid, :obj:`ValueError` is raised. |
154 | 168 | """
|
155 | 169 | validate(value, self.__type)
|
156 | 170 | if (validator := self.__validator) is not None:
|
157 |
| - if not validator(value): |
| 171 | + validator_res: bool|None |
| 172 | + validator_err: Exception|None = None |
| 173 | + try: |
| 174 | + validator_res = validator(value) |
| 175 | + except Exception as e: |
| 176 | + validator_res = False |
| 177 | + validator_err = e |
| 178 | + if validator_res is False: |
158 | 179 | name_str = (
|
159 | 180 | f" {self.__name!r}" if hasattr(self, "__name") else ""
|
160 | 181 | )
|
161 | 182 | raise ValueError(
|
162 | 183 | f"Invalid value for option{name_str}: " f"{value!r}."
|
163 |
| - ) |
| 184 | + ) from validator_err |
164 | 185 |
|
165 | 186 | def reset(self, instance: OptionManager) -> ValueT:
|
166 | 187 | """
|
|
0 commit comments