Coerce a provided input value to the declared type.
(
name: str, value: Any, input_def: dict[str, Any]
)
| 1261 | |
| 1262 | @staticmethod |
| 1263 | def _coerce_input( |
| 1264 | name: str, value: Any, input_def: dict[str, Any] |
| 1265 | ) -> Any: |
| 1266 | """Coerce a provided input value to the declared type.""" |
| 1267 | input_type = input_def.get("type", "string") |
| 1268 | enum_values = input_def.get("enum") |
| 1269 | |
| 1270 | if input_type == "number": |
| 1271 | # Reject bools explicitly: ``bool`` is a subclass of ``int`` so |
| 1272 | # ``float(True)`` succeeds and would silently coerce a YAML |
| 1273 | # authoring mistake like ``type: number`` + ``default: true`` |
| 1274 | # into ``1``. Fail fast instead. |
| 1275 | if isinstance(value, bool): |
| 1276 | msg = f"Input {name!r} expected a number, got {value!r}." |
| 1277 | raise ValueError(msg) |
| 1278 | try: |
| 1279 | value = float(value) |
| 1280 | if value == int(value): |
| 1281 | value = int(value) |
| 1282 | except (ValueError, TypeError, OverflowError): |
| 1283 | # OverflowError: `int(value)` raises it for an infinite float |
| 1284 | # (e.g. a `default: .inf` authoring mistake), which would |
| 1285 | # otherwise escape validate_workflow's `except ValueError` and |
| 1286 | # break its "return errors, never raise" contract. Surface it as |
| 1287 | # the same clean "expected a number" error as NaN does. |
| 1288 | msg = f"Input {name!r} expected a number, got {value!r}." |
| 1289 | raise ValueError(msg) from None |
| 1290 | elif input_type == "boolean": |
| 1291 | if isinstance(value, str): |
| 1292 | if value.lower() in ("true", "1", "yes"): |
| 1293 | value = True |
| 1294 | elif value.lower() in ("false", "0", "no"): |
| 1295 | value = False |
| 1296 | else: |
| 1297 | msg = f"Input {name!r} expected a boolean, got {value!r}." |
| 1298 | raise ValueError(msg) |
| 1299 | elif not isinstance(value, bool): |
| 1300 | msg = f"Input {name!r} expected a boolean, got {value!r}." |
| 1301 | raise ValueError(msg) |
| 1302 | elif input_type == "string": |
| 1303 | # Without this, ``type: string`` accepts any Python value |
| 1304 | # (numbers, lists, dicts) because nothing else rejects it — |
| 1305 | # YAML ``default: 5`` would slip through. Require an actual |
| 1306 | # string so authoring mistakes fail at resolve time. |
| 1307 | if not isinstance(value, str): |
| 1308 | msg = f"Input {name!r} expected a string, got {value!r}." |
| 1309 | raise ValueError(msg) |
| 1310 | |
| 1311 | if enum_values is not None and value not in enum_values: |
| 1312 | msg = ( |
| 1313 | f"Input {name!r} value {value!r} not in allowed " |
| 1314 | f"values: {enum_values}." |
| 1315 | ) |
| 1316 | raise ValueError(msg) |
| 1317 | |
| 1318 | return value |
| 1319 | |
| 1320 | def list_runs(self) -> list[dict[str, Any]]: |