from typing import Union as _Union
__all__ = ["safe_eval_number"]
[docs]def safe_eval_number(s: _Union[float, int, str]) -> float:
"""Convert 's' to a number. This supports normal floats,
but also simple maths expressions like 1/1.2,
plus anything that ends with a "%" is recognised
as a percentage
Examples
--------
safe_eval_number(0.3) -> 0.3
safe_eval_number("5%") -> 0.05
safe_eval_number("1/4") -> 0.25
safe_eval_number("(30+100)%) -> 1.3
"""
if isinstance(s, float) or isinstance(s, int):
return s
if not isinstance(s, str):
s = str(s)
try:
# this is about as save as eval gets in python
# (and we allow math so that "sqrt(3)" and "2 * pi" will work)
import math
x = eval(s, {"__builtins__": math}, {})
v = float(x)
if v.is_integer():
return int(x)
else:
return v
except Exception:
pass
if isinstance(s, str):
s = s.strip()
if s.endswith("%"):
try:
return safe_eval_number(s[0:-1]) / 100.0
except Exception:
pass
raise ValueError(f"Cannot interpret '{s}' as a float")