util.py 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546
  1. # Python repr() for complex numbers, and a little broken for floats.
  2. # Until such time it is fixed, we'll do a better.
  3. # More could be done here though.
  4. from typing import Any
  5. from math import copysign
  6. def is_negative_zero(n: float) -> bool:
  7. """Returns true if n is -0.0"""
  8. return n == 0.0 and copysign(1, n) == -1
  9. def better_repr(v: Any) -> Any:
  10. """Work around Python's unorthogonal and unhelpful repr() for primitive float
  11. and complex."""
  12. if isinstance(v, float):
  13. # float values 'nan' and 'inf' are not directly
  14. # representable in Python before Python 3.5. In Python 3.5
  15. # it is accessible via a library constant math.inf. We
  16. # will canonicalize representation of these value as
  17. # float('nan') and float('inf')
  18. if str(v) in frozenset(["nan", "-nan", "inf", "-inf"]):
  19. return "float('%s')" % v
  20. elif is_negative_zero(v):
  21. return "-0.0"
  22. return repr(v)
  23. elif isinstance(v, complex):
  24. real = better_repr(v.real)
  25. imag = better_repr(v.imag)
  26. # FIXME: we could probably use repr() in most cases
  27. # sort out when that's possible.
  28. # The below is however round-tripable, and Python's repr() isn't.
  29. return "complex(%s, %s)" % (real, imag)
  30. elif isinstance(v, tuple):
  31. if len(v) == 1:
  32. return "(%s,)" % better_repr(v[0])
  33. return "(%s)" % ", ".join(better_repr(i) for i in v)
  34. elif isinstance(v, list):
  35. if len(v) == 1:
  36. return "[%s,]" % better_repr(v[0])
  37. return "[%s]" % ", ".join(better_repr(i) for i in v)
  38. # TODO: elif deal with sets and dicts
  39. else:
  40. return repr(v)