"""Safe coercion of JSON-transported values into typed OPC write values. Fixes 1879 BUG-1 (bool('false') is True in Python) and BUG-2 (uncaught TypeError/ValueError from int/float on bad input). """ _FALSE_STRINGS = {'false', '0', 'no', 'off', ''} class ConversionError(ValueError): """Raised when a raw value cannot be coerced to the requested type.""" def _to_bool(raw) -> bool: if isinstance(raw, bool): return raw if isinstance(raw, (int, float)): return bool(raw) return str(raw).strip().lower() not in _FALSE_STRINGS def to_typed_value(raw, data_type: str): """Coerce raw (often a JSON string) to the given data_type. data_type: 'boolean' | 'int' | 'float' | 'double' | 'string'. Raises ConversionError on any invalid input (never a bare TypeError). """ if not isinstance(data_type, str): raise ConversionError(f'data_type must be a string, got {type(data_type).__name__}') dtype = data_type.strip().lower() try: if dtype in ('bool', 'boolean'): return _to_bool(raw) if dtype in ('int', 'integer'): if isinstance(raw, str) and ('.' in raw or raw.strip() == ''): raise ConversionError(f'invalid int: {raw!r}') return int(raw) if dtype in ('float', 'double', 'real'): return float(raw) if dtype in ('str', 'string'): return str(raw) except (TypeError, ValueError) as e: raise ConversionError(f'cannot convert {raw!r} to {data_type}: {e}') from e raise ConversionError(f'unknown data_type: {data_type!r}')