43 lines
1.4 KiB
Python
43 lines
1.4 KiB
Python
"""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).
|
|
"""
|
|
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}')
|